ffmml 0.1.1

Famicon (a.k.a. NES) Flavored Music Macro Language
Documentation
use crate::{
    channel::ChannelNames,
    comment::{Comment, MaybeComment},
    types::OscillatorKind,
};
use std::marker::PhantomData;
use textparse::{
    components::{Char, NonEmpty, OneOfThree, Str, While},
    Parse, Parser, Position, Span,
};

#[derive(Debug, Clone, Span, Parse)]
pub enum Definition {
    Title(Title),
    Composer(Composer),
    Programer(Programer),
    Channel(Channel),
}

#[derive(Debug, Clone, Span, Parse)]
#[parse(name = "#TITLE")]
pub struct Title(DefineString<Str<'T', 'I', 'T', 'L', 'E'>>);

impl Title {
    pub fn get(&self) -> &str {
        &self.0.value
    }
}

#[derive(Debug, Clone, Span, Parse)]
#[parse(name = "#COMPOSER")]
pub struct Composer(DefineString<Str<'C', 'O', 'M', 'P', 'O', 'S', 'E', 'R'>>);

impl Composer {
    pub fn get(&self) -> &str {
        &self.0.value
    }
}

#[derive(Debug, Clone, Span, Parse)]
#[parse(name = "#PROGRAMER")]
pub struct Programer(DefineString<Str<'P', 'R', 'O', 'G', 'R', 'A', 'M', 'E', 'R'>>);

impl Programer {
    pub fn get(&self) -> &str {
        &self.0.value
    }
}

#[derive(Debug, Clone, Span)]
pub struct Channel {
    start: Position,
    channel_names: ChannelNames,
    oscillator_kind: OscillatorKind,
    end: Position,
}

impl Channel {
    pub fn channel_names(&self) -> &ChannelNames {
        &self.channel_names
    }

    pub fn oscillator_kind(&self) -> OscillatorKind {
        self.oscillator_kind
    }
}

impl Parse for Channel {
    fn parse(parser: &mut Parser) -> Option<Self> {
        let start = parser.current_position();
        let _: Str<'#', 'C', 'A', 'N', 'N', 'E', 'L'> = parser.parse()?;
        let _: NonEmpty<While<SpaceOrTabOrComment>> = parser.parse()?;
        let channel_names = parser.parse()?;
        let _: NonEmpty<While<SpaceOrTabOrComment>> = parser.parse()?;
        let oscillator_kind = parser.parse()?;
        let end = parser.current_position();

        Some(Self {
            start,
            channel_names,
            oscillator_kind,
            end,
        })
    }

    fn name() -> Option<fn() -> String> {
        Some(|| "#CHANNEL".to_owned())
    }
}

#[derive(Debug, Clone, Span)]
struct DefineString<T> {
    start: Position,
    label: PhantomData<T>,
    value: String,
    end: Position,
}

impl<T: Parse> Parse for DefineString<T> {
    fn parse(parser: &mut Parser) -> Option<Self> {
        let start = parser.current_position();
        let _: (Char<'#'>, T, NonEmpty<While<SpaceOrTabOrComment>>) = parser.parse()?;
        let mut end;
        let mut value = String::new();
        loop {
            let _: MaybeComment = parser.parse()?;

            end = parser.current_position();
            let c = parser.read_char().unwrap_or('\n');
            if c == '\n' {
                break;
            }
            value.push(c);
        }
        if value.is_empty() {
            return None;
        }
        Some(Self {
            start,
            label: PhantomData,
            value,
            end,
        })
    }
}

type SpaceOrTabOrComment = OneOfThree<Char<' ', false>, Char<'\t', false>, Comment>;