Skip to main content

confium_wasm/
attributes.rs

1//! `Predicate` — attribute-based threshold policy DSL, browser-side evaluate-only.
2
3use confium_attributes::{
4    Predicate as RustPredicate, SignerAttributes, evaluate, parse as dsl_parse,
5};
6use wasm_bindgen::prelude::*;
7
8/// Parsed DSL predicate. Construct via [`Predicate::parse`] and evaluate
9/// via [`Predicate::satisfied_by`].
10#[wasm_bindgen]
11pub struct Predicate {
12    inner: RustPredicate,
13}
14
15#[wasm_bindgen]
16impl Predicate {
17    /// Parse a DSL expression into a Predicate.
18    ///
19    /// Examples:
20    ///   - `min_count("role:director", 3)`
21    ///   - `and(min_count("role:director", 3), min_distinct("region", 3))`
22    ///   - `or(any("expertise"), all("emergency"))`
23    ///   - `not(none("nationality:cn"))`
24    #[wasm_bindgen(constructor)]
25    pub fn parse(expr: &str) -> Result<Predicate, JsValue> {
26        let inner = dsl_parse(expr).map_err(|e| JsValue::from_str(&e.to_string()))?;
27        Ok(Self { inner })
28    }
29
30    /// Evaluate the predicate against a list of signers. Each signer is a
31    /// plain JS object mapping attribute name -> array of values:
32    ///
33    /// ```js
34    /// predicate.satisfiedBy([
35    ///   { "role:director": ["yes"], "region": ["europe"] },
36    ///   { "role:director": ["yes"], "region": ["americas"] },
37    /// ]);
38    /// ```
39    pub fn satisfied_by(&self, signers_json: &str) -> Result<bool, JsValue> {
40        let parsed: Vec<SignerEntry> = serde_json::from_str(signers_json)
41            .map_err(|e| JsValue::from_str(&format!("invalid signers JSON: {e}")))?;
42        let owned: Vec<SignerAttributes> = parsed
43            .into_iter()
44            .map(|entry| {
45                let mut s = SignerAttributes::new();
46                for (k, values) in entry.0 {
47                    for v in values {
48                        s.add(k.clone(), v);
49                    }
50                }
51                s
52            })
53            .collect();
54        let refs: Vec<&SignerAttributes> = owned.iter().collect();
55        Ok(evaluate(&self.inner, &refs))
56    }
57}
58
59#[derive(serde::Deserialize)]
60struct SignerEntry(std::collections::HashMap<String, Vec<String>>);