accumulo_access/
parser.rs

1// Copyright 2024 Lars Wilhelmsen <sral-backwards@sral.org>. All rights reserved.
2// Use of this source code is governed by the MIT or Apache-2.0 license that can be found in the LICENSE_MIT or LICENSE_APACHE files.
3
4use crate::lexer::{Lexer, Operator, Token};
5use thiserror::Error;
6use crate::authorization_expression::AuthorizationExpression;
7
8/// `ParserError` is returned when the parser encounters an error.
9#[derive(Error, Debug, PartialEq, Clone)]
10pub enum ParserError {
11    /// The scope (top-level or set of parentheses) is empty.
12    EmptyScope,
13    /// The scope is missing an operator ('&' or '|').
14    MissingOperator,
15    /// The parser encountered an unexpected token.
16    UnexpectedToken(Token),
17    /// The parser encountered a mix of operators ('&' and '|').
18    MixingOperators,
19    /// The parser encountered a lexer error.
20    LexerError(crate::lexer::LexerError),
21}
22
23impl std::fmt::Display for ParserError {
24    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
25        match self {
26            ParserError::EmptyScope => write!(f, "Empty scope"),
27            ParserError::MissingOperator => write!(f, "Missing operator"),
28            ParserError::UnexpectedToken(token) => write!(f, "Unexpected token: {}", token),
29            ParserError::MixingOperators => write!(f, "Mixing operators"),
30            ParserError::LexerError(e) => write!(f, "{}", e),
31        }
32    }
33}
34
35#[derive(Debug)]
36struct Scope {
37    nodes: Vec<AuthorizationExpression>,
38    access_tokens: Vec<String>,
39    operator: Option<Operator>,
40}
41
42impl Scope {
43    fn new() -> Self {
44        Scope {
45            nodes: Vec::new(),
46            access_tokens: Vec::new(),
47            operator: None,
48        }
49    }
50
51    fn append_node(&mut self, token: AuthorizationExpression) {
52        self.nodes.push(token);
53    }
54
55    fn append_access_token(&mut self, label: String) {
56        self.access_tokens.push(label);
57    }
58
59    fn disjunction(&mut self) -> Result<(), ParserError> {
60        self.set_operator(&Operator::Disjunction)
61    }
62
63    fn conjunction(&mut self) -> Result<(), ParserError> {
64        self.set_operator(&Operator::Conjunction)
65    }
66
67    fn set_operator(&mut self, operator: &Operator) -> Result<(), ParserError> {
68        match operator {
69            Operator::Conjunction => {
70                if let Some(Operator::Disjunction) = self.operator {
71                    return Err(ParserError::MixingOperators);
72                }
73            }
74            Operator::Disjunction => {
75                if let Some(Operator::Conjunction) = self.operator {
76                    return Err(ParserError::MixingOperators);
77                }
78            }
79        }
80        self.operator = Some(operator.clone());
81        Ok(())
82    }
83
84    fn build(&mut self) -> Result<AuthorizationExpression, ParserError> {
85       if self.access_tokens.is_empty() && self.nodes.is_empty() {
86           return Ok(AuthorizationExpression::Nil)
87       }
88       
89        if self.access_tokens.len() == 1 && self.nodes.is_empty() {
90            return Ok(AuthorizationExpression::AccessToken(
91                self.access_tokens.pop().unwrap(),
92            ));
93        }
94        // if it is a scope wrapping a single node, return the node
95        if self.nodes.len() == 1 && self.access_tokens.is_empty() {
96            return Ok(self.nodes.pop().unwrap());
97        }
98        if self.operator.is_none() {
99            return Err(ParserError::MissingOperator);
100        }
101        let operator = self.operator.take().unwrap();
102        let mut nodes = Vec::with_capacity(self.access_tokens.len() + self.nodes.len());
103
104        while let Some(label) = self.access_tokens.pop() {
105            nodes.push(AuthorizationExpression::AccessToken(label));
106        }
107
108        while let Some(token) = self.nodes.pop() {
109            nodes.push(token);
110        }
111        match operator {
112            Operator::Conjunction => Ok(AuthorizationExpression::ConjunctionOf(nodes)),
113            Operator::Disjunction => Ok(AuthorizationExpression::DisjunctionOf(nodes))
114        }
115    }
116}
117
118/// `Parser` is used to parse an expression and return an `AuthorizationExpression`-based tree.
119pub struct Parser<'a> {
120    lexer: Lexer<'a>,
121}
122
123impl<'a> Parser<'a> {
124    /// Creates a new `Parser` instance.
125    ///
126    /// # Arguments
127    ///
128    /// * `lexer` - The `Lexer` instance to use for tokenization.
129    pub fn new(lexer: Lexer<'a>) -> Self {
130        Parser { lexer }
131    }
132
133    /// Parse the input string and return an AuthorizationExpression.
134    /// If the input string is invalid, a ParserError is returned.
135    ///
136    /// # Example
137    /// ```
138    ///  use std::collections::HashSet;
139    ///  use accumulo_access::{Lexer, Parser};
140    ///  let input = "label1&label5&(label3|label8|\"label 🕺\")";
141    ///  let lexer: Lexer<'_> = Lexer::new(input);
142    ///  let mut parser = Parser::new(lexer);
143    ///  let ast = parser.parse().unwrap();
144    ///  let authorized_tokens : &HashSet<String> = &[
145    ///    String::from("label1"),
146    ///    String::from("label5"),
147    ///    String::from("label 🕺"),
148    ///  ].iter().cloned().collect();
149    ///  assert_eq!(ast.evaluate(&authorized_tokens), true);
150    /// ```
151    pub fn parse(&mut self) -> Result<AuthorizationExpression, ParserError> {
152        let mut scope = Scope::new();
153        while let Some(result) = self.lexer.next() {
154            match result {
155                Ok(token) => {
156                    match token {
157                        Token::AccessToken(value) => scope.append_access_token(value),
158                        Token::OpenParen => {
159                            let node = self.parse()?;
160                            scope.append_node(node.clone()); // The clone here is apparently important.
161                        }
162                        Token::And => scope.conjunction()?,
163                        Token::Or => scope.disjunction()?,
164                        Token::CloseParen => return scope.build(),
165                    }
166                }
167                Err(e) => {
168                    return Err(ParserError::LexerError(e));  
169                } 
170            }
171        }
172        scope.build()
173    }
174}