1use std::collections::HashSet;
5use std::fs::{create_dir_all, read_dir};
6
7use crate::DataStore;
8
9use super::util::{get_crate_info, get_project_top_level_path, lookup_rust_api_data_type, lookup_rust_api_ndata_method_suffix};
10
11pub(crate) fn rebuild_rust_api() {
13 let store = DataStore::new();
14 let project_top_level_path = get_project_top_level_path();
15
16 let mut crates = HashSet::new();
17 let lib_entries_for_crates = read_dir("data")
18 .expect("Failed to read 'data' directory for API rebuilding.");
19 for dir_entry_result in lib_entries_for_crates {
20 let dir_entry = dir_entry_result.expect("Error reading a directory entry in 'data'.");
21 let lib_name = dir_entry.file_name().into_string().expect("Library name is not valid UTF-8.");
22 let lib_metadata = store.lib_info(&lib_name);
23 let (root_name, _) = get_crate_info(&lib_metadata);
24 if root_name != "." {
25 crates.insert(root_name);
26 }
27 }
28
29 let mut api_struct_init_str = "pub const fn new() -> api {\n api {\n".to_string();
30 let mut api_struct_def_str = "pub struct api {\n".to_string();
31 let mut control_struct_defs_str = String::new();
32 let mut command_wrapper_struct_defs_str = String::new();
33 let mut impl_blocks_str = String::new();
34
35 let lib_entries = read_dir("data")
36 .expect("Failed to read 'data' directory for API rebuilding.");
37
38 for db_result in lib_entries {
39 let lib_entry = db_result.expect("Error reading library entry for API rebuilding.");
40 let lib_name = lib_entry.file_name().into_string()
41 .expect("Library name is not valid UTF-8 for API rebuilding.");
42
43 if store.exists(&lib_name, "controls") {
44 let safe_lib_name = lib_name.replace("-", "_");
45 api_struct_init_str.push_str(&format!(" {}: {} {{\n", safe_lib_name, safe_lib_name));
46 api_struct_def_str.push_str(&format!(" pub {}: {},\n", safe_lib_name, safe_lib_name));
47 control_struct_defs_str.push_str(&format!("pub struct {} {{\n", safe_lib_name));
48
49 let controls_data = store.get_data(&lib_name, "controls");
50 let list = controls_data.get_object("data").get_array("list");
51
52 for control_val in list.objects() {
53 let control = control_val.object();
54 let ctl_name = control.get_string("name");
55 let safe_ctl_name = ctl_name.replace("-", "_");
56 let ctl_id = control.get_string("id");
57
58 if store.exists(&lib_name, &ctl_id) {
59 let struct_name = format!("{}_{}", safe_lib_name, safe_ctl_name);
60 api_struct_init_str.push_str(&format!(" {}: {} {{}},\n", safe_ctl_name, struct_name));
61 control_struct_defs_str.push_str(&format!(" pub {}: {},\n", safe_ctl_name, struct_name));
62 command_wrapper_struct_defs_str.push_str(&format!("pub struct {} {{}}\n", struct_name));
63
64 let ctldata = store.get_data(&lib_name, &ctl_id);
65 let d = ctldata.get_object("data");
66
67 if d.has("cmd") {
68 let cmdlist = d.get_array("cmd");
69 if cmdlist.len() > 0 {
70 impl_blocks_str.push_str(&format!("impl {} {{\n", struct_name));
71 for command_val in cmdlist.objects() {
72 let command = command_val.object();
73 let cmd_name = command.get_string("name");
74 let safe_cmd_name = cmd_name.replace("-", "_");
75 let cmd_id_in_control = command.get_string("id");
76
77 if store.exists(&lib_name, &cmd_id_in_control) {
78 let meta_for_cmd_type = store.get_data(&lib_name, &cmd_id_in_control);
79 let data_for_cmd_type = meta_for_cmd_type.get_object("data");
80
81 if data_for_cmd_type.has("type") {
83 if data_for_cmd_type.get_string("type") == "rust" {
84 let rust_meta_file_id = data_for_cmd_type.get_string("rust");
85 let rust_cmd_actual_meta = store.get_data(&lib_name, &rust_meta_file_id).get_object("data");
86 let params_array = rust_cmd_actual_meta.get_array("params");
87 let rtype_str = rust_cmd_actual_meta.get_string("returntype");
88 let ntype_ret = lookup_rust_api_ndata_method_suffix(&rtype_str);
89 let rtype_rust = lookup_rust_api_data_type(&rtype_str);
90
91 let mut params_str_for_fn_def = String::new();
92 let mut params_setup_str_for_body = String::new();
93
94 for param_val in params_array.objects() {
95 let param = param_val.object();
96 let pname = param.get_string("name");
97 let ptype = param.get_string("type");
98 let dtype = lookup_rust_api_data_type(&ptype);
99 let ntype = lookup_rust_api_ndata_method_suffix(&ptype);
100 params_str_for_fn_def.push_str(&format!(", {}: {}", pname, dtype));
101 let q = if dtype == "String" { "&" } else { "" };
102 let method_prefix = if ntype == "property" { "set" } else { "put" };
103 params_setup_str_for_body.push_str(&format!(
104 " d.{}_{}(\"{}\", {}{});\n",
105 method_prefix, ntype, pname, q, pname
106 ));
107 }
108
109 impl_blocks_str.push_str(&format!(" pub fn {} (&self{}", safe_cmd_name, params_str_for_fn_def));
110 impl_blocks_str.push_str(&format!(") -> {} {{\n", rtype_rust));
111
112 let d_mut = if params_array.len() > 0 { "mut " } else { "" };
113 impl_blocks_str.push_str(&format!(" let {}d = ndata::dataobject::DataObject::new();\n", d_mut));
114
115 impl_blocks_str.push_str(¶ms_setup_str_for_body);
116 impl_blocks_str.push_str(&format!(
117 " flowlang::rustcmd::RustCmd::new(\"{}\").execute(d).expect(\"Rust command execution failed\").get_{}(\"a\")\n }}\n",
118 rust_meta_file_id,
119 ntype_ret
120 ));
121 }
122 }
123 }
124 }
125 impl_blocks_str.push_str("}\n");
126 }
127 }
128 }
129 }
130 api_struct_init_str.push_str(" },\n");
131 control_struct_defs_str.push_str("}\n");
132 }
133 }
134 api_struct_init_str.push_str(" }\n}\n");
135 api_struct_def_str.push_str("}");
136
137 let use_statements = r#"#![allow(non_camel_case_types, unused_variables)]
139use ndata::dataobject::DataObject;
140use ndata::dataarray::DataArray;
141use ndata::databytes::DataBytes;
142use ndata::data::Data;
143"#;
144 let final_api_code = format!(
147 "{}\n{}\n{}\n{}\n{}\n{}",
148 use_statements,
149 command_wrapper_struct_defs_str,
150 control_struct_defs_str,
151 api_struct_def_str,
152 api_struct_init_str,
153 impl_blocks_str
154 );
155
156 for crate_name in crates {
157 let api_file_path = project_top_level_path.join(crate_name).join("src").join("api.rs");
158 if let Some(parent_dir) = api_file_path.parent() {
159 create_dir_all(parent_dir).expect(&format!("Failed to create directory for api.rs: {:?}", parent_dir));
160 }
161 std::fs::write(&api_file_path, &final_api_code)
162 .expect(&format!("Unable to write API file to {:?}", api_file_path));
163 }
164}