use syn::token;
use super::helpers::ident;
use super::{ToSyn, ToSynError};
use crate::pure::ast::{PureEnum, PureField, PureFields, PureStruct, PureVariant};
impl ToSyn for PureStruct {
type Output = syn::ItemStruct;
fn to_syn(&self) -> Result<syn::ItemStruct, ToSynError> {
Ok(syn::ItemStruct {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
struct_token: token::Struct::default(),
ident: ident(&self.name),
generics: self.generics.to_syn()?,
fields: self.fields.to_syn()?,
semi_token: matches!(self.fields, PureFields::Unit | PureFields::Tuple(_))
.then(token::Semi::default),
})
}
}
impl ToSyn for PureFields {
type Output = syn::Fields;
fn to_syn(&self) -> Result<syn::Fields, ToSynError> {
match self {
PureFields::Named(fields) => Ok(syn::Fields::Named(syn::FieldsNamed {
brace_token: token::Brace::default(),
named: fields
.iter()
.map(|f| f.to_syn())
.collect::<Result<_, _>>()?,
})),
PureFields::Tuple(types) => Ok(syn::Fields::Unnamed(syn::FieldsUnnamed {
paren_token: token::Paren::default(),
unnamed: types
.iter()
.map(|ty| {
Ok(syn::Field {
attrs: vec![],
vis: syn::Visibility::Inherited,
mutability: syn::FieldMutability::None,
ident: None,
colon_token: None,
ty: ty.to_syn()?,
})
})
.collect::<Result<_, ToSynError>>()?,
})),
PureFields::Unit => Ok(syn::Fields::Unit),
}
}
}
impl ToSyn for PureField {
type Output = syn::Field;
fn to_syn(&self) -> Result<syn::Field, ToSynError> {
Ok(syn::Field {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
mutability: syn::FieldMutability::None,
ident: Some(ident(&self.name)),
colon_token: Some(token::Colon::default()),
ty: self.ty.to_syn()?,
})
}
}
impl ToSyn for PureEnum {
type Output = syn::ItemEnum;
fn to_syn(&self) -> Result<syn::ItemEnum, ToSynError> {
Ok(syn::ItemEnum {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
enum_token: token::Enum::default(),
ident: ident(&self.name),
generics: self.generics.to_syn()?,
brace_token: token::Brace::default(),
variants: self
.variants
.iter()
.map(|v| v.to_syn())
.collect::<Result<_, _>>()?,
})
}
}
impl ToSyn for PureVariant {
type Output = syn::Variant;
fn to_syn(&self) -> Result<syn::Variant, ToSynError> {
let discriminant = self
.discriminant
.as_ref()
.map(|d| {
let expr: syn::Expr = syn::parse_str(d).map_err(|e| ToSynError::ParseExpr {
input: d.to_string(),
message: e.to_string(),
})?;
Ok((token::Eq::default(), expr))
})
.transpose()?;
Ok(syn::Variant {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
ident: ident(&self.name),
fields: self.fields.to_syn()?,
discriminant,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pure::ast::{PureGenerics, PureType, PureVis};
use quote::ToTokens;
#[test]
fn test_pure_struct_simple() {
let s = PureStruct {
attrs: vec![],
vis: PureVis::Public,
name: "Foo".to_string(),
generics: PureGenerics::default(),
fields: PureFields::Named(vec![PureField {
attrs: vec![],
vis: PureVis::Private,
name: "x".to_string(),
ty: PureType::Path("i32".to_string()),
}]),
};
let syn_struct = s.to_syn().unwrap();
let output = syn_struct.to_token_stream().to_string();
assert!(output.contains("pub"), "Output: {}", output);
assert!(output.contains("Foo"), "Output: {}", output);
assert!(output.contains("x"), "Output: {}", output);
}
#[test]
fn test_pure_struct_tuple() {
let s = PureStruct {
attrs: vec![],
vis: PureVis::Private,
name: "Point".to_string(),
generics: PureGenerics::default(),
fields: PureFields::Tuple(vec![
PureType::Path("i32".to_string()),
PureType::Path("i32".to_string()),
]),
};
let syn_struct = s.to_syn().unwrap();
let output = syn_struct.to_token_stream().to_string();
assert!(output.contains("Point"), "Output: {}", output);
assert!(output.contains("i32"), "Output: {}", output);
}
#[test]
fn test_pure_enum_simple() {
let e = PureEnum {
attrs: vec![],
vis: PureVis::Public,
name: "Status".to_string(),
generics: PureGenerics::default(),
variants: vec![
PureVariant {
attrs: vec![],
name: "Active".to_string(),
fields: PureFields::Unit,
discriminant: None,
},
PureVariant {
attrs: vec![],
name: "Inactive".to_string(),
fields: PureFields::Unit,
discriminant: None,
},
],
};
let syn_enum = e.to_syn().unwrap();
let output = syn_enum.to_token_stream().to_string();
assert!(output.contains("Status"), "Output: {}", output);
assert!(output.contains("Active"), "Output: {}", output);
assert!(output.contains("Inactive"), "Output: {}", output);
}
#[test]
fn test_pure_enum_with_discriminant() {
let e = PureEnum {
attrs: vec![],
vis: PureVis::Private,
name: "Code".to_string(),
generics: PureGenerics::default(),
variants: vec![PureVariant {
attrs: vec![],
name: "A".to_string(),
fields: PureFields::Unit,
discriminant: Some("1".to_string()),
}],
};
let syn_enum = e.to_syn().unwrap();
let output = syn_enum.to_token_stream().to_string();
assert!(output.contains("= 1"), "Output: {}", output);
}
}