mcfunction-debugger 2.0.0

A debugger for Minecraft's *.mcfunction files that does not require any Minecraft mods
Documentation
// Mcfunction-Debugger is a debugger for Minecraft's *.mcfunction files that does not require any
// Minecraft mods.
//
// © Copyright (C) 2021-2024 Adrodoc <adrodoc55@googlemail.com> & Skagaros <skagaros@gmail.com>
//
// This file is part of Mcfunction-Debugger.
//
// Mcfunction-Debugger 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.
//
// Mcfunction-Debugger 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 Mcfunction-Debugger.
// If not, see <http://www.gnu.org/licenses/>.

pub mod block;
pub mod coordinate;
pub mod entity;
pub mod nbt;
pub mod range;
pub mod resource;

use crate::{
    generator::parser::command::{
        argument::{
            brigadier::parse_unquoted_string, minecraft::entity::MinecraftSelectorParserError,
            ArgumentValue, ArgumentValueEnum, MinecraftSelector,
        },
        resource_location::ResourceLocationRef,
    },
    utils::Map0,
};
use strum::EnumString;

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftDimension<'l>(pub ResourceLocationRef<&'l str>);

impl<'l> ArgumentValue<'l> for MinecraftDimension<'l> {
    fn parse(string: &'l str) -> Result<(Self, usize), String> {
        Ok(ResourceLocationRef::parse(string)?.map0(Self))
    }
}

#[derive(Clone, Copy, Debug, EnumString, Eq, PartialEq)]
#[strum(serialize_all = "snake_case")]
pub enum MinecraftEntityAnchor {
    Eyes,
    Feet,
}

impl ArgumentValueEnum for MinecraftEntityAnchor {}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftFunction<'l>(pub ResourceLocationRef<&'l str>);

impl<'l> ArgumentValue<'l> for MinecraftFunction<'l> {
    fn parse(string: &'l str) -> Result<(Self, usize), String> {
        Ok(ResourceLocationRef::parse(string)?.map0(Self))
    }
}

#[derive(Clone, Copy, Debug, EnumString, Eq, PartialEq)]
#[strum(serialize_all = "snake_case")]
pub enum MinecraftHeightmap {
    MotionBlocking,
    MotionBlockingNoLeaves,
    OceanFloor,
    WorldSurface,
}

impl ArgumentValueEnum for MinecraftHeightmap {}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftMessage<'l> {
    pub message: &'l str,
    pub selectors: Vec<(MinecraftSelector<'l>, usize, usize)>,
}

impl<'l> ArgumentValue<'l> for MinecraftMessage<'l> {
    fn parse(message: &'l str) -> Result<(Self, usize), String> {
        let mut index = 0;
        let mut selectors = Vec::new();
        while let Some(i) = &message[index..].find('@') {
            index += i;
            match MinecraftSelector::parse(&message[index..]) {
                Ok((selector, len)) => {
                    selectors.push((selector, index, index + len));
                    index += len;
                }
                Err(
                    MinecraftSelectorParserError::MissingSelectorType
                    | MinecraftSelectorParserError::UnknownSelectorType(..),
                ) => {
                    index += 1;
                }
                Err(e) => return Err(e.to_string()),
            }
        }

        Ok((MinecraftMessage { message, selectors }, message.len()))
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftObjective<'l>(pub &'l str);

impl<'l> ArgumentValue<'l> for MinecraftObjective<'l> {
    fn parse(string: &'l str) -> Result<(Self, usize), String> {
        Ok(parse_unquoted_string(string).map0(Self))
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftObjectiveCriteria<'l>(&'l str);

impl<'l> ArgumentValue<'l> for MinecraftObjectiveCriteria<'l> {
    fn parse(string: &'l str) -> Result<(Self, usize), String> {
        let len = string.find(' ').unwrap_or(string.len());
        Ok((&string[..len], len).map0(Self))
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MinecraftOperation {
    Assignment,     // =
    Addition,       // +=
    Subtraction,    // -=
    Multiplication, // *=
    Division,       // /=
    Modulus,        // %=
    Swapping,       // ><
    Minimum,        // <
    Maximum,        // >
}

impl ArgumentValue<'_> for MinecraftOperation {
    fn parse(string: &str) -> Result<(Self, usize), String> {
        let len = string.find(' ').unwrap_or(string.len());
        match &string[..len] {
            "=" => Ok((Self::Assignment, len)),
            "+=" => Ok((Self::Addition, len)),
            "-=" => Ok((Self::Subtraction, len)),
            "*=" => Ok((Self::Multiplication, len)),
            "/=" => Ok((Self::Division, len)),
            "%=" => Ok((Self::Modulus, len)),
            ">< " => Ok((Self::Swapping, len)),
            "<" => Ok((Self::Minimum, len)),
            ">" => Ok((Self::Maximum, len)),
            _ => Err("Invalid operation".to_string()),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum MinecraftScoreHolder<'l> {
    Selector(MinecraftSelector<'l>),
    Wildcard,
    String(&'l str),
}

impl<'l> ArgumentValue<'l> for MinecraftScoreHolder<'l> {
    fn parse(string: &'l str) -> Result<(Self, usize), String> {
        if string.starts_with('@') {
            let (selector, len) = MinecraftSelector::parse(string)?;
            Ok((Self::Selector(selector), len))
        } else {
            let len = string.find(' ').unwrap_or(string.len());
            let parsed = &string[..len];
            let parsed = if parsed == "*" {
                Self::Wildcard
            } else {
                Self::String(parsed)
            };
            Ok((parsed, len))
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftScoreboardSlot<'l>(&'l str);

impl<'l> ArgumentValue<'l> for MinecraftScoreboardSlot<'l> {
    fn parse(string: &'l str) -> Result<(Self, usize), String> {
        Ok(parse_unquoted_string(string).map0(Self))
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftSwizzle;

impl ArgumentValue<'_> for MinecraftSwizzle {
    fn parse(string: &str) -> Result<(Self, usize), String> {
        let len = string
            .find(' ')
            .ok_or("Failed to parse swizzle".to_string())?;
        Ok((Self, len))
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftTeam<'l>(&'l str);

impl<'l> ArgumentValue<'l> for MinecraftTeam<'l> {
    fn parse(string: &'l str) -> Result<(Self, usize), String> {
        Ok(parse_unquoted_string(string).map0(Self))
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct MinecraftTime {
    pub time: f32,
    pub unit: MinecraftTimeUnit,
}

impl ArgumentValue<'_> for MinecraftTime {
    fn parse(string: &str) -> Result<(Self, usize), String> {
        let float_len = string
            .find(|c| c < '0' || c > '9' && c != '.' && c != '-')
            .unwrap_or(string.len());
        let float_sting = &string[..float_len];
        let time = float_sting
            .parse()
            .map_err(|_| format!("Expected float but got '{}'", &float_sting))?;
        let (unit, len) = match string[float_len..].chars().next() {
            Some(unit) if unit != ' ' => {
                let unit = match unit {
                    't' => MinecraftTimeUnit::Tick,
                    's' => MinecraftTimeUnit::Second,
                    'd' => MinecraftTimeUnit::Day,
                    unit => return Err(format!("Unknown unit '{}'", unit)),
                };
                (unit, float_len + 1)
            }
            _ => (MinecraftTimeUnit::Tick, float_len),
        };

        Ok((MinecraftTime { time, unit }, len))
    }
}

impl MinecraftTime {
    pub fn as_ticks(&self) -> u32 {
        let ticks = self.time * self.unit.factor() as f32;
        ticks.round() as u32
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MinecraftTimeUnit {
    Tick,
    Second,
    Day,
}

impl MinecraftTimeUnit {
    pub fn factor(&self) -> u32 {
        match self {
            MinecraftTimeUnit::Tick => 1,
            MinecraftTimeUnit::Second => 20,
            MinecraftTimeUnit::Day => 24000,
        }
    }
}