357 lines
9.8 KiB
Rust
357 lines
9.8 KiB
Rust
use crate::job::{Job, Jobs};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
#[serde(default)]
|
|
pub struct Project {
|
|
pub components: Vec<Component>,
|
|
pub groups: Vec<Group>,
|
|
pub env: HashMap<String, String>,
|
|
pub tasks: Vec<TaskDefinition>,
|
|
}
|
|
|
|
impl Project {
|
|
fn get_absolute_task(&self, name: &str) -> Option<Jobs> {
|
|
self.tasks
|
|
.iter()
|
|
.find(|t| t.name == name)
|
|
.map(|task| {
|
|
let job = self.build_project_task_job(task);
|
|
let mut jobs = task
|
|
.before
|
|
.iter()
|
|
.map(|name| self.get_absolute_task(name))
|
|
.flatten()
|
|
.map(|jobs| jobs.to_vec())
|
|
.flatten()
|
|
.collect::<Vec<_>>();
|
|
jobs.push(job);
|
|
Jobs::new(jobs)
|
|
})
|
|
.or_else(|| {
|
|
self.components.iter().find_map(|component| {
|
|
component
|
|
.tasks
|
|
.iter()
|
|
.find(|t| format!("{}:{}", component.name, t.name) == name)
|
|
.map(|task| {
|
|
let job = self.build_component_task_job(component, task);
|
|
let mut jobs = task
|
|
.before
|
|
.iter()
|
|
.map(|name| self.get_absolute_task(name))
|
|
.flatten()
|
|
.map(|jobs| jobs.to_vec())
|
|
.flatten()
|
|
.collect::<Vec<_>>();
|
|
jobs.push(job);
|
|
Jobs::new(jobs)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
fn get_relative_task(&self, task_name: &str, component_name: &str) -> Option<Jobs> {
|
|
if let Some(component) = self.components.iter().find(|c| c.name == component_name) {
|
|
component
|
|
.tasks
|
|
.iter()
|
|
.find(|t| t.name == task_name)
|
|
.map(|task| {
|
|
let job = self.build_component_task_job(component, task);
|
|
let mut jobs = task
|
|
.before
|
|
.iter()
|
|
.map(|name| self.get_by_name(name))
|
|
.flatten()
|
|
.map(|jobs| jobs.to_vec())
|
|
.flatten()
|
|
.collect::<Vec<_>>();
|
|
jobs.push(job);
|
|
Jobs::new(jobs)
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn get_component(&self, name: &str) -> Option<Jobs> {
|
|
self.components.iter().find(|c| c.name == name).map(|c| {
|
|
let component_job = self.build_component_job(c);
|
|
let mut absolute_tasks = c
|
|
.before
|
|
.iter()
|
|
.map(|name| self.get_absolute_task(name))
|
|
.flatten()
|
|
.map(|jobs| jobs.to_vec())
|
|
.flatten()
|
|
.collect::<Vec<_>>();
|
|
let mut relative_tasks = c
|
|
.before
|
|
.iter()
|
|
.map(|name| self.get_relative_task(name, &c.name))
|
|
.flatten()
|
|
.map(|jobs| jobs.to_vec())
|
|
.flatten()
|
|
.collect::<Vec<_>>();
|
|
|
|
absolute_tasks.append(&mut relative_tasks);
|
|
absolute_tasks.push(component_job);
|
|
Jobs::new(absolute_tasks)
|
|
})
|
|
}
|
|
|
|
fn build_component_job(&self, c: &Component) -> Job {
|
|
let mut env = self.env.clone();
|
|
env.extend(c.env.clone());
|
|
Job {
|
|
name: c.name.clone(),
|
|
env,
|
|
..Job::default()
|
|
}
|
|
}
|
|
|
|
fn build_project_task_job(&self, task: &TaskDefinition) -> Job {
|
|
let mut env = self.env.clone();
|
|
env.extend(task.env.clone());
|
|
Job {
|
|
name: task.name.clone(),
|
|
..Job::default()
|
|
}
|
|
}
|
|
|
|
fn build_component_task_job(&self, component: &Component, task: &TaskDefinition) -> Job {
|
|
let mut env = self.env.clone();
|
|
env.extend(component.env.clone());
|
|
env.extend(task.env.clone());
|
|
Job {
|
|
name: format!("{}:{}", component.name.clone(), task.name.clone()),
|
|
env,
|
|
..Job::default()
|
|
}
|
|
}
|
|
|
|
pub fn get_by_name(&self, name: &str) -> Option<Jobs> {
|
|
self.get_component(name)
|
|
.or_else(|| self.get_absolute_task(name))
|
|
}
|
|
|
|
pub fn from_str(s: &str) -> Self {
|
|
serde_yaml::from_str(s).unwrap()
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
#[serde(default)]
|
|
pub struct Group {
|
|
name: String,
|
|
components: Vec<Component>,
|
|
env: HashMap<String, String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct Component {
|
|
pub env: HashMap<String, String>,
|
|
pub name: String,
|
|
pub command: String,
|
|
pub path: Option<String>,
|
|
pub retry: bool,
|
|
pub keep_alive: bool,
|
|
pub retry_delay: u64,
|
|
pub before: Vec<String>,
|
|
pub tasks: Vec<TaskDefinition>,
|
|
}
|
|
|
|
impl Default for Component {
|
|
fn default() -> Self {
|
|
Self {
|
|
env: HashMap::new(),
|
|
name: "UNNAMED".to_string(),
|
|
command: String::new(),
|
|
path: None,
|
|
retry: false,
|
|
keep_alive: true,
|
|
retry_delay: 2,
|
|
before: vec![],
|
|
tasks: vec![],
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
#[serde(default)]
|
|
pub struct TaskDefinition {
|
|
pub env: HashMap<String, String>,
|
|
pub name: String,
|
|
pub command: String,
|
|
pub path: Option<String>,
|
|
pub retry: bool,
|
|
pub keep_alive: bool,
|
|
pub retry_delay: u64,
|
|
pub before: Vec<String>,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::Project;
|
|
|
|
#[test]
|
|
fn component_by_name() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
components:
|
|
- name: test-component
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("test-component").unwrap();
|
|
assert_eq!(jobs.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn project_task() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
tasks:
|
|
- name: task1
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("task1").unwrap();
|
|
assert_eq!(jobs.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn component_task() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
components:
|
|
- name: c1
|
|
tasks:
|
|
- name: task1
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("c1:task1").unwrap();
|
|
assert_eq!(jobs.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn component_dependent_absolute_component_task() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
components:
|
|
- name: main-cmp
|
|
before:
|
|
- main-cmp:sub-task
|
|
tasks:
|
|
- name: sub-task
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("main-cmp").unwrap();
|
|
assert_eq!(jobs.len(), 2);
|
|
assert_eq!(jobs.first().unwrap().name, "main-cmp:sub-task");
|
|
}
|
|
|
|
#[test]
|
|
fn component_dependent_relative_component_task() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
components:
|
|
- name: main-cmp
|
|
before:
|
|
- sub-task
|
|
tasks:
|
|
- name: sub-task
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("main-cmp").unwrap();
|
|
assert_eq!(jobs.len(), 2);
|
|
assert_eq!(jobs.first().unwrap().name, "main-cmp:sub-task");
|
|
}
|
|
|
|
#[test]
|
|
fn complicated_dependencies() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
components:
|
|
- name: ui
|
|
before:
|
|
- build-ui
|
|
tasks:
|
|
- name: build-ui
|
|
- name: server
|
|
before:
|
|
- setup
|
|
tasks:
|
|
- name: setup
|
|
- name: build
|
|
before:
|
|
- server:setup
|
|
tasks:
|
|
- name: build
|
|
before:
|
|
- ui:build-ui
|
|
- server:build
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("build").unwrap();
|
|
assert_eq!(jobs.get(0).unwrap().name, "ui:build-ui");
|
|
assert_eq!(jobs.get(1).unwrap().name, "server:setup");
|
|
assert_eq!(jobs.get(2).unwrap().name, "server:build");
|
|
assert_eq!(jobs.get(3).unwrap().name, "build");
|
|
assert_eq!(jobs.len(), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn component_env() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
env:
|
|
foo: one
|
|
sub: two
|
|
components:
|
|
- name: main-cmp
|
|
env:
|
|
sub: three
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("main-cmp").unwrap();
|
|
let job = jobs.first().unwrap();
|
|
assert_eq!(job.env.get("foo"), Some(&String::from("one")));
|
|
assert_eq!(job.env.get("sub"), Some(&String::from("three")));
|
|
}
|
|
|
|
#[test]
|
|
fn task_env() {
|
|
let project = Project::from_str(
|
|
r#"
|
|
env:
|
|
root: ten
|
|
foo: one
|
|
sub: two
|
|
components:
|
|
- name: main-cmp
|
|
env:
|
|
sub: three
|
|
tasks:
|
|
- name: t
|
|
env:
|
|
foo: four
|
|
"#,
|
|
);
|
|
|
|
let jobs = project.get_by_name("main-cmp:t").unwrap();
|
|
let job = jobs.first().unwrap();
|
|
assert_eq!(job.env.get("foo"), Some(&String::from("four")));
|
|
assert_eq!(job.env.get("sub"), Some(&String::from("three")));
|
|
assert_eq!(job.env.get("root"), Some(&String::from("ten")));
|
|
}
|
|
}
|