crypt-configs 0.1.0

A simple config format
Documentation
//! Config Body Object
//! Created 6/4/2026 - Nyx

use std::collections::HashMap;

#[derive(Debug, PartialEq, Eq)]
/// A Crypt Configuration object
/// 
/// Uses a simple map for string identifiers to [CryptObject]s.
/// Configs can contain other nested configs, string literals, and identifiers.
/// String literals and identifiers are both stored as a [String].
/// 
/// # Example
/// 
/// Example crypt file:
/// 
/// ./tests/test.crypt
/// ```crypt
/// version = "0.1.0";
/// name = "Test";
/// type = "Executable"
/// ```
/// 
/// 
/// Opening the file:
/// ```
/// use crypt_configs::{open_config, body::CryptObject};
/// 
/// let config = match open_config("tests/test.crypt") {
///     Ok(c) => c,
///     Err(e) => panic!("Failed to open test file!"),
/// };
/// 
/// assert_eq!(config.get("version").unwrap().try_into(), Ok(&String::from("0.1.0")));
/// assert_eq!(config.get("name").unwrap().try_into(), Ok(&String::from("Test")));
/// assert_eq!(config.get("type").unwrap().try_into(), Ok(&String::from("Executable")));
/// ```
pub struct CryptConfig {
    items: HashMap<String, CryptObject>
}

impl CryptConfig {
    pub (crate) fn new() -> Self {
        Self {
            items: HashMap::new()
        }
    }

    pub (crate) fn add_item(&mut self, identifier: String, value: CryptObject) {
        self.items.insert(identifier, value);
    }

    /// Retrieves an item from the config
    /// 
    /// The object is ambiguously wrapped in a [CryptObject]. 
    pub fn get<S>(&self, ident: S) -> Option<&CryptObject> 
    where 
        S: Into<String>
    {
        self.items.get(&ident.into())
    } 
}

#[derive(Debug, PartialEq, Eq)]
/// An ambiguously wrapped object in a crypt config.
/// 
/// crypt objects come in four variants: identifiers, string literals, 
/// bodies (nested configs), and nulls.
/// 
/// CryptObject implements [TryInto] for &String and &CryptConfig for quick 
/// conversions. More complex conversion methods are additionally implemented.
pub enum CryptObject {
    Identifier(String),
    String(String),
    Body(CryptConfig),
    Null,
}

impl CryptObject {
    /// Unwraps an identifier crypt object into its [String] value
    /// 
    /// # Panics
    /// 
    /// Panics if the crypt object is not an identifier. Unlike TryInto this will
    /// not automatically convert string literals.
    pub fn unwrap_identifier(&self) -> &String {
        match self {
            Self::Identifier(ident) => ident,
            _ => panic!("Unwrapped a non-identifier crypt object!")
        }
    }

    /// Unwraps an identifier crypt object into its [String] value
    /// 
    /// # Panics
    /// 
    /// Panics if the crypt object is not an identifier with a special 
    /// message given by msg. Unlike TryInto this will
    /// not automatically convert string literals.
    pub fn expect_identifier(&self, msg: &str) -> &String {
        match self {
            Self::Identifier(ident) => ident,
            _ => panic!("{}", msg)
        }
    }

    /// Unwraps an identifier crypt object into its string value, alternatively if
    /// the object isn't an identifier it will return the alternate string value.
    pub fn unwrap_identifier_or<'a>(&'a self, other: &'a String) -> &'a String {
        match self {
            Self::Identifier(ident) => ident,
            _ => other
        }
    }


    /// Unwraps a string literal crypt object into its [String] value
    /// 
    /// # Panics
    /// 
    /// Panics if the crypt object is not an identifier. Unlike TryInto this will
    /// not automatically convert identifier.
    pub fn unwrap_string(&self) -> &String {
        match self {
            Self::String(s) => s,
            _ => panic!("Unwrapped a non-identifier crypt object!")
        }
    }

    /// Unwraps a string literal crypt object into its [String] value
    /// 
    /// # Panics
    /// 
    /// Panics if the crypt object is not an identifier with a special 
    /// message given by msg. Unlike TryInto this will
    /// not automatically convert identifiers.
    pub fn expect_string(&self, msg: &str) -> &String {
        match self {
            Self::String(s) => s,
            _ => panic!("{}", msg)
        }
    }

    /// Unwraps a string literal crypt object into its string value, alternatively if
    /// the object isn't a string literal it will return the alternate string value.
    pub fn unwrap_string_or<'a>(&'a self, other: &'a String) -> &'a String {
        match self {
            Self::String(s) => s,
            _ => other
        }
    }

    /// Unwraps a string literal crypt object into its [String] value
    /// 
    /// # Panics
    /// 
    /// Panics if the crypt object is not an identifier. Unlike TryInto this will
    /// not automatically convert identifier.
    pub fn unwrap_nested_config(&self) -> &CryptConfig {
        match self {
            Self::Body(b) => b,
            _ => panic!("Unwrapped a non-identifier crypt object!")
        }
    }

    /// Unwraps a string literal crypt object into its [String] value
    /// 
    /// # Panics
    /// 
    /// Panics if the crypt object is not an identifier with a special 
    /// message given by msg. Unlike TryInto this will
    /// not automatically convert identifiers.
    pub fn expect_nested_config(&self, msg: &str) -> &CryptConfig {
        match self {
            Self::Body(b) => b,
            _ => panic!("{}", msg)
        }
    }
}




impl<'a> TryInto<&'a String> for &'a CryptObject {
    type Error = CryptConversionError;

    fn try_into(self) -> Result<&'a String, Self::Error> {
        match self {
            CryptObject::String(v) => Ok(v),
            CryptObject::Identifier(id) => Ok(id),
            _ => Err(CryptConversionError::CannotConvertToString)
        }
    }
}

impl<'a> TryInto<&'a CryptConfig> for &'a CryptObject {
    type Error = CryptConversionError;

    fn try_into(self) -> Result<&'a CryptConfig, Self::Error> {
        match self {
            CryptObject::Body(b) => Ok(b),
            _ => Err(CryptConversionError::CannotConvertToNestedConfig)
        }
    }
}


#[derive(Debug, PartialEq, Eq)]
pub enum CryptConversionError {
    CannotConvertToString,
    CannotConvertToNestedConfig,
    CannotConvertToIdentifier,
}

impl std::fmt::Display for CryptConversionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::CannotConvertToString => write!(f, "Crypt object cannot be converted to string!"),
            Self::CannotConvertToIdentifier => write!(f, "Crypt object cannot be converted to an identifier!"),
            Self::CannotConvertToNestedConfig => write!(f, "Crypt object cannot be converted to a nested config!"),
        }
    }
}