pub mod bitbucket_ci;
pub mod deriver;
pub mod env;
pub mod fs;
pub mod git;
pub mod github_ci;
pub mod gitlab_ci;
pub mod jenkins_ci;
pub mod selector;
pub mod travis_ci;
use std::path::Path;
use std::sync::LazyLock;
use cli_utils::{BoxError, BoxResult};
use thiserror::Error;
use crate::environment::Environment;
use crate::var::{Confidence, Key, C_HIGH};
use crate::{cleanup, std_error, tools, validator, value_conversions};
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Hierarchy {
Low,
Middle,
High,
Higher,
EvenHigher,
Top,
}
static NO_PROPS: LazyLock<Vec<String>> = LazyLock::new(Vec::<String>::new);
#[derive(Error, Debug)]
pub enum Error {
#[error("The value '{low_level_value}' - fetched from the underlying source - was bad: {msg}")]
BadLowLevelValue {
msg: String,
low_level_value: String,
},
#[error(transparent)]
ConversionError(#[from] value_conversions::Error),
#[error(transparent)]
IO(#[from] std::io::Error),
#[error(transparent)]
Git(#[from] tools::git::Error),
#[error(transparent)]
Std(#[from] std_error::Error),
#[error(transparent)]
Other(#[from] BoxError),
}
pub type ConfVal = (Confidence, String);
pub type RetrieveRes = BoxResult<Option<ConfVal>>;
pub trait VarSource {
fn is_usable(&self, environment: &mut Environment) -> bool;
fn hierarchy(&self) -> Hierarchy;
fn type_name(&self) -> &'static str;
fn properties(&self) -> &Vec<String>;
fn display(&self) -> String {
format!("{}{:?}", self.type_name(), self.properties())
}
fn retrieve(&self, environment: &mut Environment, key: Key) -> RetrieveRes;
fn version_from_build_tag(&self, environment: &mut Environment, key: Key) -> RetrieveRes {
assert!(matches!(key, Key::Version));
Ok(self
.retrieve(environment, Key::BuildTag)?
.map(|conf_val| cleanup::conf_version(environment, conf_val))
.filter(|conf_val| {
if let Ok(validity) = validator::get(key)(environment, &conf_val.1) {
validity.is_good()
} else {
false
}
}))
}
}
#[must_use]
pub fn var(
environment: &Environment,
key: &str,
confidence: Confidence,
) -> Option<(Confidence, String)> {
environment
.vars
.get(key)
.map(|val| (confidence, val.clone()))
}
fn ref_ok_or_err<'t>(refr: &str, part: Option<&'t str>) -> Result<&'t str, Error> {
part.ok_or_else(|| Error::BadLowLevelValue {
msg: "Invalid git reference, should be 'refs/<TYPE>/<NAME>'".to_owned(),
low_level_value: refr.to_owned(),
})
}
fn ref_extract_name_if_type_matches(refr: &str, required_ref_type: &str) -> RetrieveRes {
let mut parts = refr.split('/');
let extracted_ref_type = ref_ok_or_err(refr, parts.nth(1))?;
Ok(if extracted_ref_type == required_ref_type {
let branch_name = ref_ok_or_err(refr, parts.next())?;
Some((C_HIGH, branch_name.to_owned()))
} else {
None
})
}
pub fn ref_extract_branch(refr: &str) -> RetrieveRes {
ref_extract_name_if_type_matches(refr, "heads")
}
pub fn ref_extract_tag(refr: &str) -> RetrieveRes {
ref_extract_name_if_type_matches(refr, "tags")
}
fn is_git_repo_root(repo_path: Option<&Path>) -> bool {
tools::git::Repo::try_from(repo_path).is_ok()
}
#[must_use]
pub fn default_list(repo_path: &Path) -> Vec<Box<dyn VarSource>> {
let mut sources: Vec<Box<dyn VarSource>> = vec![];
if is_git_repo_root(Some(repo_path)) {
sources.push(Box::new(git::VarSource {}));
}
sources.push(Box::new(fs::VarSource {}));
sources.push(Box::new(bitbucket_ci::VarSource {}));
sources.push(Box::new(github_ci::VarSource {}));
sources.push(Box::new(gitlab_ci::VarSource {}));
sources.push(Box::new(jenkins_ci::VarSource {}));
sources.push(Box::new(travis_ci::VarSource {}));
sources.push(Box::new(env::VarSource {}));
sources.push(Box::new(selector::VarSource {}));
sources.push(Box::new(deriver::VarSource {}));
sources.push(Box::new(deriver::VarSource {}));
if log::log_enabled!(log::Level::Trace) {
for source in &sources {
log::trace!("Registered source {}.", source.display());
}
}
sources
}