flowrclib/compiler/
parser.rs1#[cfg(feature = "debugger")]
2use std::collections::BTreeMap;
3
4use log::{debug, info, trace};
5use url::Url;
6
7use flowcore::deserializers::deserializer::get_deserializer;
8use flowcore::model::flow_definition::FlowDefinition;
9use flowcore::model::flow_manifest::Cargo;
10use flowcore::model::input::InputInitializer;
11use flowcore::model::metadata::MetaData;
12use flowcore::model::name::HasName;
13use flowcore::model::name::Name;
14use flowcore::model::process::Process;
15use flowcore::model::process::Process::FlowProcess;
16use flowcore::model::process::Process::FunctionProcess;
17use flowcore::model::route::Route;
18use flowcore::provider::Provider;
19
20use crate::errors::*;
21
22#[derive(PartialEq, Eq)]
24pub enum LibType {
25 RustLib,
27}
28
29pub fn parse(
68 url: &Url,
69 provider: &dyn Provider,
70) -> Result<Process> {
71 parse_process(
72 &Route::default(),
73 &Name::default(),
74 0,
75 &mut 0,
76 url,
77 provider,
78 &BTreeMap::new(),
79 0,
80 )
81}
82
83#[allow(clippy::too_many_arguments)]
84fn parse_process(
85 parent_route: &Route,
86 alias: &Name,
87 parent_flow_id: usize,
88 flow_count: &mut usize,
89 url: &Url,
90 provider: &dyn Provider,
91 initializations: &BTreeMap<String, InputInitializer>,
92 level: usize,
93) -> Result<Process> {
94 let (resolved_url, reference) = provider
95 .resolve_url(url, "root", &["toml"])
96 .chain_err(|| format!("Could not resolve the url: '{url}'"))?;
97
98 let contents = provider
99 .get_contents(&resolved_url)
100 .chain_err(|| format!("Could not get contents of resolved url: '{resolved_url}'"))?;
101
102 if !alias.is_empty() {
103 info!("Loading process with alias = '{alias}'");
104 }
105
106 let content = String::from_utf8(contents).chain_err(|| "Could not read UTF8 contents")?;
107 let deserializer = get_deserializer::<Process>(&resolved_url)?;
108 debug!(
109 "Loading process from url = '{resolved_url}' with deserializer: '{}'", deserializer.name());
110 let mut process = deserializer
111 .deserialize(&content, Some(&resolved_url))
112 .chain_err(|| format!("Could not parse a valid flow process from '{url}'"))?;
113
114 match process {
115 FlowProcess(ref mut flow) => {
116 flow.config(
117 &resolved_url,
118 parent_route,
119 alias,
120 *flow_count,
121 initializations,
122 )?;
123 *flow_count += 1;
124 debug!("Deserialized the Flow, now parsing sub-processes");
125 parse_process_refs(
126 flow,
127 flow_count,
128 provider,
129 level,
130 )?;
131 flow.build_connections(level)?;
132 }
133 FunctionProcess(ref mut function) => {
134 function.config(
135 url,
136 &resolved_url,
137 parent_route,
138 alias,
139 parent_flow_id,
140 reference,
141 initializations,
142 )?;
143 }
144 }
145
146 Ok(process)
147}
148
149pub fn parse_metadata(url: &Url, provider: &dyn Provider) -> Result<(MetaData, LibType)> {
153 trace!("Loading Metadata");
154 let (resolved_url, _) = provider
155 .resolve_url(url, "Cargo", &["toml"])
156 .chain_err(|| format!("Could not resolve the url: '{url}'"))?;
157
158 if &resolved_url != url {
159 debug!("Source URL '{url}' resolved to: '{resolved_url}'");
160 }
161
162 let contents = provider
163 .get_contents(&resolved_url)
164 .chain_err(|| format!("Could not get contents of resolved url: '{resolved_url}'"))?;
165 let content = String::from_utf8(contents).chain_err(|| "Could not read UTF8 contents")?;
166
167 let deserializer = get_deserializer::<Cargo>(&resolved_url)?;
168
169 let cargo: Cargo = deserializer.deserialize(&content, Some(&resolved_url))?;
170
171 Ok((cargo.package, LibType::RustLib))
172}
173
174fn parse_process_refs(
178 flow: &mut FlowDefinition,
179 flow_count: &mut usize,
180 provider: &dyn Provider,
181 level: usize,
182) -> Result<()> {
183 for process_ref in &mut flow.process_refs {
184 let subprocess_url = flow
185 .source_url
186 .join(&process_ref.source)
187 .map_err(|e| e.to_string())?;
188 let process = parse_process(
189 &flow.route,
190 process_ref.alias(),
191 flow.id,
192 flow_count,
193 &subprocess_url,
194 provider,
195 &process_ref.initializations,
196 level + 1,
197 )?;
198 process_ref.set_alias(process.name());
199
200 if let FunctionProcess(function) = &process {
203 if let Some(lib_ref) = function.get_lib_reference() {
204 flow.lib_references.insert(lib_ref.clone());
205 }
206
207 if let Some(context_ref) = function.get_context_reference() {
208 flow.context_references.insert(context_ref.clone());
209 }
210 }
211
212 flow.subprocesses
213 .insert(process_ref.alias().to_owned(), process);
214 }
215
216 Ok(())
217}
218
219#[cfg(test)]
220mod test {
221 use url::Url;
222
223 use flowcore::deserializers::deserializer::get_deserializer;
224 use flowcore::model::flow_manifest::Cargo;
225 use flowcore::model::metadata::MetaData;
226
227 #[test]
228 fn deserialize_library() {
229 let cargo_toml = r#"[package]
230name = "Flow Standard Library"
231version = "0.11.0"
232authors = ["Andrew Mackenzie <andrew@mackenzie-serres.net>"]
233description = "The standard library for 'flow' programs compiled with the 'flowc' compiler"
234
235exclude = "../..""#;
236 let url = Url::parse("file:///fake.toml").expect("Could not parse URL");
237 let deserializer = get_deserializer::<Cargo>(&url).expect("Could not get deserializer");
238 let cargo: Cargo = deserializer
239 .deserialize(cargo_toml, Some(&url))
240 .expect("Could not deserialize");
241 let _: MetaData = cargo.package;
242 }
243}