use super::model::{Step, Workflow};
use crate::criterion::{criterion_of, read_criterion};
use crate::error::{DefinitionError, Fault, Position};
use crate::executor::{AGENT, EXECUTORS};
use crate::fields::{STEP_FIELDS, TOP_FIELDS, text_of, unknown_fields};
use serde_yaml::Value as Yaml;
impl Step {
pub fn of(value: &Yaml) -> Step {
let criteria = value
.get("criteria")
.and_then(|v| v.as_sequence())
.map(|items| items.iter().map(criterion_of).collect())
.unwrap_or_default();
let mut executor = text_of(value, "executor");
if executor.is_empty() {
executor = AGENT.to_string();
}
Step {
name: text_of(value, "name"),
description: text_of(value, "description"),
executor,
criteria,
}
}
}
impl Workflow {
pub fn of(payload: &Yaml) -> Workflow {
Workflow {
name: text_of(payload, "name"),
description: text_of(payload, "description"),
steps: payload
.get("steps")
.and_then(|v| v.as_sequence())
.map(|items| items.iter().map(Step::of).collect())
.unwrap_or_default(),
}
}
}
pub fn validate(payload: &Yaml) -> Result<(), DefinitionError> {
let top = payload
.as_mapping()
.ok_or_else(|| DefinitionError::new(Position::Top, Fault::TopNotMapping))?;
if text_of(payload, "name").is_empty() {
return Err(DefinitionError::new(Position::Top, Fault::MissingName));
}
let steps = payload
.get("steps")
.and_then(|v| v.as_sequence())
.filter(|items| !items.is_empty())
.ok_or_else(|| DefinitionError::new(Position::Top, Fault::MissingSteps))?;
let unknown = unknown_fields(top, &TOP_FIELDS);
if !unknown.is_empty() {
return Err(DefinitionError::new(
Position::Top,
Fault::UnknownTopFields(unknown),
));
}
for (index, step) in steps.iter().enumerate() {
validate_step(step, index + 1)?;
}
Ok(())
}
fn validate_step(value: &Yaml, position: usize) -> Result<(), DefinitionError> {
let at = || Position::Step(position);
let step_map = value
.as_mapping()
.ok_or_else(|| DefinitionError::new(at(), Fault::MissingStepName))?;
if text_of(value, "name").is_empty() {
return Err(DefinitionError::new(at(), Fault::MissingStepName));
}
let extra = unknown_fields(step_map, &STEP_FIELDS);
if !extra.is_empty() {
return Err(DefinitionError::new(at(), Fault::UnknownStepFields(extra)));
}
let mut executor = text_of(value, "executor");
if executor.is_empty() {
executor = AGENT.to_string();
}
if !EXECUTORS.contains(&executor.as_str()) {
return Err(DefinitionError::new(
at(),
Fault::BadStepExecutor { got: executor },
));
}
let criteria: &[Yaml] = match value.get("criteria") {
None | Some(Yaml::Null) => &[],
Some(Yaml::Sequence(items)) => items.as_slice(),
Some(_) => return Err(DefinitionError::new(at(), Fault::CriteriaNotList)),
};
for (order, criterion) in criteria.iter().enumerate() {
read_criterion(criterion, position, order + 1)?;
}
Ok(())
}
impl Workflow {
pub fn from_value(payload: &Yaml) -> Result<Workflow, DefinitionError> {
validate(payload)?;
Ok(Workflow::of(payload))
}
}
impl Step {
pub fn from_value(value: &Yaml, position: usize) -> Result<Step, DefinitionError> {
validate_step(value, position)?;
Ok(Step::of(value))
}
}