Skip to main content

deepwoken_reqparse/model/
req.rs

1use core::fmt;
2use std::{borrow::Borrow, collections::{BTreeSet, HashSet}, str::FromStr};
3
4use serde::{Deserialize, Deserializer, Serialize, de};
5
6use crate::{Stat, util::statmap::StatMap};
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
9pub enum Reducability {
10    Reducible,
11    Strict,
12}
13
14impl fmt::Display for Reducability {
15    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16        match self {
17            Reducability::Reducible => write!(f, "r"),
18            Reducability::Strict => write!(f, "s"),
19        }
20    }
21}
22
23pub type StatSet = BTreeSet<Stat>;
24
25#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub struct Atom {
27    pub(crate) reducability: Reducability,
28    pub(crate) value: i64,
29    /// Stats to sum up to meet value (mostly will be a singular stat)
30    pub(crate) stats: StatSet,
31}
32
33impl Atom {
34    pub fn new(r: Reducability) -> Self {
35        Self {
36            reducability: r,
37            value: 0,
38            stats: BTreeSet::new(),
39        }
40    }
41
42    pub fn strict() -> Self {
43        Self {
44            reducability: Reducability::Strict,
45            value: 0,
46            stats: BTreeSet::new(),
47        }
48    }
49
50    pub fn reducible() -> Self {
51        Self {
52            reducability: Reducability::Reducible,
53            value: 0,
54            stats: BTreeSet::new(),
55        }
56    }
57
58    pub fn value(mut self, v: i64) -> Self {
59        self.value = v;
60        self
61    }
62
63    pub fn reducability(mut self, r: Reducability) -> Self {
64        self.reducability = r;
65        self
66    }
67
68    /// Adds a stat to the stat summation requirement.
69    pub fn stat(mut self, stat: Stat) -> Self {
70        self.stats.insert(stat);
71        self
72    }
73
74    pub fn add_stat(&mut self, stat: Stat) {
75        self.stats.insert(stat);
76    }
77
78    pub fn satisfied_by(&self, stats: &StatMap) -> bool {
79        let sum: i64 = self
80            .stats
81            .iter()
82            .map(|s| {
83                if s == &Stat::Total {
84                    stats.cost()
85                } else {
86                    stats.get(s)
87                }
88            })
89            .sum();
90
91        sum >= self.value
92    }
93
94    // is it trivially satisfied
95    pub fn is_empty(&self) -> bool {
96        self.stats.is_empty() && self.value == 0
97    }
98}
99
100impl fmt::Display for Atom {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        if self.stats.len() == 1 {
103            write!(
104                f, "{}{} {}", 
105                self.value, 
106                self.reducability, 
107                self.stats.first().unwrap().short_name()
108            )
109        } else {
110            // multi-stat (display as expr)
111            let sum_expr = self
112                .stats
113                .iter()
114                .map(|s| s.short_name().to_string())
115                .collect::<Vec<String>>()
116                .join(" + ");
117
118            write!(f, "{} = {}{}", sum_expr, self.value, self.reducability)
119        }
120    }
121}
122
123#[derive(Clone, Debug, PartialEq, Eq, Hash)]
124pub enum ClauseType {
125    And,
126    Or,
127}
128
129#[derive(Clone, Debug, Hash, PartialEq, Eq)]
130pub struct Clause {
131    pub(crate) clause_type: ClauseType,
132    pub(crate) atoms: BTreeSet<Atom>,
133}
134
135impl Clause {
136    pub fn new(clause_type: ClauseType) -> Self {
137        Self {
138            clause_type,
139            atoms: BTreeSet::new(),
140        }
141    }
142
143    pub fn and() -> Self {
144        Self {
145            clause_type: ClauseType::And,
146            atoms: BTreeSet::new(),
147        }
148    }
149
150    pub fn or() -> Self {
151        Self {
152            clause_type: ClauseType::Or,
153            atoms: BTreeSet::new(),
154        }
155    }
156
157    pub fn clause_type(mut self, ct: ClauseType) -> Self {
158        self.clause_type = ct;
159        self
160    }
161
162    pub fn atoms(&self) -> &BTreeSet<Atom> {
163        &self.atoms
164    }
165
166    pub fn atoms_mut(&mut self) -> &mut BTreeSet<Atom> {
167        &mut self.atoms
168    }
169
170    pub fn insert(mut self, stats: StatSet, mut atom: Atom) -> Self {
171        atom.stats = stats;
172        self.atoms.insert(atom);
173        self
174    }
175
176    pub fn atom(mut self, atom: Atom) -> Self {
177        self.atoms.insert(atom);
178        self
179    }
180
181    pub fn add_atom(&mut self, atom: Atom) {
182        self.atoms.insert(atom);
183    }
184
185    pub fn satisfied_by(&self, stats: &StatMap) -> bool {
186        match self.clause_type {
187            ClauseType::And => self.atoms.iter().all(|atom| atom.satisfied_by(stats)),
188            ClauseType::Or => self.atoms.iter().any(|atom| atom.satisfied_by(stats)),
189        }
190    }
191
192    pub fn is_empty(&self) -> bool {
193        !self.atoms().iter().any(|a| !a.is_empty())
194    }
195}
196
197impl fmt::Display for Clause {
198    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199        let joiner = match self.clause_type {
200            ClauseType::And => ", ",
201            ClauseType::Or => " OR ",
202        };
203
204        let atom_strs: Vec<String> = self
205            .atoms
206            .iter()
207            .filter(|a| !a.is_empty())
208            .map(|atom| format!("{}", atom))
209            .collect();
210
211        write!(f, "{}", atom_strs.join(joiner))
212    }
213}
214
215#[derive(Clone, Debug, Hash)]
216pub struct Requirement {
217    // optional name for the req for referencing elsewhere
218    pub(crate) name: Option<String>,
219    // DIRECT prerequisites (does not include transitive)
220    pub(crate) prereqs: Vec<String>,
221
222    pub(crate) clauses: Vec<Clause>,
223}
224
225impl PartialEq for Requirement {
226    fn eq(&self, other: &Self) -> bool {
227        if self.clauses.len() != other.clauses.len() {
228            return false;
229        }
230
231        // a \subseteq b and b \subseteq a iff a = b ahh comparison
232        self.clauses.iter().all(|c| other.clauses.contains(c))
233            && other.clauses.iter().all(|c| self.clauses.contains(c))
234            && self.name_or_default() == other.name_or_default() 
235            // also check if string names are the same (yes names will matter now)
236    }
237}
238
239impl Eq for Requirement {}
240
241impl Requirement {
242    pub fn new() -> Self {
243        Self {
244            name: None,
245            prereqs: Vec::new(),
246            clauses: Vec::new(),
247        }
248    }
249
250    pub fn add_clause(&mut self, clause: Clause) -> &mut Self {
251        self.clauses.push(clause);
252        self
253    }
254
255    pub fn add_prereq(&mut self, prereq: &str) -> &mut Self {
256        self.prereqs.push(prereq.to_string());
257        self
258    }
259
260    pub fn name(&mut self, name: &str) -> &mut Self {
261        self.name = Some(name.to_string());
262        self
263    }
264
265    pub fn name_or_default(&self) -> String {
266        match &self.name {
267            Some(n) => n.clone(),
268            None => self.to_string(),
269        }
270    }
271
272    pub fn iter(&self) -> impl Iterator<Item = &Clause> {
273        self.clauses.iter()
274    }
275
276    pub fn and_iter(&self) -> impl Iterator<Item = &Clause> {
277        self.clauses
278            .iter()
279            .filter(|c| c.clause_type == ClauseType::And)
280    }
281
282    pub fn or_iter(&self) -> impl Iterator<Item = &Clause> {
283        self.clauses
284            .iter()
285            .filter(|c| c.clause_type == ClauseType::Or)
286    }
287
288    pub fn atoms(&self) -> impl Iterator<Item = &Atom> {
289        self.clauses.iter().flat_map(|clause| clause.atoms.iter())
290    }
291
292    pub fn add_to_all(&mut self, val: i64) -> &mut Self {
293        // construct new atoms
294        for clause in &mut self.clauses {
295            clause.atoms = clause
296                .atoms
297                .iter()
298                .map(|atom| {
299                    let mut new_atom = atom.clone();
300                    new_atom.value += val;
301                    new_atom.value = new_atom.value.clamp(0, 100);
302                    new_atom
303                })
304                .collect();
305        }
306        self
307    }
308
309    pub fn strict_atoms(&self) -> impl Iterator<Item = &Atom> {
310        self.clauses.iter().flat_map(|clause| {
311            clause
312                .atoms
313                .iter()
314                .filter(|atom| atom.reducability == Reducability::Strict)
315        })
316    }
317
318    /// Grab all the stats present in a requirement
319    pub fn used_stats(&self) -> HashSet<Stat> {
320        self.atoms().fold(HashSet::new(), |mut acc, atom| {
321            for stat in &atom.stats {
322                if stat == &Stat::Total {
323                    continue;
324                }
325
326                acc.insert(stat.clone());
327            }
328            acc
329        })
330    }
331
332    pub fn satisfied_by(&self, stats: &StatMap) -> bool {
333        self.clauses.iter().all(|clause| clause.satisfied_by(stats))
334    }
335
336    /// The requirement requires nothing and is therefore trivially satisfied (wow!)
337    pub fn is_empty(&self) -> bool {
338        !self.clauses.iter().any(|c| !c.is_empty())
339    }
340}
341
342impl From<Clause> for Requirement {
343    fn from(clause: Clause) -> Self {
344        Self {
345            name: None,
346            prereqs: Vec::new(),
347            clauses: vec![clause],
348        }
349    }
350}
351
352impl fmt::Display for Requirement {
353    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354        if !self.prereqs.is_empty() {
355            write!(f, "{} => ", self.prereqs.join(", "))?;
356        }
357        if let Some(name) = &self.name {
358            write!(f, "{} := ", name)?;
359        }
360        if self.is_empty() {
361            write!(f, "()")
362        } else {
363            let clause_strs: Vec<String> = self
364                .clauses
365                .iter()
366                .filter(|clause| !clause.is_empty())
367                .map(|clause| clause.to_string())
368                .collect();
369
370            write!(f, "{}", clause_strs.join(", "))
371        }
372    }
373}
374
375impl FromStr for Requirement {
376    type Err = String;
377
378    fn from_str(s: &str) -> Result<Self, Self::Err> {
379        crate::parse::req::parse_req(s).map_err(|e| format!("Failed to parse requirement: {}", e))
380    }
381}
382
383impl<'de> Deserialize<'de> for Requirement {
384    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
385    where
386        D: Deserializer<'de>,
387    {
388        let s = String::deserialize(deserializer)?;
389        s.parse().map_err(de::Error::custom)
390    }
391}
392
393impl Serialize for Requirement {
394    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
395    where
396        S: serde::Serializer,
397    {
398        serializer.serialize_str(&self.to_string())
399    }
400}
401
402#[derive(Clone, Copy, Debug)]
403pub enum Timing {
404    Free,
405    Post,
406}