1use crate::span::Span;
4use crate::error::{KoreError, KoreResult};
5use std::collections::HashSet;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum Effect {
9 Pure, IO, Async, GPU, Reactive, Unsafe, Alloc, Panic, }
18
19impl Effect {
20 pub fn from_str(s: &str) -> Option<Self> {
21 match s {
22 "Pure" => Some(Effect::Pure),
23 "IO" => Some(Effect::IO),
24 "Async" => Some(Effect::Async),
25 "GPU" => Some(Effect::GPU),
26 "Reactive" => Some(Effect::Reactive),
27 "Unsafe" => Some(Effect::Unsafe),
28 _ => None,
29 }
30 }
31}
32
33#[derive(Debug, Clone, Default, PartialEq, Eq)]
34pub struct EffectSet {
35 pub effects: HashSet<Effect>,
36}
37
38impl EffectSet {
39 pub fn new() -> Self { Self { effects: HashSet::new() } }
40 pub fn pure() -> Self { Self::new().with(Effect::Pure) }
41
42 pub fn with(mut self, e: Effect) -> Self {
43 self.effects.insert(e);
44 self
45 }
46
47 pub fn is_pure(&self) -> bool {
48 self.effects.is_empty() || self.effects.iter().all(|e| *e == Effect::Pure)
49 }
50
51 pub fn can_call(&self, callee: &EffectSet) -> bool {
52 if callee.is_pure() { return true; }
53 if self.is_pure() { return false; }
54 if self.effects.contains(&Effect::Unsafe) { return true; }
55 callee.effects.iter().all(|e| self.effects.contains(e))
56 }
57}
58
59pub fn check_effect_call(caller: &EffectSet, callee: &EffectSet, span: Span) -> KoreResult<()> {
60 if !caller.can_call(callee) {
61 return Err(KoreError::effect_error(
62 format!("Effect violation: {:?} cannot call {:?}", caller.effects, callee.effects),
63 span,
64 ));
65 }
66 Ok(())
67}
68