cedar_policy_core/ast/
name.rs

1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
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 itertools::Itertools;
20use serde::{Deserialize, Serialize};
21use smol_str::SmolStr;
22
23use super::PrincipalOrResource;
24use crate::{parser::err::ParseError, FromNormalizedStr};
25
26/// Arc::unwrap_or_clone() isn't stabilized as of this writing, but this is its implementation
27//
28// TODO: use `Arc::unwrap_or_clone()` once stable
29pub fn unwrap_or_clone<T: Clone>(arc: Arc<T>) -> T {
30    Arc::try_unwrap(arc).unwrap_or_else(|arc| (*arc).clone())
31}
32
33/// This is the `Name` type used to name types, functions, etc.
34/// The name can include namespaces.
35/// Clone is O(1).
36#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
37#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
38pub struct Name {
39    /// Basename
40    pub(crate) id: Id,
41    /// Namespaces
42    pub(crate) path: Arc<Vec<Id>>,
43}
44
45impl Name {
46    /// A full constructor for `Name`
47    pub fn new(basename: Id, path: impl IntoIterator<Item = Id>) -> Self {
48        Self {
49            id: basename,
50            path: Arc::new(path.into_iter().collect()),
51        }
52    }
53
54    /// Create a `Name` with no path (no namespaces).
55    pub fn unqualified_name(id: Id) -> Self {
56        Self {
57            id,
58            path: Arc::new(vec![]),
59        }
60    }
61
62    /// Create a `Name` with no path (no namespaces).
63    /// Returns an error if `s` is not a valid identifier.
64    pub fn parse_unqualified_name(s: &str) -> Result<Self, Vec<ParseError>> {
65        Ok(Self {
66            id: s.parse()?,
67            path: Arc::new(vec![]),
68        })
69    }
70
71    /// Given a type basename and a namespace (as a `Name` itself),
72    /// return a `Name` representing the type's fully qualified name
73    pub fn type_in_namespace(basename: Id, namespace: Name) -> Name {
74        let mut path = unwrap_or_clone(namespace.path);
75        path.push(namespace.id);
76        Name::new(basename, path)
77    }
78
79    /// Get the basename of the `Name` (ie, with namespaces stripped).
80    pub fn basename(&self) -> &Id {
81        &self.id
82    }
83
84    /// Get the namespace of the `Name`, as components
85    pub fn namespace_components(&self) -> impl Iterator<Item = &Id> {
86        self.path.iter()
87    }
88
89    /// Get the full namespace of the `Name`, as a single string.
90    ///
91    /// Examples:
92    /// - `foo::bar` --> the namespace is `"foo"`
93    /// - `bar` --> the namespace is `""`
94    /// - `foo::bar::baz` --> the namespace is `"foo::bar"`
95    pub fn namespace(&self) -> String {
96        self.path.iter().join("::")
97    }
98}
99
100impl std::fmt::Display for Name {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        for elem in self.path.as_ref() {
103            write!(f, "{}::", elem)?;
104        }
105        write!(f, "{}", self.id)?;
106        Ok(())
107    }
108}
109
110// allow `.parse()` on a string to make a `Name`
111impl std::str::FromStr for Name {
112    type Err = Vec<ParseError>;
113
114    fn from_str(s: &str) -> Result<Self, Vec<ParseError>> {
115        crate::parser::parse_name(s)
116    }
117}
118
119impl FromNormalizedStr for Name {
120    fn describe_self() -> &'static str {
121        "Name"
122    }
123}
124
125/// Identifier for a slot
126/// Clone is O(1).
127// This simply wraps a separate enum -- currently `ValidSlotId` -- in case we
128// want to generalize later
129#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
130#[serde(transparent)]
131pub struct SlotId(ValidSlotId);
132
133impl SlotId {
134    /// Get the slot for `principal`
135    pub fn principal() -> Self {
136        Self(ValidSlotId::Principal)
137    }
138
139    /// Get the slot for `resource`
140    pub fn resource() -> Self {
141        Self(ValidSlotId::Resource)
142    }
143
144    /// Check if a slot represents a principal
145    pub fn is_principal(&self) -> bool {
146        matches!(self, Self(ValidSlotId::Principal))
147    }
148
149    /// Check if a slot represents a resource
150    pub fn is_resource(&self) -> bool {
151        matches!(self, Self(ValidSlotId::Resource))
152    }
153}
154
155impl From<PrincipalOrResource> for SlotId {
156    fn from(v: PrincipalOrResource) -> Self {
157        match v {
158            PrincipalOrResource::Principal => SlotId::principal(),
159            PrincipalOrResource::Resource => SlotId::resource(),
160        }
161    }
162}
163
164impl std::fmt::Display for SlotId {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        write!(f, "{}", self.0)
167    }
168}
169
170/// Two possible variants for Slots
171#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
172enum ValidSlotId {
173    #[serde(rename = "?principal")]
174    Principal,
175    #[serde(rename = "?resource")]
176    Resource,
177}
178
179impl std::fmt::Display for ValidSlotId {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        let s = match self {
182            ValidSlotId::Principal => "principal",
183            ValidSlotId::Resource => "resource",
184        };
185        write!(f, "?{s}")
186    }
187}
188
189#[cfg(test)]
190mod vars_test {
191    use super::*;
192    // Make sure the vars always parse correctly
193    #[test]
194    fn vars_correct() {
195        SlotId::principal();
196        SlotId::resource();
197    }
198
199    #[test]
200    fn display() {
201        assert_eq!(format!("{}", SlotId::principal()), "?principal")
202    }
203}
204
205/// Identifiers. Anything in `Id` should be a valid identifier (and not contain,
206/// for instance, spaces or characters like '+').
207//
208// For now, internally, `Id`s are just owned `SmolString`s.
209#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
210pub struct Id(SmolStr);
211
212impl Id {
213    /// Create a new `Id` from a `String`, where it is the caller's
214    /// responsibility to ensure that the string is indeed a valid identifier.
215    ///
216    /// When possible, callers should not use this, and instead use `s.parse()`,
217    /// which checks that `s` is a valid identifier, and returns a parse error
218    /// if not.
219    ///
220    /// This method was created for the `From<cst::Ident> for Id` impl to use.
221    /// Since `parser::parse_ident()` implicitly uses that `From` impl itself,
222    /// if we tried to make that `From` impl go through `.parse()` like everyone
223    /// else, we'd get infinite recursion.  And, we assert that `cst::Ident` is
224    /// always already checked to contain a valid identifier, otherwise it would
225    /// never have been created.
226    pub(crate) fn new_unchecked(s: impl Into<SmolStr>) -> Id {
227        Id(s.into())
228    }
229
230    /// Get the underlying string
231    pub fn to_smolstr(self) -> SmolStr {
232        self.0
233    }
234}
235
236impl AsRef<str> for Id {
237    fn as_ref(&self) -> &str {
238        &self.0
239    }
240}
241
242impl std::fmt::Display for Id {
243    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244        write!(f, "{}", &self.0)
245    }
246}
247
248// allow `.parse()` on a string to make an `Id`
249impl std::str::FromStr for Id {
250    type Err = Vec<ParseError>;
251
252    fn from_str(s: &str) -> Result<Self, Vec<ParseError>> {
253        crate::parser::parse_ident(s)
254    }
255}
256
257impl FromNormalizedStr for Id {
258    fn describe_self() -> &'static str {
259        "Id"
260    }
261}
262
263#[cfg(feature = "arbitrary")]
264impl<'a> arbitrary::Arbitrary<'a> for Id {
265    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
266        // identifier syntax:
267        // IDENT     := ['_''a'-'z''A'-'Z']['_''a'-'z''A'-'Z''0'-'9']* - RESERVED
268        // BOOL      := 'true' | 'false'
269        // RESERVED  := BOOL | 'if' | 'then' | 'else' | 'in' | 'like' | 'has'
270
271        let construct_list = |s: &str| s.chars().collect::<Vec<char>>();
272        let list_concat = |s1: &[char], s2: &[char]| [s1, s2].concat();
273        // the set of the first character of an identifier
274        let head_letters = construct_list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_");
275        // the set of the remaining characters of an identifier
276        let tail_letters = list_concat(&construct_list("0123456789"), &head_letters);
277        // identifier character count minus 1
278        let remaining_length = u.int_in_range(0..=16)?;
279        let mut cs = vec![*u.choose(&head_letters)?];
280        cs.extend(
281            (0..remaining_length)
282                .map(|_| u.choose(&tail_letters))
283                .collect::<Result<Vec<&char>, _>>()?,
284        );
285        let mut s: String = cs.into_iter().collect();
286        // Should the parsing fails, the string should be reserved word.
287        // Append a `_` to create a valid Id.
288        if crate::parser::parse_ident(&s).is_err() {
289            s.push('_');
290        }
291        Ok(Self::new_unchecked(s))
292    }
293
294    fn size_hint(depth: usize) -> (usize, Option<usize>) {
295        arbitrary::size_hint::and_all(&[
296            // for arbitrary length
297            <usize as arbitrary::Arbitrary>::size_hint(depth),
298            // for arbitrary choices
299            // we use the size hint of a vector of `u8` to get an underestimate of bytes required by the sequence of choices.
300            <Vec<u8> as arbitrary::Arbitrary>::size_hint(depth),
301        ])
302    }
303}
304
305#[cfg(test)]
306mod test {
307
308    use super::*;
309
310    #[test]
311    fn normalized_id() {
312        Id::from_normalized_str("foo").expect("should be OK");
313        Id::from_normalized_str("foo::bar").expect_err("shouldn't be OK");
314        Id::from_normalized_str(r#"foo::"bar""#).expect_err("shouldn't be OK");
315        Id::from_normalized_str(" foo").expect_err("shouldn't be OK");
316        Id::from_normalized_str("foo ").expect_err("shouldn't be OK");
317        Id::from_normalized_str("foo\n").expect_err("shouldn't be OK");
318        Id::from_normalized_str("foo//comment").expect_err("shouldn't be OK");
319    }
320
321    #[test]
322    fn normalized_name() {
323        Name::from_normalized_str("foo").expect("should be OK");
324        Name::from_normalized_str("foo::bar").expect("should be OK");
325        Name::from_normalized_str(r#"foo::"bar""#).expect_err("shouldn't be OK");
326        Name::from_normalized_str(" foo").expect_err("shouldn't be OK");
327        Name::from_normalized_str("foo ").expect_err("shouldn't be OK");
328        Name::from_normalized_str("foo\n").expect_err("shouldn't be OK");
329        Name::from_normalized_str("foo//comment").expect_err("shouldn't be OK");
330    }
331}