1use super::id::Id;
18use itertools::Itertools;
19use miette::Diagnostic;
20use ref_cast::RefCast;
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22use smol_str::ToSmolStr;
23use std::fmt::Display;
24use std::str::FromStr;
25use std::sync::Arc;
26
27use crate::parser::err::{ParseError, ParseErrors, ToASTError};
28use crate::parser::Loc;
29use crate::FromNormalizedStr;
30
31use super::{PrincipalOrResource, UnreservedId};
32use thiserror::Error;
33
34#[derive(Debug, Clone)]
41pub struct InternalName {
42 pub(crate) id: Id,
44 pub(crate) path: Arc<Vec<Id>>,
46 pub(crate) loc: Option<Loc>,
48}
49
50impl PartialEq for InternalName {
52 fn eq(&self, other: &Self) -> bool {
53 self.id == other.id && self.path == other.path
54 }
55}
56impl Eq for InternalName {}
57
58impl std::hash::Hash for InternalName {
59 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
60 self.id.hash(state);
63 self.path.hash(state);
64 }
65}
66
67impl PartialOrd for InternalName {
68 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
69 Some(self.cmp(other))
70 }
71}
72impl Ord for InternalName {
73 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
74 self.id.cmp(&other.id).then(self.path.cmp(&other.path))
75 }
76}
77
78impl From<Id> for InternalName {
80 fn from(value: Id) -> Self {
81 Self::unqualified_name(value)
82 }
83}
84
85impl TryFrom<InternalName> for Id {
89 type Error = ();
90 fn try_from(value: InternalName) -> Result<Self, Self::Error> {
91 if value.is_unqualified() {
92 Ok(value.id)
93 } else {
94 Err(())
95 }
96 }
97}
98
99impl InternalName {
100 pub fn new(basename: Id, path: impl IntoIterator<Item = Id>, loc: Option<Loc>) -> Self {
102 Self {
103 id: basename,
104 path: Arc::new(path.into_iter().collect()),
105 loc,
106 }
107 }
108
109 pub fn unqualified_name(id: Id) -> Self {
111 Self {
112 id,
113 path: Arc::new(vec![]),
114 loc: None,
115 }
116 }
117
118 pub fn __cedar() -> Self {
120 Self::unqualified_name(Id::new_unchecked("__cedar"))
122 }
123
124 pub fn parse_unqualified_name(s: &str) -> Result<Self, ParseErrors> {
127 Ok(Self {
128 id: s.parse()?,
129 path: Arc::new(vec![]),
130 loc: None,
131 })
132 }
133
134 pub fn type_in_namespace(
137 basename: Id,
138 namespace: InternalName,
139 loc: Option<Loc>,
140 ) -> InternalName {
141 let mut path = Arc::unwrap_or_clone(namespace.path);
142 path.push(namespace.id);
143 InternalName::new(basename, path, loc)
144 }
145
146 pub fn loc(&self) -> Option<&Loc> {
148 self.loc.as_ref()
149 }
150
151 pub fn basename(&self) -> &Id {
153 &self.id
154 }
155
156 pub fn namespace_components(&self) -> impl Iterator<Item = &Id> {
158 self.path.iter()
159 }
160
161 pub fn namespace(&self) -> String {
168 self.path.iter().join("::")
169 }
170
171 pub fn qualify_with(&self, namespace: Option<&InternalName>) -> InternalName {
190 if self.is_unqualified() {
191 match namespace {
192 Some(namespace) => Self::new(
193 self.basename().clone(),
194 namespace
195 .namespace_components()
196 .chain(std::iter::once(namespace.basename()))
197 .cloned(),
198 self.loc.clone(),
199 ),
200 None => self.clone(),
201 }
202 } else {
203 self.clone()
204 }
205 }
206
207 pub fn qualify_with_name(&self, namespace: Option<&Name>) -> InternalName {
209 let ns = namespace.map(AsRef::as_ref);
210 self.qualify_with(ns)
211 }
212
213 pub fn is_unqualified(&self) -> bool {
215 self.path.is_empty()
216 }
217
218 pub fn is_reserved(&self) -> bool {
221 self.path
222 .iter()
223 .chain(std::iter::once(&self.id))
224 .any(|id| id.is_reserved())
225 }
226}
227
228impl std::fmt::Display for InternalName {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 for elem in self.path.as_ref() {
231 write!(f, "{}::", elem)?;
232 }
233 write!(f, "{}", self.id)?;
234 Ok(())
235 }
236}
237
238impl Serialize for InternalName {
241 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
242 where
243 S: Serializer,
244 {
245 self.to_smolstr().serialize(serializer)
246 }
247}
248
249impl std::str::FromStr for InternalName {
251 type Err = ParseErrors;
252
253 fn from_str(s: &str) -> Result<Self, Self::Err> {
254 crate::parser::parse_internal_name(s)
255 }
256}
257
258impl FromNormalizedStr for InternalName {
259 fn describe_self() -> &'static str {
260 "internal name"
261 }
262}
263
264#[cfg(feature = "arbitrary")]
265impl<'a> arbitrary::Arbitrary<'a> for InternalName {
266 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
267 let path_size = u.int_in_range(0..=8)?;
268 Ok(Self {
269 id: u.arbitrary()?,
270 path: Arc::new(
271 (0..path_size)
272 .map(|_| u.arbitrary())
273 .collect::<Result<Vec<Id>, _>>()?,
274 ),
275 loc: None,
276 })
277 }
278}
279
280struct NameVisitor;
281
282impl<'de> serde::de::Visitor<'de> for NameVisitor {
283 type Value = InternalName;
284
285 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 formatter.write_str("a name consisting of an optional namespace and id")
287 }
288
289 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
290 where
291 E: serde::de::Error,
292 {
293 InternalName::from_normalized_str(value)
294 .map_err(|err| serde::de::Error::custom(format!("invalid name `{value}`: {err}")))
295 }
296}
297
298impl<'de> Deserialize<'de> for InternalName {
301 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
302 where
303 D: Deserializer<'de>,
304 {
305 deserializer.deserialize_str(NameVisitor)
306 }
307}
308
309#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
314#[serde(transparent)]
315pub struct SlotId(pub(crate) ValidSlotId);
316
317impl SlotId {
318 pub fn principal() -> Self {
320 Self(ValidSlotId::Principal)
321 }
322
323 pub fn resource() -> Self {
325 Self(ValidSlotId::Resource)
326 }
327
328 pub fn is_principal(&self) -> bool {
330 matches!(self, Self(ValidSlotId::Principal))
331 }
332
333 pub fn is_resource(&self) -> bool {
335 matches!(self, Self(ValidSlotId::Resource))
336 }
337}
338
339impl From<PrincipalOrResource> for SlotId {
340 fn from(v: PrincipalOrResource) -> Self {
341 match v {
342 PrincipalOrResource::Principal => SlotId::principal(),
343 PrincipalOrResource::Resource => SlotId::resource(),
344 }
345 }
346}
347
348impl std::fmt::Display for SlotId {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 write!(f, "{}", self.0)
351 }
352}
353
354#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
356pub(crate) enum ValidSlotId {
357 #[serde(rename = "?principal")]
358 Principal,
359 #[serde(rename = "?resource")]
360 Resource,
361}
362
363impl std::fmt::Display for ValidSlotId {
364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365 let s = match self {
366 ValidSlotId::Principal => "principal",
367 ValidSlotId::Resource => "resource",
368 };
369 write!(f, "?{s}")
370 }
371}
372
373#[derive(Debug, Clone)]
375pub struct Slot {
376 pub id: SlotId,
378 pub loc: Option<Loc>,
380}
381
382impl PartialEq for Slot {
385 fn eq(&self, other: &Self) -> bool {
386 self.id == other.id
387 }
388}
389impl Eq for Slot {}
390
391impl std::hash::Hash for Slot {
392 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
393 self.id.hash(state);
396 }
397}
398
399#[cfg(test)]
400mod vars_test {
401 use super::*;
402 #[test]
404 fn vars_correct() {
405 SlotId::principal();
406 SlotId::resource();
407 }
408
409 #[test]
410 fn display() {
411 assert_eq!(format!("{}", SlotId::principal()), "?principal")
412 }
413}
414
415#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, RefCast)]
421#[repr(transparent)]
422#[serde(transparent)]
423pub struct Name(pub(crate) InternalName);
424
425impl From<UnreservedId> for Name {
426 fn from(value: UnreservedId) -> Self {
427 Self::unqualified_name(value)
428 }
429}
430
431impl TryFrom<Name> for UnreservedId {
432 type Error = ();
433 fn try_from(value: Name) -> Result<Self, Self::Error> {
434 if value.0.is_unqualified() {
435 Ok(value.basename())
436 } else {
437 Err(())
438 }
439 }
440}
441
442impl Display for Name {
443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444 self.0.fmt(f)
445 }
446}
447
448impl FromStr for Name {
449 type Err = ParseErrors;
450 fn from_str(s: &str) -> Result<Self, Self::Err> {
451 let n: InternalName = s.parse()?;
452 n.try_into().map_err(ParseErrors::singleton)
453 }
454}
455
456impl FromNormalizedStr for Name {
457 fn describe_self() -> &'static str {
458 "Name"
459 }
460}
461
462impl<'de> Deserialize<'de> for Name {
465 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
466 where
467 D: Deserializer<'de>,
468 {
469 deserializer
470 .deserialize_str(NameVisitor)
471 .and_then(|n| n.try_into().map_err(serde::de::Error::custom))
472 }
473}
474
475impl Name {
476 pub fn parse_unqualified_name(s: &str) -> Result<Self, ParseErrors> {
479 InternalName::parse_unqualified_name(s)
480 .and_then(|n| n.try_into().map_err(ParseErrors::singleton))
481 }
482
483 pub fn unqualified_name(id: UnreservedId) -> Self {
485 Self(InternalName::unqualified_name(id.0))
487 }
488
489 pub fn basename_as_ref(&self) -> &Id {
492 self.0.basename()
493 }
494
495 pub fn basename(&self) -> UnreservedId {
498 #![allow(clippy::unwrap_used)]
500 self.0.basename().clone().try_into().unwrap()
501 }
502
503 pub fn is_unqualified(&self) -> bool {
505 self.0.is_unqualified()
506 }
507
508 pub fn qualify_with(&self, namespace: Option<&InternalName>) -> InternalName {
512 self.0.qualify_with(namespace)
513 }
514
515 pub fn qualify_with_name(&self, namespace: Option<&Self>) -> Self {
520 Self(self.as_ref().qualify_with(namespace.map(|n| n.as_ref())))
523 }
524
525 pub fn loc(&self) -> Option<&Loc> {
527 self.0.loc()
528 }
529}
530
531#[derive(Debug, Clone, PartialEq, Eq, Error, Diagnostic, Hash)]
533#[error("The name `{0}` contains `__cedar`, which is reserved")]
534pub struct ReservedNameError(pub(crate) InternalName);
535
536impl ReservedNameError {
537 pub fn name(&self) -> &InternalName {
539 &self.0
540 }
541}
542
543impl From<ReservedNameError> for ParseError {
544 fn from(value: ReservedNameError) -> Self {
545 ParseError::ToAST(ToASTError::new(
546 value.clone().into(),
547 match &value.0.loc {
548 Some(loc) => loc.clone(),
549 None => {
550 let name_str = value.0.to_string();
551 Loc::new(0..(name_str.len()), name_str.into())
552 }
553 },
554 ))
555 }
556}
557
558impl TryFrom<InternalName> for Name {
559 type Error = ReservedNameError;
560 fn try_from(value: InternalName) -> Result<Self, Self::Error> {
561 if value.is_reserved() {
562 Err(ReservedNameError(value))
563 } else {
564 Ok(Self(value))
565 }
566 }
567}
568
569impl<'a> TryFrom<&'a InternalName> for &'a Name {
570 type Error = ReservedNameError;
571 fn try_from(value: &'a InternalName) -> Result<&'a Name, ReservedNameError> {
572 if value.is_reserved() {
573 Err(ReservedNameError(value.clone()))
574 } else {
575 Ok(<Name as RefCast>::ref_cast(value))
576 }
577 }
578}
579
580impl From<Name> for InternalName {
581 fn from(value: Name) -> Self {
582 value.0
583 }
584}
585
586impl AsRef<InternalName> for Name {
587 fn as_ref(&self) -> &InternalName {
588 &self.0
589 }
590}
591
592#[cfg(feature = "arbitrary")]
593impl<'a> arbitrary::Arbitrary<'a> for Name {
594 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
595 let path_size = u.int_in_range(0..=8)?;
599 let basename: UnreservedId = u.arbitrary()?;
600 let path: Vec<UnreservedId> = (0..path_size)
601 .map(|_| u.arbitrary())
602 .collect::<Result<Vec<_>, _>>()?;
603 let name = InternalName::new(basename.into(), path.into_iter().map(|id| id.into()), None);
604 #[allow(clippy::unwrap_used)]
606 Ok(name.try_into().unwrap())
607 }
608
609 fn size_hint(depth: usize) -> (usize, Option<usize>) {
610 <InternalName as arbitrary::Arbitrary>::size_hint(depth)
611 }
612}
613
614#[cfg(test)]
615mod test {
616 use super::*;
617
618 #[test]
619 fn normalized_name() {
620 InternalName::from_normalized_str("foo").expect("should be OK");
621 InternalName::from_normalized_str("foo::bar").expect("should be OK");
622 InternalName::from_normalized_str(r#"foo::"bar""#).expect_err("shouldn't be OK");
623 InternalName::from_normalized_str(" foo").expect_err("shouldn't be OK");
624 InternalName::from_normalized_str("foo ").expect_err("shouldn't be OK");
625 InternalName::from_normalized_str("foo\n").expect_err("shouldn't be OK");
626 InternalName::from_normalized_str("foo//comment").expect_err("shouldn't be OK");
627 }
628
629 #[test]
630 fn qualify_with() {
631 assert_eq!(
632 "foo::bar::baz",
633 InternalName::from_normalized_str("baz")
634 .unwrap()
635 .qualify_with(Some(&"foo::bar".parse().unwrap()))
636 .to_smolstr()
637 );
638 assert_eq!(
639 "C::D",
640 InternalName::from_normalized_str("C::D")
641 .unwrap()
642 .qualify_with(Some(&"A::B".parse().unwrap()))
643 .to_smolstr()
644 );
645 assert_eq!(
646 "A::B::C::D",
647 InternalName::from_normalized_str("D")
648 .unwrap()
649 .qualify_with(Some(&"A::B::C".parse().unwrap()))
650 .to_smolstr()
651 );
652 assert_eq!(
653 "B::C::D",
654 InternalName::from_normalized_str("B::C::D")
655 .unwrap()
656 .qualify_with(Some(&"A".parse().unwrap()))
657 .to_smolstr()
658 );
659 assert_eq!(
660 "A",
661 InternalName::from_normalized_str("A")
662 .unwrap()
663 .qualify_with(None)
664 .to_smolstr()
665 )
666 }
667
668 #[test]
669 fn test_reserved() {
670 for n in [
671 "__cedar",
672 "__cedar::A",
673 "__cedar::A::B",
674 "A::__cedar",
675 "A::__cedar::B",
676 ] {
677 assert!(InternalName::from_normalized_str(n).unwrap().is_reserved());
678 }
679
680 for n in ["__cedarr", "A::_cedar", "A::___cedar::B"] {
681 assert!(!InternalName::from_normalized_str(n).unwrap().is_reserved());
682 }
683 }
684}