use super::assume_punct; use crate::generate::StreamBuilder; use crate::parse::{ident_eq, read_tokens_until_punct}; use crate::prelude::{Ident, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; #[derive(Debug)] pub struct Generics { lifetimes_and_generics: Vec, } impl Generics { pub fn try_take(input: &mut Peekable>) -> Result> { let maybe_punct = input.peek(); if let Some(TokenTree::Punct(punct)) = maybe_punct { if punct.as_char() == '<' { let punct = super::assume_punct(input.next(), '<'); let mut result = Generics { lifetimes_and_generics: Vec::new(), }; loop { match input.peek() { Some(TokenTree::Punct(punct)) if punct.as_char() == '\'' => { result .lifetimes_and_generics .push(Lifetime::take(input)?.into()); super::consume_punct_if(input, ','); } Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => { assume_punct(input.next(), '>'); break; } Some(TokenTree::Ident(_)) => { result .lifetimes_and_generics .push(Generic::take(input)?.into()); super::consume_punct_if(input, ','); } x => { return Err(Error::InvalidRustSyntax( x.map(|x| x.span()).unwrap_or_else(|| punct.span()), )); } } } return Ok(Some(result)); } } Ok(None) } pub fn has_lifetime(&self) -> bool { self.lifetimes_and_generics .iter() .any(|lt| lt.is_lifetime()) } pub fn impl_generics(&self) -> StreamBuilder { let mut result = StreamBuilder::new(); result.punct('<'); for (idx, generic) in self.lifetimes_and_generics.iter().enumerate() { if idx > 0 { result.punct(','); } if generic.is_lifetime() { result.lifetime(generic.ident()); } else { result.ident(generic.ident()); } if generic.has_constraints() { result.punct(':'); result.extend(generic.constraints()); } } result.punct('>'); result } pub fn impl_generics_with_additional_lifetime(&self, lifetime: &str) -> StreamBuilder { assert!(self.has_lifetime()); let mut result = StreamBuilder::new(); result.punct('<'); result.lifetime_str(lifetime); if self.has_lifetime() { for (idx, lt) in self .lifetimes_and_generics .iter() .filter_map(|lt| lt.as_lifetime()) .enumerate() { result.punct(if idx == 0 { ':' } else { '+' }); result.lifetime(lt.ident.clone()); } } for generic in &self.lifetimes_and_generics { result.punct(','); if generic.is_lifetime() { result.lifetime(generic.ident()); } else { result.ident(generic.ident()); } if generic.has_constraints() { result.punct(':'); result.extend(generic.constraints()); } } result.punct('>'); result } pub fn type_generics(&self) -> StreamBuilder { let mut result = StreamBuilder::new(); result.punct('<'); for (idx, generic) in self.lifetimes_and_generics.iter().enumerate() { if idx > 0 { result.punct(','); } if generic.is_lifetime() { result.lifetime(generic.ident()); } else { result.ident(generic.ident()); } } result.punct('>'); result } } #[derive(Debug)] enum LifetimeOrGeneric { Lifetime(Lifetime), Generic(Generic), } impl LifetimeOrGeneric { fn is_lifetime(&self) -> bool { matches!(self, LifetimeOrGeneric::Lifetime(_)) } fn ident(&self) -> Ident { match self { Self::Lifetime(lt) => lt.ident.clone(), Self::Generic(gen) => gen.ident.clone(), } } fn as_lifetime(&self) -> Option<&Lifetime> { match self { Self::Lifetime(lt) => Some(lt), Self::Generic(_) => None, } } fn has_constraints(&self) -> bool { match self { Self::Lifetime(lt) => !lt.constraint.is_empty(), Self::Generic(gen) => !gen.constraints.is_empty(), } } fn constraints(&self) -> Vec { match self { Self::Lifetime(lt) => lt.constraint.clone(), Self::Generic(gen) => gen.constraints.clone(), } } } impl From for LifetimeOrGeneric { fn from(lt: Lifetime) -> Self { Self::Lifetime(lt) } } impl From for LifetimeOrGeneric { fn from(gen: Generic) -> Self { Self::Generic(gen) } } #[test] fn test_generics_try_take() { use crate::token_stream; assert!(Generics::try_take(&mut token_stream("")).unwrap().is_none()); assert!(Generics::try_take(&mut token_stream("foo")) .unwrap() .is_none()); assert!(Generics::try_take(&mut token_stream("()")) .unwrap() .is_none()); let stream = &mut token_stream("struct Foo<'a, T>()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let generics = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); let stream = &mut token_stream("struct Foo()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let generics = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "A"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "B"); let stream = &mut token_stream("struct Foo<'a, T: Display>()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); let stream = &mut token_stream("struct Foo<'a, T: for<'a> Bar<'a> + 'static>()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); let stream = &mut token_stream( "struct Baz Bar<'a, for<'b> Bar<'b, for<'c> Bar<'c, u32>>>> {}", ); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Baz"); let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 1); assert_eq!(generics.lifetimes_and_generics[0].ident(), "T"); let stream = &mut token_stream("struct Baz<()> {}"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Baz"); assert!(Generics::try_take(stream) .unwrap_err() .is_invalid_rust_syntax()); let stream = &mut token_stream("struct Bar SomeStruct, B>"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Bar"); let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "A"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "B"); } #[derive(Debug)] pub struct Lifetime { ident: Ident, constraint: Vec, } impl Lifetime { pub fn take(input: &mut Peekable>) -> Result { let start = super::assume_punct(input.next(), '\''); let ident = match input.peek() { Some(TokenTree::Ident(_)) => super::assume_ident(input.next()), Some(t) => return Err(Error::ExpectedIdent(t.span())), None => return Err(Error::ExpectedIdent(start.span())), }; let mut constraint = Vec::new(); if let Some(TokenTree::Punct(p)) = input.peek() { if p.as_char() == ':' { assume_punct(input.next(), ':'); constraint = super::read_tokens_until_punct(input, &[',', '>'])?; } } Ok(Self { ident, constraint }) } #[cfg(test)] fn is_ident(&self, s: &str) -> bool { self.ident.to_string() == s } } #[test] fn test_lifetime_take() { use crate::token_stream; use std::panic::catch_unwind; assert!(Lifetime::take(&mut token_stream("'a")) .unwrap() .is_ident("a")); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0"))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'("))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("')"))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0'"))).is_err()); let stream = &mut token_stream("'a: 'b>"); let lifetime = Lifetime::take(stream).unwrap(); assert_eq!(lifetime.ident, "a"); assert_eq!(lifetime.constraint.len(), 2); assume_punct(stream.next(), '>'); assert!(stream.next().is_none()); } #[derive(Debug)] pub struct Generic { ident: Ident, constraints: Vec, } impl Generic { pub fn take(input: &mut Peekable>) -> Result { let ident = super::assume_ident(input.next()); let mut constraints = Vec::new(); if let Some(TokenTree::Punct(punct)) = input.peek() { if punct.as_char() == ':' { super::assume_punct(input.next(), ':'); constraints = super::read_tokens_until_punct(input, &['>', ','])?; } } Ok(Generic { ident, constraints }) } } #[derive(Debug)] pub struct GenericConstraints { constraints: Vec, } impl GenericConstraints { pub fn try_take(input: &mut Peekable>) -> Result> { match input.peek() { Some(TokenTree::Ident(ident)) => { if !ident_eq(ident, "where") { return Ok(None); } } _ => { return Ok(None); } } input.next(); let constraints = read_tokens_until_punct(input, &['{', '('])?; Ok(Some(Self { constraints })) } pub fn where_clause(&self) -> StreamBuilder { let mut result = StreamBuilder::new(); result.ident_str("where"); result.extend(self.constraints.clone()); result } } #[test] fn test_generic_constraints_try_take() { use super::{DataType, StructBody, Visibility}; use crate::token_stream; let stream = &mut token_stream("struct Foo where Foo: Bar { }"); super::DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_some()); let stream = &mut token_stream("struct Foo { }"); super::DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("struct Foo where Foo: Bar(Foo)"); super::DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_some()); let stream = &mut token_stream("struct Foo()"); super::DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("struct Foo()"); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("{}"); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream(""); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("pub(crate) struct Test {}"); assert_eq!(Visibility::Pub, Visibility::try_take(stream).unwrap()); let (data_type, ident) = DataType::take(stream).unwrap(); assert_eq!(data_type, DataType::Struct); assert_eq!(ident, "Test"); let constraints = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(constraints.lifetimes_and_generics.len(), 1); assert_eq!(constraints.lifetimes_and_generics[0].ident(), "T"); let body = StructBody::take(stream).unwrap(); assert_eq!(body.fields.len(), 0); }