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
11pub 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#[derive(Debug, Clone)]
20pub struct Config {
21 pub only_annotated: bool,
23 pub ignored_patterns: Vec<String>,
25 pub output_structure: OutputStructure,
27}
28
29#[derive(Debug, Clone, ValueEnum)]
30pub enum OutputStructure {
31 Nested,
33 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
51pub struct ZorshGen {
53 config: Config,
54}
55
56impl ZorshGen {
57 pub fn new(config: Config) -> Self {
58 Self { config }
59 }
60
61 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 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 generator.generate_module("root", &dependencies)
79 }
80}
81
82pub 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}