bromlife 0.1.0

Bindings to libbromlife for O Markup Language parsing.
// Copyright (C) 2026  gemmaro
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use bromlife_sys::{
    oml_event, oml_event_is_body, oml_event_is_empty, oml_event_is_ignored, oml_event_is_label,
    oml_event_is_left, oml_event_is_normal, oml_event_is_null, oml_event_is_right,
    oml_event_is_vocab, oml_event_is_word, oml_event_label_len, oml_event_label_offset,
    oml_event_len, oml_event_offset, oml_next, oml_parser,
};

pub struct Parser<'a> {
    parser: oml_parser,
    input: &'a str,
}

impl<'a> Parser<'a> {
    pub fn new(input: &'a str) -> Self {
        let len = input.len();
        let bytes = input.as_ptr();
        Self {
            parser: unsafe { oml_parser(bytes, len) },
            input,
        }
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        let &mut Parser { mut parser, input } = self;
        let event = unsafe { oml_next(&mut parser) };
        if unsafe { oml_event_is_empty(event) } {
            return None;
        }
        Some(Event { event, input })
    }
}

pub struct Event<'a> {
    event: oml_event,
    input: &'a str,
}

impl<'a> Event<'a> {
    pub fn len(&self) -> usize {
        let Event { event, input: _ } = self;
        unsafe { oml_event_len(*event) }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn offset(&self) -> usize {
        let Event { event, input: _ } = self;
        unsafe { oml_event_offset(*event) }
    }

    pub fn as_str(&self) -> &str {
        let Event { event: _, input } = self;
        let offset = self.offset();
        &input[offset..offset + self.len()]
    }

    pub fn is_null(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_null(*event) }
    }

    pub fn is_left(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_left(*event) }
    }

    pub fn is_right(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_right(*event) }
    }

    pub fn is_body(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_body(*event) }
    }

    pub fn is_normal(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_normal(*event) }
    }

    pub fn is_ignored(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_ignored(*event) }
    }

    pub fn is_vocab(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_vocab(*event) }
    }

    pub fn is_word(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_word(*event) }
    }

    pub fn is_label(&self) -> bool {
        let Event { event, input: _ } = self;
        unsafe { oml_event_is_label(*event) }
    }

    fn _label_len(&self) -> usize {
        let Event { event, input: _ } = self;
        unsafe { oml_event_label_len(*event) }
    }

    pub fn label_len(&self) -> Option<usize> {
        if !self.is_left() || !self.is_normal() {
            return None;
        }
        Some(self._label_len())
    }

    fn _label_offset(&self) -> usize {
        let Event { event, input: _ } = self;
        unsafe { oml_event_label_offset(*event) }
    }

    pub fn label_offset(&self) -> Option<usize> {
        if !self.is_left() || !self.is_normal() {
            return None;
        }
        Some(self._label_offset())
    }

    pub fn label(&self) -> Option<&str> {
        if !self.is_left() || !self.is_normal() {
            return None;
        }
        let Event { event: _, input } = self;
        let offset = self._label_offset();
        Some(&input[offset..offset + self._label_len()])
    }
}