use std::collections::BTreeSet;
use std::fmt;
use std::fmt::Display;
use serde_derive::{Deserialize, Serialize};
use url::Url;
use crate::deserializers::deserializer::get_deserializer;
use crate::errors::*;
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 FlowManifest {
metadata: MetaData,
lib_references: BTreeSet<Url>,
context_references: BTreeSet<Url>,
functions: Vec<RuntimeFunction>,
#[cfg(feature = "debugger")]
source_urls: BTreeSet<(Url, Url)>,
}
impl Display for FlowManifest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (id, function) in self.functions.iter().enumerate() {
writeln!(f, " Function #{id} Implementation: {}", function.get_implementation_url())?;
}
write!(f, "")
}
}
impl FlowManifest {
pub fn new(metadata: MetaData) -> Self {
FlowManifest {
metadata,
lib_references: BTreeSet::<Url>::new(),
context_references: BTreeSet::<Url>::new(),
functions: Vec::<RuntimeFunction>::new(),
#[cfg(feature = "debugger")]
source_urls: BTreeSet::<(Url, Url)>::new(),
}
}
pub fn add_function(&mut self, function: RuntimeFunction) {
self.functions.push(function);
}
pub fn functions(&self) -> &Vec<RuntimeFunction> {
&self.functions
}
pub fn get_functions(&mut self) -> &mut Vec<RuntimeFunction> {
&mut self.functions
}
pub fn take_functions(self) -> Vec<RuntimeFunction> {
self.functions
}
pub fn get_metadata(&self) -> &MetaData {
&self.metadata
}
pub fn get_lib_references(&self) -> &BTreeSet<Url> {
&self.lib_references
}
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 = lib_references.clone();
}
pub fn set_context_references(&mut self, context_references: &BTreeSet<Url>) {
self.context_references = context_references.clone();
}
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: BTreeSet<(Url, Url)>) {
self.source_urls = source_urls;
}
#[cfg(feature = "debugger")]
pub fn get_source_urls(&self) -> &BTreeSet<(Url, 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_deserializer::<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 &mut manifest.functions {
function.set_implementation_url(&resolved_url)?;
}
Ok((manifest, url))
}
}
#[cfg(test)]
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()],
}
}
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\": [
{
\"name\": \"print\",
\"route\": \"/print\",
\"function_id\": 0,
\"flow_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");
}
}