resonata 0.3.0

A music theory library for Rust.
Documentation
use std::{fmt::{self, Display, Formatter, Debug}, str::FromStr, ops::{Add, Sub, AddAssign, SubAssign}};
use super::super::*;

impl From<i8> for Accidental {
    fn from(value: i8) -> Self {
        if value < 0 {
            Flat(value.abs() as u8)
        } else if value == 0 {
            Natural
        } else {
            Sharp(value as u8)
        }
    }
}

impl From<Accidental> for i8 {
    fn from(acc: Accidental) -> Self {
        match acc {
            Flat(n) => -(n as i8),
            Natural => 0,
            Sharp(n) => n as i8,
        }
    }
}

impl Add<i8> for Accidental {
    type Output = Self;

    fn add(self, amount: i8) -> Self::Output {
        Self::from(i8::from(self) + amount)
    }
}

impl AddAssign<i8> for Accidental {
    fn add_assign(&mut self, amount: i8) {
        *self = *self + amount;
    }
}

impl Sub<i8> for Accidental {
    type Output = Self;
    
    fn sub(self, amount: i8) -> Self::Output {
        Self::from(i8::from(self) - amount)
    }
}

impl SubAssign<i8> for Accidental {
    fn sub_assign(&mut self, amount: i8) {
        *self = *self - amount;
    }
}

impl FromStr for Accidental {
    type Err = ResonataError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {

        let mut sharp_count = 0;
        let mut flat_count = 0;
    
        for c in s.chars() {
            match c {
                '#' | '' => sharp_count += 1,
                'x' | '𝄪' => sharp_count += 2,
                'b' | '' => flat_count += 1,
                '' => {
                    if sharp_count == 0 && flat_count == 0 {
                        return Ok(Natural)
                    } else {
                        nope!(InvalidAccidentalCombination)
                    }
                },
                _ => nope!(InvalidAccidental)
            }
        }
    
        if sharp_count > 0 && flat_count > 0 {
            nope!(InvalidAccidentalCombination)
        } else if flat_count > 0 {
            Ok(Flat(flat_count))
        } else {
            Ok(Sharp(sharp_count))
        }
    }    
}

impl Display for Accidental {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let token = match self {
            Flat(n) => "".repeat(*n as usize),
            Natural => "".to_string(),
            Sharp(n) => {
                let mut token;
                if *n % 2 == 0 {
                    token = "𝄪".repeat(*n as usize / 2);
                } else {
                    token = "".to_string();
                    token.push_str(&"𝄪".repeat((*n as usize - 1) / 2));
                }
                token
            }
        };
        write!(f, "{}", token)
    }   
}

impl Debug for Accidental {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }   
}