#![allow(clippy::module_name_repetitions)] use std::{borrow::Cow, collections::HashSet, ops::Add}; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum MacroString { String(String), MacroString(Vec), } #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum MacroStringPart { String(String), MacroUsage(String), } impl MacroString { /// Returns whether the [`MacroString`] contains any macro usages #[must_use] pub fn contains_macro(&self) -> bool { match self { Self::String(_) => false, Self::MacroString(parts) => !parts .iter() .all(|p| matches!(p, MacroStringPart::String(_))), } } /// Compiles to a string that Minecraft can interpret #[must_use] pub fn compile(&self) -> String { match self { Self::String(s) => s.to_owned(), Self::MacroString(parts) => parts .iter() .map(|p| match p { MacroStringPart::String(s) => Cow::Borrowed(s), MacroStringPart::MacroUsage(m) => Cow::Owned(format!("$({m})")), }) .collect::>() .iter() .map(|p| p.as_str()) .collect::>() .join(""), } } /// Returns the amount of lines the string has #[must_use] pub fn line_count(&self) -> usize { match self { Self::String(s) => s.split('\n').count(), Self::MacroString(parts) => { parts .iter() .map(|p| match p { MacroStringPart::String(s) => s.split('\n').count() - 1, MacroStringPart::MacroUsage(_) => 0, }) .sum::() + 1 } } } /// Returns the names of the macros used in the [`MacroString`] #[must_use] pub fn get_macros(&self) -> HashSet<&str> { match self { Self::String(_) => HashSet::new(), Self::MacroString(parts) => parts .iter() .filter_map(|p| match p { MacroStringPart::String(_) => None, MacroStringPart::MacroUsage(m) => Some(m.as_str()), }) .collect(), } } } impl From for MacroString { fn from(value: String) -> Self { Self::String(value) } } impl From<&str> for MacroString { fn from(value: &str) -> Self { Self::String(value.to_string()) } } impl From for MacroStringPart { fn from(value: String) -> Self { Self::String(value) } } impl From<&str> for MacroStringPart { fn from(value: &str) -> Self { Self::String(value.to_string()) } } impl Add for MacroString { type Output = Self; fn add(self, rhs: Self) -> Self::Output { match (self, rhs) { (Self::String(mut s1), Self::String(s2)) => { s1.push_str(&s2); Self::String(s1) } (Self::String(s1), Self::MacroString(s2)) => Self::MacroString( std::iter::once(MacroStringPart::String(s1)) .chain(s2) .collect(), ), (Self::MacroString(mut s1), Self::String(s2)) => { s1.push(MacroStringPart::String(s2)); Self::MacroString(s1) } (Self::MacroString(mut s1), Self::MacroString(s2)) => { s1.extend(s2); Self::MacroString(s1) } } } } impl Add<&str> for MacroString { type Output = Self; fn add(self, rhs: &str) -> Self::Output { match self { Self::String(mut s1) => { s1.push_str(rhs); Self::String(s1) } Self::MacroString(mut s1) => { s1.push(MacroStringPart::String(rhs.to_string())); Self::MacroString(s1) } } } }