zorsh_gen_rs/
lib.rs

1pub mod code_generator;
2pub mod converter;
3pub mod dependency_resolver;
4pub mod source_loader;
5pub mod type_parser;
6
7use anyhow::Result;
8use clap::ValueEnum;
9use std::path::Path;
10
11// Re-export main types for easier usage
12pub use code_generator::ZorshGenerator;
13pub use converter::ZorshConverter;
14pub use dependency_resolver::DependencyResolver;
15pub use source_loader::SourceLoader;
16pub use type_parser::TypeParser;
17
18/// Configuration options for the Zorsh generator
19#[derive(Debug, Clone)]
20pub struct Config {
21    /// Only process structs with #[derive(BorshSerialize)]
22    pub only_annotated: bool,
23    /// Skip files and directories matching these patterns
24    pub ignored_patterns: Vec<String>,
25    /// Output directory structure (flat or nested)
26    pub output_structure: OutputStructure,
27}
28
29#[derive(Debug, Clone, ValueEnum)]
30pub enum OutputStructure {
31    /// Maintain the same directory structure as input
32    Nested,
33    /// Put all files in a single directory
34    Flat,
35}
36
37impl Default for Config {
38    fn default() -> Self {
39        Self {
40            only_annotated: true,
41            ignored_patterns: vec![
42                "tests/".to_string(),
43                "examples/".to_string(),
44                "target/".to_string(),
45            ],
46            output_structure: OutputStructure::Nested,
47        }
48    }
49}
50
51/// Main entry point for the library
52pub struct ZorshGen {
53    config: Config,
54}
55
56impl ZorshGen {
57    pub fn new(config: Config) -> Self {
58        Self { config }
59    }
60
61    /// Convert Rust files in input_path to Zorsh TypeScript files in output_path
62    pub fn convert<P: AsRef<Path>>(&self, input_path: P, output_path: P) -> Result<()> {
63        let converter = ZorshConverter::new(input_path, output_path, self.config.clone());
64        converter.convert()
65    }
66
67    /// Process a single Rust file and return the generated Zorsh code as a string
68    pub fn convert_str(&self, rust_code: &str) -> Result<String> {
69        let mut parser = TypeParser::new("root".to_string(), self.config.only_annotated);
70        parser.parse_file(rust_code)?;
71
72        let resolver = DependencyResolver::new(parser.structs.clone(), parser.enums.clone());
73        let dependencies = resolver.resolve()?;
74
75        let generator = ZorshGenerator::new(parser.structs, parser.enums);
76
77        // Since we're processing a single string, treat it as a single module
78        generator.generate_module("root", &dependencies)
79    }
80}
81
82// Convenience function for quick conversions
83pub fn convert_str(rust_code: &str) -> Result<String> {
84    ZorshGen::new(Config::default()).convert_str(rust_code)
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_basic_struct() -> Result<()> {
93        let input = r#"
94            #[derive(BorshSerialize)]
95            struct User {
96                name: String,
97                age: u32,
98            }
99        "#;
100
101        let output = convert_str(input)?;
102        assert!(output.contains("export const UserSchema"));
103        assert!(output.contains("b.string()"));
104        assert!(output.contains("b.u32()"));
105        Ok(())
106    }
107
108    #[test]
109    fn test_complex_types() -> Result<()> {
110        let input = r#"
111            #[derive(BorshSerialize)]
112            struct Player {
113                inventory: HashMap<String, Vec<Item>>,
114                status: Option<PlayerStatus>,
115            }
116
117            #[derive(BorshSerialize)]
118            struct Item {
119                name: String,
120                quantity: u32,
121            }
122
123            #[derive(BorshSerialize)]
124            enum PlayerStatus {
125                Idle,
126                Fighting,
127            }
128        "#;
129
130        let output = convert_str(input)?;
131        assert!(output.contains("b.hashMap("));
132        assert!(output.contains("b.vec("));
133        assert!(output.contains("b.option("));
134        Ok(())
135    }
136}