use serde_json::Value;
pub struct ConnectorRef<'a> {
pub function: &'a str,
pub connector: &'a str,
pub input: &'a Value,
}
pub fn connector_refs(tasks: &Value) -> Vec<ConnectorRef<'_>> {
super::steps::leaf_tasks(tasks)
.into_iter()
.filter_map(|task| {
let function = task.get("function")?;
let name = function.get("name")?.as_str()?;
if !crate::engine::CONNECTOR_FUNCTIONS.contains(&name) {
return None;
}
let input = function.get("input")?;
Some(ConnectorRef {
function: name,
connector: input.get("connector")?.as_str()?,
input,
})
})
.collect()
}
#[derive(Clone, Copy)]
pub struct ConnectorFacts {
pub connector_type: crate::connector::ConnectorType,
pub is_mongo: bool,
}
pub enum RefProblem<'a> {
Missing { connector: &'a str },
WrongType {
function: &'a str,
connector: &'a str,
actual: crate::connector::ConnectorType,
wanted: &'static [crate::connector::ConnectorType],
},
MissingMongoDatabase {
function: &'a str,
connector: &'a str,
},
}
pub fn check_connector_refs<'a, F>(tasks: &'a Value, facts: F) -> Vec<RefProblem<'a>>
where
F: Fn(&str) -> Option<ConnectorFacts>,
{
let mut problems = Vec::new();
for r in connector_refs(tasks) {
let Some(facts) = facts(r.connector) else {
problems.push(RefProblem::Missing {
connector: r.connector,
});
continue;
};
if let Some(wanted) = super::required_connector_types(r.function)
&& !wanted.contains(&facts.connector_type)
{
problems.push(RefProblem::WrongType {
function: r.function,
connector: r.connector,
actual: facts.connector_type,
wanted,
});
continue;
}
if facts.is_mongo
&& super::requires_mongo_database(r.function)
&& !r
.input
.get("database")
.is_some_and(|d| d.as_str().is_some_and(|s| !s.trim().is_empty()))
{
problems.push(RefProblem::MissingMongoDatabase {
function: r.function,
connector: r.connector,
});
}
}
problems
}
pub fn channel_call_targets(tasks: &Value) -> (Vec<&str>, bool) {
let mut targets = Vec::new();
let mut dynamic = false;
for task in super::steps::leaf_tasks(tasks) {
let Some(input) = task
.get("function")
.filter(|f| f.get("name").and_then(|n| n.as_str()) == Some("channel_call"))
.and_then(|f| f.get("input"))
else {
continue;
};
if let Some(target) = input.get("channel").and_then(|c| c.as_str())
&& !target.is_empty()
&& !targets.contains(&target)
{
targets.push(target);
}
if input.get("channel_logic").is_some_and(|l| !l.is_null()) {
dynamic = true;
}
}
(targets, dynamic)
}