Skip to main content

manabrew_engine/spellability/
spell_ability_predicates.rs

1//! Predicate functions for filtering spell abilities.
2//!
3//! Mirrors Java's `SpellAbilityPredicates.java` — provides closure-based
4//! predicates for filtering spell abilities by API type, sub-abilities, etc.
5
6use crate::ability::api_type::ApiType;
7use crate::spellability::SpellAbility;
8
9/// Returns a predicate that matches spell abilities with the given API type.
10/// Mirrors Java's `SpellAbilityPredicates.isApi(ApiType)`.
11pub fn is_api(api: ApiType) -> impl Fn(&SpellAbility) -> bool {
12    move |sa: &SpellAbility| sa.api == Some(api)
13}
14
15/// Returns a predicate that matches spell abilities whose sub-ability chain
16/// contains an ability with the given API type.
17/// Mirrors Java's `SpellAbilityPredicates.hasSubAbilityApi(ApiType)`.
18pub fn has_sub_ability_api(api: ApiType) -> impl Fn(&SpellAbility) -> bool {
19    move |sa: &SpellAbility| {
20        let mut current = sa.sub_ability.as_deref();
21        while let Some(sub) = current {
22            if sub.api == Some(api) {
23                return true;
24            }
25            current = sub.sub_ability.as_deref();
26        }
27        false
28    }
29}
30
31/// Returns a predicate that checks if a spell ability matches all given
32/// restriction strings. Each restriction is checked against the ability's params.
33/// Mirrors Java's `SpellAbilityPredicates.isValid(String[])`.
34pub fn is_valid<'a>(restrictions: &'a [&'a str]) -> impl Fn(&SpellAbility) -> bool + 'a {
35    move |sa: &SpellAbility| {
36        for &restriction in restrictions {
37            // Check if the restriction matches a param key set to "True"
38            if let Some(key) = restriction.strip_prefix('!') {
39                // Negated restriction: must NOT have the param
40                if sa.param_is_true(key) {
41                    return false;
42                }
43            } else if restriction.contains('$') {
44                // Key-value restriction: "Key$ Value" — check param equals value
45                let parts: Vec<&str> = restriction.splitn(2, '$').collect();
46                if parts.len() == 2 {
47                    let key = parts[0].trim();
48                    let expected = parts[1].trim();
49                    match sa.param_value(key) {
50                        Some(val) if val.eq_ignore_ascii_case(expected) => {}
51                        _ => return false,
52                    }
53                }
54            } else {
55                // Simple flag restriction: param must be "True"
56                if !sa.param_is_true(restriction) {
57                    return false;
58                }
59            }
60        }
61        true
62    }
63}