cedar_policy_validator/cedar_schema/
parser.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Parser for schemas in the Cedar syntax
18
19use std::sync::Arc;
20
21use lalrpop_util::lalrpop_mod;
22use miette::Diagnostic;
23use thiserror::Error;
24
25use super::{
26    ast::Schema,
27    err::{self, ParseError, ParseErrors, SchemaWarning, ToJsonSchemaErrors},
28    to_json_schema::cedar_schema_to_json_schema,
29};
30use crate::json_schema;
31use cedar_policy_core::extensions::Extensions;
32
33lalrpop_mod!(
34    #[allow(warnings, unused, missing_docs, missing_debug_implementations)]
35    //PANIC SAFETY: lalrpop uses unwraps, and we are trusting lalrpop to generate correct code
36    #[allow(clippy::unwrap_used)]
37    //PANIC SAFETY: lalrpop uses slicing, and we are trusting lalrpop to generate correct code
38    #[allow(clippy::indexing_slicing)]
39    //PANIC SAFETY: lalrpop uses unreachable, and we are trusting lalrpop to generate correct code
40    #[allow(clippy::unreachable)]
41    //PANIC SAFETY: lalrpop uses panic, and we are trusting lalrpop to generate correct code
42    #[allow(clippy::panic)]
43    pub grammar,
44    "/src/cedar_schema/grammar.rs"
45);
46
47/// This helper function calls a generated parser, collects errors that could be
48/// generated multiple ways, and returns a single Result where the error type is
49/// [`err::ParseErrors`].
50fn parse_collect_errors<'a, P, T>(
51    parser: &P,
52    parse: impl FnOnce(
53        &P,
54        &mut Vec<err::RawErrorRecovery<'a>>,
55        &Arc<str>,
56        &'a str,
57    ) -> Result<T, err::RawParseError<'a>>,
58    text: &'a str,
59) -> Result<T, err::ParseErrors> {
60    let mut errs = Vec::new();
61    let result = parse(parser, &mut errs, &Arc::from(text), text);
62
63    let errors = errs
64        .into_iter()
65        .map(|rc| ParseError::from_raw_error_recovery(rc, Arc::from(text)))
66        .collect::<Vec<ParseError>>();
67    let parsed = match result {
68        Ok(parsed) => parsed,
69        Err(e) => {
70            return Err(ParseErrors::new(
71                ParseError::from_raw_parse_error(e, Arc::from(text)),
72                errors,
73            ));
74        }
75    };
76    match ParseErrors::from_iter(errors) {
77        Some(errors) => Err(errors),
78        // No Errors: good to return parse
79        None => Ok(parsed),
80    }
81}
82
83// Thread-safe "global" parsers, initialized at first use
84lazy_static::lazy_static! {
85    static ref SCHEMA_PARSER: grammar::SchemaParser = grammar::SchemaParser::new();
86    static ref TYPE_PARSER: grammar::TypeParser = grammar::TypeParser::new();
87}
88
89/// Parse errors for parsing a schema in the Cedar syntax
90//
91// This is NOT a publicly exported error type.
92#[derive(Debug, Diagnostic, Error)]
93#[non_exhaustive]
94pub enum CedarSchemaParseErrors {
95    /// Parse error for the Cedar syntax
96    #[error(transparent)]
97    #[diagnostic(transparent)]
98    SyntaxError(#[from] err::ParseErrors),
99    /// Error converting the parsed representation into the internal JSON representation
100    #[error(transparent)]
101    #[diagnostic(transparent)]
102    JsonError(#[from] ToJsonSchemaErrors),
103}
104
105/// Parse a schema fragment, in the Cedar syntax, into a [`json_schema::Fragment`],
106/// possibly generating warnings
107pub fn parse_cedar_schema_fragment<'a>(
108    src: &str,
109    extensions: &Extensions<'a>,
110) -> Result<
111    (
112        json_schema::Fragment<crate::RawName>,
113        impl Iterator<Item = SchemaWarning> + 'a,
114    ),
115    CedarSchemaParseErrors,
116> {
117    let ast: Schema = parse_collect_errors(&*SCHEMA_PARSER, grammar::SchemaParser::parse, src)?;
118    let tuple = cedar_schema_to_json_schema(ast, extensions)?;
119    Ok(tuple)
120}
121
122/// Parse schema from text
123pub fn parse_schema(text: &str) -> Result<Schema, err::ParseErrors> {
124    parse_collect_errors(&*SCHEMA_PARSER, grammar::SchemaParser::parse, text)
125}