pub mod diff;
pub mod filter;
pub mod interpolation;
pub mod parser;
pub mod profile;
pub mod redact;
pub mod schema;
pub mod validator;
use std::fmt;
#[derive(Debug, Clone)]
pub struct EnvFile {
pub entries: Vec<Entry>,
}
#[derive(Debug, Clone)]
pub enum Entry {
KeyValue { key: String, value: String },
Comment(String),
Blank,
}
impl EnvFile {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn vars(&self) -> Vec<(&str, &str)> {
self.entries
.iter()
.filter_map(|e| match e {
Entry::KeyValue { key, value } => Some((key.as_str(), value.as_str())),
_ => None,
})
.collect()
}
pub fn keys(&self) -> Vec<&str> {
self.vars().into_iter().map(|(k, _)| k).collect()
}
#[allow(dead_code)]
pub fn get(&self, key: &str) -> Option<&str> {
self.entries.iter().rev().find_map(|e| match e {
Entry::KeyValue { key: k, value } if k == key => Some(value.as_str()),
_ => None,
})
}
pub fn var_count(&self) -> usize {
self.entries
.iter()
.filter(|e| matches!(e, Entry::KeyValue { .. }))
.count()
}
}
impl Default for EnvFile {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for EnvFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for entry in &self.entries {
match entry {
Entry::KeyValue { key, value } => {
if value.contains(' ')
|| value.contains('"')
|| value.contains('\'')
|| value.contains('#')
|| value.contains('$')
|| value.contains('\\')
|| value.contains('\n')
|| value.contains('\t')
|| value.contains('\r')
|| value.is_empty()
{
let escaped = value
.replace('\\', "\\\\")
.replace('"', "\\\"")
.replace('\n', "\\n")
.replace('\t', "\\t")
.replace('\r', "\\r");
writeln!(f, "{key}=\"{escaped}\"")?;
} else {
writeln!(f, "{key}={value}")?;
}
}
Entry::Comment(text) => writeln!(f, "{text}")?,
Entry::Blank => writeln!(f)?,
}
}
Ok(())
}
}