ryo-source 0.1.0

High-speed Rust AST manipulation engine
Documentation
//! ToSyn implementations for const, static, type alias, mod, trait, and macro items.

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> {
        // Determine if this is a file module (mod foo;) or inline module (mod foo { ... })
        let has_content = !self.items.is_empty();

        // Build content from items
        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);
    }
}