use syn::token;
use super::helpers::{ident, make_abi, try_parse_path};
use super::{ToSyn, ToSynError};
use crate::pure::ast::{PureImpl, PureImplItem};
impl ToSyn for PureImpl {
type Output = syn::ItemImpl;
fn to_syn(&self) -> Result<syn::ItemImpl, ToSynError> {
Ok(syn::ItemImpl {
attrs: self
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
defaultness: None,
unsafety: if self.is_unsafe {
Some(token::Unsafe::default())
} else {
None
},
impl_token: token::Impl::default(),
generics: self.generics.to_syn()?,
trait_: self
.trait_
.as_ref()
.map(|t| Ok((None, try_parse_path(t)?, token::For::default())))
.transpose()?,
self_ty: Box::new(syn::parse_str(&self.self_ty).map_err(|e| {
ToSynError::ParseType {
input: self.self_ty.clone(),
message: e.to_string(),
}
})?),
brace_token: token::Brace::default(),
items: self
.items
.iter()
.map(|i| i.to_syn())
.collect::<Result<Vec<_>, _>>()?,
})
}
}
impl ToSyn for PureImplItem {
type Output = syn::ImplItem;
fn to_syn(&self) -> Result<syn::ImplItem, ToSynError> {
Ok(match self {
PureImplItem::Fn(f) => syn::ImplItem::Fn(syn::ImplItemFn {
attrs: f
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: f.vis.to_syn()?,
defaultness: None,
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,
},
},
block: f.body.to_syn()?,
}),
PureImplItem::Const(c) => syn::ImplItem::Const(syn::ImplItemConst {
attrs: c
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: c.vis.to_syn()?,
defaultness: None,
const_token: token::Const::default(),
ident: ident(&c.name),
generics: syn::Generics::default(),
colon_token: token::Colon::default(),
ty: c.ty.to_syn()?,
eq_token: token::Eq::default(),
expr: c
.value
.as_ref()
.ok_or_else(|| ToSynError::MissingValue {
context: format!("ImplItemConst '{}' must have a value", c.name),
})?
.to_syn()?,
semi_token: token::Semi::default(),
}),
PureImplItem::Type(t) => syn::ImplItem::Type(syn::ImplItemType {
attrs: t
.attrs
.iter()
.map(|a| a.to_syn())
.collect::<Result<Vec<_>, _>>()?,
vis: t.vis.to_syn()?,
defaultness: None,
type_token: token::Type::default(),
ident: ident(&t.name),
generics: t.generics.to_syn()?,
eq_token: token::Eq::default(),
ty: t.ty.to_syn()?,
semi_token: token::Semi::default(),
}),
PureImplItem::Other(s) => syn::parse_str(s).map_err(|e| ToSynError::Other {
message: format!("Failed to parse impl item '{}': {}", s, e),
})?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pure::ast::{PureBlock, PureFn, PureGenerics, PureType, PureVis};
use quote::ToTokens;
#[test]
fn test_pure_impl_inherent() {
let impl_block = PureImpl {
attrs: vec![],
generics: PureGenerics::default(),
is_unsafe: false,
trait_: None,
self_ty: "Foo".to_string(),
items: vec![],
};
let syn_impl = impl_block.to_syn().unwrap();
let output = syn_impl.to_token_stream().to_string();
assert!(output.contains("impl"), "Output: {}", output);
assert!(output.contains("Foo"), "Output: {}", output);
}
#[test]
fn test_pure_impl_trait() {
let impl_block = PureImpl {
attrs: vec![],
generics: PureGenerics::default(),
is_unsafe: false,
trait_: Some("Clone".to_string()),
self_ty: "Foo".to_string(),
items: vec![],
};
let syn_impl = impl_block.to_syn().unwrap();
let output = syn_impl.to_token_stream().to_string();
assert!(output.contains("Clone"), "Output: {}", output);
assert!(output.contains("for"), "Output: {}", output);
}
#[test]
fn test_pure_impl_with_method() {
let impl_block = PureImpl {
attrs: vec![],
generics: PureGenerics::default(),
is_unsafe: false,
trait_: None,
self_ty: "Foo".to_string(),
items: vec![PureImplItem::Fn(PureFn {
attrs: vec![],
vis: PureVis::Public,
is_async: false,
is_async_inferred: false,
is_const: false,
is_unsafe: false,
abi: None,
name: "new".to_string(),
generics: PureGenerics::default(),
params: vec![],
ret: Some(PureType::Path("Self".to_string())),
body: PureBlock::default(),
})],
};
let syn_impl = impl_block.to_syn().unwrap();
let output = syn_impl.to_token_stream().to_string();
assert!(output.contains("new"), "Output: {}", output);
assert!(output.contains("Self"), "Output: {}", output);
}
}