1use std::collections::BTreeSet;
2
3#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub enum Effect {
5 Pure,
6 IO,
7 Random,
8 Time,
9 ExternalCall(String),
10}
11
12#[derive(Debug, Clone, Default)]
13pub struct EffectSet {
14 effects: BTreeSet<Effect>,
15}
16
17impl EffectSet {
18 pub fn is_pure(&self) -> bool {
19 self.effects.is_empty() || (self.effects.len() == 1 && self.effects.contains(&Effect::Pure))
20 }
21
22 pub fn insert(&mut self, effect: Effect) {
23 if effect != Effect::Pure {
24 self.effects.insert(effect);
25 }
26 }
27
28 pub fn contains(&self, effect: &Effect) -> bool {
29 self.effects.contains(effect)
30 }
31
32 pub fn union(&self, other: &Self) -> Self {
33 let mut out = self.clone();
34 out.effects.extend(other.effects.iter().cloned());
35 out
36 }
37
38 pub fn iter(&self) -> std::collections::btree_set::Iter<'_, Effect> {
39 self.effects.iter()
40 }
41
42 pub fn to_names(&self) -> Vec<String> {
43 self.effects
44 .iter()
45 .map(|e| match e {
46 Effect::Pure => "pure".to_string(),
47 Effect::IO => "io".to_string(),
48 Effect::Random => "random".to_string(),
49 Effect::Time => "time".to_string(),
50 Effect::ExternalCall(name) => format!("external:{name}"),
51 })
52 .collect()
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn default_is_pure() {
62 let set = EffectSet::default();
63 assert!(set.is_pure());
64 assert!(set.to_names().is_empty());
65 }
66
67 #[test]
68 fn union_combines_effects() {
69 let mut a = EffectSet::default();
70 a.insert(Effect::IO);
71 let mut b = EffectSet::default();
72 b.insert(Effect::Random);
73 let u = a.union(&b);
74 assert!(!u.is_pure());
75 let names = u.to_names();
76 assert!(names.contains(&"io".to_string()));
77 assert!(names.contains(&"random".to_string()));
78 }
79}