zits/
lib.rs

1#![allow(unused_doc_comments)]
2
3mod to_typescript;
4mod typescript;
5pub mod utils;
6pub(crate) mod casing;
7pub(crate) mod holochain_imports;
8mod parser;
9
10
11use std::fs::File;
12use std::io::{BufRead, BufReader, Write};
13use std::path::{Path, PathBuf};
14use walkdir::WalkDir;
15use crate::parser::*;
16
17
18const MAGIC_FIRST_LINE: &str = "/* This file is generated by zits. Do not edit manually */";
19
20
21pub struct GenConfig {
22    can_debug: bool,
23    //can_proxy: bool,
24    can_hc_imports: bool,
25    uses_typeinterface: bool,
26    zome_name: String,
27}
28
29
30///
31pub fn generate_typescript_bindings(
32    input: Vec<PathBuf>,
33    external_imports: Vec<String>,
34    output: PathBuf,
35    can_debug: bool,
36    can_hc_imports: bool,
37    can_proxy: bool,
38    can_fns: bool,
39    external_fns: Vec<String>,
40    maybe_default_zome_name: Option<String>,
41) {
42    let uses_typeinterface = output
43       .as_os_str()
44       .to_str()
45       .map(|x| x.ends_with(".d.ts"))
46       .unwrap_or(true);
47
48
49    let file_name = output.file_stem().unwrap().to_str().unwrap();
50    let zome_name: &str = file_name.split(".").collect::<Vec<&str>>()[0];
51    let default_zome_name: String = if let Some(dzn) = maybe_default_zome_name {
52        dzn
53    } else {
54        zome_name.to_string()
55    };
56
57    let mut state: ParseState = ParseState::new(
58        GenConfig {
59            can_debug,
60            //can_proxy,
61            can_hc_imports,
62            uses_typeinterface,
63            zome_name: zome_name.to_string(),
64        });
65
66    state.set_external_import_header(external_imports);
67
68    if !can_debug {
69        state.write_type_defs_header();
70        state.write_zome_fn_names_header(&zome_name);
71        state.write_zome_integrity_header(&zome_name, &default_zome_name);
72    }
73
74
75    /// Parse input files
76    for input_path in input {
77        if !input_path.exists() {
78            if can_debug {
79                println!("Path `{:#?}` does not exist", input_path);
80            }
81
82            state.unprocessed_files.push(input_path);
83            continue;
84        }
85
86        if input_path.is_dir() {
87            for entry in WalkDir::new(input_path.clone()).sort_by_file_name() {
88                match entry {
89                    Ok(dir_entry) => {
90                        let path = dir_entry.into_path();
91
92                        // skip dir files because they're going to be recursively crawled by WalkDir
93                        if !path.is_dir() {
94                            // make sure it is a rust file
95                            let extension = path.extension();
96                            if extension.is_some() && extension.unwrap().eq_ignore_ascii_case("rs")
97                            {
98                                state.parse_rust_file(path);
99                            } else if can_debug {
100                                println!("Encountered non-rust file `{:#?}`", path);
101                            }
102                        } else if can_debug {
103                            println!("Encountered directory `{:#?}`", path);
104                        }
105                    }
106                    Err(_) => {
107                        println!(
108                            "An error occurred whilst walking directory `{:#?}`...",
109                            input_path.clone()
110                        );
111                        continue;
112                    }
113                }
114            }
115        } else {
116            state.parse_rust_file(input_path);
117        }
118    }
119
120
121
122    if can_proxy {
123        /// Append header
124        let copy = state.zome_proxy_output.clone();
125        state.zome_proxy_output.clear();
126        state.write_zome_proxy_header(&zome_name, &default_zome_name);
127        state.zome_proxy_output.push_str(&copy);
128        /// ZomeProxy file footer
129        state.zome_proxy_output.push_str(&format!("}}\n"));
130        /// Append type imports to ZomeProxy
131        state.write_type_defs_import(&zome_name);
132        /// Fn footer
133        state.write_zome_fn_names_footer(external_fns, &zome_name, &default_zome_name);
134    }
135
136    state.write_zome_integrity_footer(&zome_name, &default_zome_name);
137
138    /** */
139    if can_debug {
140        println!("\n");
141        println!("======================================");
142        println!("Debug mode try run output");
143        println!("======================================");
144        //println!("======================================");
145        println!("TYPE DEFS FILE for \"{}\"", zome_name);
146        println!("======================================");
147        println!("{}", state.type_defs_output);
148        println!("======================================");
149        ///
150        println!("INTEGRITY TYPES FILE for \"{}\"", zome_name);
151        println!("======================================");
152        println!("{}", state.zome_integrity_output);
153        println!("======================================");
154        ///
155        if can_proxy {
156            println!("ZomeProxy FILE for \"{}\"", zome_name);
157            println!("======================================");
158            println!("{}", state.zome_proxy_output);
159            println!("======================================");
160        }
161        if can_fns {
162            println!("Function Names for \"{}\"", zome_name);
163            println!("======================================");
164            println!("{}", state.zome_fn_names_output);
165            println!("======================================");
166        }
167    } else {
168        println!("======================================");
169        println!("Bindings generated for \"{}\"", zome_name);
170        println!("======================================");
171
172        let count_const = state.converted_items["const"].len();
173        let count_type = state.converted_items["type"].len();
174        let count_struct = state.converted_items["struct"].len();
175        let count_enum = state.converted_items["enum"].len();
176        let count_fn = state.converted_items["fn"].len();
177
178        println!("Total Items found: {}", count_const + count_type + count_struct + count_enum + count_fn);
179        if count_const > 0 {println!("  -  const: {}", count_const)}
180        if count_type > 0 {println!("  -   type: {}", count_type)}
181        if count_struct > 0 {println!("  - struct: {}", count_struct)}
182        if count_enum > 0 {println!("  -   enum: {}", count_enum)}
183        if count_fn > 0 {println!("  -     fn: {}", count_fn)}
184
185        // Verify that the output file either doesn't exists or has been generated by zits.
186        let original_file_path = Path::new(&output);
187        if original_file_path.exists() {
188            if !original_file_path.is_file() {
189                panic!("Specified output path is a directory but must be a file.")
190            }
191            let original_file = File::open(original_file_path).expect("Couldn't open output file");
192            let mut buffer = BufReader::new(original_file);
193
194            let mut first_line = String::new();
195
196            buffer
197                .read_line(&mut first_line)
198                .expect("Unable to read line");
199
200            if first_line.trim() != MAGIC_FIRST_LINE {
201                panic!("Aborting: specified output file exists but doesn't seem to be a zits output file: {}", first_line)
202            }
203        }
204
205        if count_const + count_type + count_struct + count_enum > 0 {
206            let mut types_output: PathBuf = output.clone();
207            types_output.set_file_name(format!("{}.types.ts", zome_name));
208            let mut types_file: File = File::create(&types_output).expect("Unable to write to file");
209            match types_file.write_all(state.type_defs_output.as_bytes()) {
210                Ok(_) => println!("Successfully generated types: {:#?}", types_output),
211                Err(_) => println!("Failed to generate types, an error occurred."),
212            }
213        } else {
214            println!("Types file not generated as no types have been found.");
215        }
216
217        /// Integrity file
218        let mut integrity_output: PathBuf = output.clone();
219        integrity_output.set_file_name(format!("{}.integrity.ts", zome_name));
220        //println!("integrity_output: {:?}", integrity_output);
221        let mut proxy_file: File = File::create(&integrity_output).expect("Unable to write to file");
222        match proxy_file.write_all(state.zome_integrity_output.as_bytes()) {
223            Ok(_) => println!("Successfully generated Integrity: {:#?}", integrity_output),
224            Err(_) => println!("Failed to generate Integrity, an error occurred."),
225        }
226
227        /// Proxy file
228        if can_proxy {
229            let mut proxy_output: PathBuf = output.clone();
230            proxy_output.set_file_name(format!("{}.proxy.ts", zome_name));
231            //println!("ProxyFile: {:?}", proxy_output);
232            let mut proxy_file: File = File::create(&proxy_output).expect("Unable to write to file");
233            match proxy_file.write_all(state.zome_proxy_output.as_bytes()) {
234                Ok(_) => println!("Successfully generated ZomeProxy: {:#?}", proxy_output),
235                Err(_) => println!("Failed to generate ZomeProxy, an error occurred."),
236            }
237        }
238
239        /// FnNames file
240        if can_fns {
241            if count_fn > 0 {
242                let mut fn_output: PathBuf = output.clone();
243                fn_output.set_file_name(format!("{}.fn.ts", zome_name));
244                //println!("ProxyFile: {:?}", proxy_output);
245                let mut fn_file: File = File::create(&fn_output).expect("Unable to write to file");
246                match fn_file.write_all(state.zome_fn_names_output.as_bytes()) {
247                    Ok(_) => println!("Successfully generated FnNames: {:#?}", fn_output),
248                    Err(_) => println!("Failed to generate FnNames, an error occurred."),
249                }
250            } else {
251                println!("FnNames file not generated as no functions have been found");
252            }
253        }
254    }
255
256    if state.unprocessed_files.len() > 0 {
257        println!("[zits][info] Could not parse the following files:");
258        for unprocessed_file in state.unprocessed_files {
259            println!("• {:#?}", unprocessed_file);
260        }
261    }
262    println!("======================================");
263}