biscuit_parser/
builder.rs

1/*
2 * Copyright (c) 2019 Geoffroy Couprie <contact@geoffroycouprie.com> and Contributors to the Eclipse Foundation.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5//! helper functions and structure to create tokens and blocks
6use std::{
7    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
8    time::{SystemTime, UNIX_EPOCH},
9};
10
11#[cfg(feature = "datalog-macro")]
12use quote::{quote, ToTokens};
13
14/// Builder for a Datalog value
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub enum Term {
17    Variable(String),
18    Integer(i64),
19    Str(String),
20    Date(u64),
21    Bytes(Vec<u8>),
22    Bool(bool),
23    Set(BTreeSet<Term>),
24    Parameter(String),
25    Null,
26    Array(Vec<Term>),
27    Map(BTreeMap<MapKey, Term>),
28}
29
30impl Term {
31    fn extract_parameters(&self, parameters: &mut HashMap<String, Option<Term>>) {
32        match self {
33            Term::Parameter(name) => {
34                parameters.insert(name.to_string(), None);
35            }
36            Term::Set(s) => {
37                for term in s {
38                    term.extract_parameters(parameters);
39                }
40            }
41            Term::Array(a) => {
42                for term in a {
43                    term.extract_parameters(parameters);
44                }
45            }
46            Term::Map(m) => {
47                for (key, term) in m {
48                    if let MapKey::Parameter(name) = key {
49                        parameters.insert(name.to_string(), None);
50                    }
51                    term.extract_parameters(parameters);
52                }
53            }
54            _ => {}
55        }
56    }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60pub enum MapKey {
61    Parameter(String),
62    Integer(i64),
63    Str(String),
64}
65
66impl From<&Term> for Term {
67    fn from(i: &Term) -> Self {
68        match i {
69            Term::Variable(ref v) => Term::Variable(v.clone()),
70            Term::Integer(ref i) => Term::Integer(*i),
71            Term::Str(ref s) => Term::Str(s.clone()),
72            Term::Date(ref d) => Term::Date(*d),
73            Term::Bytes(ref s) => Term::Bytes(s.clone()),
74            Term::Bool(b) => Term::Bool(*b),
75            Term::Set(ref s) => Term::Set(s.clone()),
76            Term::Parameter(ref p) => Term::Parameter(p.clone()),
77            Term::Null => Term::Null,
78            Term::Array(ref a) => Term::Array(a.clone()),
79            Term::Map(ref m) => Term::Map(m.clone()),
80        }
81    }
82}
83
84impl AsRef<Term> for Term {
85    fn as_ref(&self) -> &Term {
86        self
87    }
88}
89
90#[cfg(feature = "datalog-macro")]
91impl ToTokens for Term {
92    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
93        tokens.extend(match self {
94            Term::Variable(v) => quote! { ::biscuit_auth::builder::Term::Variable(#v.to_string()) },
95            Term::Integer(v) => quote! { ::biscuit_auth::builder::Term::Integer(#v) },
96            Term::Str(v) => quote! { ::biscuit_auth::builder::Term::Str(#v.to_string()) },
97            Term::Date(v) => quote! { ::biscuit_auth::builder::Term::Date(#v) },
98            Term::Bool(v) => quote! { ::biscuit_auth::builder::Term::Bool(#v) },
99            Term::Parameter(v) => quote! { ::biscuit_auth::builder::Term::Parameter(#v.to_string()) },
100            Term::Bytes(v) => quote! { ::biscuit_auth::builder::Term::Bytes(<[u8]>::into_vec(Box::new([ #(#v),*]))) },
101            Term::Set(v) => {
102                quote! {{
103                    use std::iter::FromIterator;
104                    ::biscuit_auth::builder::Term::Set(::std::collections::BTreeSet::from_iter(<[::biscuit_auth::builder::Term]>::into_vec(Box::new([ #(#v),*]))))
105                }}
106            }
107            Term::Null => quote! { ::biscuit_auth::builder::Term::Null },
108            Term::Array(v) => {
109                quote! {{
110                    use std::iter::FromIterator;
111                    ::biscuit_auth::builder::Term::Array(::std::vec::Vec::from_iter(<[::biscuit_auth::builder::Term]>::into_vec( Box::new([ #(#v),*]))))
112                }}
113            }
114            Term::Map(m) => {
115                let  it = m.iter().map(|(key, term)| MapEntry {key, term });
116                quote! {{
117                    use std::iter::FromIterator;
118                    ::biscuit_auth::builder::Term::Map(::std::collections::BTreeMap::from_iter(<[(::biscuit_auth::builder::MapKey,::biscuit_auth::builder::Term)]>::into_vec(Box::new([ #(#it),*]))))
119                }}
120            }
121        })
122    }
123}
124
125#[cfg(feature = "datalog-macro")]
126struct MapEntry<'a> {
127    key: &'a MapKey,
128    term: &'a Term,
129}
130
131#[cfg(feature = "datalog-macro")]
132impl<'a> ToTokens for MapEntry<'a> {
133    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
134        let term = self.term;
135        tokens.extend(match self.key {
136            MapKey::Parameter(p) => {
137                quote! { (::biscuit_auth::builder::MapKey::Parameter(#p.to_string()) , #term )}
138            }
139            MapKey::Integer(i) => {
140                quote! { (::biscuit_auth::builder::MapKey::Integer(#i) , #term )}
141            }
142            MapKey::Str(s) => {
143                quote! { (::biscuit_auth::builder::MapKey::Str(#s.to_string()) , #term )}
144            }
145        });
146    }
147}
148
149#[derive(Clone, Debug, Hash, PartialEq, Eq)]
150pub enum Scope {
151    Authority,
152    Previous,
153    PublicKey(PublicKey),
154    Parameter(String),
155}
156
157#[cfg(feature = "datalog-macro")]
158impl ToTokens for Scope {
159    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
160        tokens.extend(match self {
161            Scope::Authority => quote! { ::biscuit_auth::builder::Scope::Authority},
162            Scope::Previous => quote! { ::biscuit_auth::builder::Scope::Previous},
163            Scope::PublicKey(pk) => {
164                let bytes = pk.key.iter();
165                match pk.algorithm {
166                    Algorithm::Ed25519 => quote! { ::biscuit_auth::builder::Scope::PublicKey(
167                        ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*], ::biscuit_auth::builder::Algorithm::Ed25519).unwrap()
168                      )},
169                    Algorithm::Secp256r1 => quote! { ::biscuit_auth::builder::Scope::PublicKey(
170                        ::biscuit_auth::PublicKey::from_bytes(&[#(#bytes),*], ::biscuit_auth::builder::Algorithm::Secp256r1).unwrap()
171                      )},
172                }
173            }
174            Scope::Parameter(v) => {
175                quote! { ::biscuit_auth::builder::Scope::Parameter(#v.to_string())}
176            }
177        })
178    }
179}
180
181/// Builder for a Datalog dicate, used in facts and rules
182#[derive(Debug, Clone, PartialEq, Hash, Eq)]
183pub struct Predicate {
184    pub name: String,
185    pub terms: Vec<Term>,
186}
187
188impl Predicate {
189    pub fn new<T: Into<Vec<Term>>>(name: String, terms: T) -> Predicate {
190        Predicate {
191            name,
192            terms: terms.into(),
193        }
194    }
195}
196
197#[cfg(feature = "datalog-macro")]
198impl ToTokens for Predicate {
199    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
200        let name = &self.name;
201        let terms = self.terms.iter();
202        tokens.extend(quote! {
203            ::biscuit_auth::builder::Predicate::new(
204              #name.to_string(),
205              <[::biscuit_auth::builder::Term]>::into_vec(Box::new([#(#terms),*]))
206            )
207        })
208    }
209}
210
211/// Builder for a Datalog fact
212#[derive(Debug, Clone, PartialEq, Eq)]
213pub struct Fact {
214    pub predicate: Predicate,
215    pub parameters: Option<HashMap<String, Option<Term>>>,
216}
217
218impl Fact {
219    pub fn new<T: Into<Vec<Term>>>(name: String, terms: T) -> Fact {
220        let mut parameters = HashMap::new();
221        let terms: Vec<Term> = terms.into();
222
223        for term in &terms {
224            term.extract_parameters(&mut parameters);
225        }
226        Fact {
227            predicate: Predicate::new(name, terms),
228            parameters: Some(parameters),
229        }
230    }
231}
232
233#[cfg(feature = "datalog-macro")]
234impl ToTokens for Fact {
235    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
236        let name = &self.predicate.name;
237        let terms = self.predicate.terms.iter();
238        tokens.extend(quote! {
239            ::biscuit_auth::builder::Fact::new(
240              #name.to_string(),
241              <[::biscuit_auth::builder::Term]>::into_vec(Box::new([#(#terms),*]))
242            )
243        })
244    }
245}
246
247/// Builder for a Datalog expression
248#[derive(Debug, Clone, PartialEq, Eq)]
249pub struct Expression {
250    pub ops: Vec<Op>,
251}
252
253#[cfg(feature = "datalog-macro")]
254impl ToTokens for Expression {
255    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
256        let ops = self.ops.iter();
257        tokens.extend(quote! {
258          ::biscuit_auth::builder::Expression {
259            ops: <[::biscuit_auth::builder::Op]>::into_vec(Box::new([#(#ops),*]))
260          }
261        });
262    }
263}
264
265/// Builder for an expression operation
266#[derive(Debug, Clone, PartialEq, Eq)]
267pub enum Op {
268    Value(Term),
269    Unary(Unary),
270    Binary(Binary),
271    Closure(Vec<String>, Vec<Op>),
272}
273
274impl Op {
275    fn collect_parameters(&self, parameters: &mut HashMap<String, Option<Term>>) {
276        match self {
277            Op::Value(term) => {
278                term.extract_parameters(parameters);
279            }
280            Op::Closure(_, ops) => {
281                for op in ops {
282                    op.collect_parameters(parameters);
283                }
284            }
285            _ => {}
286        }
287    }
288}
289
290#[derive(Debug, Clone, PartialEq, Eq)]
291pub enum Unary {
292    Negate,
293    Parens,
294    Length,
295    TypeOf,
296    Ffi(String),
297}
298
299#[derive(Debug, Clone, PartialEq, Eq)]
300pub enum Binary {
301    LessThan,
302    GreaterThan,
303    LessOrEqual,
304    GreaterOrEqual,
305    Equal,
306    Contains,
307    Prefix,
308    Suffix,
309    Regex,
310    Add,
311    Sub,
312    Mul,
313    Div,
314    And,
315    Or,
316    Intersection,
317    Union,
318    BitwiseAnd,
319    BitwiseOr,
320    BitwiseXor,
321    NotEqual,
322    HeterogeneousEqual,
323    HeterogeneousNotEqual,
324    LazyAnd,
325    LazyOr,
326    All,
327    Any,
328    Get,
329    Ffi(String),
330    TryOr,
331}
332
333#[cfg(feature = "datalog-macro")]
334impl ToTokens for Op {
335    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
336        tokens.extend(match self {
337            Op::Value(t) => quote! { ::biscuit_auth::builder::Op::Value(#t) },
338            Op::Unary(u) => quote! { ::biscuit_auth::builder::Op::Unary(#u) },
339            Op::Binary(b) => quote! { ::biscuit_auth::builder::Op::Binary(#b) },
340            Op::Closure(params, os) => quote! {
341            ::biscuit_auth::builder::Op::Closure(
342                    <[String]>::into_vec(Box::new([#(#params.to_string()),*])),
343                    <[::biscuit_auth::builder::Op]>::into_vec(Box::new([#(#os),*]))
344                    )
345            },
346        });
347    }
348}
349
350#[cfg(feature = "datalog-macro")]
351impl ToTokens for Unary {
352    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
353        tokens.extend(match self {
354            Unary::Negate => quote! {::biscuit_auth::builder::Unary::Negate },
355            Unary::Parens => quote! {::biscuit_auth::builder::Unary::Parens },
356            Unary::Length => quote! {::biscuit_auth::builder::Unary::Length },
357            Unary::TypeOf => quote! {::biscuit_auth::builder::Unary::TypeOf },
358            Unary::Ffi(name) => quote! {::biscuit_auth::builder::Unary::Ffi(#name.to_string()) },
359        });
360    }
361}
362
363#[cfg(feature = "datalog-macro")]
364impl ToTokens for Binary {
365    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
366        tokens.extend(match self {
367            Binary::LessThan => quote! { ::biscuit_auth::builder::Binary::LessThan  },
368            Binary::GreaterThan => quote! { ::biscuit_auth::builder::Binary::GreaterThan  },
369            Binary::LessOrEqual => quote! { ::biscuit_auth::builder::Binary::LessOrEqual  },
370            Binary::GreaterOrEqual => quote! { ::biscuit_auth::builder::Binary::GreaterOrEqual  },
371            Binary::Equal => quote! { ::biscuit_auth::builder::Binary::Equal  },
372            Binary::Contains => quote! { ::biscuit_auth::builder::Binary::Contains  },
373            Binary::Prefix => quote! { ::biscuit_auth::builder::Binary::Prefix  },
374            Binary::Suffix => quote! { ::biscuit_auth::builder::Binary::Suffix  },
375            Binary::Regex => quote! { ::biscuit_auth::builder::Binary::Regex  },
376            Binary::Add => quote! { ::biscuit_auth::builder::Binary::Add  },
377            Binary::Sub => quote! { ::biscuit_auth::builder::Binary::Sub  },
378            Binary::Mul => quote! { ::biscuit_auth::builder::Binary::Mul  },
379            Binary::Div => quote! { ::biscuit_auth::builder::Binary::Div  },
380            Binary::And => quote! { ::biscuit_auth::builder::Binary::And  },
381            Binary::Or => quote! { ::biscuit_auth::builder::Binary::Or  },
382            Binary::Intersection => quote! { ::biscuit_auth::builder::Binary::Intersection  },
383            Binary::Union => quote! { ::biscuit_auth::builder::Binary::Union  },
384            Binary::BitwiseAnd => quote! { ::biscuit_auth::builder::Binary::BitwiseAnd  },
385            Binary::BitwiseOr => quote! { ::biscuit_auth::builder::Binary::BitwiseOr  },
386            Binary::BitwiseXor => quote! { ::biscuit_auth::builder::Binary::BitwiseXor  },
387            Binary::NotEqual => quote! { ::biscuit_auth::builder::Binary::NotEqual },
388            Binary::HeterogeneousEqual => {
389                quote! { ::biscuit_auth::builder::Binary::HeterogeneousEqual}
390            }
391            Binary::HeterogeneousNotEqual => {
392                quote! { ::biscuit_auth::builder::Binary::HeterogeneousNotEqual}
393            }
394            Binary::LazyAnd => quote! { ::biscuit_auth::builder::Binary::LazyAnd },
395            Binary::LazyOr => quote! { ::biscuit_auth::builder::Binary::LazyOr },
396            Binary::All => quote! { ::biscuit_auth::builder::Binary::All },
397            Binary::Any => quote! { ::biscuit_auth::builder::Binary::Any },
398            Binary::Get => quote! { ::biscuit_auth::builder::Binary::Get },
399            Binary::Ffi(name) => quote! {::biscuit_auth::builder::Binary::Ffi(#name.to_string()) },
400            Binary::TryOr => quote! { ::biscuit_auth::builder::Binary::TryOr },
401        });
402    }
403}
404
405#[derive(Debug, Clone, Hash, PartialEq, Eq)]
406pub struct PublicKey {
407    pub key: Vec<u8>,
408    pub algorithm: Algorithm,
409}
410
411#[derive(Debug, Clone, Hash, PartialEq, Eq)]
412pub enum Algorithm {
413    Ed25519,
414    Secp256r1,
415}
416
417/// Builder for a Datalog rule
418#[derive(Debug, Clone, PartialEq, Eq)]
419pub struct Rule {
420    pub head: Predicate,
421    pub body: Vec<Predicate>,
422    pub expressions: Vec<Expression>,
423    pub parameters: Option<HashMap<String, Option<Term>>>,
424    pub scopes: Vec<Scope>,
425    pub scope_parameters: Option<HashMap<String, Option<PublicKey>>>,
426}
427
428impl Rule {
429    pub fn new(
430        head: Predicate,
431        body: Vec<Predicate>,
432        expressions: Vec<Expression>,
433        scopes: Vec<Scope>,
434    ) -> Rule {
435        let mut parameters = HashMap::new();
436        let mut scope_parameters = HashMap::new();
437
438        for term in &head.terms {
439            term.extract_parameters(&mut parameters);
440        }
441
442        for predicate in &body {
443            for term in &predicate.terms {
444                term.extract_parameters(&mut parameters);
445            }
446        }
447
448        for expression in &expressions {
449            for op in &expression.ops {
450                op.collect_parameters(&mut parameters);
451            }
452        }
453
454        for scope in &scopes {
455            if let Scope::Parameter(name) = &scope {
456                scope_parameters.insert(name.to_string(), None);
457            }
458        }
459
460        Rule {
461            head,
462            body,
463            expressions,
464            parameters: Some(parameters),
465            scopes,
466            scope_parameters: Some(scope_parameters),
467        }
468    }
469
470    pub fn validate_variables(&self) -> Result<(), String> {
471        let mut free_variables: HashSet<String> = HashSet::default();
472        for term in self.head.terms.iter() {
473            if let Term::Variable(s) = term {
474                free_variables.insert(s.to_string());
475            }
476        }
477
478        for e in self.expressions.iter() {
479            for op in e.ops.iter() {
480                if let Op::Value(Term::Variable(s)) = op {
481                    free_variables.insert(s.to_string());
482                }
483            }
484        }
485
486        for predicate in self.body.iter() {
487            for term in predicate.terms.iter() {
488                if let Term::Variable(v) = term {
489                    free_variables.remove(v);
490                    if free_variables.is_empty() {
491                        return Ok(());
492                    }
493                }
494            }
495        }
496
497        if free_variables.is_empty() {
498            Ok(())
499        } else {
500            Err(format!(
501                    "the rule contains variables that are not bound by predicates in the rule's body: {}",
502                    free_variables
503                    .iter()
504                    .map(|s| format!("${}", s))
505                    .collect::<Vec<_>>()
506                    .join(", ")
507                    ))
508        }
509    }
510}
511
512#[cfg(feature = "datalog-macro")]
513impl ToTokens for Rule {
514    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
515        let head = &self.head;
516        let body = self.body.iter();
517        let expressions = self.expressions.iter();
518        let scopes = self.scopes.iter();
519        tokens.extend(quote! {
520          ::biscuit_auth::builder::Rule::new(
521            #head,
522            <[::biscuit_auth::builder::Predicate]>::into_vec(Box::new([#(#body),*])),
523            <[::biscuit_auth::builder::Expression]>::into_vec(Box::new([#(#expressions),*])),
524            <[::biscuit_auth::builder::Scope]>::into_vec(Box::new([#(#scopes),*]))
525          )
526        });
527    }
528}
529
530/// Builder for a Biscuit check
531#[derive(Debug, Clone, PartialEq, Eq)]
532pub struct Check {
533    pub queries: Vec<Rule>,
534    pub kind: CheckKind,
535}
536
537#[derive(Debug, Clone, PartialEq, Eq)]
538pub enum CheckKind {
539    One,
540    All,
541    Reject,
542}
543
544#[cfg(feature = "datalog-macro")]
545impl ToTokens for Check {
546    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
547        let queries = self.queries.iter();
548        let kind = &self.kind;
549        tokens.extend(quote! {
550          ::biscuit_auth::builder::Check {
551            queries: <[::biscuit_auth::builder::Rule]>::into_vec(Box::new([#(#queries),*])),
552            kind: #kind,
553          }
554        });
555    }
556}
557
558#[cfg(feature = "datalog-macro")]
559impl ToTokens for CheckKind {
560    fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
561        tokens.extend(match self {
562            CheckKind::One => quote! {
563              ::biscuit_auth::builder::CheckKind::One
564            },
565            CheckKind::All => quote! {
566              ::biscuit_auth::builder::CheckKind::All
567            },
568            CheckKind::Reject => quote! {
569              ::biscuit_auth::builder::CheckKind::Reject
570            },
571        });
572    }
573}
574
575#[derive(Debug, Clone, PartialEq, Eq)]
576pub enum PolicyKind {
577    Allow,
578    Deny,
579}
580
581#[cfg(feature = "datalog-macro")]
582impl ToTokens for PolicyKind {
583    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
584        tokens.extend(match self {
585            PolicyKind::Allow => quote! {
586              ::biscuit_auth::builder::PolicyKind::Allow
587            },
588            PolicyKind::Deny => quote! {
589              ::biscuit_auth::builder::PolicyKind::Deny
590            },
591        });
592    }
593}
594
595/// Builder for a Biscuit policy
596#[derive(Debug, Clone, PartialEq, Eq)]
597pub struct Policy {
598    pub queries: Vec<Rule>,
599    pub kind: PolicyKind,
600}
601
602#[cfg(feature = "datalog-macro")]
603impl ToTokens for Policy {
604    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
605        let queries = self.queries.iter();
606        let kind = &self.kind;
607        tokens.extend(quote! {
608          ::biscuit_auth::builder::Policy{
609            kind: #kind,
610            queries: <[::biscuit_auth::builder::Rule]>::into_vec(Box::new([#(#queries),*])),
611          }
612        });
613    }
614}
615
616/// creates a new fact
617pub fn fact<I: AsRef<Term>>(name: &str, terms: &[I]) -> Fact {
618    let pred = pred(name, terms);
619    Fact::new(pred.name, pred.terms)
620}
621
622/// creates a predicate
623pub fn pred<I: AsRef<Term>>(name: &str, terms: &[I]) -> Predicate {
624    Predicate {
625        name: name.to_string(),
626        terms: terms.iter().map(|term| term.as_ref().clone()).collect(),
627    }
628}
629
630/// creates a rule
631pub fn rule<T: AsRef<Term>>(head_name: &str, head_terms: &[T], predicates: &[Predicate]) -> Rule {
632    Rule::new(
633        pred(head_name, head_terms),
634        predicates.to_vec(),
635        Vec::new(),
636        vec![],
637    )
638}
639
640/// creates a rule with constraints
641pub fn constrained_rule<T: AsRef<Term>>(
642    head_name: &str,
643    head_terms: &[T],
644    predicates: &[Predicate],
645    expressions: &[Expression],
646) -> Rule {
647    Rule::new(
648        pred(head_name, head_terms),
649        predicates.to_vec(),
650        expressions.to_vec(),
651        vec![],
652    )
653}
654
655/// creates a check
656pub fn check<P: AsRef<Predicate>>(predicates: &[P], kind: CheckKind) -> Check {
657    let empty_terms: &[Term] = &[];
658    Check {
659        queries: vec![Rule::new(
660            pred("query", empty_terms),
661            predicates.iter().map(|p| p.as_ref().clone()).collect(),
662            vec![],
663            vec![],
664        )],
665        kind,
666    }
667}
668
669/// creates an integer value
670pub fn int(i: i64) -> Term {
671    Term::Integer(i)
672}
673
674/// creates a string
675pub fn string(s: &str) -> Term {
676    Term::Str(s.to_string())
677}
678
679/// creates a date
680///
681/// internally the date will be stored as seconds since UNIX_EPOCH
682pub fn date(t: &SystemTime) -> Term {
683    let dur = t.duration_since(UNIX_EPOCH).unwrap();
684    Term::Date(dur.as_secs())
685}
686
687/// creates a variable for a rule
688pub fn var(s: &str) -> Term {
689    Term::Variable(s.to_string())
690}
691
692/// creates a variable for a rule
693pub fn variable(s: &str) -> Term {
694    Term::Variable(s.to_string())
695}
696
697/// creates a byte array
698pub fn bytes(s: &[u8]) -> Term {
699    Term::Bytes(s.to_vec())
700}
701
702/// creates a boolean
703pub fn boolean(b: bool) -> Term {
704    Term::Bool(b)
705}
706
707/// creates a set
708pub fn set(s: BTreeSet<Term>) -> Term {
709    Term::Set(s)
710}
711
712/// creates a null
713pub fn null() -> Term {
714    Term::Null
715}
716
717/// creates an array
718pub fn array(a: Vec<Term>) -> Term {
719    Term::Array(a)
720}
721
722/// creates a map
723pub fn map(m: BTreeMap<MapKey, Term>) -> Term {
724    Term::Map(m)
725}
726
727/// creates a parameter
728pub fn parameter(p: &str) -> Term {
729    Term::Parameter(p.to_string())
730}