use validated_project::Project; use crate::{ plan::{self, ClonedPlan, PlanPathExt}, project::{self, ProjectPlan}, schema_validator::SchemaValidator, }; pub struct State {} impl State { pub fn new() -> Self { Self {} } pub async fn build_state( &self, project_plan: &ProjectPlan, cloned_plan: &Option, ) -> anyhow::Result { let project = project::RawProject::from_path(&project_plan.root).await?; let plan = if let Some(_cloned_plan) = cloned_plan { Some(plan::RawPlan::from_path(&project_plan.plan_path()).await?) } else { None }; Ok(RawState { project, plan }) } pub async fn validate_state(&self, state: &RawState) -> anyhow::Result { // 2. Prepare context for actions and components if let Some(plan) = &state.plan { SchemaValidator::new().validate(plan, &state.project)?; } // 3. Match against schema from plan let project = validated_project::Project::from_path(&state.project.root).await?; Ok(ValidatedState { project: Some(project), plan: None, }) } } pub struct RawState { project: project::RawProject, plan: Option, } #[derive(Default)] pub struct ValidatedState { project: Option, plan: Option, } mod validated_project { use std::{ collections::BTreeMap, path::{Path, PathBuf}, }; use anyhow::anyhow; use toml::Table; use crate::project::CUDDLE_PROJECT_FILE; pub struct Project { value: Value, pub root: PathBuf, } impl Project { pub fn new(value: Value, root: &Path) -> Self { Self { value, root: root.to_path_buf(), } } pub fn from_file(content: &str, root: &Path) -> anyhow::Result { let table: Table = toml::from_str(content)?; let config = Config::default(); let project = table .get("project") .ok_or(anyhow!("cuddle.toml doesn't provide a [project] table"))?; let value: Value = project.into(); Ok(Self::new(value, root)) } pub async fn from_path(path: &Path) -> anyhow::Result { let cuddle_file = path.join(CUDDLE_PROJECT_FILE); if !cuddle_file.exists() { anyhow::bail!("no cuddle.toml project file found"); } let cuddle_project_file = tokio::fs::read_to_string(cuddle_file).await?; Self::from_file(&cuddle_project_file, path) } } #[derive(Default)] struct Config { project: BTreeMap, } pub enum Value { String(String), Bool(bool), Array(Vec), Map(BTreeMap), } impl From<&toml::Value> for Value { fn from(value: &toml::Value) -> Self { match value { toml::Value::String(s) => Self::String(s.clone()), toml::Value::Integer(i) => Self::String(i.to_string()), toml::Value::Float(f) => Self::String(f.to_string()), toml::Value::Boolean(b) => Self::Bool(*b), toml::Value::Datetime(dt) => Self::String(dt.to_string()), toml::Value::Array(array) => Self::Array(array.iter().map(|i| i.into()).collect()), toml::Value::Table(tbl) => { Self::Map(tbl.iter().map(|(k, v)| (k.clone(), v.into())).collect()) } } } } } pub struct Plan {}