biscuit_auth/token/builder/
fact.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 */
5use std::{collections::HashMap, convert::TryFrom, fmt, str::FromStr};
6
7use nom::Finish;
8
9use crate::{
10    datalog::{self, SymbolTable},
11    error,
12};
13
14use super::{Convert, Predicate, Term, ToAnyParam};
15
16/// Builder for a Datalog fact
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Fact {
19    pub predicate: Predicate,
20    pub parameters: Option<HashMap<String, Option<Term>>>,
21}
22
23impl Fact {
24    pub fn new<T: Into<Vec<Term>>>(name: String, terms: T) -> Fact {
25        let mut parameters = HashMap::new();
26        let terms: Vec<Term> = terms.into();
27
28        for term in &terms {
29            term.extract_parameters(&mut parameters);
30        }
31        Fact {
32            predicate: Predicate::new(name, terms),
33            parameters: Some(parameters),
34        }
35    }
36
37    pub fn validate(&self) -> Result<(), error::Token> {
38        match &self.parameters {
39            None => Ok(()),
40            Some(parameters) => {
41                let invalid_parameters = parameters
42                    .iter()
43                    .filter_map(
44                        |(name, opt_term)| {
45                            if opt_term.is_none() {
46                                Some(name)
47                            } else {
48                                None
49                            }
50                        },
51                    )
52                    .map(|name| name.to_string())
53                    .collect::<Vec<_>>();
54
55                if invalid_parameters.is_empty() {
56                    Ok(())
57                } else {
58                    Err(error::Token::Language(
59                        biscuit_parser::error::LanguageError::Parameters {
60                            missing_parameters: invalid_parameters,
61                            unused_parameters: vec![],
62                        },
63                    ))
64                }
65            }
66        }
67    }
68
69    /// replace a parameter with the term argument
70    pub fn set<T: Into<Term>>(&mut self, name: &str, term: T) -> Result<(), error::Token> {
71        if let Some(parameters) = self.parameters.as_mut() {
72            match parameters.get_mut(name) {
73                None => Err(error::Token::Language(
74                    biscuit_parser::error::LanguageError::Parameters {
75                        missing_parameters: vec![],
76                        unused_parameters: vec![name.to_string()],
77                    },
78                )),
79                Some(v) => {
80                    *v = Some(term.into());
81                    Ok(())
82                }
83            }
84        } else {
85            Err(error::Token::Language(
86                biscuit_parser::error::LanguageError::Parameters {
87                    missing_parameters: vec![],
88                    unused_parameters: vec![name.to_string()],
89                },
90            ))
91        }
92    }
93
94    /// replace a parameter with the term argument, without raising an error
95    /// if the parameter is not present in the fact description
96    pub fn set_lenient<T: Into<Term>>(&mut self, name: &str, term: T) -> Result<(), error::Token> {
97        if let Some(parameters) = self.parameters.as_mut() {
98            match parameters.get_mut(name) {
99                None => Ok(()),
100                Some(v) => {
101                    *v = Some(term.into());
102                    Ok(())
103                }
104            }
105        } else {
106            Err(error::Token::Language(
107                biscuit_parser::error::LanguageError::Parameters {
108                    missing_parameters: vec![],
109                    unused_parameters: vec![name.to_string()],
110                },
111            ))
112        }
113    }
114
115    #[cfg(feature = "datalog-macro")]
116    pub fn set_macro_param<T: ToAnyParam>(
117        &mut self,
118        name: &str,
119        param: T,
120    ) -> Result<(), error::Token> {
121        use super::AnyParam;
122
123        match param.to_any_param() {
124            AnyParam::Term(t) => self.set_lenient(name, t),
125            AnyParam::PublicKey(_) => Ok(()),
126        }
127    }
128
129    pub(super) fn apply_parameters(&mut self) {
130        if let Some(parameters) = self.parameters.clone() {
131            self.predicate.terms = self
132                .predicate
133                .terms
134                .drain(..)
135                .map(|t| t.apply_parameters(&parameters))
136                .collect();
137        }
138    }
139}
140
141impl Convert<datalog::Fact> for Fact {
142    fn convert(&self, symbols: &mut SymbolTable) -> datalog::Fact {
143        let mut fact = self.clone();
144        fact.apply_parameters();
145
146        datalog::Fact {
147            predicate: fact.predicate.convert(symbols),
148        }
149    }
150
151    fn convert_from(f: &datalog::Fact, symbols: &SymbolTable) -> Result<Self, error::Format> {
152        Ok(Fact {
153            predicate: Predicate::convert_from(&f.predicate, symbols)?,
154            parameters: None,
155        })
156    }
157}
158
159impl fmt::Display for Fact {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        let mut fact = self.clone();
162        fact.apply_parameters();
163
164        fact.predicate.fmt(f)
165    }
166}
167
168impl From<biscuit_parser::builder::Fact> for Fact {
169    fn from(f: biscuit_parser::builder::Fact) -> Self {
170        Fact {
171            predicate: f.predicate.into(),
172            //    pub parameters: Option<HashMap<String, Option<Term>>>,
173            parameters: f.parameters.map(|h| {
174                h.into_iter()
175                    .map(|(k, v)| (k, v.map(|term| term.into())))
176                    .collect()
177            }),
178        }
179    }
180}
181
182impl TryFrom<&str> for Fact {
183    type Error = error::Token;
184
185    fn try_from(value: &str) -> Result<Self, Self::Error> {
186        Ok(biscuit_parser::parser::fact(value)
187            .finish()
188            .map(|(_, o)| o.into())
189            .map_err(biscuit_parser::error::LanguageError::from)?)
190    }
191}
192
193impl FromStr for Fact {
194    type Err = error::Token;
195
196    fn from_str(s: &str) -> Result<Self, Self::Err> {
197        Ok(biscuit_parser::parser::fact(s)
198            .finish()
199            .map(|(_, o)| o.into())
200            .map_err(biscuit_parser::error::LanguageError::from)?)
201    }
202}