ferric_crypto_lib 0.2.7

A library for Ferric Crypto
Documentation
use crate::prelude::{CharacterParseError, ALPHABET};
use crate::utils::{BaseString, EncodedString, StringType, THRESHOLD};
use rayon::prelude::*;
#[cfg(feature = "python-integration")]
use pyo3_helper_macros::{py3_bind_pub, exclude};

#[cfg_attr(feature = "python-integration", py3_bind_pub(supported = {StringType, BaseString, EncodedString, Vec<usize>}))]
impl BaseString {
    pub fn new(data: String) -> Self {
        Self { data }
    }

    /// Encodes a string to a vector of indices based on the alphabet.
    ///
    /// # Example
    ///
    /// ```
    /// # use ferric_crypto_lib::utils::{BaseString, EncodedString, StringType};
    ///
    /// let t = BaseString::new("abc".to_string());
    /// assert_eq!(t.encode().unwrap(), EncodedString::new(vec![0, 1, 2], StringType::Standard));
    /// ```
    pub fn encode(&self) -> Result<EncodedString, CharacterParseError> {
        let data = self.data.to_lowercase();
        let data_len = data.chars().count();

        #[cfg(feature = "debug")]
        {
            // Log the input data and its length
            dbg!(&data, data_len);
        }

        let encoded = if data_len > THRESHOLD {
            data.par_chars()
                .map(|x| {
                    #[cfg(feature = "debug")]
                    {
                        let position = crate::prelude::ALPHABET.chars().position(|y| y == x);

                        dbg!(&x, &position); // Log each character and its index (sequential branch)

                        position.ok_or(CharacterParseError::InvalidCharacter(x))
                    }
                    #[cfg(not(feature = "debug"))]
                    {
                        ALPHABET
                            .chars()
                            .position(|y| y == x)
                            .ok_or(CharacterParseError::InvalidCharacter(x))
                    }
                })
                .collect::<Result<Vec<_>, _>>()?
        } else {
            data.chars()
                .map(|x| {
                    #[cfg(feature = "debug")]
                    {
                        let position = crate::prelude::ALPHABET.chars().position(|y| y == x);

                        dbg!(&x, &position); // Log each character and its index (sequential branch)

                        position.ok_or(CharacterParseError::InvalidCharacter(x))
                    }
                    #[cfg(not(feature = "debug"))]
                    {
                        ALPHABET
                            .chars()
                            .position(|y| y == x)
                            .ok_or(CharacterParseError::InvalidCharacter(x))
                    }
                })
                .collect::<Result<Vec<_>, _>>()?
        };

        #[cfg(feature = "debug")]
        {
            // Log the final encoded data
            dbg!(&encoded);
        }

        // Return based on whether the lengths match
        if data_len == encoded.len() {
            Ok(EncodedString::new(encoded, StringType::Standard))
        } else {
            Err(CharacterParseError::UnknownStringType)
        }
    }

    /// Encodes a string to a vector of indices based on the alphabet.
    ///
    /// # Example
    ///
    /// ```
    /// # use ferric_crypto_lib::utils::{BaseString, EncodedString, StringType};
    ///
    /// let t = BaseString::new("abc".to_string());
    /// assert_eq!(t.encode_asym().unwrap(), EncodedString::new(vec![1, 2, 3], StringType::Assymetric));
    /// ```
    pub fn encode_asym(&self) -> Result<EncodedString, CharacterParseError> {
        let data = self.data.to_lowercase();
        let data_len = data.chars().count();

        // Log the input data and its length
        #[cfg(feature = "debug")]
        dbg!(&data, data_len);

        let encoded = if data_len > THRESHOLD {
            data.par_chars()
                .map(|x| {
                    let position = ALPHABET.chars().position(|y| y == x).map(|index| index + 1); // Add 1 to avoid zero

                    #[cfg(feature = "debug")]
                    dbg!(&x, &position); // Log each character and its index

                    position.ok_or(CharacterParseError::InvalidCharacter(x))
                })
                .collect::<Result<Vec<_>, _>>()?
        } else {
            data.chars()
                .map(|x| {
                    let position = ALPHABET.chars().position(|y| y == x).map(|index| index + 1); // Add 1 to avoid zero

                    #[cfg(feature = "debug")]
                    dbg!(&x, &position); // Log each character and its index

                    position.ok_or(CharacterParseError::InvalidCharacter(x))
                })
                .collect::<Result<Vec<_>, _>>()?
        };

        // Log the final encoded data
        #[cfg(feature = "debug")]
        dbg!(&encoded);

        Ok(EncodedString::new(encoded, StringType::Assymetric))
    }



    // include more encodings here, such as base64 and binary encodings
}

impl BaseString {
    #[cfg_attr(feature = "python-integration", exclude)]
    pub fn to_uppercase(self) -> Self {
        let cpy = self.clone();
        let cpy = cpy.data.to_uppercase();
        Self { data: cpy }
    }

    #[cfg_attr(feature = "python-integration", exclude)]
    pub fn to_lowercase(self) -> Self {
        let cpy = self.clone();
        let cpy = cpy.data.to_lowercase();
        Self { data: cpy }
    }

    pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
        self.data.chars()
    }

    pub fn par_chars(&self) -> rayon::str::Chars<'_> {
        self.data.par_chars()
    }

}

#[cfg_attr(feature = "python-integration", py3_bind_pub(supported = {StringType, BaseString, EncodedString, Vec<usize>}))]
impl EncodedString {
    // represents the encoded string as a vector of usize
    pub fn new(data: Vec<usize>, str_type: StringType) -> Self {
        Self { data, str_type }
    }

    // error handling falls on the user not the lib in this case, user should know what has gone wrong and handle it
    pub fn decode(&self) -> Result<BaseString, CharacterParseError> {
        match self.str_type {
            StringType::Standard => self.decode_std(),
            StringType::Assymetric => self.decode_asym(),
            _ => Err(CharacterParseError::UnknownStringType),
        }
    }

    fn decode_std(&self) -> Result<BaseString, CharacterParseError> {
        let decoded = if self.data.len() > THRESHOLD {
            self.data
                .par_iter()
                .map(|&x| {
                    ALPHABET
                        .chars()
                        .nth(x)
                        .ok_or(CharacterParseError::InvalidIndex(x))
                })
                .collect::<Result<String, _>>()?
        } else {
            self.data
                .iter()
                .map(|&x| {
                    ALPHABET
                        .chars()
                        .nth(x)
                        .ok_or(CharacterParseError::InvalidIndex(x))
                })
                .collect::<Result<String, _>>()?
        };

        Ok(BaseString::new(decoded))
    }

    fn decode_asym(&self) -> Result<BaseString, CharacterParseError> {
        let decoded = if self.data.len() > THRESHOLD {
            self.data
                .par_iter()
                .map(|&x| {
                    ALPHABET
                        .chars()
                        .nth(x - 1)
                        .ok_or(CharacterParseError::InvalidIndex(x))
                })
                .collect::<Result<String, _>>()?
        } else {
            self.data
                .iter()
                .map(|&x| {
                    ALPHABET
                        .chars()
                        .nth(x - 1)
                        .ok_or(CharacterParseError::InvalidIndex(x))
                })
                .collect::<Result<String, _>>()?
        };

        Ok(BaseString::new(decoded))
    }

    /// # Example
    ///
    /// ```
    /// # use ferric_crypto_lib::utils::{EncodedString, StringType};
    ///
    /// let t = EncodedString::new(vec![1, 2, 3], StringType::Standard);
    /// assert_eq!(t.flatten(), "123".to_string());
    /// ```
    ///
    /// ```
    /// # use ferric_crypto_lib::utils::{EncodedString, StringType};
    ///
    /// let t = EncodedString::new(vec![1, 2, 3], StringType::Assymetric);
    /// assert_eq!(t.flatten(), "10203".to_string());
    /// ```
    pub fn flatten(&self) -> String {
        let data = self.data.clone();
        let mut new_str = String::new();
        if self.str_type == StringType::Standard {
            for x in data {
                new_str.push_str(&x.to_string());
            }
        } else {
            // if we do not have a standard string, we treat it as a asymetric string
            // if any number but the first number is less then 10, add a 0 to the start
            for (i, x) in data.iter().enumerate() {
                if i != 0 && *x < 10 {
                    new_str.push_str(&format!("0{}", x));
                } else {
                    new_str.push_str(&x.to_string());
                }
            }
        }
        new_str
    }
}

impl EncodedString {
    pub fn iter(&self) -> impl Iterator<Item = &usize> {
        self.data.iter()
    }

    pub fn par_iter(&self) -> rayon::slice::Iter<'_, usize> {
        self.data.par_iter()
    }
}

#[cfg(feature = "python-integration")]
mod python_integration {
    use super::*;
    use pyo3::prelude::*;

    #[pymethods]
    impl EncodedString {
        pub fn __str__(&self) -> PyResult<String> {
            Ok(format!("Encoded String: data = {:?}, type = {:?}", self.data, self.str_type))
        }
    }

    #[pymethods]
    impl BaseString {
        pub fn __str__(&self) -> PyResult<String> {
            Ok(format!("Base String: data = {:?}", self.data))
        }
    }
}