manabrew-engine 0.2.3

Magic: The Gathering rules engine — a Rust port of Forge
Documentation
use std::borrow::Cow;

use serde::{Deserialize, Serialize};

use crate::parsing::{
    keys, parse_semantic_param_value, SemanticParamValue, SemanticProducedMana,
    SemanticProducedManaCombo,
};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProducedMana {
    Any,
    Chosen,
    Combo(ProducedManaCombo),
    Special(String),
    Fixed(Vec<String>),
    Raw(String),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProducedManaCombo {
    Any,
    Chosen,
    ColorIdentity,
    Colors(Vec<String>),
    Raw(String),
}

impl ProducedMana {
    pub fn from_semantic(value: &SemanticProducedMana<'_>) -> Self {
        match value {
            SemanticProducedMana::Any => Self::Any,
            SemanticProducedMana::Chosen => Self::Chosen,
            SemanticProducedMana::Combo(combo) => Self::Combo(match combo {
                SemanticProducedManaCombo::Any => ProducedManaCombo::Any,
                SemanticProducedManaCombo::Chosen => ProducedManaCombo::Chosen,
                SemanticProducedManaCombo::ColorIdentity => ProducedManaCombo::ColorIdentity,
                SemanticProducedManaCombo::Colors(colors) => ProducedManaCombo::Colors(
                    colors.iter().map(|value| (*value).to_string()).collect(),
                ),
                SemanticProducedManaCombo::Raw(raw) => ProducedManaCombo::Raw((*raw).to_string()),
            }),
            SemanticProducedMana::Special(kind) => Self::Special((*kind).to_string()),
            SemanticProducedMana::Fixed(tokens) => {
                Self::Fixed(tokens.iter().map(|value| (*value).to_string()).collect())
            }
            SemanticProducedMana::Raw(raw) => Self::Raw((*raw).to_string()),
        }
    }

    pub fn from_raw_boundary(raw: &str) -> Self {
        match parse_semantic_param_value(keys::PRODUCED, raw) {
            SemanticParamValue::ProducedMana(produced) => Self::from_semantic(&produced),
            _ => Self::Raw(raw.trim().to_string()),
        }
    }

    pub fn as_script_text(&self) -> Cow<'_, str> {
        match self {
            Self::Any => Cow::Borrowed("Any"),
            Self::Chosen => Cow::Borrowed("Chosen"),
            Self::Combo(ProducedManaCombo::Any) => Cow::Borrowed("Combo Any"),
            Self::Combo(ProducedManaCombo::Chosen) => Cow::Borrowed("Combo Chosen"),
            Self::Combo(ProducedManaCombo::ColorIdentity) => Cow::Borrowed("Combo ColorIdentity"),
            Self::Combo(ProducedManaCombo::Colors(colors)) => Cow::Owned(if colors.is_empty() {
                "Combo".to_string()
            } else {
                format!("Combo {}", colors.join(" "))
            }),
            Self::Combo(ProducedManaCombo::Raw(raw)) => Cow::Owned(if raw.is_empty() {
                "Combo".to_string()
            } else {
                format!("Combo {raw}")
            }),
            Self::Special(kind) => Cow::Owned(format!("Special {kind}")),
            Self::Fixed(tokens) => match tokens.as_slice() {
                [single] => Cow::Borrowed(single.as_str()),
                _ => Cow::Owned(tokens.join(" ")),
            },
            Self::Raw(raw) => Cow::Borrowed(raw.as_str()),
        }
    }

    pub fn is_combo_color_identity(&self) -> bool {
        matches!(self, Self::Combo(ProducedManaCombo::ColorIdentity))
    }

    pub fn is_any_like(&self) -> bool {
        match self {
            Self::Any | Self::Combo(ProducedManaCombo::Any) => true,
            Self::Raw(raw) => raw
                .split_whitespace()
                .any(|tok| tok.eq_ignore_ascii_case("Any")),
            _ => false,
        }
    }

    pub fn is_choice_like(&self) -> bool {
        match self {
            Self::Any | Self::Combo(_) => true,
            Self::Raw(raw) => {
                raw.contains(',')
                    || raw
                        .split_whitespace()
                        .any(|tok| tok.eq_ignore_ascii_case("Any"))
            }
            _ => false,
        }
    }

    pub fn special_kind(&self) -> Option<&str> {
        match self {
            Self::Special(kind) => Some(kind),
            _ => None,
        }
    }

    pub fn fixed_tokens(&self) -> Option<&[String]> {
        match self {
            Self::Fixed(tokens) => Some(tokens),
            _ => None,
        }
    }
}