num_base 0.4.2

Crate for manipulating with numbers (integers) in different bases.
Documentation
#[cfg(test)]
mod tests;

mod error;
pub use error::AlphabetError;

const DEFAULT: &str = "0123456789abcdefghijklmnopqrstuvwxyz";
const UPPERCASE: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const DECIMAL: &str = "0123456789";
const LATIN: &str = "abcdefghijklmnopqrstuvwxyz";
const LATIN_UPPERCASE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

/// Alphabet used in `Based` number.
// When adding new variants, add also in `Self::get()`, `Self::simplify()` and their tests, and `impl PartialEq for Alphabet`
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub enum Alphabet {
    /// ```"0123456789abcdefghijklmnopqrstuvwxyz"```
    #[default]
    Default,
    /// ```"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"```
    UpperCase,
    /// ```"0123456789"```
    Decimal,
    /// ```"abcdefghijklmnopqrstuvwxyz"```
    Latin,
    /// ```"ABCDEFGHIJKLMNOPQRSTUVWXYZ"```
    LatinUpperCase,
    /// ```
    /// # use num_base::Alphabet;
    /// let alphabet = Alphabet::new("abcdefghijklmnopqrstuvwxyz");
    /// ```
    /// **See: [Alphabet::check()] or [Alphabet::is_valid()]**.
    Custom(String),
}

impl Alphabet {
    /// Creates a new `Alphabet::Custom`.
    ///
    /// ```
    /// # use num_base::Alphabet;
    /// let alphabet = Alphabet::new("abcdefghijklmnopqrstuvwxyz");
    /// ```
    pub fn new<T: Into<Alphabet>>(abc: T) -> Self {
        abc.into().check().unwrap()
    }

    /// Creates a new `Alphabet::Custom`.
    pub fn new_unchecked<T: Into<Alphabet>>(abc: T) -> Self {
        abc.into()
    }

    fn valid(&self) -> (bool, Vec<char>) {
        let v1: Vec<char> = self.get().chars().collect();
        let mut v2 = v1.clone();

        v2.sort();
        v2.dedup();

        (v1.len() == v2.len(), v2)
    }

    /// Checks for repeated characters.
    ///
    /// Use [check()](Self::check()) to get error info.
    pub fn is_valid(&self) -> bool {
        self.valid().0
    }

    /// Sorts alphabet and removes all repeated characters.
    pub fn to_valid(self) -> Self {
        let valid = &self.valid();
        if !valid.0 { return Self::Custom(valid.1.iter().collect()) }

        self
    }

    /// Checks for repeated characters.
    pub fn check(self) -> Result<Self, AlphabetError> {
        if !self.valid().0 { return Err( AlphabetError::RepeatedCharacters ) }

        Ok( self )
    }

    /// Returns `Alphabet` as a String.
    ///
    /// ```
    /// # use num_base::Alphabet;
    /// assert_eq!(Alphabet::Default.get(), "0123456789abcdefghijklmnopqrstuvwxyz")
    /// ```
    /// ```
    /// # use num_base::Alphabet;
    /// assert_eq!(Alphabet::from("abcd").get(), "abcd")
    /// ```
    pub fn get(&self) -> String {
        match self {
            Self::Default => DEFAULT.into(),
            Self::UpperCase => UPPERCASE.into(),
            Self::Decimal => DECIMAL.into(),
            Self::Latin => LATIN.into(),
            Self::LatinUpperCase => LATIN_UPPERCASE.into(),
            Self::Custom(abc) => abc.into(),
        }
    }

    /// Changes `Alphabet::Custom` to predefined alphabets if they have the same value.
    pub fn simplify(self) -> Self {
        match self.get().as_str() {
            DEFAULT => Self::Default,
            UPPERCASE => Self::UpperCase,
            DECIMAL => Self::Decimal,
            LATIN => Self::Latin,
            LATIN_UPPERCASE => Self::LatinUpperCase,
            abc => Self::new_unchecked(abc),
        }
    }

    #[doc(hidden)]
    /// Returns char at index `i`
    pub fn nth_char(&self, i: usize) -> Result<char, AlphabetError> {
        self.get().chars()
            .nth(i)
            .ok_or(AlphabetError::TooSmallAlphabet(i))
    }

    #[doc(hidden)]
    /// Returns index of char in `Alphabet`
    pub fn chars_index(&self, c: char) -> Result<usize, AlphabetError> {
        self.get().chars()
            .position(|ch| ch == c)
            .ok_or(AlphabetError::CharacterNotIncluded(c))
    }
}

impl PartialEq for Alphabet {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Default, Self::Default) => true,
            (Self::UpperCase, Self::UpperCase) => true,
            (Self::Decimal, Self::Decimal) => true,
            (Self::Latin, Self::Latin) => true,
            (Self::LatinUpperCase, Self::LatinUpperCase) => true,
            (Self::Custom(abc1), Self::Custom(abc2)) => abc1 == abc2,
            _ => false
        }
    }
}

use std::convert::From;

impl From<String> for Alphabet {
    fn from(abc: String) -> Self {
        Self::Custom(abc)
    }
}

impl From<&str> for Alphabet {
    fn from(abc: &str) -> Self {
        Self::Custom(abc.to_owned())
    }
}