ffmml 0.1.0

Famicon (a.k.a. NES) Flavored Music Macro Language
Documentation
use crate::{
    comment::CommentsOrWhitespaces,
    types::{Int, NoteEnvelope, PitchEnvelope, Timbres, Vibrato, VolumeEnvelope},
};
use std::collections::BTreeMap;
use textparse::{
    components::{Char, Empty, Str},
    Parse, Parser, Span,
};

#[derive(Debug, Default, Clone)]
pub struct Macros {
    pub volumes: BTreeMap<MacroNumber, VolumeMacro>,
    pub timbres: BTreeMap<MacroNumber, TimbreMacro>,
    pub pitches: BTreeMap<MacroNumber, PitchMacro>,
    pub arpeggios: BTreeMap<MacroNumber, ArpeggioMacro>,
    pub vibratos: BTreeMap<MacroNumber, VibratoMacro>,
}

impl Macros {
    pub fn parse(&mut self, parser: &mut Parser) -> Option<()> {
        while parser.peek_char() == Some('@') {
            if let Some(m) = parser.parse::<VolumeMacro>() {
                self.volumes.insert(m.number(), m);
            } else if let Some(m) = parser.parse::<TimbreMacro>() {
                self.timbres.insert(m.number(), m);
            } else if let Some(m) = parser.parse::<PitchMacro>() {
                self.pitches.insert(m.number(), m);
            } else if let Some(m) = parser.parse::<ArpeggioMacro>() {
                self.arpeggios.insert(m.number(), m);
            } else if let Some(m) = parser.parse::<VibratoMacro>() {
                self.vibratos.insert(m.number(), m);
            } else {
                return None;
            }
            let _: CommentsOrWhitespaces = parser.parse()?;
        }
        Some(())
    }
}

#[derive(Debug, Clone, Copy, Span, Parse)]
pub struct MacroNumber(Int<0, 127>);

impl PartialEq for MacroNumber {
    fn eq(&self, other: &Self) -> bool {
        self.0.get() == other.0.get()
    }
}

impl Eq for MacroNumber {}

impl PartialOrd for MacroNumber {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.0.get().partial_cmp(&other.0.get())
    }
}

impl Ord for MacroNumber {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.get().cmp(&other.0.get())
    }
}

#[derive(Debug, Clone, Span, Parse)]
struct MacroKey<Prefix> {
    _at: Char<'@'>,
    _prefix: Prefix,
    number: MacroNumber,
    _space0: CommentsOrWhitespaces,
    _equal: Char<'='>,
    _space1: CommentsOrWhitespaces,
}

#[derive(Debug, Clone, Span, Parse)]
pub struct VolumeMacro {
    key: MacroKey<Char<'v'>>,
    envelope: VolumeEnvelope,
}

impl VolumeMacro {
    pub fn number(&self) -> MacroNumber {
        self.key.number
    }

    pub fn envelope(&self) -> &VolumeEnvelope {
        &self.envelope
    }
}

#[derive(Debug, Clone, Span, Parse)]
pub struct PitchMacro {
    key: MacroKey<Str<'E', 'P'>>,
    envelope: PitchEnvelope,
}

impl PitchMacro {
    pub fn number(&self) -> MacroNumber {
        self.key.number
    }

    pub fn envelope(&self) -> &PitchEnvelope {
        &self.envelope
    }
}

#[derive(Debug, Clone, Span, Parse)]
pub struct ArpeggioMacro {
    key: MacroKey<Str<'E', 'N'>>,
    envelope: NoteEnvelope,
}

impl ArpeggioMacro {
    pub fn number(&self) -> MacroNumber {
        self.key.number
    }

    pub fn envelope(&self) -> &NoteEnvelope {
        &self.envelope
    }
}

#[derive(Debug, Clone, Span, Parse)]
pub struct TimbreMacro {
    key: MacroKey<Empty>,
    timbres: Timbres,
}

impl TimbreMacro {
    pub fn number(&self) -> MacroNumber {
        self.key.number
    }

    pub fn timbres(&self) -> &Timbres {
        &self.timbres
    }
}

#[derive(Debug, Clone, Span, Parse)]
pub struct VibratoMacro {
    key: MacroKey<Str<'M', 'P'>>,
    vibrato: Vibrato,
}

impl VibratoMacro {
    pub fn number(&self) -> MacroNumber {
        self.key.number
    }

    pub fn vibrato(&self) -> &Vibrato {
        &self.vibrato
    }
}