1pub mod package {
2 use serde::Serialize;
3 use std::io::ErrorKind;
4 use std::{
5 fs::{self, DirEntry},
6 io::Error,
7 path::Path,
8 };
9 use tucana::shared::{DefinitionDataType, FlowType, RuntimeFunctionDefinition};
10
11 #[derive(Serialize, Clone, Debug)]
12 pub struct DefinitionError {
13 pub definition: String,
14 pub definition_type: MetaType,
15 pub error: String,
16 }
17
18 #[derive(Debug)]
19 pub struct Parser {
20 pub features: Vec<Feature>,
21 }
22
23 #[derive(Serialize, Clone, Debug)]
24 pub struct Feature {
25 pub name: String,
26 pub data_types: Vec<DefinitionDataType>,
27 pub flow_types: Vec<FlowType>,
28 pub runtime_functions: Vec<RuntimeFunctionDefinition>,
29 pub errors: Vec<DefinitionError>,
30 }
31
32 impl Feature {
33 fn new(name: String) -> Self {
34 Feature {
35 name,
36 data_types: Vec::new(),
37 flow_types: Vec::new(),
38 runtime_functions: Vec::new(),
39 errors: Vec::new(),
40 }
41 }
42 }
43
44 impl Parser {
45 pub fn from_path(path: &str) -> Option<Self> {
46 let reader = Reader::from_path(path)?;
47
48 Some(Self::from_reader(reader))
49 }
50
51 pub fn from_reader(reader: Reader) -> Self {
52 let mut features: Vec<Feature> = vec![];
53
54 for meta in &reader.meta {
55 let feature = features.iter_mut().find(|f| f.name == meta.name);
56
57 if let Some(existing) = feature {
58 Parser::append_meta(existing, meta);
59 } else {
60 let mut new_feature = Feature::new(meta.name.clone());
61 Parser::append_meta(&mut new_feature, meta);
62 features.push(new_feature);
63 }
64 }
65
66 Parser { features }
67 }
68
69 pub fn extract_identifier(definition: &str, meta_type: MetaType) -> String {
70 let field_name = match meta_type {
71 MetaType::DataType | MetaType::FlowType => "identifier",
72 MetaType::RuntimeFunction => "runtime_name",
73 };
74
75 if let Some(start) = definition.find(&format!("\"{field_name}\"")) {
77 if let Some(colon_pos) = definition[start..].find(':') {
79 let after_colon = &definition[start + colon_pos + 1..];
80
81 let trimmed = after_colon.trim_start();
83 if let Some(stripped) = trimmed.strip_prefix('"') {
84 if let Some(end_quote) = stripped.find('"') {
86 return trimmed[1..end_quote + 1].to_string();
87 }
88 }
89 }
90 }
91
92 definition.to_string()
94 }
95
96 fn append_meta(feature: &mut Feature, meta: &Meta) {
97 let definition = meta.definition_string.as_str();
98 match meta.r#type {
99 MetaType::DataType => {
100 match serde_json::from_str::<DefinitionDataType>(definition) {
101 Ok(data_type) => feature.data_types.push(data_type),
102 Err(err) => feature.errors.push(DefinitionError {
103 definition: Parser::extract_identifier(definition, MetaType::DataType),
104 definition_type: MetaType::DataType,
105 error: err.to_string(),
106 }),
107 }
108 }
109 MetaType::FlowType => match serde_json::from_str::<FlowType>(definition) {
110 Ok(flow_type) => feature.flow_types.push(flow_type),
111 Err(err) => feature.errors.push(DefinitionError {
112 definition: Parser::extract_identifier(definition, MetaType::FlowType),
113 definition_type: MetaType::FlowType,
114 error: err.to_string(),
115 }),
116 },
117 MetaType::RuntimeFunction => {
118 match serde_json::from_str::<RuntimeFunctionDefinition>(definition) {
119 Ok(func) => feature.runtime_functions.push(func),
120 Err(err) => feature.errors.push(DefinitionError {
121 definition: Parser::extract_identifier(
122 definition,
123 MetaType::RuntimeFunction,
124 ),
125 definition_type: MetaType::RuntimeFunction,
126 error: err.to_string(),
127 }),
128 }
129 }
130 }
131 }
132 }
133
134 #[derive(Serialize, Debug, Clone, Copy)]
135 pub enum MetaType {
136 FlowType,
137 DataType,
138 RuntimeFunction,
139 }
140
141 impl std::fmt::Display for MetaType {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 match self {
144 MetaType::FlowType => write!(f, "FlowType"),
145 MetaType::DataType => write!(f, "DataType"),
146 MetaType::RuntimeFunction => write!(f, "RuntimeFunction"),
147 }
148 }
149 }
150
151 pub struct Reader {
152 pub meta: Vec<Meta>,
153 }
154
155 #[derive(Clone)]
156 pub struct Meta {
157 pub name: String,
158 pub r#type: MetaType,
159 pub definition_string: String,
160 pub path: String,
161 }
162
163 impl Meta {
164 pub fn read_from_file<P>(
165 name: String,
166 r#type: MetaType,
167 file_path: P,
168 ) -> Result<Meta, Error>
169 where
170 P: AsRef<Path>,
171 {
172 let path = match file_path.as_ref().to_str() {
173 Some(path) => path,
174 None => return Err(Error::new(ErrorKind::InvalidInput, "Invalid path")),
175 };
176
177 if !path.ends_with("json") {
178 return Err(Error::new(
179 ErrorKind::InvalidInput,
180 format!(
181 "File {} does not end with .json",
182 file_path.as_ref().display()
183 ),
184 ));
185 }
186
187 let content = match fs::read_to_string(&file_path) {
188 Ok(content) => content,
189 Err(err) => {
190 println!("Error reading file: {err}");
191 return Err(err);
192 }
193 };
194
195 Ok(Meta {
196 name,
197 r#type,
198 definition_string: content,
199 path: path.to_string(),
200 })
201 }
202 }
203
204 impl Reader {
217 pub fn from_path(path: &str) -> Option<Reader> {
218 let mut result: Vec<Meta> = vec![];
219
220 for feature_path in fs::read_dir(path).unwrap() {
222 let feature_path_result = match feature_path {
223 Ok(path) => path,
224 Err(_) => continue,
225 };
226
227 let feature_name = match get_file_name(&feature_path_result) {
228 Some(file_name) => file_name,
229 None => continue,
230 };
231
232 for type_path in fs::read_dir(feature_path_result.path()).unwrap() {
234 let type_path_result = match type_path {
235 Ok(path) => path,
236 Err(_) => continue,
237 };
238
239 let meta_type = match get_file_name(&type_path_result) {
240 Some(name) => match name.as_str() {
241 "flow_type" => MetaType::FlowType,
242 "data_type" => MetaType::DataType,
243 "runtime_definition" => MetaType::RuntimeFunction,
244 _ => continue,
245 },
246 None => continue,
247 };
248
249 for definition_path in fs::read_dir(type_path_result.path()).unwrap() {
251 let definition_path_result = match definition_path {
252 Ok(path) => path,
253 Err(_) => continue,
254 };
255
256 if definition_path_result.file_type().unwrap().is_file() {
257 let meta = Meta::read_from_file(
258 feature_name.clone(),
259 meta_type,
260 definition_path_result.path(),
261 );
262
263 if let Ok(meta_result) = meta {
264 result.push(meta_result);
265 }
266 } else {
267 for sub_definition_path in
268 fs::read_dir(definition_path_result.path()).unwrap()
269 {
270 let sub_definition_path_result = match sub_definition_path {
271 Ok(path) => path,
272 Err(_) => continue,
273 };
274
275 let meta = Meta::read_from_file(
276 feature_name.clone(),
277 meta_type,
278 sub_definition_path_result.path(),
279 );
280
281 if let Ok(meta_result) = meta {
282 result.push(meta_result);
283 }
284 }
285 }
286 }
287 }
288 }
289
290 Some(Reader { meta: result })
291 }
292 }
293
294 fn get_file_name(entry: &DirEntry) -> Option<String> {
295 entry
296 .file_name()
297 .to_str()
298 .map(|file_name| file_name.to_string())
299 }
300}