1#[cfg(feature = "debugger")]
2use std::collections::BTreeMap;
3use std::collections::BTreeSet;
4use std::collections::HashMap;
5use std::fmt;
6use std::fmt::Display;
7
8use serde_derive::{Deserialize, Serialize};
9use url::Url;
10
11use crate::deserializers::deserializer::get;
12use crate::errors::{Result, ResultExt};
13use crate::model::flow_definition::FlowDefinition;
14use crate::model::metadata::MetaData;
15use crate::model::runtime_function::RuntimeFunction;
16use crate::provider::Provider;
17
18pub const DEFAULT_MANIFEST_FILENAME: &str = "manifest";
20
21impl From<&FlowDefinition> for MetaData {
22 fn from(flow: &FlowDefinition) -> Self {
23 flow.metadata.clone()
24 }
25}
26
27#[derive(Clone, Deserialize, Serialize, PartialEq, Eq)]
28pub struct Cargo {
30 pub package: MetaData,
32}
33
34#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)]
35pub struct FlowInfo {
37 pub process_id: usize,
39 pub parent_id: Option<usize>,
41 pub sub_flow_ids: Vec<usize>,
43}
44
45#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)]
46pub struct FlowManifest {
49 metadata: MetaData,
51 lib_references: BTreeSet<Url>,
53 context_references: BTreeSet<Url>,
55 functions: HashMap<usize, RuntimeFunction>,
57 #[serde(default)]
59 flows: HashMap<usize, FlowInfo>,
60 #[cfg(feature = "debugger")]
61 source_urls: BTreeMap<String, Url>,
63}
64
65impl Display for FlowManifest {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 for function in self.functions.values() {
68 writeln!(
69 f,
70 " Function #{} Implementation: {}",
71 function.id(),
72 function.get_implementation_url()
73 )?;
74 }
75 write!(f, "")
76 }
77}
78
79impl FlowManifest {
80 #[must_use]
82 pub fn new(metadata: MetaData) -> Self {
83 FlowManifest {
84 metadata,
85 lib_references: BTreeSet::<Url>::new(),
86 context_references: BTreeSet::<Url>::new(),
87 functions: HashMap::new(),
88 flows: HashMap::new(),
89 #[cfg(feature = "debugger")]
90 source_urls: BTreeMap::<String, Url>::new(),
91 }
92 }
93
94 pub fn add_function(&mut self, function: RuntimeFunction) {
96 self.functions.insert(function.id(), function);
97 }
98
99 pub fn add_flow_info(&mut self, flow_info: FlowInfo) {
101 self.flows.insert(flow_info.process_id, flow_info);
102 }
103
104 #[must_use]
106 pub fn flows(&self) -> &HashMap<usize, FlowInfo> {
107 &self.flows
108 }
109
110 #[must_use]
112 pub fn functions(&self) -> &HashMap<usize, RuntimeFunction> {
113 &self.functions
114 }
115
116 pub fn get_functions(&mut self) -> &mut HashMap<usize, RuntimeFunction> {
118 &mut self.functions
119 }
120
121 #[must_use]
123 pub fn take_functions(self) -> HashMap<usize, RuntimeFunction> {
124 self.functions
125 }
126
127 #[must_use]
129 pub fn get_metadata(&self) -> &MetaData {
130 &self.metadata
131 }
132
133 #[must_use]
135 pub fn get_lib_references(&self) -> &BTreeSet<Url> {
136 &self.lib_references
137 }
138
139 #[must_use]
141 pub fn get_context_references(&self) -> &BTreeSet<Url> {
142 &self.context_references
143 }
144
145 pub fn set_lib_references(&mut self, lib_references: &BTreeSet<Url>) {
147 self.lib_references.clone_from(lib_references);
148 }
149
150 pub fn set_context_references(&mut self, context_references: &BTreeSet<Url>) {
152 self.context_references.clone_from(context_references);
153 }
154
155 pub fn add_lib_reference(&mut self, lib_reference: &Url) {
157 self.lib_references.insert(lib_reference.clone());
158 }
159
160 pub fn add_context_reference(&mut self, context_reference: &Url) {
162 self.context_references.insert(context_reference.clone());
163 }
164
165 #[cfg(feature = "debugger")]
167 pub fn set_source_urls(&mut self, source_urls: BTreeMap<String, Url>) {
168 self.source_urls = source_urls;
169 }
170
171 #[cfg(feature = "debugger")]
173 #[must_use]
174 pub fn get_source_urls(&self) -> &BTreeMap<String, Url> {
175 &self.source_urls
176 }
177
178 pub fn load(provider: &dyn Provider, manifest_url: &Url) -> Result<(FlowManifest, Url)> {
187 let (resolved_url, _) = provider
188 .resolve_url(manifest_url, DEFAULT_MANIFEST_FILENAME, &["json"])
189 .chain_err(|| "Could not resolve url for manifest.json")?;
190
191 let contents = provider
192 .get_contents(&resolved_url)
193 .chain_err(|| "Could not get contents while attempting to load manifest")?;
194
195 let url = resolved_url.clone();
196 let content =
197 String::from_utf8(contents).chain_err(|| "Could not convert from utf8 to String")?;
198 let deserializer = get::<FlowManifest>(&resolved_url)?;
199 let mut manifest = deserializer
200 .deserialize(&content, Some(&resolved_url))
201 .chain_err(|| format!("Could not create a FlowManifest from '{manifest_url}'"))?;
202
203 for function in manifest.functions.values_mut() {
207 function.set_implementation_url(&resolved_url)?;
208 }
209
210 Ok((manifest, url))
211 }
212}
213
214#[cfg(test)]
215#[allow(clippy::unwrap_used, clippy::expect_used)]
216mod test {
217 use url::Url;
218
219 use crate::errors::Result;
220 use crate::model::input::Input;
221 use crate::model::runtime_function::RuntimeFunction;
222 use crate::provider::Provider;
223
224 use super::{FlowManifest, MetaData};
225
226 fn test_meta_data() -> MetaData {
227 MetaData {
228 name: "test".into(),
229 version: "0.0.0".into(),
230 description: "a test".into(),
231 authors: vec!["me".into()],
232 }
233 }
234
235 #[allow(clippy::module_name_repetitions)]
236 pub struct TestProvider {
237 test_content: &'static str,
238 }
239
240 impl Provider for TestProvider {
241 fn resolve_url(
242 &self,
243 source: &Url,
244 _default_filename: &str,
245 _extensions: &[&str],
246 ) -> Result<(Url, Option<Url>)> {
247 Ok((source.clone(), None))
248 }
249
250 fn get_contents(&self, _url: &Url) -> Result<Vec<u8>> {
251 Ok(self.test_content.as_bytes().to_owned())
252 }
253 }
254
255 #[test]
256 fn create() {
257 let _ = FlowManifest::new(test_meta_data());
258 }
259
260 fn test_function() -> RuntimeFunction {
261 RuntimeFunction::new(
262 #[cfg(feature = "debugger")]
263 "test",
264 #[cfg(feature = "debugger")]
265 "/test",
266 "file://fake/test",
267 vec![Input::new(
268 #[cfg(feature = "debugger")]
269 "",
270 0,
271 false,
272 None,
273 None,
274 )],
275 0,
276 0,
277 &[],
278 false,
279 )
280 }
281
282 #[test]
283 fn add_function() {
284 let function = test_function();
285
286 let mut manifest = FlowManifest::new(test_meta_data());
287 manifest.add_function(function);
288 assert_eq!(manifest.functions.len(), 1);
289 }
290
291 #[test]
292 fn load_manifest() {
293 let test_content = "{
294 \"metadata\": {
295 \"name\": \"\",
296 \"version\": \"0.1.0\",
297 \"description\": \"\",
298 \"authors\": []
299 },
300 \"manifest_dir\": \"fake dir\",
301 \"lib_references\": [
302 ],
303 \"context_references\": [
304 \"context://\"
305 ],
306 \"functions\": {
307 \"0\": {
308 \"name\": \"print\",
309 \"route\": \"/print\",
310 \"process_id\": 0,
311 \"parent_id\": 0,
312 \"implementation_location\": \"context://stdio/stdout\",
313 \"inputs\": [ {} ]
314 }
315 },
316 \"source_urls\": {}
317 }";
318 let provider = TestProvider { test_content };
319
320 FlowManifest::load(
321 &provider,
322 &Url::parse("http://ibm.com/fake.json").expect("Could not parse URL"),
323 )
324 .expect("Could not load manifest");
325 }
326}