scuffle_cedar_policy_codegen/
lib.rs

1//! Cedar is a policy language used to express permisisons using a relationship model.
2//!
3//! This crate extends the [`cedar-policy`](https://docs.rs/cedar-policy) crate by adding code generator for cedar schemas.
4//!
5//!
6//! You can then use this in combo with cedar to have type-safe schema evaluation.
7//!
8//! ## Example
9//!
10//! ```rust
11//! # fn inner() {
12//! let schema = std::fs::read_to_string("./static.cedarschema").expect("failed to read");
13//!
14//! let config = scuffle_cedar_policy_codegen::Config::default()
15//!     .generate_from_schema(&schema)
16//!     .expect("valid schema");
17//!
18//! let output = std::path::PathBuf::from(std::env::var_os("OUT_DIR").expect("no such env")).join("generated.rs");
19//! std::fs::write(output, config.to_string()).expect("failed to write output");
20//! # }
21//! ```
22//!
23//! ## License
24//!
25//! This project is licensed under the MIT or Apache-2.0 license.
26//! You can choose between one of them if you use this work.
27//!
28//! `SPDX-License-Identifier: MIT OR Apache-2.0`
29#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
30#![cfg_attr(docsrs, feature(doc_auto_cfg))]
31#![deny(missing_docs)]
32#![deny(unreachable_pub)]
33#![deny(clippy::mod_module_files)]
34#![deny(clippy::undocumented_unsafe_blocks)]
35#![deny(clippy::multiple_unsafe_ops_per_block)]
36
37mod cedar_action;
38mod cedar_namespace;
39mod codegen;
40mod error;
41mod module;
42mod types;
43mod utils;
44
45use cedar_policy_core::extensions::Extensions;
46use cedar_policy_core::validator::RawName;
47use cedar_policy_core::validator::cedar_schema::SchemaWarning;
48use cedar_policy_core::validator::json_schema::Fragment;
49pub use error::{CodegenError, CodegenResult};
50
51use crate::utils::process_fragment;
52
53/// A config for the code generator.
54pub struct Config {
55    pub(crate) crate_path: syn::Path,
56}
57
58impl Default for Config {
59    fn default() -> Self {
60        Self {
61            crate_path: syn::parse_quote!(::scuffle_cedar_policy),
62        }
63    }
64}
65
66/// The output from the code generator.
67pub struct CodegenOutput {
68    file: syn::File,
69    warnings: Vec<SchemaWarning>,
70}
71
72impl CodegenOutput {
73    /// Get warnings produced from the parser
74    pub fn warnings(&self) -> &[SchemaWarning] {
75        &self.warnings
76    }
77}
78
79impl quote::ToTokens for CodegenOutput {
80    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
81        self.file.to_tokens(tokens);
82    }
83}
84
85impl std::fmt::Display for CodegenOutput {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        prettyplease::unparse(&self.file).fmt(f)
88    }
89}
90
91impl Config {
92    /// Create a new config
93    pub fn new() -> Self {
94        Self::default()
95    }
96
97    /// Provide a different path to find the `scuffle_cedar_policy` crate.
98    pub fn crate_path(&mut self, path: syn::Path) -> &mut Self {
99        self.crate_path = path;
100        self
101    }
102
103    /// Generate code from a given schema string.
104    pub fn generate_from_schema(&self, schema: &str) -> CodegenResult<CodegenOutput> {
105        let (fragment, warnings) = Fragment::from_cedarschema_str(schema, Extensions::all_available())?;
106        self.generate_from_fragment(&fragment).map(|mut out| {
107            out.warnings.extend(warnings);
108            out
109        })
110    }
111
112    /// Generate code from a given json string.
113    pub fn generate_from_json(&self, schema_json: &str) -> CodegenResult<CodegenOutput> {
114        let fragment = Fragment::from_json_str(schema_json)?;
115        self.generate_from_fragment(&fragment)
116    }
117
118    /// Generate code from a fragment.
119    pub fn generate_from_fragment(&self, fragment: &Fragment<RawName>) -> CodegenResult<CodegenOutput> {
120        let file = process_fragment(fragment, self)?;
121
122        Ok(CodegenOutput {
123            file,
124            warnings: Vec::new(),
125        })
126    }
127}