Skip to main content

cedar_policy_cli/command/
check_parse.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::{path::PathBuf, str::FromStr};
18
19use cedar_policy::Expression;
20use clap::Args;
21use miette::Report;
22
23use crate::{load_entities, CedarExitCode, OptionalPoliciesArgs, OptionalSchemaArgs, PoliciesArgs};
24
25#[derive(Args, Debug)]
26pub struct CheckParseArgs {
27    /// Policies args (incorporated by reference)
28    #[command(flatten)]
29    pub policies: OptionalPoliciesArgs,
30    /// Expression to parse
31    #[arg(long)]
32    pub expression: Option<String>,
33    /// Schema args (incorporated by reference)
34    #[command(flatten)]
35    pub schema: OptionalSchemaArgs,
36    /// File containing JSON representation of a Cedar entity hierarchy
37    #[arg(long = "entities", value_name = "FILE")]
38    pub entities_file: Option<PathBuf>,
39}
40
41pub fn check_parse(args: &CheckParseArgs) -> CedarExitCode {
42    // for backwards compatibility: if no policies/schema/entities/expression
43    // are provided, read policies from stdin and check that they parse
44    if args.policies.policies_file.is_none()
45        && args.schema.schema_file.is_none()
46        && args.entities_file.is_none()
47        && args.expression.is_none()
48    {
49        let pargs = PoliciesArgs {
50            policies_file: None, // read from stdin
51            policy_format: args.policies.policy_format,
52            template_linked_file: args.policies.template_linked_file.clone(),
53        };
54        match pargs.get_policy_set() {
55            Ok(_) => return CedarExitCode::Success,
56            Err(e) => {
57                println!("{e:?}");
58                return CedarExitCode::Failure;
59            }
60        }
61    }
62
63    #[expect(
64        clippy::useless_let_if_seq,
65        reason = "exit_code is mutated by later expressions"
66    )]
67    let mut exit_code = CedarExitCode::Success;
68    if let Err(e) = args.policies.get_policy_set() {
69        println!("{e:?}");
70        exit_code = CedarExitCode::Failure;
71    }
72    if let Some(e) = args
73        .expression
74        .as_ref()
75        .and_then(|expr| Expression::from_str(expr).err())
76    {
77        println!("{:?}", Report::new(e));
78        exit_code = CedarExitCode::Failure;
79    }
80    let schema = match args.schema.get_schema() {
81        Ok(schema) => schema,
82        Err(e) => {
83            println!("{e:?}");
84            exit_code = CedarExitCode::Failure;
85            None
86        }
87    };
88    if let Some(e) = args
89        .entities_file
90        .as_ref()
91        .and_then(|e| load_entities(e, schema.as_ref()).err())
92    {
93        println!("{e:?}");
94        exit_code = CedarExitCode::Failure;
95    }
96    exit_code
97}