use crate::utils::{color_print, ColorEnum};
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::fmt;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CiInfo {
pub provider: String,
pub repo_owner: String,
pub repo_name: String,
pub token_env_var: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AllResults {
pub job_name: String,
pub execution_time: String,
pub results: Vec<TaskResult>,
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum PassFail {
Pass,
Fail,
}
impl std::fmt::Display for PassFail {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum DocsKind {
Markdown,
Text,
URL,
}
#[derive(Deserialize, Debug, Clone)]
pub struct Docs {
pub name: String,
pub description: Option<String>,
pub kind: DocsKind,
pub path: String,
}
#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
pub struct TaskResult {
pub name: String,
pub command: String,
pub stage: i8,
pub result: PassFail,
pub elapsed_time: i64,
pub file_path: String,
}
#[derive(Debug, Clone)]
pub struct ValidationError {
pub message: String,
}
impl Default for ValidationError {
fn default() -> Self {
ValidationError {
message: String::from("Error: Roxfile syntax is invalid!"),
}
}
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl Error for ValidationError {}
pub trait Validate {
fn validate(&self) -> Result<(), ValidationError>;
}
#[derive(Deserialize, Debug, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct Task {
pub name: String,
pub command: Option<String>,
pub description: Option<String>,
pub file_path: Option<String>,
pub uses: Option<String>,
pub values: Option<Vec<String>>,
pub hide: Option<bool>,
pub workdir: Option<String>,
}
impl Validate for Task {
fn validate(&self) -> Result<(), ValidationError> {
let task_fail_message = format!("> Task '{}' failed validation!", self.name);
if self.command.is_none() & self.uses.is_none() {
color_print(vec![task_fail_message], ColorEnum::Red);
return Err(ValidationError {
message: "A Task must implement either 'command' or 'uses'!".to_owned(),
});
}
if self.uses.is_some() & self.command.is_some() {
color_print(vec![task_fail_message], ColorEnum::Red);
return Err(ValidationError {
message: "A Task cannot implement both 'command' & 'uses'!".to_owned(),
});
}
if self.uses.is_some() & self.values.is_none() {
color_print(vec![task_fail_message], ColorEnum::Red);
return Err(ValidationError {
message: "A Task that implements 'uses' must also implement 'values'!".to_owned(),
});
}
if self.uses.is_none() & self.values.is_some() {
color_print(vec![task_fail_message], ColorEnum::Red);
return Err(ValidationError {
message: "A Task that implements 'values' must also implement 'uses'!".to_owned(),
});
}
Ok(())
}
}
#[derive(Deserialize, Debug, Default, Clone)]
#[serde(deny_unknown_fields)]
pub struct Template {
pub name: String,
pub command: String,
pub symbols: Vec<String>,
}
impl Validate for Template {
fn validate(&self) -> Result<(), ValidationError> {
let failure_message = format!("> Template '{}' failed validation!", self.name);
for symbol in &self.symbols {
let exists = self.command.contains(symbol);
if !exists {
color_print(vec![failure_message], ColorEnum::Red);
return Err(ValidationError {
message: "A Template's 'symbols' must all exist within its 'command'!"
.to_owned(),
});
}
}
Ok(())
}
}
#[derive(Deserialize, Debug, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct Pipeline {
pub name: String,
pub description: Option<String>,
pub stages: Vec<Vec<String>>,
}
#[derive(Deserialize, Debug, Default, Clone)]
#[serde(deny_unknown_fields)]
pub struct RoxFile {
pub ci: Option<CiInfo>,
pub docs: Option<Vec<Docs>>,
pub tasks: Vec<Task>,
pub pipelines: Option<Vec<Pipeline>>,
pub templates: Option<Vec<Template>>,
pub additional_files: Option<Vec<String>>,
}
impl Validate for RoxFile {
fn validate(&self) -> Result<(), ValidationError> {
for task in &self.tasks {
task.validate()?
}
if let Some(templates) = &self.templates {
for template in templates {
template.validate()?
}
}
Ok(())
}
}