~james/core

core/crates/knot-capability/src/lib.rs -rw-r--r-- 3.3 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 17 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum Capability {
    Tangled1,
    KnotAcl,
    RepoDidInput,
    KeepCommit,
    AtprotoFirehose,
    RepoTokenMint,
    SocialWrites,
    Foster,
}

impl Capability {
    const ALL: &'static [Capability] = &[
        Capability::Tangled1,
        Capability::KnotAcl,
        Capability::RepoDidInput,
        Capability::KeepCommit,
        Capability::AtprotoFirehose,
        Capability::RepoTokenMint,
        Capability::SocialWrites,
        Capability::Foster,
    ];

    pub const SERVED: &'static [Capability] = &[
        Capability::Tangled1,
        Capability::KnotAcl,
        Capability::RepoDidInput,
        Capability::AtprotoFirehose,
        Capability::RepoTokenMint,
        Capability::SocialWrites,
    ];

    pub const fn token(self) -> &'static str {
        match self {
            Capability::Tangled1 => "tangled1.0",
            Capability::KnotAcl => "knot-acl",
            Capability::RepoDidInput => "repo-did-input",
            Capability::KeepCommit => "knot-keepcommit",
            Capability::AtprotoFirehose => "atproto-firehose",
            Capability::RepoTokenMint => "knot-repo-token-mint",
            Capability::SocialWrites => "knot-social-writes",
            Capability::Foster => "knot-foster",
        }
    }

    pub fn parse(token: &str) -> Option<Self> {
        Self::ALL.iter().copied().find(|cap| cap.token() == token)
    }

    pub fn served(fostering: bool) -> impl IntoIterator<Item = Capability> {
        Self::SERVED
            .iter()
            .copied()
            .chain(fostering.then_some(Capability::Foster))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn every_capability_parses_back_from_its_token() {
        Capability::ALL.iter().copied().for_each(|cap| {
            assert_eq!(Capability::parse(cap.token()), Some(cap), "{cap:?}");
        });
    }

    #[test]
    fn tokens_are_distinct() {
        let tokens: Vec<&str> = Capability::ALL.iter().map(|cap| cap.token()).collect();
        let mut unique = tokens.clone();
        unique.sort_unstable();
        unique.dedup();
        assert_eq!(unique.len(), tokens.len(), "two variants share a token");
    }

    #[test]
    fn unknown_tokens_are_rejected() {
        ["", "knot_acl", "KNOT-ACL", "atproto-firehose "]
            .iter()
            .for_each(|token| assert_eq!(Capability::parse(token), None, "{token:?}"));
    }

    #[test]
    fn served_capabilities_are_real_capabilities() {
        Capability::SERVED.iter().copied().for_each(|cap| {
            assert!(Capability::ALL.contains(&cap), "{cap:?} is not in ALL");
        });
    }

    #[test]
    fn served_without_fostering_is_the_static_list() {
        assert!(
            Capability::served(false)
                .into_iter()
                .eq(Capability::SERVED.iter().copied())
        );
    }

    #[test]
    fn served_with_fostering_appends_exactly_foster() {
        let served: Vec<_> = Capability::served(true).into_iter().collect();
        assert_eq!(served.len(), Capability::SERVED.len() + 1);
        assert!(
            served[..Capability::SERVED.len()]
                .iter()
                .eq(Capability::SERVED.iter())
        );
        assert_eq!(
            *served.last().expect("one more than SERVED"),
            Capability::Foster
        );
    }
}