use syn::token;
use super::helpers::{ident, make_abi, try_parse_path};
use super::{ToSyn, ToSynError};
use crate::pure::ast::{
MacroDelimiter, PureConst, PureMacro, PureMod, PureStatic, PureTrait, PureTraitItem,
PureTypeAlias,
};
impl ToSyn for PureConst {
type Output = syn::ItemConst;
fn to_syn(&self) -> Result<syn::ItemConst, ToSynError> {
Ok(syn::ItemConst {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
const_token: token::Const::default(),
ident: ident(&self.name),
generics: syn::Generics::default(),
colon_token: token::Colon::default(),
ty: Box::new(self.ty.to_syn()?),
eq_token: token::Eq::default(),
expr: Box::new(
self.value
.as_ref()
.ok_or_else(|| ToSynError::MissingValue {
context: format!("ItemConst '{}' must have a value", self.name),
})?
.to_syn()?,
),
semi_token: token::Semi::default(),
})
}
}
impl ToSyn for PureStatic {
type Output = syn::ItemStatic;
fn to_syn(&self) -> Result<syn::ItemStatic, ToSynError> {
Ok(syn::ItemStatic {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
static_token: token::Static::default(),
mutability: if self.is_mut {
syn::StaticMutability::Mut(token::Mut::default())
} else {
syn::StaticMutability::None
},
ident: ident(&self.name),
colon_token: token::Colon::default(),
ty: Box::new(self.ty.to_syn()?),
eq_token: token::Eq::default(),
expr: Box::new(self.value.to_syn()?),
semi_token: token::Semi::default(),
})
}
}
impl ToSyn for PureTypeAlias {
type Output = syn::ItemType;
fn to_syn(&self) -> Result<syn::ItemType, ToSynError> {
Ok(syn::ItemType {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
type_token: token::Type::default(),
ident: ident(&self.name),
generics: self.generics.to_syn()?,
eq_token: token::Eq::default(),
ty: Box::new(self.ty.to_syn()?),
semi_token: token::Semi::default(),
})
}
}
impl ToSyn for PureMod {
type Output = syn::ItemMod;
fn to_syn(&self) -> Result<syn::ItemMod, ToSynError> {
let has_content = !self.items.is_empty();
let content = if has_content {
let all_items: Vec<syn::Item> = self
.items
.iter()
.map(|i| i.to_syn())
.collect::<Result<Vec<_>, _>>()?;
Some((token::Brace::default(), all_items))
} else {
None
};
Ok(syn::ItemMod {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
unsafety: None,
mod_token: token::Mod::default(),
ident: ident(&self.name),
content,
semi: if has_content {
None
} else {
Some(token::Semi::default())
},
})
}
}
impl ToSyn for PureTrait {
type Output = syn::ItemTrait;
fn to_syn(&self) -> Result<syn::ItemTrait, ToSynError> {
Ok(syn::ItemTrait {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: self.vis.to_syn()?,
unsafety: if self.is_unsafe {
Some(token::Unsafe::default())
} else {
None
},
auto_token: if self.is_auto {
Some(token::Auto::default())
} else {
None
},
restriction: None,
trait_token: token::Trait::default(),
ident: ident(&self.name),
generics: self.generics.to_syn()?,
colon_token: if self.supertraits.is_empty() {
None
} else {
Some(token::Colon::default())
},
supertraits: self
.supertraits
.iter()
.filter_map(|s| syn::parse_str::<syn::TypeParamBound>(s).ok())
.collect(),
brace_token: token::Brace::default(),
items: self
.items
.iter()
.map(|i| i.to_syn())
.collect::<Result<Vec<_>, _>>()?,
})
}
}
impl ToSyn for PureTraitItem {
type Output = syn::TraitItem;
fn to_syn(&self) -> Result<syn::TraitItem, ToSynError> {
Ok(match self {
PureTraitItem::Fn(f) => syn::TraitItem::Fn(syn::TraitItemFn {
attrs: f
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
sig: syn::Signature {
constness: if f.is_const {
Some(token::Const::default())
} else {
None
},
asyncness: if f.is_async {
Some(token::Async::default())
} else {
None
},
unsafety: if f.is_unsafe {
Some(token::Unsafe::default())
} else {
None
},
abi: f.abi.as_ref().map(|a| make_abi(a)),
fn_token: token::Fn::default(),
ident: ident(&f.name),
generics: f.generics.to_syn()?,
paren_token: token::Paren::default(),
inputs: f
.params
.iter()
.map(|p| p.to_syn())
.collect::<Result<_, _>>()?,
variadic: None,
output: match &f.ret {
Some(ty) => {
syn::ReturnType::Type(token::RArrow::default(), Box::new(ty.to_syn()?))
}
None => syn::ReturnType::Default,
},
},
default: if f.body.stmts.is_empty() {
None
} else {
Some(f.body.to_syn()?)
},
semi_token: if f.body.stmts.is_empty() {
Some(token::Semi::default())
} else {
None
},
}),
PureTraitItem::Const(c) => syn::TraitItem::Const(syn::TraitItemConst {
attrs: c
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
const_token: token::Const::default(),
ident: ident(&c.name),
generics: syn::Generics::default(),
colon_token: token::Colon::default(),
ty: c.ty.to_syn()?,
default: c
.value
.as_ref()
.map(|v| Ok((token::Eq::default(), v.to_syn()?)))
.transpose()?,
semi_token: token::Semi::default(),
}),
PureTraitItem::Type {
name,
bounds,
default,
} => syn::TraitItem::Type(syn::TraitItemType {
attrs: vec![],
type_token: token::Type::default(),
ident: ident(name),
generics: syn::Generics::default(),
colon_token: if bounds.is_empty() {
None
} else {
Some(token::Colon::default())
},
bounds: bounds
.iter()
.filter_map(|b| syn::parse_str::<syn::TypeParamBound>(b).ok())
.collect(),
default: default
.as_ref()
.map(|ty| Ok((token::Eq::default(), ty.to_syn()?)))
.transpose()?,
semi_token: token::Semi::default(),
}),
PureTraitItem::Other(s) => syn::parse_str(s).map_err(|e| ToSynError::Other {
message: format!("Failed to parse trait item '{}': {}", s, e),
})?,
})
}
}
impl ToSyn for PureMacro {
type Output = syn::ItemMacro;
fn to_syn(&self) -> Result<syn::ItemMacro, ToSynError> {
let tokens: proc_macro2::TokenStream =
self.tokens
.parse()
.map_err(|e: proc_macro2::LexError| ToSynError::Other {
message: format!("Failed to parse macro tokens '{}': {}", self.tokens, e),
})?;
Ok(syn::ItemMacro {
attrs: vec![],
ident: None,
mac: syn::Macro {
path: try_parse_path(&self.path)?,
bang_token: token::Not::default(),
delimiter: match self.delimiter {
MacroDelimiter::Paren => syn::MacroDelimiter::Paren(token::Paren::default()),
MacroDelimiter::Brace => syn::MacroDelimiter::Brace(token::Brace::default()),
MacroDelimiter::Bracket => {
syn::MacroDelimiter::Bracket(token::Bracket::default())
}
},
tokens,
},
semi_token: Some(token::Semi::default()),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pure::ast::{PureExpr, PureGenerics, PureType, PureVis};
use quote::ToTokens;
#[test]
fn test_pure_const() {
let c = PureConst {
attrs: vec![],
vis: PureVis::Public,
name: "MAX".to_string(),
ty: PureType::Path("i32".to_string()),
value: Some(PureExpr::Lit("100".to_string())),
};
let syn_const = c.to_syn().unwrap();
let output = syn_const.to_token_stream().to_string();
assert!(output.contains("const"), "Output: {}", output);
assert!(output.contains("MAX"), "Output: {}", output);
assert!(output.contains("100"), "Output: {}", output);
}
#[test]
fn test_pure_static() {
let s = PureStatic {
attrs: vec![],
vis: PureVis::Private,
is_mut: true,
name: "COUNTER".to_string(),
ty: PureType::Path("i32".to_string()),
value: PureExpr::Lit("0".to_string()),
};
let syn_static = s.to_syn().unwrap();
let output = syn_static.to_token_stream().to_string();
assert!(output.contains("static"), "Output: {}", output);
assert!(output.contains("mut"), "Output: {}", output);
assert!(output.contains("COUNTER"), "Output: {}", output);
}
#[test]
fn test_pure_type_alias() {
let t = PureTypeAlias {
attrs: vec![],
vis: PureVis::Public,
name: "Result".to_string(),
generics: PureGenerics::default(),
ty: PureType::Path("std::result::Result<T, Error>".to_string()),
};
let syn_type = t.to_syn().unwrap();
let output = syn_type.to_token_stream().to_string();
assert!(output.contains("type"), "Output: {}", output);
assert!(output.contains("Result"), "Output: {}", output);
}
#[test]
fn test_pure_mod_declaration() {
let m = PureMod {
attrs: vec![],
vis: PureVis::Public,
name: "utils".to_string(),
items: vec![],
};
let syn_mod = m.to_syn().unwrap();
let output = syn_mod.to_token_stream().to_string();
assert!(output.contains("mod"), "Output: {}", output);
assert!(output.contains("utils"), "Output: {}", output);
}
#[test]
fn test_pure_trait_simple() {
let t = PureTrait {
attrs: vec![],
vis: PureVis::Public,
is_unsafe: false,
is_auto: false,
name: "Drawable".to_string(),
generics: PureGenerics::default(),
supertraits: vec![],
items: vec![],
};
let syn_trait = t.to_syn().unwrap();
let output = syn_trait.to_token_stream().to_string();
assert!(output.contains("trait"), "Output: {}", output);
assert!(output.contains("Drawable"), "Output: {}", output);
}
}