use super::{
AnyNumberOf, Code, Command, Configuration, ConfigurationEntry, Interpretter2, Job, Reference,
Rule, Trigger,
};
use crate::job::Relation;
use std::{ffi::OsStr, path::PathBuf};
use url::Url;
pub struct Entry {
namespace: Vec<Box<str>>,
source: Url,
definition: Definition,
}
impl core::fmt::Debug for Entry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Entry")
.field("namespace", &self.namespace)
.field("source", &self.source.to_string())
.field("definition", &self.definition)
.finish()
}
}
#[derive(Debug)]
enum Definition {
Job(Job),
Interpretter(Interpretter2),
Project { name: Box<str> },
}
impl ConfigurationEntry for Entry {
fn configure<C: Configuration>(self, config: &mut C) -> Result<(), C::Error> {
match self.definition {
Definition::Job(Job {
job: name,
runs,
script,
when,
then,
}) => {
let name = Self::normalize_name(&self.source, name);
let mut relations = Self::configure_rules(when);
relations.extend(Self::apply_triggers(then));
config.configure_job(
&self.namespace,
name,
self.source.as_str().into(),
Self::normalize_script(script),
runs,
relations,
)?;
Ok(())
}
Definition::Project { name } => {
config.configure_project(&self.namespace, name, self.source)
}
Definition::Interpretter(Interpretter2 {
interpretter,
provider,
spec,
}) => config.configure_interpretter(
&self.namespace,
interpretter,
self.source.as_str().into(),
provider,
spec,
),
}
}
}
impl Entry {
pub fn project(namespace: &[Box<str>], source: &Url, name: Box<str>) -> Entry {
Entry {
namespace: namespace.to_vec(),
source: source.clone(),
definition: Definition::Project { name },
}
}
pub fn job(namespace: &[Box<str>], source: &Url, job: Job) -> Entry {
Entry {
namespace: namespace.to_vec(),
source: source.clone(),
definition: Definition::Job(job),
}
}
pub fn interpretter(
namespace: &[Box<str>],
source: &Url,
interpretter: Interpretter2,
) -> Entry {
Entry {
namespace: namespace.to_vec(),
source: source.clone(),
definition: Definition::Interpretter(interpretter),
}
}
fn normalize_script(script: AnyNumberOf<Command>) -> Vec<Box<str>> {
match script {
AnyNumberOf::None => vec![],
AnyNumberOf::Single(one) => vec![one],
AnyNumberOf::Multiple(vec) => vec,
}
.into_iter()
.map(|c| match c {
Command::Float(v) => v.to_string().into_boxed_str(),
Command::Integer(v) => v.to_string().into_boxed_str(),
Command::SignedInteger(v) => v.to_string().into_boxed_str(),
Command::Bool(v) => v.to_string().into_boxed_str(),
Command::String(v) => v,
})
.collect()
}
fn apply_triggers(then: AnyNumberOf<Trigger>) -> Vec<Relation> {
let mut trigs = vec![];
for trigger in then {
match trigger {
Trigger::Start { start, code } => {
let codes = Self::normalize_codes(code);
for target in Self::normalize_reference(start) {
trigs.push(Relation::Trigger {
target,
codes: codes.clone(),
});
}
}
}
}
trigs
}
fn normalize_codes(code: AnyNumberOf<Code>) -> Vec<crate::job::Code> {
let code_fix = |code| match code {
Code::Explicit(code) => code.into(),
Code::Range { min, max, mask } => (min, max, mask).into(),
};
code.into_iter().map(code_fix).collect()
}
fn normalize_reference(reference: Reference) -> Vec<Box<str>> {
match reference {
Reference::Single(r) => vec![r],
Reference::Multiple(vec) => vec,
}
}
fn configure_rules(when: AnyNumberOf<Rule>) -> Vec<Relation> {
when.into_iter()
.enumerate()
.map(|(o, r)| r.to_job_rule(o))
.map(Relation::When)
.collect::<Vec<_>>()
}
fn normalize_name(source: &Url, name: Option<Box<str>>) -> Box<str> {
if let Some(name) = name {
return name;
}
let mut path = source.path();
if let Some(size) = PathBuf::from(path).file_name().map(OsStr::len) {
path = &path[path.len() - size..];
}
if path.to_ascii_lowercase().ends_with(".yaml")
|| path.to_ascii_lowercase().ends_with(".yml")
{
path = &path[0..PathBuf::from(path)
.file_stem()
.map(OsStr::len)
.unwrap_or(path.len())];
}
if path.to_ascii_lowercase().ends_with(".willdo") {
path = &path[0..PathBuf::from(path)
.file_stem()
.map(OsStr::len)
.unwrap_or(path.len())];
}
path.into()
}
}