use crate::{Error, Project, Stage, Template};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tracing::{debug, instrument};
pub const RECIPE_FOLDER: &str = "rcp";
pub const RECIPE_CFG_TEMPLATE: &str = "[stages.build]
actions = [
\"echo Hello from {{{ recipe_name }}}\"
]
[stages.deploy]
actions = [
\"echo Hello from {{{ recipe_name }}}\"
]
";
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Recipe {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub stages: HashMap<String, Stage>,
}
impl Recipe {
#[instrument(skip(project))]
pub fn create(project: Arc<Project>, name: &str) -> Result<(), Error> {
debug!("Create new recipe {}", name);
if name.contains("..") {
return Err(Error::CreateRecipeError(
"illegal sequence in recipe name",
name.to_string(),
));
}
let recipe_folder = Path::new(&project.path).join(RECIPE_FOLDER);
create_dir_all(&recipe_folder)?;
let mut template_map = HashMap::new();
template_map.insert(format!("{}.toml", name), RECIPE_CFG_TEMPLATE.to_string());
let template = Template::from_map(template_map);
let mut template_data = HashMap::new();
template_data.insert("recipe_name", name);
template.render(recipe_folder.as_path(), &template_data)?;
Ok(())
}
#[instrument(skip(project))]
pub fn from_name(project: Arc<Project>, name: &str) -> Result<Arc<Self>, Error> {
{
if let Some(recipe) = project.recipes.read().unwrap().get(name) {
return Ok(recipe.clone());
}
}
debug!("Load recipe from name {}", name);
let recipe = Arc::new(Self::from_path(get_recipe_path(&project.path, name))?);
project
.recipes
.write()
.unwrap()
.insert(name.to_string(), recipe.clone());
Ok(recipe)
}
fn from_path(path: PathBuf) -> Result<Self, Error> {
let mut file = File::open(path)?;
let mut data = String::new();
file.read_to_string(&mut data)?;
Self::from_toml(&data)
}
fn from_toml(data: &str) -> Result<Self, Error> {
let service = toml::from_str(data)?;
Ok(service)
}
}
fn get_recipe_path<'a>(project_path: &'a str, name: &str) -> PathBuf {
Path::new(project_path)
.join(RECIPE_FOLDER)
.join(format!("{}.toml", name))
}