use crate::Environment;
use super::layer::{Layer, Origin, Setting, SettingPath, value_from_text};
use super::{ConfigError, SECRETS_FILE_VARIABLE};
pub const ENVIRONMENT_PREFIX: &str = "ONETASKGRAPH_";
const RESERVED: &[&str] = &[SECRETS_FILE_VARIABLE];
const RESERVED_NAMESPACE: &str = "ONETASKGRAPH_LIVE_";
const SEGMENT_SEPARATOR: &str = "__";
fn reserved(variable: &str) -> bool {
RESERVED.contains(&variable) || variable.starts_with(RESERVED_NAMESPACE)
}
pub fn layer(environment: &Environment) -> Result<Layer, ConfigError> {
for variable in environment.unusable() {
if variable.starts_with(ENVIRONMENT_PREFIX) && !reserved(variable) {
return Err(ConfigError::setting(
variable,
"this variable's value is not valid Unicode, so it cannot be read as a \
setting",
"export it again with a value this shell and this process agree on, or \
unset it and set the setting in a configuration document instead.",
));
}
}
let mut settings = Vec::new();
for (variable, raw) in environment.iter() {
let Some(encoded) = variable.strip_prefix(ENVIRONMENT_PREFIX) else {
continue;
};
if reserved(variable) {
continue;
}
settings.push(Setting {
key: path_from(encoded, variable)?,
value: value_from_text(raw),
origin: Origin::Environment {
variable: variable.to_owned(),
},
});
}
Ok(Layer::new(settings))
}
fn path_from(encoded: &str, variable: &str) -> Result<SettingPath, ConfigError> {
let segments: Vec<String> = encoded
.split(SEGMENT_SEPARATOR)
.enumerate()
.map(|(index, segment)| decode_segment(segment, index, encoded))
.collect();
SettingPath::new(segments, variable)
}
fn decode_segment(segment: &str, index: usize, encoded: &str) -> String {
let lowered = segment.to_ascii_lowercase();
if index == 1 && encoded.starts_with("SOURCES__") {
lowered.replace('_', "-")
} else {
lowered
}
}
#[must_use]
pub fn variable_for(key: &SettingPath) -> String {
let segments: Vec<String> = key
.segments()
.iter()
.map(|segment| segment.to_ascii_uppercase().replace('-', "_"))
.collect();
format!("{ENVIRONMENT_PREFIX}{}", segments.join(SEGMENT_SEPARATOR))
}