cedar_policy_validator/human_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
17use std::sync::Arc;
18
19use lalrpop_util::lalrpop_mod;
20use miette::Diagnostic;
21use thiserror::Error;
22
23use super::{
24    ast::Schema,
25    err::{self, ParseError, ParseErrors, SchemaWarning, ToJsonSchemaErrors},
26    to_json_schema::{custom_schema_to_json_schema, custom_type_to_json_type},
27};
28
29lalrpop_mod!(
30    #[allow(warnings, unused)]
31    //PANIC SAFETY: lalrpop uses unwraps, and we are trusting lalrpop to generate correct code
32    #[allow(clippy::unwrap_used)]
33    //PANIC SAFETY: lalrpop uses slicing, and we are trusting lalrpop to generate correct code
34    #[allow(clippy::indexing_slicing)]
35    //PANIC SAFETY: lalrpop uses unreachable, and we are trusting lalrpop to generate correct code
36    #[allow(clippy::unreachable)]
37    //PANIC SAFETY: lalrpop uses panic, and we are trusting lalrpop to generate correct code
38    #[allow(clippy::panic)]
39    pub grammar,
40    "/src/human_schema/grammar.rs"
41);
42
43/// This helper function calls a generated parser, collects errors that could be
44/// generated multiple ways, and returns a single Result where the error type is
45/// [`err::ParseErrors`].
46fn parse_collect_errors<'a, P, T>(
47    parser: &P,
48    parse: impl FnOnce(
49        &P,
50        &mut Vec<err::RawErrorRecovery<'a>>,
51        &Arc<str>,
52        &'a str,
53    ) -> Result<T, err::RawParseError<'a>>,
54    text: &'a str,
55) -> Result<T, err::ParseErrors> {
56    let mut errs = Vec::new();
57    let result = parse(parser, &mut errs, &Arc::from(text), text);
58
59    let errors = errs
60        .into_iter()
61        .map(Into::into)
62        .collect::<Vec<ParseError>>();
63    let parsed = match result {
64        Ok(parsed) => parsed,
65        Err(e) => {
66            return Err(ParseErrors::new(e.into(), errors));
67        }
68    };
69    match ParseErrors::from_iter(errors) {
70        Some(errors) => Err(errors),
71        // No Errors: good to return parse
72        None => Ok(parsed),
73    }
74}
75
76// Thread-safe "global" parsers, initialized at first use
77lazy_static::lazy_static! {
78    static ref SCHEMA_PARSER: grammar::SchemaParser = grammar::SchemaParser::new();
79    static ref TYPE_PARSER: grammar::TypeParser = grammar::TypeParser::new();
80}
81
82#[derive(Debug, Diagnostic, Error)]
83pub enum HumanSyntaxParseErrors {
84    #[error(transparent)]
85    #[diagnostic(transparent)]
86    NaturalSyntaxError(#[from] err::ParseErrors),
87    #[error(transparent)]
88    #[diagnostic(transparent)]
89    JsonError(#[from] ToJsonSchemaErrors),
90}
91
92pub fn parse_type(src: &str) -> Result<crate::SchemaType, HumanSyntaxParseErrors> {
93    let ty = parse_collect_errors(&*TYPE_PARSER, grammar::TypeParser::parse, src)?;
94    Ok(custom_type_to_json_type(ty)?)
95}
96
97pub fn parse_natural_schema_fragment(
98    src: &str,
99) -> Result<(crate::SchemaFragment, impl Iterator<Item = SchemaWarning>), HumanSyntaxParseErrors> {
100    let ast: Schema = parse_collect_errors(&*SCHEMA_PARSER, grammar::SchemaParser::parse, src)?;
101    let tuple = custom_schema_to_json_schema(ast)?;
102    Ok(tuple)
103}
104
105/// Parse schema from text
106pub fn parse_schema(text: &str) -> Result<Schema, err::ParseErrors> {
107    parse_collect_errors(&*SCHEMA_PARSER, grammar::SchemaParser::parse, text)
108}