env_pair.rs•5.98 kB
use crate::MicrosandboxError;
use getset::Getters;
use serde::{Deserialize, Serialize};
use std::{fmt, str::FromStr};
//--------------------------------------------------------------------------------------------------
// Types
//--------------------------------------------------------------------------------------------------
/// Represents an environment variable pair.
///
/// This struct encapsulates a variable name and its corresponding value.
/// It is used to manage environment variables for processes.
///
/// ## Examples
///
/// ```
/// use microsandbox_core::config::EnvPair;
/// use std::str::FromStr;
///
/// // Create a new environment variable pair
/// let env_pair = EnvPair::new("PATH", "/usr/local/bin:/usr/bin");
///
/// assert_eq!(env_pair.get_name(), "PATH");
/// assert_eq!(env_pair.get_value(), "/usr/local/bin:/usr/bin");
///
/// // Parse an environment variable pair from a string
/// let env_pair = EnvPair::from_str("USER=alice").unwrap();
///
/// assert_eq!(env_pair.get_name(), "USER");
/// assert_eq!(env_pair.get_value(), "alice");
/// ```
#[derive(Debug, Hash, Clone, PartialEq, Eq, Getters)]
#[getset(get = "pub with_prefix")]
pub struct EnvPair {
    /// The environment variable name.
    name: String,
    /// The value of the environment variable.
    value: String,
}
//--------------------------------------------------------------------------------------------------
// Methods
//--------------------------------------------------------------------------------------------------
impl EnvPair {
    /// Creates a new `EnvPair` with the given variable name and value.
    ///
    /// # Arguments
    ///
    /// * `var` - The name of the environment variable.
    /// * `value` - The value of the environment variable.
    ///
    /// ## Examples
    ///
    /// ```
    /// use microsandbox_core::config::EnvPair;
    ///
    /// let env_pair = EnvPair::new("HOME", "/home/user");
    /// assert_eq!(env_pair.get_name(), "HOME");
    /// assert_eq!(env_pair.get_value(), "/home/user");
    /// ```
    pub fn new<S: Into<String>>(name: S, value: S) -> Self {
        Self {
            name: name.into(),
            value: value.into(),
        }
    }
}
//--------------------------------------------------------------------------------------------------
// Trait Implementations
//--------------------------------------------------------------------------------------------------
impl FromStr for EnvPair {
    type Err = MicrosandboxError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (var, value) = s
            .split_once('=')
            .ok_or_else(|| MicrosandboxError::InvalidEnvPair(s.to_string()))?;
        if var.is_empty() {
            return Err(MicrosandboxError::InvalidEnvPair(s.to_string()));
        }
        Ok(Self::new(var, value))
    }
}
impl fmt::Display for EnvPair {
    /// Formats the environment variable pair following the format "<var>=<value>".
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}={}", self.name, self.value)
    }
}
impl Serialize for EnvPair {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(&self.to_string())
    }
}
impl<'de> Deserialize<'de> for EnvPair {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Self::from_str(&s).map_err(serde::de::Error::custom)
    }
}
//--------------------------------------------------------------------------------------------------
// Tests
//--------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_env_pair_new() {
        let env_pair = EnvPair::new("VAR", "VALUE");
        assert_eq!(env_pair.name, String::from("VAR"));
        assert_eq!(env_pair.value, String::from("VALUE"));
    }
    #[test]
    fn test_env_pair_from_str() -> anyhow::Result<()> {
        let env_pair: EnvPair = "VAR=VALUE".parse()?;
        assert_eq!(env_pair.name, String::from("VAR"));
        assert_eq!(env_pair.value, String::from("VALUE"));
        let env_pair: EnvPair = "VAR=".parse()?;
        assert_eq!(env_pair.name, String::from("VAR"));
        assert_eq!(env_pair.value, String::from(""));
        assert!("VAR".parse::<EnvPair>().is_err());
        assert!("=VALUE".parse::<EnvPair>().is_err());
        Ok(())
    }
    #[test]
    fn test_env_pair_display() {
        let env_pair = EnvPair::new("VAR", "VALUE");
        assert_eq!(env_pair.to_string(), "VAR=VALUE");
        let env_pair = EnvPair::new("VAR", "");
        assert_eq!(env_pair.to_string(), "VAR=");
    }
    #[test]
    fn test_env_pair_serialize_deserialize() -> anyhow::Result<()> {
        let env_pair = EnvPair::new("VAR", "VALUE");
        let serialized = serde_json::to_string(&env_pair)?;
        assert_eq!(serialized, "\"VAR=VALUE\"");
        let deserialized: EnvPair = serde_json::from_str(&serialized)?;
        assert_eq!(deserialized, env_pair);
        let env_pair = EnvPair::new("VAR", "");
        let serialized = serde_json::to_string(&env_pair)?;
        assert_eq!(serialized, "\"VAR=\"");
        let deserialized: EnvPair = serde_json::from_str(&serialized)?;
        assert_eq!(deserialized, env_pair);
        Ok(())
    }
    #[test]
    fn test_env_pair_with_special_characters() -> anyhow::Result<()> {
        let env_pair: EnvPair = "VAR_WITH_UNDERSCORE=VALUE WITH SPACES".parse()?;
        assert_eq!(env_pair.name, "VAR_WITH_UNDERSCORE");
        assert_eq!(env_pair.value, "VALUE WITH SPACES");
        let env_pair: EnvPair = "VAR.WITH.DOTS=VALUE_WITH_UNDERSCORE".parse()?;
        assert_eq!(env_pair.name, "VAR.WITH.DOTS");
        assert_eq!(env_pair.value, "VALUE_WITH_UNDERSCORE");
        Ok(())
    }
}