bincode-orig/derive/src/parse/generics.rs

418 lines
14 KiB
Rust

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<LifetimeOrGeneric>,
}
impl Generics {
pub fn try_take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Option<Self>> {
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<TokenTree> {
match self {
Self::Lifetime(lt) => lt.constraint.clone(),
Self::Generic(gen) => gen.constraints.clone(),
}
}
}
impl From<Lifetime> for LifetimeOrGeneric {
fn from(lt: Lifetime) -> Self {
Self::Lifetime(lt)
}
}
impl From<Generic> 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<A, B>()");
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<T: for<'a> 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<A: FnOnce(&'static str) -> 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<TokenTree>,
}
impl Lifetime {
pub fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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<TokenTree>,
}
impl Generic {
pub fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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<TokenTree>,
}
impl GenericConstraints {
pub fn try_take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Option<Self>> {
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<T: Encodeable> {}");
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);
}