use std::borrow::Cow; use std::collections::BTreeMap; use std::io; use avril_caps::{Capabilities, env_name}; #[derive(Clone, Default)] pub struct Environment { values: BTreeMap, } fn name(value: &str) -> io::Result> { env_name(value).map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error)) } fn authorize(caps: &Capabilities, name: &str) -> io::Result<()> { caps.check_env(name) .map_err(|error| io::Error::new(io::ErrorKind::PermissionDenied, error)) } impl Environment { pub fn new(values: impl IntoIterator) -> io::Result { let mut result = Self::default(); for (key, value) in values { if value.contains('\0') { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Environment values cannot contain NUL", )); } result.values.insert(name(&key)?.into_owned(), (key, value)); } Ok(result) } pub fn from_process() -> Self { let values = std::env::vars_os().filter_map(|(key, value)| { let key = key.into_string().ok()?; let value = value.into_string().ok()?; Some((name(&key).ok()?.into_owned(), (key, value))) }); Self { values: values.collect() } } pub fn with_defaults(mut self, defaults: Self) -> Self { for (key, value) in defaults.values { self.values.entry(key).or_insert(value); } self } pub fn get(&self, caps: &Capabilities, key: &str) -> io::Result> { let key = name(key)?; authorize(caps, &key)?; Ok(self .values .get(key.as_ref()) .map(|(_, value)| value.clone())) } pub fn set(&mut self, caps: &Capabilities, key: &str, value: String) -> io::Result<()> { let normalized = name(key)?; if value.contains('\0') { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Environment values cannot contain NUL", )); } authorize(caps, &normalized)?; self.values .insert(normalized.into_owned(), (key.to_owned(), value)); Ok(()) } pub fn delete(&mut self, caps: &Capabilities, key: &str) -> io::Result<()> { let key = name(key)?; authorize(caps, &key)?; self.values.remove(key.as_ref()); Ok(()) } pub fn to_object(&self, caps: &Capabilities) -> Vec<(String, String)> { self.values .iter() .filter(|(key, _)| caps.check_env(key).is_ok()) .map(|(_, entry)| entry.clone()) .collect() } }