use std::borrow::Cow;
use std::str::FromStr;
use std::env;
use serde::{Serialize, Deserialize};
use thiserror::Error;
use crate::types::*;
use crate::util::*;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Suitability)]
#[serde(deny_unknown_fields)]
pub enum VarType {
#[default]
Params,
JobContext,
TaskContext,
Scratchpad,
CommonArg,
Env
}
#[derive(Debug, Error)]
pub enum GetVarError {
#[error(transparent)]
StringSourceError(#[from] Box<StringSourceError>),
#[error("The specified StringSource returned None where it had to be Some.")]
StringSourceIsNone,
#[error("Tried to use VarType::CommonArg outside of a common context.")]
NotInCommonContext,
#[error("The value of the environment variable wasn't valid UTF-8")]
EnvVarIsNotUtf8
}
impl From<StringSourceError> for GetVarError {
fn from(value: StringSourceError) -> Self {
Self::StringSourceError(Box::new(value))
}
}
impl VarType {
pub fn get<'a>(&self, name: &str, task_state: &TaskStateView<'a>) -> Result<Option<Cow<'a, str>>, GetVarError> {
Ok(match self {
Self::Params => task_state.params .vars.get(name).map(|x| Cow::Borrowed(x.as_str())),
Self::JobContext => task_state.job_context.vars.get(name).map(|x| Cow::Borrowed(x.as_str())),
Self::TaskContext => task_state.context .vars.get(name).map(|x| Cow::Borrowed(x.as_str())),
Self::Scratchpad => task_state.scratchpad .vars.get(name).map(|x| Cow::Borrowed(x.as_str())),
Self::CommonArg => task_state.common_args.ok_or(GetVarError::NotInCommonContext)?.vars.get(name).map(|x| Cow::Borrowed(x.as_str())),
Self::Env => match env::var(name) {
Ok(value) => Some(Cow::Owned(value)),
Err(env::VarError::NotPresent) => None,
Err(env::VarError::NotUnicode(_)) => Err(GetVarError::EnvVarIsNotUtf8)?
}
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(remote = "Self")]
pub struct VarRef {
#[serde(default, skip_serializing_if = "is_default")]
pub r#type: VarType,
pub name: StringSource
}
impl VarRef {
pub fn get<'a>(&self, task_state: &TaskStateView<'a>) -> Result<Option<Cow<'a, str>>, GetVarError> {
debug!(VarRef::get, self);
self.r#type.get(get_str!(self.name, task_state, GetVarError), task_state)
}
}
impl FromStr for VarRef {
type Err = std::convert::Infallible;
fn from_str(name: &str) -> Result<VarRef, Self::Err> {
Ok(name.into())
}
}
impl From<StringSource> for VarRef {
fn from(name: StringSource) -> Self {
Self {
r#type: Default::default(),
name
}
}
}
impl From<String> for VarRef {
fn from(name: String) -> Self {
StringSource::String(name).into()
}
}
impl From<&str> for VarRef {
fn from(name: &str) -> Self {
name.to_string().into()
}
}
string_or_struct_magic!(VarRef);
impl Suitability for VarRef {
fn assert_suitability(&self, config: &Cleaner) {
match (&self.r#type, &self.name) {
(VarType::Params , StringSource::String(name)) => assert!(config.docs.vars .contains_key(name), "Undocumented Var: {name}"),
(VarType::JobContext , StringSource::String(name)) => assert!(config.docs.job_context.vars .contains_key(name), "Undocumented JobContext var: {name}"),
(VarType::TaskContext, StringSource::String(name)) => assert!(config.docs.task_context.vars.contains_key(name), "Undocumented TaskContext var: {name}"),
(VarType::Env , StringSource::String(name)) => assert!(config.docs.environment_vars .contains_key(name), "Undocumented Env var: {name}"),
(VarType::CommonArg | VarType::Scratchpad, StringSource::String(_)) => {},
_ => panic!("Unsuitable VarRef: {self:?}")
}
}
}