use crate::gml::GMCode;
use crate::prelude::*;
use crate::wad::elements::validate_identifier;
impl GMNamedElement for GMCode {
fn name(&self) -> &String {
&self.name
}
fn name_mut(&mut self) -> &mut String {
&mut self.name
}
fn validate_name(&self) -> Result<()> {
let name: &str = &self.name;
validate(name).with_context(|| format!("strictly validating code entry name {name:?}"))
}
}
fn validate(mut name: &str) -> Result<()> {
name = name
.strip_prefix("gml_")
.ok_or("Code entry name does not start with \"gml_\"")?;
let split = name
.split_once('_')
.ok_or("Code entry name does not have a kind`:")?;
let kind: &str = split.0;
name = split.1;
match kind {
"Script" => {}
"GlobalScript" => {
if name.chars().all(|c| c.is_ascii_digit()) {
return Ok(());
}
}
"Room" => {
let event_kind: &str;
(name, event_kind) = name
.rsplit_once('_')
.ok_or("Missing event kind in Room code entry name")?;
validate_room_event_kind(event_kind)?;
}
"RoomCC" => {
let e = "RoomCC code entry name has too few parts (not enough underscores to be valid)";
let (instance_id, event_kind) = last_three_parts(&mut name).ok_or(e)?;
validate_room_event_kind(event_kind)?;
instance_id
.parse::<u16>()
.context_src("parsing Instance ID in RoomCC code entry")?;
}
"Object" => {
if name.contains("_Collision_") {
return validate_identifier(name);
}
let e = "Object code entry name has too few parts (not enough underscores to be valid)";
let (event_type, event_subtype) = last_three_parts(&mut name).ok_or(e)?;
let event_subtype: u32 = event_subtype
.parse::<u32>()
.context_src("parsing Event subtype in Object code entry")?;
validate_event(event_type, event_subtype)?;
}
_ => bail!("Invalid code entry kind {kind:?}"),
}
validate_identifier(name)
}
fn last_three_parts<'a>(string: &mut &'a str) -> Option<(&'a str, &'a str)> {
let mut iter = string.rsplitn(3, '_');
let part3: &str = iter.next()?;
let part2: &str = iter.next()?;
let part1: &str = iter.next()?;
*string = part1;
Some((part2, part3))
}
fn validate_room_event_kind(event_kind: &str) -> Result<()> {
match event_kind {
"Create" | "PreCreate" => Ok(()),
_ => Err(err!("Invalid Room event kind {event_kind:?}")),
}
}
fn validate_event(event_type: &str, subtype: u32) -> Result<()> {
use crate::wad::elements::game_object::event::EventSubtype;
use crate::wad::elements::game_object::event::subtype::Alarm;
use crate::wad::elements::game_object::event::subtype::Draw;
use crate::wad::elements::game_object::event::subtype::Gesture;
use crate::wad::elements::game_object::event::subtype::Key;
use crate::wad::elements::game_object::event::subtype::Mouse;
use crate::wad::elements::game_object::event::subtype::Other;
use crate::wad::elements::game_object::event::subtype::Step;
let error: Option<Error> = match event_type {
"Create" => assert_zero(subtype),
"Destroy" => assert_zero(subtype),
"Alarm" => Alarm::parse(subtype).err(),
"Step" => Step::parse(subtype).err(),
"Keyboard" => Key::parse(subtype).err(),
"Mouse" => Mouse::parse(subtype).err(),
"Other" => Other::parse(subtype).err(),
"Draw" => Draw::parse(subtype).err(),
"KeyPress" => Key::parse(subtype).err(),
"KeyRelease" => Key::parse(subtype).err(),
"Trigger" => assert_zero(subtype),
"CleanUp" => assert_zero(subtype),
"Gesture" => Gesture::parse(subtype).err(),
"PreCreate" => assert_zero(subtype),
_ => bail!("Invalid event type {event_type:?}"),
};
error.map_or(Ok(()), Err)
}
fn assert_zero(subtype: u32) -> Option<Error> {
if subtype == 0 {
None
} else {
Some(err!(
"Expected event subtype 0 for this event type, got {subtype}"
))
}
}