use std::io::Write;
use std::path::Path;
use log::info;
use flowcore::model::flow_definition::FlowDefinition;
use flowcore::model::function_definition::FunctionDefinition;
use flowcore::model::name::HasName;
use flowcore::model::process::Process::{FlowProcess, FunctionProcess};
use flowcore::model::route::{HasRoute, Route};
use crate::compiler::compile::CompilerTables;
use crate::dumper::create_output_file;
use crate::dumper::flow_to_dot::{input_initializers_to_dot, INPUT_PORTS, output_name_to_port};
use crate::errors::*;
pub fn dump_functions(
flow: &FlowDefinition,
tables: &CompilerTables,
output_dir: &Path,
) -> std::io::Result<()> {
info!(
"\n=== Dumper: Dumping functions in dot format to '{}'",
output_dir.display()
);
let mut dot_file = create_output_file(output_dir, "functions", "dot")?;
info!("\tGenerating functions.dot, Use \"dotty\" to view it");
dot_file.write_all(
format!(
"digraph {} {{\nnodesep=1.0\n",
str::replace(&flow.alias.to_string(), "-", "_")
)
.as_bytes(),
)?;
dot_file.write_all(format!("labelloc=t;\nlabel = \"{}\";\n", flow.route()).as_bytes())?;
let functions = process_refs_to_dot(flow, tables).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
"Could not create dot content for process_refs",
)
})?;
dot_file.write_all(functions.as_bytes())?;
dot_file.write_all(b"}")
}
fn process_refs_to_dot(
flow: &FlowDefinition,
tables: &CompilerTables,
) -> Result<String> {
let mut output = String::new();
for process_ref in &flow.process_refs {
let process = flow
.subprocesses
.get(process_ref.alias())
.ok_or("Could not find process named in process_ref")?;
match process {
FlowProcess(ref subflow) => {
output.push_str(&format!("\nsubgraph cluster_{} {{",
str::replace(&subflow.alias.to_string(), "-", "_"))
);
output.push_str(&format!("label = \"{}\";", subflow.route()));
output.push_str(&process_refs_to_dot(subflow, tables)?);
output.push_str("}\n");
}
FunctionProcess(ref function) => {
output_compiled_function(function.route(), tables, &mut output);
}
}
}
Ok(output)
}
fn function_to_dot(function: &FunctionDefinition, functions: &[FunctionDefinition]) -> String {
let mut function_string = String::new();
let md_path = function
.get_source_url()
.to_string()
.replace("toml", "html");
function_string.push_str(&format!(
"r{}[style=filled, fillcolor=coral, URL=\"{}\", label=\"{} (#{})\"];",
function.get_id(),
md_path,
function.alias(),
function.get_id()
));
function_string.push_str(&input_initializers_to_dot(function, &format!("r{}", function.get_id())));
for destination in function.get_output_connections() {
let input_port = INPUT_PORTS[destination.destination_io_number % INPUT_PORTS.len()];
let destination_function = &functions[destination.destination_id];
let source_port = output_name_to_port(&destination.source);
let destination_name = destination_function
.get_inputs()
.get(destination.destination_io_number)
.expect("Could not get input")
.name()
.to_string();
function_string.push_str(&format!(
"r{}:{} -> r{}:{} [taillabel = \"{}\", headlabel = \"{}\"];",
function.get_id(),
source_port,
destination.destination_id,
input_port,
destination.source,
destination_name
));
}
function_string
}
fn output_compiled_function(
route: &Route,
tables: &CompilerTables,
output: &mut String,
) {
for function in &tables.functions {
if function.route() == route {
output.push_str(&function_to_dot(function, &tables.functions));
}
}
}