Skip to main content

ryo_source/pure/to_syn/
impls.rs

1//! ToSyn implementations for impl blocks.
2
3use syn::token;
4
5use super::helpers::{ident, make_abi, try_parse_path};
6use super::{ToSyn, ToSynError};
7use crate::pure::ast::{PureImpl, PureImplItem};
8
9impl ToSyn for PureImpl {
10    type Output = syn::ItemImpl;
11
12    fn to_syn(&self) -> Result<syn::ItemImpl, ToSynError> {
13        Ok(syn::ItemImpl {
14            attrs: self
15                .attrs
16                .iter()
17                .map(|a| a.to_syn())
18                .collect::<Result<Vec<_>, _>>()?,
19            defaultness: None,
20            unsafety: if self.is_unsafe {
21                Some(token::Unsafe::default())
22            } else {
23                None
24            },
25            impl_token: token::Impl::default(),
26            generics: self.generics.to_syn()?,
27            trait_: self
28                .trait_
29                .as_ref()
30                .map(|t| Ok((None, try_parse_path(t)?, token::For::default())))
31                .transpose()?,
32            self_ty: Box::new(syn::parse_str(&self.self_ty).map_err(|e| {
33                ToSynError::ParseType {
34                    input: self.self_ty.clone(),
35                    message: e.to_string(),
36                }
37            })?),
38            brace_token: token::Brace::default(),
39            items: self
40                .items
41                .iter()
42                .map(|i| i.to_syn())
43                .collect::<Result<Vec<_>, _>>()?,
44        })
45    }
46}
47
48impl ToSyn for PureImplItem {
49    type Output = syn::ImplItem;
50
51    fn to_syn(&self) -> Result<syn::ImplItem, ToSynError> {
52        Ok(match self {
53            PureImplItem::Fn(f) => syn::ImplItem::Fn(syn::ImplItemFn {
54                attrs: f
55                    .attrs
56                    .iter()
57                    .map(|a| a.to_syn())
58                    .collect::<Result<Vec<_>, _>>()?,
59                vis: f.vis.to_syn()?,
60                defaultness: None,
61                sig: syn::Signature {
62                    constness: if f.is_const {
63                        Some(token::Const::default())
64                    } else {
65                        None
66                    },
67                    asyncness: if f.is_async {
68                        Some(token::Async::default())
69                    } else {
70                        None
71                    },
72                    unsafety: if f.is_unsafe {
73                        Some(token::Unsafe::default())
74                    } else {
75                        None
76                    },
77                    abi: f.abi.as_ref().map(|a| make_abi(a)),
78                    fn_token: token::Fn::default(),
79                    ident: ident(&f.name),
80                    generics: f.generics.to_syn()?,
81                    paren_token: token::Paren::default(),
82                    inputs: f
83                        .params
84                        .iter()
85                        .map(|p| p.to_syn())
86                        .collect::<Result<_, _>>()?,
87                    variadic: None,
88                    output: match &f.ret {
89                        Some(ty) => {
90                            syn::ReturnType::Type(token::RArrow::default(), Box::new(ty.to_syn()?))
91                        }
92                        None => syn::ReturnType::Default,
93                    },
94                },
95                block: f.body.to_syn()?,
96            }),
97            PureImplItem::Const(c) => syn::ImplItem::Const(syn::ImplItemConst {
98                attrs: c
99                    .attrs
100                    .iter()
101                    .map(|a| a.to_syn())
102                    .collect::<Result<Vec<_>, _>>()?,
103                vis: c.vis.to_syn()?,
104                defaultness: None,
105                const_token: token::Const::default(),
106                ident: ident(&c.name),
107                generics: syn::Generics::default(),
108                colon_token: token::Colon::default(),
109                ty: c.ty.to_syn()?,
110                eq_token: token::Eq::default(),
111                expr: c
112                    .value
113                    .as_ref()
114                    .ok_or_else(|| ToSynError::MissingValue {
115                        context: format!("ImplItemConst '{}' must have a value", c.name),
116                    })?
117                    .to_syn()?,
118                semi_token: token::Semi::default(),
119            }),
120            PureImplItem::Type(t) => syn::ImplItem::Type(syn::ImplItemType {
121                attrs: t
122                    .attrs
123                    .iter()
124                    .map(|a| a.to_syn())
125                    .collect::<Result<Vec<_>, _>>()?,
126                vis: t.vis.to_syn()?,
127                defaultness: None,
128                type_token: token::Type::default(),
129                ident: ident(&t.name),
130                generics: t.generics.to_syn()?,
131                eq_token: token::Eq::default(),
132                ty: t.ty.to_syn()?,
133                semi_token: token::Semi::default(),
134            }),
135            PureImplItem::Other(s) => syn::parse_str(s).map_err(|e| ToSynError::Other {
136                message: format!("Failed to parse impl item '{}': {}", s, e),
137            })?,
138        })
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::pure::ast::{PureBlock, PureFn, PureGenerics, PureType, PureVis};
146    use quote::ToTokens;
147
148    #[test]
149    fn test_pure_impl_inherent() {
150        let impl_block = PureImpl {
151            attrs: vec![],
152            generics: PureGenerics::default(),
153            is_unsafe: false,
154            trait_: None,
155            self_ty: "Foo".to_string(),
156            items: vec![],
157        };
158        let syn_impl = impl_block.to_syn().unwrap();
159        let output = syn_impl.to_token_stream().to_string();
160        assert!(output.contains("impl"), "Output: {}", output);
161        assert!(output.contains("Foo"), "Output: {}", output);
162    }
163
164    #[test]
165    fn test_pure_impl_trait() {
166        let impl_block = PureImpl {
167            attrs: vec![],
168            generics: PureGenerics::default(),
169            is_unsafe: false,
170            trait_: Some("Clone".to_string()),
171            self_ty: "Foo".to_string(),
172            items: vec![],
173        };
174        let syn_impl = impl_block.to_syn().unwrap();
175        let output = syn_impl.to_token_stream().to_string();
176        assert!(output.contains("Clone"), "Output: {}", output);
177        assert!(output.contains("for"), "Output: {}", output);
178    }
179
180    #[test]
181    fn test_pure_impl_with_method() {
182        let impl_block = PureImpl {
183            attrs: vec![],
184            generics: PureGenerics::default(),
185            is_unsafe: false,
186            trait_: None,
187            self_ty: "Foo".to_string(),
188            items: vec![PureImplItem::Fn(PureFn {
189                attrs: vec![],
190                vis: PureVis::Public,
191                is_async: false,
192                is_async_inferred: false,
193                is_const: false,
194                is_unsafe: false,
195                abi: None,
196                name: "new".to_string(),
197                generics: PureGenerics::default(),
198                params: vec![],
199                ret: Some(PureType::Path("Self".to_string())),
200                body: PureBlock::default(),
201            })],
202        };
203        let syn_impl = impl_block.to_syn().unwrap();
204        let output = syn_impl.to_token_stream().to_string();
205        assert!(output.contains("new"), "Output: {}", output);
206        assert!(output.contains("Self"), "Output: {}", output);
207    }
208}