use std::{
collections::{BTreeMap, HashMap},
mem,
path::Path,
};
use serde::{Deserialize, Serialize};
use serde_yaml::Value;
use crate::{
inherit_option_value, merge_map_values,
types::DynErrResult,
utils::{get_path_relative_to_base, read_env_file},
};
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub(crate) enum StringOrVecString {
Single(String),
Multiple(Vec<String>),
}
impl Default for StringOrVecString {
fn default() -> Self {
StringOrVecString::Multiple(Vec::new())
}
}
pub(crate) struct StringOrVecStringIter<'a> {
task_extend: &'a StringOrVecString,
index: usize,
}
impl StringOrVecString {
pub(crate) fn iter(&self) -> StringOrVecStringIter {
StringOrVecStringIter {
task_extend: self,
index: 0,
}
}
pub(crate) fn is_empty(&self) -> bool {
match self {
StringOrVecString::Single(_) => false,
StringOrVecString::Multiple(v) => v.is_empty(),
}
}
}
impl<'a> Iterator for StringOrVecStringIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
match self.task_extend {
StringOrVecString::Single(s) => {
if self.index == 0 {
self.index += 1;
Some(s)
} else {
None
}
}
StringOrVecString::Multiple(v) => {
if self.index < v.len() {
let item = &v[self.index];
self.index += 1;
Some(item)
} else {
None
}
}
}
}
}
#[derive(Deserialize, Serialize, Default, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct CommonFields {
#[serde(default)]
pub(crate) wd: Option<String>,
#[serde(default)]
pub(crate) env: HashMap<String, String>,
#[serde(default)]
pub(crate) dotenv: StringOrVecString,
#[serde(default)]
pub(crate) vars: HashMap<String, Value>,
#[serde(default)]
pub(crate) incl: BTreeMap<String, String>,
#[serde(default)]
pub(crate) extend: StringOrVecString,
}
impl CommonFields {
pub(crate) fn extend(&mut self, other: &CommonFields) {
inherit_option_value!(self.wd, other.wd);
merge_map_values!(self.env, &other.env);
merge_map_values!(self.vars, &other.vars);
merge_map_values!(self.incl, &other.incl);
}
pub(crate) fn setup(&mut self, base_path: &Path) -> DynErrResult<()> {
let envfiles = mem::take(&mut self.dotenv);
for env_file in envfiles.iter() {
let env_file = get_path_relative_to_base(base_path, &env_file);
let env_variables = read_env_file(env_file.as_path())?;
for (key, val) in env_variables {
self.env.entry(key).or_insert(val);
}
}
Ok(())
}
}