code0_definition_reader/
lib.rs

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            // Look for the field pattern: "field_name": "value" or "field_name":"value"
76            if let Some(start) = definition.find(&format!("\"{field_name}\"")) {
77                // Find the colon after the field name
78                if let Some(colon_pos) = definition[start..].find(':') {
79                    let after_colon = &definition[start + colon_pos + 1..];
80
81                    // Skip whitespace and find the opening quote
82                    let trimmed = after_colon.trim_start();
83                    if let Some(stripped) = trimmed.strip_prefix('"') {
84                        // Find the closing quote
85                        if let Some(end_quote) = stripped.find('"') {
86                            return trimmed[1..end_quote + 1].to_string();
87                        }
88                    }
89                }
90            }
91
92            // Fallback: return the whole definition if identifier can't be extracted
93            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    /// Reader
205    ///
206    /// Expecting the file system to look like:
207    /// - <path>
208    ///   - <feature>
209    ///     - <flow_types>
210    ///     - <data_types>
211    ///     - <runtime_functions>
212    ///    - <feature>
213    ///     - <flow_types>
214    ///     - <data_types>
215    ///     - <runtime_functions>
216    impl Reader {
217        pub fn from_path(path: &str) -> Option<Reader> {
218            let mut result: Vec<Meta> = vec![];
219
220            // Reading the path folder
221            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                // Reading the feature folder
233                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                    // Reading the type folder
250                    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}