pub mod semver;
pub mod workflow;
use crate::{cmd::GlobalArgs, config::workflow::WorkflowFile, log, ErrToStr};
use std::{
fs::canonicalize,
path::{Path, PathBuf},
};
#[derive(Default, Debug, clap::Parser)]
pub struct ConfigArgs {
#[clap(long, global = true)]
pub manifest_path: Option<std::path::PathBuf>,
#[clap(long, alias = "cfg", short)]
pub config: Option<std::path::PathBuf>,
#[clap(long, value_enum, value_name = "TOGGLE", default_value_t = Toggle::On, verbatim_doc_comment)]
pub check_cfg_compatibility: Toggle,
}
#[derive(Debug, Clone, PartialEq, clap::ValueEnum)]
pub enum Toggle {
On,
Off,
}
impl Default for Toggle {
fn default() -> Self {
Self::On
}
}
pub const WELL_KNOWN_CFG_PATHS: &[&str] = &["zepter.yaml", ".zepter.yaml"];
pub fn search_config<P: AsRef<Path>>(workspace: P) -> Result<PathBuf, Vec<PathBuf>> {
let paths: Vec<PathBuf> = vec![
workspace.as_ref().to_path_buf(),
workspace.as_ref().join(".cargo"),
workspace.as_ref().join(".config"),
];
let mut searched = vec![];
for (name, path) in WELL_KNOWN_CFG_PATHS
.iter()
.flat_map(|name| paths.iter().map(move |path| (name, path)))
{
let mut path = path.join(name);
if path.exists() {
path = canonicalize(path).expect("Failed to canonicalize path");
return Ok(path)
}
searched.push(path);
}
Err(searched)
}
impl ConfigArgs {
pub fn load_or_panic(&self) -> WorkflowFile {
self.load().unwrap_or_else(|e| {
eprintln!("{e}");
std::process::exit(GlobalArgs::error_code_cfg_parsing());
})
}
pub fn load(&self) -> Result<WorkflowFile, String> {
let path = self.locate_config()?;
log::debug!("Using config file: {path:?}");
let cfg = WorkflowFile::from_path(path)?;
if self.check_cfg_compatibility == Toggle::On {
cfg.check_cfg_compatibility()?;
}
Ok(cfg)
}
fn locate_config(&self) -> Result<PathBuf, String> {
if let Some(path) = &self.config {
let path = canonicalize(path).err_to_str()?;
if path.exists() {
Ok(path)
} else {
Err(format!("Provided config path does not exist: {path:?}"))
}
} else {
let root = self.locate_workspace()?;
match search_config(root) {
Ok(cfg) => Ok(cfg),
Err(searched) => {
println!("Failed to find config file in any of these locations:");
for path in searched {
println!(" - {}", path.display());
}
Err("Could not find a config file".into())
},
}
}
}
fn locate_workspace(&self) -> Result<PathBuf, String> {
let mut cmd = std::process::Command::new("cargo");
cmd.arg("locate-project").args([
"--message-format",
"plain",
"--workspace",
"--offline",
"--locked",
]);
if let Some(path) = &self.manifest_path {
cmd.arg("--manifest-path").arg(path);
}
let output = cmd.output().err_to_str()?;
let path = String::from_utf8(output.stdout).map(PathBuf::from).err_to_str()?;
if let Some(root) = path.parent() {
return Ok(root.into())
}
let err = output.stderr;
let err = String::from_utf8(err).err_to_str()?;
let err = err.replace("\n", "\n\t");
Err(format!("Failed to find the workspace root with `cargo locate-project`:\n\n\t{err}"))
}
}