diff --git a/Cargo.lock b/Cargo.lock index 7d2f843..7259baa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "equivalent" version = "1.0.2" @@ -210,6 +216,7 @@ checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" name = "typle" version = "0.11.0" dependencies = [ + "either", "macrotest", "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4c94e3d..6c76104 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ rust-version = "1.68.2" proc-macro = true [dependencies] +either = "1" proc-macro2 = "1" quote = "1" syn = { version = "2.0.43", features = ["full", "extra-traits"] } diff --git a/README.md b/README.md index 9c21276..196b725 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ struct MultipleHandlers { #[typle(Tuple for 0..=3)] impl HandleStuff for MultipleHandlers where - T: Tuple, // `T`` is a tuple with 0 to 12 components. + T: Tuple, // `T`` is a tuple with 0 to 3 components. T<_>: HandleStuff, // All components implement `HandleStuff`. { type Output = (typle!(i in .. => T<{i}>::Output)); diff --git a/src/context.rs b/src/context.rs index 5d48dce..6cfa9e5 100644 --- a/src/context.rs +++ b/src/context.rs @@ -23,7 +23,7 @@ use syn::{ use zip_clone::ZipClone as _; use crate::constant::{evaluate_bool, evaluate_range, evaluate_usize}; -use crate::context::shared::{abort, Replacement}; +use crate::context::shared::{abort, Replacements}; use crate::syn_ext::GeneralFunction; use crate::TypleMacro; @@ -184,7 +184,7 @@ impl TypleContext { let path = &trait_bound.path; if path.leading_colon.is_none() && path.segments.len() == 1 { if let Some(segment) = path.segments.first() { - if segment.ident == self.typle_macro.ident { + if segment.ident == self.typle_macro.trait_ident { match &segment.arguments { PathArguments::None => { if result.is_some() { @@ -491,7 +491,8 @@ impl TypleContext { None, ) = (segments.get(0), segments.get(1), segments.get(2)) { - if *ident1 == self.typle_macro.ident && ident2 == "Bounds" + if *ident1 == self.typle_macro.trait_ident + && ident2 == "Bounds" { let token_stream = std::mem::take(&mut mac.tokens); let predicates = @@ -554,7 +555,10 @@ impl TypleContext { Ok(()) } - fn expand_predicates(&self, token_stream: TokenStream) -> Replacement { + fn expand_predicates( + &self, + token_stream: TokenStream, + ) -> Replacements>> { self.expand_typle_macro(token_stream, |context, token_stream| { // Divide the token_stream at the first single : let mut bounded = Vec::new(); @@ -808,7 +812,7 @@ impl TypleContext { }), ReturnType::Type(_, t) => *t, }; - let typle_trait_name = &self.typle_macro.ident; + let typle_trait_name = &self.typle_macro.trait_ident; // A method body is moved to a trait implementation on a dfferent type. // Any instances of `Self` in the method body are converted to the diff --git a/src/context/expr.rs b/src/context/expr.rs index ee0f311..9746a08 100644 --- a/src/context/expr.rs +++ b/src/context/expr.rs @@ -1,4 +1,6 @@ -use crate::context::shared::Replacement; +use either::Either; + +use crate::context::shared::Replacements; use super::*; @@ -466,7 +468,7 @@ impl TypleContext { &mut paren.expr, Expr::Verbatim(TokenStream::new()), )) { - Replacement::Singleton(inner) => { + Replacements::Singleton(Ok(inner)) => { paren.expr = Box::new(inner); } iter => { @@ -565,19 +567,24 @@ impl TypleContext { Ok(()) } - pub(super) fn replace_expr_in_list(&self, mut expr: Expr) -> Replacement { + pub(super) fn replace_expr_in_list( + &self, + mut expr: Expr, + ) -> Replacements>> { let mut state = BlockState::default(); match &mut expr { Expr::Macro(syn::ExprMacro { mac, .. }) => { if let Some(ident) = mac.path.get_ident() { if ident == "typle" || ident == "typle_args" { let token_stream = std::mem::take(&mut mac.tokens); - return self.expand_typle_macro(token_stream, |context, token_stream| { - let mut expr = syn::parse2::(token_stream)?; - let mut state = BlockState::default(); - context.replace_expr(&mut expr, &mut state)?; - Ok(expr) - }); + return self + .expand_typle_macro(token_stream, |context, token_stream| { + let mut expr = syn::parse2::(token_stream)?; + let mut state = BlockState::default(); + context.replace_expr(&mut expr, &mut state)?; + Ok(expr) + }) + .map_iterator(Either::Left); } } } @@ -587,15 +594,15 @@ impl TypleContext { let mut iter = array.elems.iter_mut().fuse(); if let (Some(field), None) = (iter.next(), iter.next()) { if let Err(e) = self.replace_expr(field, &mut state) { - return Replacement::Error(e); + return Replacements::Singleton(Err(e)); } if let Some((start, end)) = evaluate_range(field) { let start = match start { Bound::Included(Err(span)) | Bound::Excluded(Err(span)) => { - return Replacement::Error(syn::Error::new( + return Replacements::Singleton(Err(syn::Error::new( span, "expected integer for start of range", - )); + ))); } Bound::Included(Ok(start)) => start, Bound::Excluded(Ok(start)) => start.saturating_add(1), @@ -603,39 +610,37 @@ impl TypleContext { }; let end = match end { Bound::Included(Err(span)) | Bound::Excluded(Err(span)) => { - return Replacement::Error(syn::Error::new( + return Replacements::Singleton(Err(syn::Error::new( span, "expected integer for end of range", - )); + ))); } Bound::Included(Ok(end)) => end.saturating_add(1), Bound::Excluded(Ok(end)) => end, Bound::Unbounded => match self.typle_len { Some(end) => end, None => { - return Replacement::Error(syn::Error::new( + return Replacements::Singleton(Err(syn::Error::new( expr.span(), "need an explicit range end", - )); + ))); } }, }; - return Replacement::Iterator({ + return Replacements::Iterator(Either::Right({ let span = index.span(); - Box::new((start..end).zip_clone(expr.clone()).map( - move |(i, base)| { - Ok(Expr::Field(syn::ExprField { - attrs: Vec::new(), - base, - dot_token: token::Dot::default(), - member: Member::Unnamed(syn::Index { - index: i as u32, - span, - }), - })) - }, - )) - }); + (start..end).zip_clone(expr.clone()).map(move |(i, base)| { + Ok(Expr::Field(syn::ExprField { + attrs: Vec::new(), + base, + dot_token: token::Dot::default(), + member: Member::Unnamed(syn::Index { + index: i as u32, + span, + }), + })) + }) + })); } } } @@ -643,8 +648,8 @@ impl TypleContext { _ => {} } match self.replace_expr(&mut expr, &mut state) { - Ok(()) => Replacement::Singleton(expr), - Err(e) => Replacement::Error(e), + Ok(()) => Replacements::Singleton(Ok(expr)), + Err(e) => Replacements::Singleton(Err(e)), } } diff --git a/src/context/pat.rs b/src/context/pat.rs index b834106..03fe74b 100644 --- a/src/context/pat.rs +++ b/src/context/pat.rs @@ -1,4 +1,4 @@ -use crate::context::shared::Replacement; +use crate::context::shared::Replacements; use super::*; @@ -21,7 +21,7 @@ impl TypleContext { &mut paren.pat, Pat::Verbatim(TokenStream::new()), )) { - Replacement::Singleton(inner) => { + Replacements::Singleton(Ok(inner)) => { paren.pat = Box::new(inner); } iter => { @@ -111,7 +111,10 @@ impl TypleContext { Ok(()) } - pub(super) fn replace_pat_in_list(&self, mut pat: Pat) -> Replacement { + pub(super) fn replace_pat_in_list( + &self, + mut pat: Pat, + ) -> Replacements>> { let mut state = BlockState::default(); match &mut pat { Pat::Macro(syn::PatMacro { mac, .. }) => { @@ -130,8 +133,8 @@ impl TypleContext { _ => {} } match self.replace_pat(&mut pat, &mut state) { - Ok(()) => Replacement::Singleton(pat), - Err(e) => Replacement::Error(e), + Ok(()) => Replacements::Singleton(Ok(pat)), + Err(e) => Replacements::Singleton(Err(e)), } } diff --git a/src/context/shared.rs b/src/context/shared.rs index 787d88e..ad89636 100644 --- a/src/context/shared.rs +++ b/src/context/shared.rs @@ -8,34 +8,60 @@ macro_rules! abort { pub(super) use abort; -pub(super) enum Replacement { - /// A single value, not from a typle expansion - Singleton(T), - /// An empty typle expansion +pub(super) enum Replacements +where + I: Iterator, +{ Empty, - /// A typle expansion - Iterator(Box>>), - /// An error outside of the iterator - Error(syn::Error), + Singleton(I::Item), + Iterator(I), } -impl Iterator for Replacement { - type Item = syn::Result; +impl Replacements +where + I: Iterator, +{ + pub(super) fn map_iterator(self, f: F) -> Replacements + where + J: Iterator, + F: Fn(I) -> J, + { + match self { + Replacements::Empty => Replacements::Empty, + Replacements::Singleton(item) => Replacements::Singleton(item), + Replacements::Iterator(iter) => Replacements::Iterator(f(iter)), + } + } +} +impl Iterator for Replacements +where + I: Iterator, +{ + type Item = I::Item; fn next(&mut self) -> Option { - match std::mem::replace(self, Replacement::Empty) { - Replacement::Singleton(t) => Some(Ok(t)), - Replacement::Empty => None, - Replacement::Iterator(mut iterator) => { + match std::mem::replace(self, Replacements::Empty) { + Replacements::Empty => None, + Replacements::Singleton(t) => Some(t), + Replacements::Iterator(mut iterator) => { let item = iterator.next(); - *self = Replacement::Iterator(iterator); + *self = Replacements::Iterator(iterator); item } - Replacement::Error(t) => Some(Err(t)), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + Replacements::Empty => (0, Some(0)), + Replacements::Singleton(_) => (1, Some(1)), + Replacements::Iterator(iterator) => iterator.size_hint(), } } } +impl ExactSizeIterator for Replacements where I: ExactSizeIterator {} + impl TypleContext { pub(super) fn parse_pattern_range( &self, @@ -101,7 +127,7 @@ impl TypleContext { suspicious_ident, format!( "range start invalid, possibly missing `{}: {}` bound", - suspicious_ident, self.typle_macro.ident + suspicious_ident, self.typle_macro.trait_ident ) ); } else { @@ -119,7 +145,7 @@ impl TypleContext { suspicious_ident, format!( "range end invalid, possibly missing `{}: {}` bound", - suspicious_ident, self.typle_macro.ident + suspicious_ident, self.typle_macro.trait_ident ) ); } else { @@ -340,7 +366,9 @@ impl TypleContext { } // Path T::U if ident2 == "LEN" { - if ident1 == &self.typle_macro.ident || self.typles.contains_key(ident1) { + if ident1 == &self.typle_macro.trait_ident + || self.typles.contains_key(ident1) + { // Tuple::LEN or T::LEN let Some(typle_len) = self.typle_len else { abort!(ident2, "LEN not defined outside fn or impl"); @@ -355,7 +383,9 @@ impl TypleContext { state.suspicious_ident = Some(ident1.clone()); } } else if ident2 == "LAST" { - if ident1 == &self.typle_macro.ident || self.typles.contains_key(ident1) { + if ident1 == &self.typle_macro.trait_ident + || self.typles.contains_key(ident1) + { // Tuple::LAST or T::LAST let Some(typle_len) = self.typle_len else { abort!(ident2, "LAST not defined outside fn or impl"); @@ -371,7 +401,9 @@ impl TypleContext { state.suspicious_ident = Some(ident1.clone()); } } else if ident2 == "MAX" { - if ident1 == &self.typle_macro.ident || self.typles.contains_key(ident1) { + if ident1 == &self.typle_macro.trait_ident + || self.typles.contains_key(ident1) + { // Tuple::MAX or ::MAX return Ok(Some(Lit::Int(syn::LitInt::new( &self.typle_macro.max_len.to_string(), @@ -381,7 +413,9 @@ impl TypleContext { state.suspicious_ident = Some(ident1.clone()); } } else if ident2 == "MIN" { - if ident1 == &self.typle_macro.ident || self.typles.contains_key(ident1) { + if ident1 == &self.typle_macro.trait_ident + || self.typles.contains_key(ident1) + { // Tuple::MIN or ::MIN return Ok(Some(Lit::Int(syn::LitInt::new( &self.typle_macro.min_len.to_string(), @@ -400,7 +434,11 @@ impl TypleContext { Ok(None) } - pub(super) fn expand_typle_macro(&self, token_stream: TokenStream, f: F) -> Replacement + pub(super) fn expand_typle_macro( + &self, + token_stream: TokenStream, + f: F, + ) -> Replacements>> where T: 'static, F: Fn(&TypleContext, TokenStream) -> syn::Result + 'static, @@ -409,18 +447,18 @@ impl TypleContext { let (pattern, range) = match self.parse_pattern_range(&mut tokens) { Ok(t) => t, Err(e) => { - return Replacement::Error(e); + return Replacements::Singleton(Err(e)); } }; if range.is_empty() { - return Replacement::Empty; + return Replacements::Empty; } let token_stream = tokens.collect::(); let mut context = self.clone(); if let Some(ident) = pattern.clone() { context.constants.insert(ident, 0); } - return Replacement::Iterator(Box::new(range.zip_clone(token_stream).flat_map( + return Replacements::Iterator(range.zip_clone(token_stream).flat_map( move |(index, token_stream)| { if let Some(ident) = &pattern { *context.constants.get_mut(ident).unwrap() = index; @@ -431,6 +469,6 @@ impl TypleContext { Err(e) => Some(Err(e)), } }, - ))); + )); } } diff --git a/src/context/type.rs b/src/context/type.rs index d54414c..fef310b 100644 --- a/src/context/type.rs +++ b/src/context/type.rs @@ -1,4 +1,6 @@ -use crate::context::shared::Replacement; +use either::Either; + +use crate::context::shared::Replacements; use super::*; @@ -41,7 +43,7 @@ impl TypleContext { &mut paren.elem, Type::Verbatim(TokenStream::new()), )) { - Replacement::Singleton(inner) => { + Replacements::Singleton(Ok(inner)) => { paren.elem = Box::new(inner); } iter => { @@ -82,17 +84,22 @@ impl TypleContext { Ok(()) } - pub(super) fn replace_type_in_list(&self, mut ty: Type) -> Replacement { + pub(super) fn replace_type_in_list( + &self, + mut ty: Type, + ) -> Replacements>> { match &mut ty { Type::Macro(syn::TypeMacro { mac }) => { if let Some(ident) = mac.path.get_ident() { if ident == "typle" || ident == "typle_args" { let token_stream = std::mem::take(&mut mac.tokens); - return self.expand_typle_macro(token_stream, |context, token_stream| { - let mut ty = syn::parse2::(token_stream)?; - context.replace_type(&mut ty)?; - Ok(ty) - }); + return self + .expand_typle_macro(token_stream, |context, token_stream| { + let mut ty = syn::parse2::(token_stream)?; + context.replace_type(&mut ty)?; + Ok(ty) + }) + .map_iterator(Either::Left); } } } @@ -113,16 +120,16 @@ impl TypleContext { let mut state = BlockState::default(); match self.replace_expr(expr, &mut state) { Ok(_) => {} - Err(e) => return Replacement::Error(e), + Err(e) => return Replacements::Singleton(Err(e)), } if let Some((start, end)) = evaluate_range(expr) { // T<{..}> let start = match start { Bound::Included(Err(span)) | Bound::Excluded(Err(span)) => { - return Replacement::Error(syn::Error::new( + return Replacements::Singleton(Err(syn::Error::new( span, "expected integer for start of range", - )); + ))); } Bound::Included(Ok(start)) => start, Bound::Excluded(Ok(start)) => start.saturating_add(1), @@ -130,24 +137,24 @@ impl TypleContext { }; let end = match end { Bound::Included(Err(span)) | Bound::Excluded(Err(span)) => { - return Replacement::Error(syn::Error::new( + return Replacements::Singleton(Err(syn::Error::new( span, "expected integer for end of range", - )); + ))); } Bound::Included(Ok(end)) => end.saturating_add(1), Bound::Excluded(Ok(end)) => end, Bound::Unbounded => match self.typle_len { Some(end) => end, None => { - return Replacement::Error(syn::Error::new( + return Replacements::Singleton(Err(syn::Error::new( expr.span(), "need an explicit range end", - )); + ))); } }, }; - return Replacement::Iterator(Box::new((start..end).map({ + return Replacements::Iterator(Either::Right((start..end).map({ let span = path.span(); let context = self.clone(); let typle = typle.clone(); @@ -161,8 +168,8 @@ impl TypleContext { _ => {} } match self.replace_type(&mut ty) { - Ok(()) => Replacement::Singleton(ty), - Err(e) => Replacement::Error(e), + Ok(()) => Replacements::Singleton(Ok(ty)), + Err(e) => Replacements::Singleton(Err(e)), } } diff --git a/src/lib.rs b/src/lib.rs index 504b95b..ebb7481 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -651,7 +651,7 @@ pub fn typle( } struct TypleMacro { - ident: Ident, + trait_ident: Ident, min_len: usize, max_len: usize, never_type: Type, @@ -743,7 +743,7 @@ impl TryFrom for TypleMacro { never_type = syn::parse2::(type_stream)?; } Ok(TypleMacro { - ident: trait_ident, + trait_ident, min_len: min, max_len: max, never_type, diff --git a/tests/compile/typle_args.rs b/tests/compile/typle_args.rs index 70c7a4c..f54c488 100644 --- a/tests/compile/typle_args.rs +++ b/tests/compile/typle_args.rs @@ -91,8 +91,8 @@ struct MultipleHandlers { #[typle(Tuple for 0..=3)] impl HandleStuff for MultipleHandlers where - T: Tuple, // `T`` is a tuple with 0 to 12 components. - T<_>: HandleStuff, // All components implement `HandleStuff`. + T: Tuple, // `T`` is a tuple with 0 to 3 components. + T<_>: HandleStuff, // All components implement `HandleStuff`. { type Output = (typle!(i in .. => T<{i}>::Output));