From af8bc7df360ef185999591b9f32715432ab65814 Mon Sep 17 00:00:00 2001 From: Jonathan Giddy Date: Wed, 24 Dec 2025 12:30:54 +0000 Subject: [PATCH] Support setting suffixes for identifiers --- README.md | 65 +++--- src/context.rs | 7 +- src/lib.rs | 92 +++++---- tests/compile/function.rs | 9 +- tests/compile/mod.expanded.rs | 373 +++------------------------------- tests/compile/typle_args.rs | 24 +-- 6 files changed, 130 insertions(+), 440 deletions(-) diff --git a/README.md b/README.md index 5b837b1..413db9d 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,14 @@ For example, to define a function to zip a pair of tuples into a tuple of pairs: ```rust #[typle(Tuple for 0..=12)] -pub fn zip( - a: A, - b: B, -) -> (typle! { +pub fn zip(a: A, b: B) -> (typle! { i in .. => (A<{i}>, B<{i}>) -}) -{ +}) { (typle! { i in .. => (a[[i]], b[[i]]) }) } + ``` The types `A` and `B` are generic but are constrained to be tuples. The tuples @@ -59,46 +56,36 @@ struct MultipleHandlers { handlers: T, } -#[typle(Tuple for 0..=3)] +#[typle(Tuple for 1..=3)] impl HandleStuff for MultipleHandlers where - T: Tuple, // `T` is a tuple with 0 to 3 components. - T<_>: HandleStuff, // All components implement `HandleStuff`. + T: Tuple, // `T` is a tuple with 1 to 3 components. + T<_>: HandleStuff, // All components implement `HandleStuff`. { + // Return a tuple of output from each handler applied to the same input. type Output = (typle! {i in .. => T<{i}>::Output}); - // Return a tuple of output from each handler applied to the same input. fn handle_stuff(&self, input: Input) -> Self::Output { - if typle_const!(T::LEN == 0) { - () - } else { - ( - typle! { - i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone()) - }, - // Avoid expensive clone for the last handler. - self.handlers[[T::LAST]].handle_stuff(input), - ) - } + ( + typle! { + i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone()) + }, + // Avoid expensive clone for the last handler. + self.handlers[[T::LAST]].handle_stuff(input), + ) } } ``` This generates the implementations ```rust -impl HandleStuff for MultipleHandlers<()> { - type Output = (); - fn handle_stuff(&self, input: Input) -> Self::Output { - { () } - } -} impl HandleStuff for MultipleHandlers<(T0,)> where T0: HandleStuff, { type Output = (T0::Output,); fn handle_stuff(&self, input: Input) -> Self::Output { - { (self.handlers.0.handle_stuff(input),) } + (self.handlers.0.handle_stuff(input),) } } impl HandleStuff for MultipleHandlers<(T0, T1)> @@ -108,12 +95,10 @@ where { type Output = (T0::Output, T1::Output); fn handle_stuff(&self, input: Input) -> Self::Output { - { - ( - self.handlers.0.handle_stuff(input.clone()), - self.handlers.1.handle_stuff(input), - ) - } + ( + self.handlers.0.handle_stuff(input.clone()), + self.handlers.1.handle_stuff(input), + ) } } impl HandleStuff for MultipleHandlers<(T0, T1, T2)> @@ -124,13 +109,11 @@ where { type Output = (T0::Output, T1::Output, T2::Output); fn handle_stuff(&self, input: Input) -> Self::Output { - { - ( - self.handlers.0.handle_stuff(input.clone()), - self.handlers.1.handle_stuff(input.clone()), - self.handlers.2.handle_stuff(input), - ) - } + ( + self.handlers.0.handle_stuff(input.clone()), + self.handlers.1.handle_stuff(input.clone()), + self.handlers.2.handle_stuff(input), + ) } } ``` diff --git a/src/context.rs b/src/context.rs index 68a3ea1..8dad60e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -200,7 +200,12 @@ impl TypleContext { } result = Some(Typle::Generic(Rc::new( (0..self.typle_len.unwrap_or(self.typle_macro.max_len)) - .map(|i| format!("{}{}", &type_ident, i)) + .map(|i| match self.typle_macro.suffixes.get(i) { + Some(suffix) => { + format!("{}{}", &type_ident, suffix) + } + None => format!("{}{}", &type_ident, i), + }) .collect(), ))); continue; diff --git a/src/lib.rs b/src/lib.rs index 0921cd5..86ceadb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -190,7 +190,7 @@ //! # Conditionals //! //! The `typle!` macro accepts an `if` statement with an optional `else` clause. -//! If there is no `else` clause the macro filters out components that do not match +//! If there is no `else` clause the macro filters out elements that do not match //! the condition. //! //! The `typle_attr_if` attribute allows conditional inclusion of attributes. It works similarly to @@ -714,7 +714,7 @@ use context::TypleContext; use proc_macro2::{Ident, TokenStream, TokenTree}; use quote::ToTokens; use syn::spanned::Spanned as _; -use syn::{Error, Item, Type, TypeNever}; +use syn::{Error, Expr, Item, Type, TypeNever}; #[doc(hidden)] #[proc_macro_attribute] @@ -756,6 +756,7 @@ struct TypleMacro { min_len: usize, max_len: usize, never_type: Type, + suffixes: Vec, } impl TryFrom for TypleMacro { @@ -767,6 +768,7 @@ impl TryFrom for TypleMacro { let mut never_type = Type::Never(TypeNever { bang_token: syn::token::Not::default(), }); + let mut suffixes = Vec::new(); let mut args_iter = args.into_iter(); // Tuple let Some(TokenTree::Ident(trait_ident)) = args_iter.next() else { @@ -780,24 +782,27 @@ impl TryFrom for TypleMacro { } } - let mut range_tokens = TokenStream::new(); - let mut never_tokens = Vec::new(); - let mut comma_seen = false; - for token in args_iter { - if comma_seen { - never_tokens.push(token); - } else { - if let TokenTree::Punct(punct) = &token { - if punct.as_char() == ',' { - comma_seen = true; - continue; - } + let mut sections = Vec::new(); + let mut section = Vec::new(); + for tt in args_iter { + match tt { + TokenTree::Punct(punct) if punct.as_char() == ',' => { + sections.push(std::mem::take(&mut section)); + } + tt => { + section.push(tt); } - range_tokens.extend([token]); } } + if !section.is_empty() { + sections.push(section); + } + let mut sections = sections.into_iter(); + let Some(range_tokens) = sections.next() else { + return Err(Error::new(default_span, "expected range")); + }; // 2..=12 - let range = syn::parse2::(range_tokens)?; + let range = syn::parse2::(range_tokens.into_iter().collect())?; let min = range .start .as_ref() @@ -822,32 +827,51 @@ impl TryFrom for TypleMacro { if max < min { return Err(Error::new(range.span(), "range contains no values")); } - if !never_tokens.is_empty() { - // never=some::Type - let mut iter = never_tokens.into_iter(); - let Some(TokenTree::Ident(ident)) = iter.next() else { + for section in sections { + let mut tokens = section.into_iter(); + let Some(TokenTree::Ident(ident)) = tokens.next() else { return Err(Error::new(default_span, "expected identifier after comma")); }; - if ident != "never" { - return Err(Error::new( - default_span, - "expected identifier 'never' after comma", - )); - } - let Some(TokenTree::Punct(punct)) = iter.next() else { - return Err(Error::new(default_span, "expected equals after never")); - }; - if punct.as_char() != '=' { - return Err(Error::new(default_span, "expected equals after never")); + if ident == "never" { + let Some(TokenTree::Punct(punct)) = tokens.next() else { + return Err(Error::new(default_span, "expected equals after never")); + }; + if punct.as_char() != '=' { + return Err(Error::new(default_span, "expected equals after never")); + } + never_type = syn::parse2::(tokens.collect())?; + } else if ident == "suffixes" { + let Some(TokenTree::Punct(punct)) = tokens.next() else { + return Err(Error::new(default_span, "expected equals after never")); + }; + if punct.as_char() != '=' { + return Err(Error::new(default_span, "expected equals after never")); + } + + let expr = syn::parse2::(tokens.collect())?; + let Expr::Array(array) = expr else { + return Err(Error::new(expr.span(), "expected array")); + }; + for elem in array.elems { + let Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(suffix), + .. + }) = elem + else { + return Err(Error::new(elem.span(), "expected string")); + }; + suffixes.push(suffix.value()); + } + } else { + return Err(Error::new(ident.span(), "unexpected identifier")); } - let type_stream = iter.collect(); - never_type = syn::parse2::(type_stream)?; } Ok(TypleMacro { trait_ident, min_len: min, max_len: max, never_type, + suffixes, }) } } @@ -1005,7 +1029,7 @@ pub fn typle_fold(item: proc_macro::TokenStream) -> proc_macro::TokenStream { /// Create variants in an enum. /// -/// In an enum, the `typle_variant` macro allows the creation of variants for each component. +/// In an enum, the `typle_variant` macro allows the creation of variants for each element. /// /// A variant is created for each index in the range provided. /// diff --git a/tests/compile/function.rs b/tests/compile/function.rs index 9731271..6abd1ad 100644 --- a/tests/compile/function.rs +++ b/tests/compile/function.rs @@ -35,14 +35,13 @@ fn heapify(params: T) -> (typle!(i in .. => Box>)) { (typle!(i in .. => Box::new(params[[i]]))) } -#[rustfmt::skip] -#[typle(Tuple for 0..=12)] +#[allow(non_camel_case_types)] +#[typle(Tuple for 0..=5, suffixes=["", "01", "x", "three"])] #[typle_attr_if(Tuple::LEN == 0, allow(unused_assignments))] pub fn zip( first: A, - second: B -) -> (typle!(i in ..Tuple::LEN => (A<{i}>, B<{i}>))) -{ + second: B, +) -> (typle!(i in ..Tuple::LEN => (A<{i}>, B<{i}>))) { (typle!(i in ..Tuple::LEN => (first[[i]], second[[i]]))) } diff --git a/tests/compile/mod.expanded.rs b/tests/compile/mod.expanded.rs index 7164478..2a0b76d 100644 --- a/tests/compile/mod.expanded.rs +++ b/tests/compile/mod.expanded.rs @@ -942,7 +942,7 @@ pub mod function { } impl _typle_fn_zip for ((), ()) { type Return = (); - #[rustfmt::skip] + #[allow(non_camel_case_types)] #[allow(unused_assignments)] fn apply(self) -> Self::Return { #[allow(unused_variables)] @@ -950,34 +950,34 @@ pub mod function { { () } } } - impl _typle_fn_zip for ((A0,), (B0,)) { - type Return = ((A0, B0),); - #[rustfmt::skip] + impl _typle_fn_zip for ((A,), (B,)) { + type Return = ((A, B),); + #[allow(non_camel_case_types)] fn apply(self) -> Self::Return { let (first, second) = self; { ((first.0, second.0),) } } } - impl _typle_fn_zip for ((A0, A1), (B0, B1)) { - type Return = ((A0, B0), (A1, B1)); - #[rustfmt::skip] + impl _typle_fn_zip for ((A, A01), (B, B01)) { + type Return = ((A, B), (A01, B01)); + #[allow(non_camel_case_types)] fn apply(self) -> Self::Return { let (first, second) = self; { ((first.0, second.0), (first.1, second.1)) } } } - impl _typle_fn_zip for ((A0, A1, A2), (B0, B1, B2)) { - type Return = ((A0, B0), (A1, B1), (A2, B2)); - #[rustfmt::skip] + impl _typle_fn_zip for ((A, A01, Ax), (B, B01, Bx)) { + type Return = ((A, B), (A01, B01), (Ax, Bx)); + #[allow(non_camel_case_types)] fn apply(self) -> Self::Return { let (first, second) = self; { ((first.0, second.0), (first.1, second.1), (first.2, second.2)) } } } - impl _typle_fn_zip - for ((A0, A1, A2, A3), (B0, B1, B2, B3)) { - type Return = ((A0, B0), (A1, B1), (A2, B2), (A3, B3)); - #[rustfmt::skip] + impl _typle_fn_zip + for ((A, A01, Ax, Athree), (B, B01, Bx, Bthree)) { + type Return = ((A, B), (A01, B01), (Ax, Bx), (Athree, Bthree)); + #[allow(non_camel_case_types)] fn apply(self) -> Self::Return { let (first, second) = self; { @@ -990,10 +990,10 @@ pub mod function { } } } - impl _typle_fn_zip - for ((A0, A1, A2, A3, A4), (B0, B1, B2, B3, B4)) { - type Return = ((A0, B0), (A1, B1), (A2, B2), (A3, B3), (A4, B4)); - #[rustfmt::skip] + impl _typle_fn_zip + for ((A, A01, Ax, Athree, A4), (B, B01, Bx, Bthree, B4)) { + type Return = ((A, B), (A01, B01), (Ax, Bx), (Athree, Bthree), (A4, B4)); + #[allow(non_camel_case_types)] fn apply(self) -> Self::Return { let (first, second) = self; { @@ -1007,313 +1007,6 @@ pub mod function { } } } - impl _typle_fn_zip - for ((A0, A1, A2, A3, A4, A5), (B0, B1, B2, B3, B4, B5)) { - type Return = ((A0, B0), (A1, B1), (A2, B2), (A3, B3), (A4, B4), (A5, B5)); - #[rustfmt::skip] - fn apply(self) -> Self::Return { - let (first, second) = self; - { - ( - (first.0, second.0), - (first.1, second.1), - (first.2, second.2), - (first.3, second.3), - (first.4, second.4), - (first.5, second.5), - ) - } - } - } - impl _typle_fn_zip - for ((A0, A1, A2, A3, A4, A5, A6), (B0, B1, B2, B3, B4, B5, B6)) { - type Return = ( - (A0, B0), - (A1, B1), - (A2, B2), - (A3, B3), - (A4, B4), - (A5, B5), - (A6, B6), - ); - #[rustfmt::skip] - fn apply(self) -> Self::Return { - let (first, second) = self; - { - ( - (first.0, second.0), - (first.1, second.1), - (first.2, second.2), - (first.3, second.3), - (first.4, second.4), - (first.5, second.5), - (first.6, second.6), - ) - } - } - } - impl _typle_fn_zip - for ((A0, A1, A2, A3, A4, A5, A6, A7), (B0, B1, B2, B3, B4, B5, B6, B7)) { - type Return = ( - (A0, B0), - (A1, B1), - (A2, B2), - (A3, B3), - (A4, B4), - (A5, B5), - (A6, B6), - (A7, B7), - ); - #[rustfmt::skip] - fn apply(self) -> Self::Return { - let (first, second) = self; - { - ( - (first.0, second.0), - (first.1, second.1), - (first.2, second.2), - (first.3, second.3), - (first.4, second.4), - (first.5, second.5), - (first.6, second.6), - (first.7, second.7), - ) - } - } - } - impl< - A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - B0, - B1, - B2, - B3, - B4, - B5, - B6, - B7, - B8, - > _typle_fn_zip - for ((A0, A1, A2, A3, A4, A5, A6, A7, A8), (B0, B1, B2, B3, B4, B5, B6, B7, B8)) { - type Return = ( - (A0, B0), - (A1, B1), - (A2, B2), - (A3, B3), - (A4, B4), - (A5, B5), - (A6, B6), - (A7, B7), - (A8, B8), - ); - #[rustfmt::skip] - fn apply(self) -> Self::Return { - let (first, second) = self; - { - ( - (first.0, second.0), - (first.1, second.1), - (first.2, second.2), - (first.3, second.3), - (first.4, second.4), - (first.5, second.5), - (first.6, second.6), - (first.7, second.7), - (first.8, second.8), - ) - } - } - } - impl< - A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - B0, - B1, - B2, - B3, - B4, - B5, - B6, - B7, - B8, - B9, - > _typle_fn_zip - for ( - (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9), - (B0, B1, B2, B3, B4, B5, B6, B7, B8, B9), - ) { - type Return = ( - (A0, B0), - (A1, B1), - (A2, B2), - (A3, B3), - (A4, B4), - (A5, B5), - (A6, B6), - (A7, B7), - (A8, B8), - (A9, B9), - ); - #[rustfmt::skip] - fn apply(self) -> Self::Return { - let (first, second) = self; - { - ( - (first.0, second.0), - (first.1, second.1), - (first.2, second.2), - (first.3, second.3), - (first.4, second.4), - (first.5, second.5), - (first.6, second.6), - (first.7, second.7), - (first.8, second.8), - (first.9, second.9), - ) - } - } - } - impl< - A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - A10, - B0, - B1, - B2, - B3, - B4, - B5, - B6, - B7, - B8, - B9, - B10, - > _typle_fn_zip - for ( - (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10), - (B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10), - ) { - type Return = ( - (A0, B0), - (A1, B1), - (A2, B2), - (A3, B3), - (A4, B4), - (A5, B5), - (A6, B6), - (A7, B7), - (A8, B8), - (A9, B9), - (A10, B10), - ); - #[rustfmt::skip] - fn apply(self) -> Self::Return { - let (first, second) = self; - { - ( - (first.0, second.0), - (first.1, second.1), - (first.2, second.2), - (first.3, second.3), - (first.4, second.4), - (first.5, second.5), - (first.6, second.6), - (first.7, second.7), - (first.8, second.8), - (first.9, second.9), - (first.10, second.10), - ) - } - } - } - impl< - A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - A10, - A11, - B0, - B1, - B2, - B3, - B4, - B5, - B6, - B7, - B8, - B9, - B10, - B11, - > _typle_fn_zip - for ( - (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11), - (B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11), - ) { - type Return = ( - (A0, B0), - (A1, B1), - (A2, B2), - (A3, B3), - (A4, B4), - (A5, B5), - (A6, B6), - (A7, B7), - (A8, B8), - (A9, B9), - (A10, B10), - (A11, B11), - ); - #[rustfmt::skip] - fn apply(self) -> Self::Return { - let (first, second) = self; - { - ( - (first.0, second.0), - (first.1, second.1), - (first.2, second.2), - (first.3, second.3), - (first.4, second.4), - (first.5, second.5), - (first.6, second.6), - (first.7, second.7), - (first.8, second.8), - (first.9, second.9), - (first.10, second.10), - (first.11, second.11), - ) - } - } - } pub fn zip(first: A, second: B) -> <(A, B) as _typle_fn_zip>::Return where (A, B): _typle_fn_zip, @@ -2929,19 +2622,13 @@ pub mod typle_args { struct MultipleHandlers { handlers: T, } - impl HandleStuff for MultipleHandlers<()> { - type Output = (); - fn handle_stuff(&self, input: Input) -> Self::Output { - { () } - } - } impl HandleStuff for MultipleHandlers<(T0,)> where T0: HandleStuff, { type Output = (T0::Output,); fn handle_stuff(&self, input: Input) -> Self::Output { - { (self.handlers.0.handle_stuff(input),) } + (self.handlers.0.handle_stuff(input),) } } impl HandleStuff for MultipleHandlers<(T0, T1)> @@ -2951,12 +2638,10 @@ pub mod typle_args { { type Output = (T0::Output, T1::Output); fn handle_stuff(&self, input: Input) -> Self::Output { - { - ( - self.handlers.0.handle_stuff(input.clone()), - self.handlers.1.handle_stuff(input), - ) - } + ( + self.handlers.0.handle_stuff(input.clone()), + self.handlers.1.handle_stuff(input), + ) } } impl HandleStuff for MultipleHandlers<(T0, T1, T2)> @@ -2967,13 +2652,11 @@ pub mod typle_args { { type Output = (T0::Output, T1::Output, T2::Output); fn handle_stuff(&self, input: Input) -> Self::Output { - { - ( - self.handlers.0.handle_stuff(input.clone()), - self.handlers.1.handle_stuff(input.clone()), - self.handlers.2.handle_stuff(input), - ) - } + ( + self.handlers.0.handle_stuff(input.clone()), + self.handlers.1.handle_stuff(input.clone()), + self.handlers.2.handle_stuff(input), + ) } } } diff --git a/tests/compile/typle_args.rs b/tests/compile/typle_args.rs index 78af77e..73ebd8a 100644 --- a/tests/compile/typle_args.rs +++ b/tests/compile/typle_args.rs @@ -88,26 +88,22 @@ struct MultipleHandlers { handlers: T, } -#[typle(Tuple for 0..=3)] +#[typle(Tuple for 1..=3)] impl HandleStuff for MultipleHandlers where - T: Tuple, // `T`` is a tuple with 0 to 3 components. + T: Tuple, // `T` is a tuple with 1 to 3 components. T<_>: HandleStuff, // All components implement `HandleStuff`. { + // Return a tuple of output from each handler applied to the same input. type Output = (typle! {i in .. => T<{i}>::Output}); - // Return a tuple of output from each handler applied to the same input. fn handle_stuff(&self, input: Input) -> Self::Output { - if typle_const!(T::LEN == 0) { - () - } else { - ( - typle! { - i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone()) - }, - // Avoid expensive clone for the last handler. - self.handlers[[T::LAST]].handle_stuff(input), - ) - } + ( + typle! { + i in ..T::LAST => self.handlers[[i]].handle_stuff(input.clone()) + }, + // Avoid expensive clone for the last handler. + self.handlers[[T::LAST]].handle_stuff(input), + ) } }