klavier-core 0.1.15

Core library of klavier.
Documentation
use std::{num::{ParseFloatError, ParseIntError}, str::FromStr};

use crate::{duration, location::{parse_location, Location}, percent::PercentU16, rhythm, tempo::TempoValue, velocity::Velocity};

#[derive(Debug)]
pub struct Validated<T>(pub T);

#[inline]
pub fn from_str<T, E>(s: &str) -> Result<Validated<T>, E> 
  where Validated<T>: FromStr<Err = E>
{
    Validated::<T>::from_str(s)
}

#[derive(Debug)]
pub struct TextInput<T, E> {
    validated: Result<Validated<T>, E>,
    buffer: String,
}

impl<T, E> TextInput<T, E>
    where Validated<T>: FromStr<Err = E>
{
    pub fn from_string(s: String) -> Self {
        let validated: Result<Validated<T>, E> = from_str(&s);
        Self { validated, buffer: s }
    }

    pub fn mutate<R, M>(&mut self, mutator: M) -> R
      where M: FnOnce(&mut String) -> R
    {
        let r = (mutator)(&mut self.buffer);
        self.validated = from_str(&self.buffer);
        r
    }

    #[inline]
    pub fn input(&self) -> &str {
        &self.buffer
    }

    #[inline]
    pub fn validate(&self) -> &Result<Validated<T>, E> {
        &self.validated
    }
}

pub enum LocationParseError {
    InvalidFormat,
}

impl FromStr for Validated<Option<Location>> {
    type Err = LocationParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Validated(None))
        } else {
            match parse_location(s) {
                Some(loc) => Ok(Validated(Some(loc))),
                None => Err(LocationParseError::InvalidFormat),
            }
        }
    }
}

impl FromStr for Validated<Option<i16>> {
    type Err = ParseIntError;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Validated(None))
        } else {
            s.parse::<i16>().map(|i| Validated(Some(i)))
        }
    }
}

impl FromStr for Validated<Option<TempoValue>> {
    type Err = ParseIntError;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Validated(None))
        } else {
            s.parse::<u16>().map(|u| Validated(Some(TempoValue::new(u))))
        }
    }
}

impl FromStr for Validated<Option<Velocity>> {
    type Err = ParseIntError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Validated(None))
        } else {
            s.parse::<u8>().map(|u| Validated(Some(Velocity::new(u))))
        }
    }
}

impl FromStr for Validated<Option<rhythm::Numerator>> {
    type Err = rhythm::NumeratorError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Validated(None))
        } else {
            rhythm::Numerator::from_str(s).map(|n| Validated(Some(n)))
        }
    }
}

#[derive(Debug)]
pub enum DurationDenominatorError {
    ParseIntError,
    InvalidValue,
}

impl FromStr for Validated<Option<duration::Denominator>> {
    type Err = DurationDenominatorError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Validated(None))
        } else {
            match s.parse::<u8>() {
                Ok(u) => match duration::Denominator::from_value(u) {
                    Some(d) => Ok(Validated(Some(d))),
                    None => Err(DurationDenominatorError::InvalidValue),
                }
                Err(_) => Err(DurationDenominatorError::ParseIntError)
            }
        }
    }
}

impl FromStr for Validated<Option<PercentU16>> {
    type Err = ParseFloatError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Validated(None))
        } else {
            match s.parse::<f32>() {
                Ok(pct) => Ok(Validated(Some(pct.into()))),
                Err(e) => Err(e),
            }
        }
    }
}