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