use std::{cell::RefCell, rc::Rc};
use super::{ProcessContext, TokenProcessor};
use crate::bms::ParseErrorWithRange;
use crate::bms::{
model::repr::BmsSourceRepresentation,
parse::{ParseWarning, Result},
prelude::*,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RepresentationProcessor {
case_sensitive_obj_id: Rc<RefCell<bool>>,
}
impl RepresentationProcessor {
pub fn new(case_sensitive_obj_id: &Rc<RefCell<bool>>) -> Self {
Self {
case_sensitive_obj_id: Rc::clone(case_sensitive_obj_id),
}
}
}
impl TokenProcessor for RepresentationProcessor {
type Output = BmsSourceRepresentation;
fn process<P: Prompter>(
&self,
ctx: &mut ProcessContext<'_, '_, P>,
) -> core::result::Result<Self::Output, ParseErrorWithRange> {
let mut repr = BmsSourceRepresentation::default();
ctx.all_tokens(|token, _prompter| match token.content() {
Token::Header { name, args } => Ok(self
.on_header(name.as_ref(), args.as_ref(), &mut repr)
.err()
.map(|warn| warn.into_wrapper(token))),
Token::Message { .. } | Token::NotACommand(_) => Ok(None),
})?;
Ok(repr)
}
}
impl RepresentationProcessor {
fn on_header(&self, name: &str, args: &str, repr: &mut BmsSourceRepresentation) -> Result<()> {
if args.is_empty() {
repr.raw_command_lines.push(format!("#{name}"));
} else {
repr.raw_command_lines.push(format!("#{name} {args}"));
}
if name.eq_ignore_ascii_case("BASE") {
if args != "62" {
return Err(ParseWarning::OutOfBase62);
}
*self.case_sensitive_obj_id.borrow_mut() = true;
}
if name.eq_ignore_ascii_case("LNMODE") {
let mode: u8 = args.parse().map_err(|_| {
ParseWarning::SyntaxError("expected integer between 1 and 3".into())
})?;
let mode = match mode {
1 => LnMode::Ln,
2 => LnMode::Cn,
3 => LnMode::Hcn,
_ => {
return Err(ParseWarning::SyntaxError(
"expected long note mode between 1 and 3".into(),
));
}
};
repr.ln_mode = mode;
}
if name.eq_ignore_ascii_case("LNTYPE") {
repr.ln_type = if args == "2" {
LnType::Mgq
} else {
LnType::Rdm
};
}
if name.eq_ignore_ascii_case("CHARSET") {
repr.charset = Some(args.into());
}
Ok(())
}
}