use std::collections::BTreeMap;
#[cfg(feature = "debugger")]
use std::collections::BTreeSet;
use log::{debug, info, trace};
use url::Url;
use flowcore::deserializers::deserializer::get_deserializer;
use flowcore::model::flow_definition::FlowDefinition;
use flowcore::model::flow_manifest::Cargo;
use flowcore::model::input::InputInitializer;
use flowcore::model::metadata::MetaData;
use flowcore::model::name::HasName;
use flowcore::model::name::Name;
use flowcore::model::process::Process;
use flowcore::model::process::Process::FlowProcess;
use flowcore::model::process::Process::FunctionProcess;
use flowcore::model::route::Route;
use flowcore::provider::Provider;
use crate::errors::*;
#[derive(PartialEq, Eq)]
pub enum LibType {
RustLib,
}
pub fn parse(
url: &Url,
provider: &dyn Provider,
#[cfg(feature = "debugger")] source_urls: &mut BTreeSet<(Url, Url)>,
) -> Result<Process> {
trace!("load()");
parse_process(
&Route::default(),
&Name::default(),
0,
&mut 0,
url,
provider,
&BTreeMap::new(),
#[cfg(feature = "debugger")]
source_urls,
0,
)
}
#[allow(clippy::too_many_arguments)]
fn parse_process(
parent_route: &Route,
alias: &Name,
parent_flow_id: usize,
flow_count: &mut usize,
url: &Url,
provider: &dyn Provider,
initializations: &BTreeMap<String, InputInitializer>,
#[cfg(feature = "debugger")] source_urls: &mut BTreeSet<(Url, Url)>,
level: usize,
) -> Result<Process> {
trace!("load_process()");
let (resolved_url, reference) = provider
.resolve_url(url, "root", &["toml"])
.chain_err(|| format!("Could not resolve the url: '{url}'"))?;
if &resolved_url != url {
debug!("Source URL '{url}' resolved to: '{resolved_url}'");
}
let contents = provider
.get_contents(&resolved_url)
.chain_err(|| format!("Could not get contents of resolved url: '{resolved_url}'"))?;
if !alias.is_empty() {
info!("Loading process with alias = '{alias}'");
}
let content = String::from_utf8(contents).chain_err(|| "Could not read UTF8 contents")?;
let deserializer = get_deserializer::<Process>(&resolved_url)?;
debug!(
"Loading process from url = '{resolved_url}' with deserializer: '{}'", deserializer.name());
let mut process = deserializer
.deserialize(&content, Some(url))
.chain_err(|| format!("Could not deserialize process from content in '{url}'"))?;
#[cfg(feature = "debugger")]
source_urls.insert((url.clone(), resolved_url.clone()));
match process {
FlowProcess(ref mut flow) => {
flow.config(
&resolved_url,
parent_route,
alias,
*flow_count,
initializations,
)?;
*flow_count += 1;
debug!("Deserialized the Flow, now loading any sub-processes");
parse_process_refs(
flow,
flow_count,
provider,
#[cfg(feature = "debugger")]
source_urls,
level,
)?;
flow.build_connections(level)?;
}
FunctionProcess(ref mut function) => {
function.config(
url,
&resolved_url,
parent_route,
alias,
parent_flow_id,
reference,
initializations,
)?;
}
}
Ok(process)
}
pub fn parse_metadata(url: &Url, provider: &dyn Provider) -> Result<(MetaData, LibType)> {
trace!("Loading Metadata");
let (resolved_url, _) = provider
.resolve_url(url, "Cargo", &["toml"])
.chain_err(|| format!("Could not resolve the url: '{url}'"))?;
if &resolved_url != url {
debug!("Source URL '{url}' resolved to: '{resolved_url}'");
}
let contents = provider
.get_contents(&resolved_url)
.chain_err(|| format!("Could not get contents of resolved url: '{resolved_url}'"))?;
let content = String::from_utf8(contents).chain_err(|| "Could not read UTF8 contents")?;
let deserializer = get_deserializer::<Cargo>(&resolved_url)?;
let cargo: Cargo = deserializer.deserialize(&content, Some(&resolved_url))?;
Ok((cargo.package, LibType::RustLib))
}
fn parse_process_refs(
flow: &mut FlowDefinition,
flow_count: &mut usize,
provider: &dyn Provider,
#[cfg(feature = "debugger")] source_urls: &mut BTreeSet<(Url, Url)>,
level: usize,
) -> Result<()> {
for process_ref in &mut flow.process_refs {
let subprocess_url = flow
.source_url
.join(&process_ref.source)
.map_err(|e| e.to_string())?;
let process = parse_process(
&flow.route,
process_ref.alias(),
flow.id,
flow_count,
&subprocess_url,
provider,
&process_ref.initializations,
#[cfg(feature = "debugger")]
source_urls,
level + 1,
)?;
process_ref.set_alias(process.name());
if let FunctionProcess(function) = &process {
if let Some(lib_ref) = function.get_lib_reference() {
flow.lib_references.insert(lib_ref.clone());
}
if let Some(context_ref) = function.get_context_reference() {
flow.context_references.insert(context_ref.clone());
}
}
flow.subprocesses
.insert(process_ref.alias().to_owned(), process);
}
Ok(())
}
#[cfg(test)]
mod test {
use url::Url;
use flowcore::deserializers::deserializer::get_deserializer;
use flowcore::model::flow_manifest::Cargo;
use flowcore::model::metadata::MetaData;
#[test]
fn deserialize_library() {
let cargo_toml = r###"[package]
name = "Flow Standard Library"
version = "0.11.0"
authors = ["Andrew Mackenzie <andrew@mackenzie-serres.net>"]
description = "The standard library for 'flow' programs compiled with the 'flowc' compiler"
exclude = "../..""###;
let url = Url::parse("file:///fake.toml").expect("Could not parse URL");
let deserializer = get_deserializer::<Cargo>(&url).expect("Could not get deserializer");
let cargo: Cargo = deserializer
.deserialize(cargo_toml, Some(&url))
.expect("Could not deserialize");
let _: MetaData = cargo.package;
}
}