cedar_policy_validator/cedar_schema/
parser.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * Copyright Cedar Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Parser for schemas in the Cedar syntax

use std::sync::Arc;

use lalrpop_util::lalrpop_mod;
use miette::Diagnostic;
use thiserror::Error;

use super::{
    ast::Schema,
    err::{self, ParseError, ParseErrors, SchemaWarning, ToJsonSchemaErrors},
    to_json_schema::cedar_schema_to_json_schema,
};
use crate::json_schema;
use cedar_policy_core::extensions::Extensions;

lalrpop_mod!(
    #[allow(warnings, unused, missing_docs, missing_debug_implementations)]
    //PANIC SAFETY: lalrpop uses unwraps, and we are trusting lalrpop to generate correct code
    #[allow(clippy::unwrap_used)]
    //PANIC SAFETY: lalrpop uses slicing, and we are trusting lalrpop to generate correct code
    #[allow(clippy::indexing_slicing)]
    //PANIC SAFETY: lalrpop uses unreachable, and we are trusting lalrpop to generate correct code
    #[allow(clippy::unreachable)]
    //PANIC SAFETY: lalrpop uses panic, and we are trusting lalrpop to generate correct code
    #[allow(clippy::panic)]
    pub grammar,
    "/src/cedar_schema/grammar.rs"
);

/// This helper function calls a generated parser, collects errors that could be
/// generated multiple ways, and returns a single Result where the error type is
/// [`err::ParseErrors`].
fn parse_collect_errors<'a, P, T>(
    parser: &P,
    parse: impl FnOnce(
        &P,
        &mut Vec<err::RawErrorRecovery<'a>>,
        &Arc<str>,
        &'a str,
    ) -> Result<T, err::RawParseError<'a>>,
    text: &'a str,
) -> Result<T, err::ParseErrors> {
    let mut errs = Vec::new();
    let result = parse(parser, &mut errs, &Arc::from(text), text);

    let errors = errs
        .into_iter()
        .map(Into::into)
        .collect::<Vec<ParseError>>();
    let parsed = match result {
        Ok(parsed) => parsed,
        Err(e) => {
            return Err(ParseErrors::new(e.into(), errors));
        }
    };
    match ParseErrors::from_iter(errors) {
        Some(errors) => Err(errors),
        // No Errors: good to return parse
        None => Ok(parsed),
    }
}

// Thread-safe "global" parsers, initialized at first use
lazy_static::lazy_static! {
    static ref SCHEMA_PARSER: grammar::SchemaParser = grammar::SchemaParser::new();
    static ref TYPE_PARSER: grammar::TypeParser = grammar::TypeParser::new();
}

/// Parse errors for parsing a schema in the Cedar syntax
//
// This is NOT a publicly exported error type.
#[derive(Debug, Diagnostic, Error)]
#[non_exhaustive]
pub enum CedarSchemaParseErrors {
    /// Parse error for the Cedar syntax
    #[error(transparent)]
    #[diagnostic(transparent)]
    SyntaxError(#[from] err::ParseErrors),
    /// Error converting the parsed representation into the internal JSON representation
    #[error(transparent)]
    #[diagnostic(transparent)]
    JsonError(#[from] ToJsonSchemaErrors),
}

/// Parse a schema fragment, in the Cedar syntax, into a [`json_schema::Fragment`],
/// possibly generating warnings
pub fn parse_cedar_schema_fragment<'a>(
    src: &str,
    extensions: &Extensions<'a>,
) -> Result<
    (
        json_schema::Fragment<crate::RawName>,
        impl Iterator<Item = SchemaWarning> + 'a,
    ),
    CedarSchemaParseErrors,
> {
    let ast: Schema = parse_collect_errors(&*SCHEMA_PARSER, grammar::SchemaParser::parse, src)?;
    let tuple = cedar_schema_to_json_schema(ast, extensions)?;
    Ok(tuple)
}

/// Parse schema from text
pub fn parse_schema(text: &str) -> Result<Schema, err::ParseErrors> {
    parse_collect_errors(&*SCHEMA_PARSER, grammar::SchemaParser::parse, text)
}