confium_attributes/ast.rs
1//! Predicate AST.
2
3// Note: serde derive removed due to infinite recursion in derive macro
4// caused by the recursive Predicate type (And/Or/Not contain Predicate).
5// DSL parser + manual serializers handle persistence.
6
7/// A predicate over signer attributes.
8#[derive(Debug, Clone)]
9#[allow(clippy::large_enum_variant)]
10pub enum Predicate {
11 /// At least `count` signers must have `attribute`.
12 MinCount {
13 /// Attribute name (e.g., "role:director").
14 attribute: String,
15 /// Required count.
16 count: usize,
17 },
18 /// At least `count` distinct values of `attribute` must appear.
19 MinDistinct {
20 /// Attribute name.
21 attribute: String,
22 /// Required distinct-value count.
23 count: usize,
24 },
25 /// No signer has `attribute`.
26 None {
27 /// Attribute that no signer must have.
28 attribute: String,
29 },
30 /// At least one signer has `attribute`.
31 Any {
32 /// Attribute that at least one signer must have.
33 attribute: String,
34 },
35 /// All signers have `attribute`.
36 All {
37 /// Attribute that every signer must have.
38 attribute: String,
39 },
40 /// Conjunction of sub-predicates.
41 And(Vec<Predicate>),
42 /// Disjunction of sub-predicates.
43 Or(Vec<Predicate>),
44 /// Negation of a sub-predicate.
45 Not(Box<Predicate>),
46}
47
48/// A wrapper for type-safe construction.
49#[derive(Debug, Clone)]
50pub struct AttributePredicate(pub Predicate);
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn predicate_constructs() {
58 let p = Predicate::And(vec![
59 Predicate::MinCount {
60 attribute: "role:director".into(),
61 count: 5,
62 },
63 Predicate::MinDistinct {
64 attribute: "region".into(),
65 count: 3,
66 },
67 ]);
68 // Just verify the API compiles
69 let AttributePredicate(_) = AttributePredicate(p.clone());
70 match p {
71 Predicate::And(inner) => assert_eq!(inner.len(), 2),
72 _ => panic!("wrong variant"),
73 }
74 }
75}