Skip to main content

aver/
effects.rs

1/// Returns true if a declared effect satisfies a required effect.
2///
3/// Rules:
4/// - Exact match: "Http.get" satisfies "Http.get"
5/// - Namespace shorthand: "Http" satisfies "Http.get" (parent covers all children)
6/// - But NOT reverse: "Http.get" does NOT satisfy "Http"
7pub fn effect_satisfies(declared: &str, required: &str) -> bool {
8    if declared == required {
9        return true;
10    }
11    // Namespace shorthand: "Disk" covers "Disk.readText"
12    if !declared.contains('.')
13        && let Some(prefix) = required.split('.').next()
14    {
15        return declared == prefix;
16    }
17    false
18}
19
20#[cfg(test)]
21mod tests {
22    use super::*;
23
24    #[test]
25    fn exact_match() {
26        assert!(effect_satisfies("Http", "Http"));
27        assert!(effect_satisfies("Console", "Console"));
28        assert!(effect_satisfies("Http.get", "Http.get"));
29        assert!(effect_satisfies("Disk.readText", "Disk.readText"));
30    }
31
32    #[test]
33    fn namespace_covers_children() {
34        assert!(effect_satisfies("Http", "Http.get"));
35        assert!(effect_satisfies("Http", "Http.post"));
36        assert!(effect_satisfies("Disk", "Disk.readText"));
37        assert!(effect_satisfies("Disk", "Disk.writeText"));
38        assert!(effect_satisfies("Terminal", "Terminal.clear"));
39    }
40
41    #[test]
42    fn child_does_not_cover_parent() {
43        assert!(!effect_satisfies("Http.get", "Http"));
44        assert!(!effect_satisfies("Disk.readText", "Disk"));
45    }
46
47    #[test]
48    fn different_children() {
49        assert!(!effect_satisfies("Http.get", "Http.post"));
50        assert!(!effect_satisfies("Disk.readText", "Disk.writeText"));
51    }
52
53    #[test]
54    fn no_cross_namespace() {
55        assert!(!effect_satisfies("Http", "Disk.readText"));
56        assert!(!effect_satisfies("Console", "Terminal.clear"));
57    }
58}