use std::path::PathBuf;
use clap::Parser;
use serde::Serialize;
use ambient_ci::{
plan::{construct_all_plans, construct_runnable_plan, RunnablePlan},
project::{ProjectError, Projects, State},
runlog::RunLog,
};
use super::{AmbientError, Config, Leaf};
#[derive(Debug, Parser)]
pub struct Plan {
#[clap(long)]
projects: Option<PathBuf>,
project: String,
#[clap(long, aliases = ["rsync", "target"])]
rsync_target: Option<String>,
#[clap(long, alias = "dput")]
dput_target: Option<String>,
#[clap(long)]
yaml: bool,
#[clap(long)]
runnable: bool,
}
impl Leaf for Plan {
fn run(&self, config: &Config, _runlog: &mut RunLog) -> Result<(), AmbientError> {
let projects = if let Some(filename) = &self.projects {
filename.to_path_buf()
} else {
config.projects().into()
};
let projects =
Projects::from_file(&projects).map_err(|err| PlanError::FromFile(projects, err))?;
let project = projects
.get(&self.project)
.ok_or(PlanError::NoSuchProject(self.project.clone()))?;
config.lint(&projects)?;
if self.runnable {
let runnable = construct_runnable_plan(project.plan()).map_err(PlanError::Runnable)?;
let text = if self.yaml {
serde_norway::to_string(&runnable).map_err(PlanError::ToYaml)?
} else {
serde_json::to_string_pretty(&runnable).map_err(PlanError::ToJson)?
};
println!("{text}");
} else {
let mut config = config.clone();
if let Some(s) = &self.dput_target {
config.set_dput_target(s);
};
if let Some(s) = &self.rsync_target {
config.set_rsync_target(s);
};
let state =
State::from_file(config.state(), &self.project).map_err(PlanError::State)?;
let (pre_plan, plan, post_plan) =
construct_all_plans(&config, &self.project, project, &state)?;
let plans = Plans {
pre_plan,
plan,
post_plan,
};
let text = if self.yaml {
serde_norway::to_string(&plans).map_err(PlanError::ToYaml)?
} else {
serde_json::to_string_pretty(&plans).map_err(PlanError::ToJson)?
};
println!("{text}");
}
Ok(())
}
}
#[derive(Serialize)]
struct Plans {
pre_plan: RunnablePlan,
plan: RunnablePlan,
post_plan: RunnablePlan,
}
#[derive(Debug, thiserror::Error)]
pub enum PlanError {
#[error("failed to load project list from file {0}")]
FromFile(PathBuf, #[source] ProjectError),
#[error("failed to serialize plans to JSON")]
ToJson(#[source] serde_json::Error),
#[error("failed to serialize plans to YAML")]
ToYaml(#[source] serde_norway::Error),
#[error("can't find project {0}")]
NoSuchProject(String),
#[error("can't construct runnable plan")]
Runnable(#[source] ambient_ci::plan::PlanError),
#[error("can't load project state")]
State(#[source] ambient_ci::project::ProjectError),
}