#[cfg(feature = "debugger")]
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Display;
use serde_derive::{Deserialize, Serialize};
use url::Url;
use crate::deserializers::deserializer::get;
use crate::errors::{Result, ResultExt};
use crate::model::flow_definition::FlowDefinition;
use crate::model::metadata::MetaData;
use crate::model::runtime_function::RuntimeFunction;
use crate::provider::Provider;
pub const DEFAULT_MANIFEST_FILENAME: &str = "manifest";
impl From<&FlowDefinition> for MetaData {
fn from(flow: &FlowDefinition) -> Self {
flow.metadata.clone()
}
}
#[derive(Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct Cargo {
pub package: MetaData,
}
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)]
pub struct FlowInfo {
pub process_id: usize,
pub parent_id: Option<usize>,
pub sub_flow_ids: Vec<usize>,
}
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)]
pub struct FlowManifest {
metadata: MetaData,
lib_references: BTreeSet<Url>,
context_references: BTreeSet<Url>,
functions: HashMap<usize, RuntimeFunction>,
#[serde(default)]
flows: HashMap<usize, FlowInfo>,
#[cfg(feature = "debugger")]
source_urls: BTreeMap<String, Url>,
}
impl Display for FlowManifest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for function in self.functions.values() {
writeln!(
f,
" Function #{} Implementation: {}",
function.id(),
function.get_implementation_url()
)?;
}
write!(f, "")
}
}
impl FlowManifest {
#[must_use]
pub fn new(metadata: MetaData) -> Self {
FlowManifest {
metadata,
lib_references: BTreeSet::<Url>::new(),
context_references: BTreeSet::<Url>::new(),
functions: HashMap::new(),
flows: HashMap::new(),
#[cfg(feature = "debugger")]
source_urls: BTreeMap::<String, Url>::new(),
}
}
pub fn add_function(&mut self, function: RuntimeFunction) {
self.functions.insert(function.id(), function);
}
pub fn add_flow_info(&mut self, flow_info: FlowInfo) {
self.flows.insert(flow_info.process_id, flow_info);
}
#[must_use]
pub fn flows(&self) -> &HashMap<usize, FlowInfo> {
&self.flows
}
#[must_use]
pub fn functions(&self) -> &HashMap<usize, RuntimeFunction> {
&self.functions
}
pub fn get_functions(&mut self) -> &mut HashMap<usize, RuntimeFunction> {
&mut self.functions
}
#[must_use]
pub fn take_functions(self) -> HashMap<usize, RuntimeFunction> {
self.functions
}
#[must_use]
pub fn get_metadata(&self) -> &MetaData {
&self.metadata
}
#[must_use]
pub fn get_lib_references(&self) -> &BTreeSet<Url> {
&self.lib_references
}
#[must_use]
pub fn get_context_references(&self) -> &BTreeSet<Url> {
&self.context_references
}
pub fn set_lib_references(&mut self, lib_references: &BTreeSet<Url>) {
self.lib_references.clone_from(lib_references);
}
pub fn set_context_references(&mut self, context_references: &BTreeSet<Url>) {
self.context_references.clone_from(context_references);
}
pub fn add_lib_reference(&mut self, lib_reference: &Url) {
self.lib_references.insert(lib_reference.clone());
}
pub fn add_context_reference(&mut self, context_reference: &Url) {
self.context_references.insert(context_reference.clone());
}
#[cfg(feature = "debugger")]
pub fn set_source_urls(&mut self, source_urls: BTreeMap<String, Url>) {
self.source_urls = source_urls;
}
#[cfg(feature = "debugger")]
#[must_use]
pub fn get_source_urls(&self) -> &BTreeMap<String, Url> {
&self.source_urls
}
pub fn load(provider: &dyn Provider, manifest_url: &Url) -> Result<(FlowManifest, Url)> {
let (resolved_url, _) = provider
.resolve_url(manifest_url, DEFAULT_MANIFEST_FILENAME, &["json"])
.chain_err(|| "Could not resolve url for manifest.json")?;
let contents = provider
.get_contents(&resolved_url)
.chain_err(|| "Could not get contents while attempting to load manifest")?;
let url = resolved_url.clone();
let content =
String::from_utf8(contents).chain_err(|| "Could not convert from utf8 to String")?;
let deserializer = get::<FlowManifest>(&resolved_url)?;
let mut manifest = deserializer
.deserialize(&content, Some(&resolved_url))
.chain_err(|| format!("Could not create a FlowManifest from '{manifest_url}'"))?;
for function in manifest.functions.values_mut() {
function.set_implementation_url(&resolved_url)?;
}
Ok((manifest, url))
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod test {
use url::Url;
use crate::errors::Result;
use crate::model::input::Input;
use crate::model::runtime_function::RuntimeFunction;
use crate::provider::Provider;
use super::{FlowManifest, MetaData};
fn test_meta_data() -> MetaData {
MetaData {
name: "test".into(),
version: "0.0.0".into(),
description: "a test".into(),
authors: vec!["me".into()],
}
}
#[allow(clippy::module_name_repetitions)]
pub struct TestProvider {
test_content: &'static str,
}
impl Provider for TestProvider {
fn resolve_url(
&self,
source: &Url,
_default_filename: &str,
_extensions: &[&str],
) -> Result<(Url, Option<Url>)> {
Ok((source.clone(), None))
}
fn get_contents(&self, _url: &Url) -> Result<Vec<u8>> {
Ok(self.test_content.as_bytes().to_owned())
}
}
#[test]
fn create() {
let _ = FlowManifest::new(test_meta_data());
}
fn test_function() -> RuntimeFunction {
RuntimeFunction::new(
#[cfg(feature = "debugger")]
"test",
#[cfg(feature = "debugger")]
"/test",
"file://fake/test",
vec![Input::new(
#[cfg(feature = "debugger")]
"",
0,
false,
None,
None,
)],
0,
0,
&[],
false,
)
}
#[test]
fn add_function() {
let function = test_function();
let mut manifest = FlowManifest::new(test_meta_data());
manifest.add_function(function);
assert_eq!(manifest.functions.len(), 1);
}
#[test]
fn load_manifest() {
let test_content = "{
\"metadata\": {
\"name\": \"\",
\"version\": \"0.1.0\",
\"description\": \"\",
\"authors\": []
},
\"manifest_dir\": \"fake dir\",
\"lib_references\": [
],
\"context_references\": [
\"context://\"
],
\"functions\": {
\"0\": {
\"name\": \"print\",
\"route\": \"/print\",
\"process_id\": 0,
\"parent_id\": 0,
\"implementation_location\": \"context://stdio/stdout\",
\"inputs\": [ {} ]
}
},
\"source_urls\": {}
}";
let provider = TestProvider { test_content };
FlowManifest::load(
&provider,
&Url::parse("http://ibm.com/fake.json").expect("Could not parse URL"),
)
.expect("Could not load manifest");
}
}