1use super::{id::Id, PrincipalOrResource, UnreservedId};
18use educe::Educe;
19use itertools::Itertools;
20use miette::Diagnostic;
21use ref_cast::RefCast;
22use serde::{Deserialize, Deserializer, Serialize, Serializer};
23use smol_str::ToSmolStr;
24use std::fmt::Display;
25use std::str::FromStr;
26use std::sync::Arc;
27use thiserror::Error;
28
29use crate::parser::err::{ParseError, ParseErrors, ToASTError};
30use crate::parser::Loc;
31use crate::FromNormalizedStr;
32
33#[derive(Educe, Debug, Clone)]
40#[educe(PartialEq, Eq, Hash, PartialOrd, Ord)]
41pub struct InternalName {
42 pub(crate) id: Id,
44 pub(crate) path: Arc<Vec<Id>>,
46 #[educe(PartialEq(ignore))]
48 #[educe(Hash(ignore))]
49 #[educe(PartialOrd(ignore))]
50 pub(crate) loc: Option<Loc>,
51}
52
53impl From<Id> for InternalName {
55 fn from(value: Id) -> Self {
56 Self::unqualified_name(value)
57 }
58}
59
60impl TryFrom<InternalName> for Id {
64 type Error = ();
65 fn try_from(value: InternalName) -> Result<Self, Self::Error> {
66 if value.is_unqualified() {
67 Ok(value.id)
68 } else {
69 Err(())
70 }
71 }
72}
73
74impl InternalName {
75 pub fn new(basename: Id, path: impl IntoIterator<Item = Id>, loc: Option<Loc>) -> Self {
77 Self {
78 id: basename,
79 path: Arc::new(path.into_iter().collect()),
80 loc,
81 }
82 }
83
84 pub fn unqualified_name(id: Id) -> Self {
86 Self {
87 id,
88 path: Arc::new(vec![]),
89 loc: None,
90 }
91 }
92
93 pub fn __cedar() -> Self {
95 Self::unqualified_name(Id::new_unchecked("__cedar"))
97 }
98
99 pub fn parse_unqualified_name(s: &str) -> Result<Self, ParseErrors> {
102 Ok(Self {
103 id: s.parse()?,
104 path: Arc::new(vec![]),
105 loc: None,
106 })
107 }
108
109 pub fn type_in_namespace(
112 basename: Id,
113 namespace: InternalName,
114 loc: Option<Loc>,
115 ) -> InternalName {
116 let mut path = Arc::unwrap_or_clone(namespace.path);
117 path.push(namespace.id);
118 InternalName::new(basename, path, loc)
119 }
120
121 pub fn loc(&self) -> Option<&Loc> {
123 self.loc.as_ref()
124 }
125
126 pub fn basename(&self) -> &Id {
128 &self.id
129 }
130
131 pub fn namespace_components(&self) -> impl Iterator<Item = &Id> {
133 self.path.iter()
134 }
135
136 pub fn namespace(&self) -> String {
143 self.path.iter().join("::")
144 }
145
146 pub fn qualify_with(&self, namespace: Option<&InternalName>) -> InternalName {
165 if self.is_unqualified() {
166 match namespace {
167 Some(namespace) => Self::new(
168 self.basename().clone(),
169 namespace
170 .namespace_components()
171 .chain(std::iter::once(namespace.basename()))
172 .cloned(),
173 self.loc.clone(),
174 ),
175 None => self.clone(),
176 }
177 } else {
178 self.clone()
179 }
180 }
181
182 pub fn qualify_with_name(&self, namespace: Option<&Name>) -> InternalName {
184 let ns = namespace.map(AsRef::as_ref);
185 self.qualify_with(ns)
186 }
187
188 pub fn is_unqualified(&self) -> bool {
190 self.path.is_empty()
191 }
192
193 pub fn is_reserved(&self) -> bool {
196 self.path
197 .iter()
198 .chain(std::iter::once(&self.id))
199 .any(|id| id.is_reserved())
200 }
201}
202
203impl std::fmt::Display for InternalName {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 for elem in self.path.as_ref() {
206 write!(f, "{}::", elem)?;
207 }
208 write!(f, "{}", self.id)?;
209 Ok(())
210 }
211}
212
213impl Serialize for InternalName {
216 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
217 where
218 S: Serializer,
219 {
220 self.to_smolstr().serialize(serializer)
221 }
222}
223
224impl std::str::FromStr for InternalName {
226 type Err = ParseErrors;
227
228 fn from_str(s: &str) -> Result<Self, Self::Err> {
229 crate::parser::parse_internal_name(s)
230 }
231}
232
233impl FromNormalizedStr for InternalName {
234 fn describe_self() -> &'static str {
235 "internal name"
236 }
237}
238
239#[cfg(feature = "arbitrary")]
240impl<'a> arbitrary::Arbitrary<'a> for InternalName {
241 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
242 let path_size = u.int_in_range(0..=8)?;
243 Ok(Self {
244 id: u.arbitrary()?,
245 path: Arc::new(
246 (0..path_size)
247 .map(|_| u.arbitrary())
248 .collect::<Result<Vec<Id>, _>>()?,
249 ),
250 loc: None,
251 })
252 }
253}
254
255struct NameVisitor;
256
257impl serde::de::Visitor<'_> for NameVisitor {
258 type Value = InternalName;
259
260 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 formatter.write_str("a name consisting of an optional namespace and id")
262 }
263
264 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
265 where
266 E: serde::de::Error,
267 {
268 InternalName::from_normalized_str(value)
269 .map_err(|err| serde::de::Error::custom(format!("invalid name `{value}`: {err}")))
270 }
271}
272
273impl<'de> Deserialize<'de> for InternalName {
276 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
277 where
278 D: Deserializer<'de>,
279 {
280 deserializer.deserialize_str(NameVisitor)
281 }
282}
283
284#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
289#[serde(transparent)]
290pub struct SlotId(pub(crate) ValidSlotId);
291
292impl SlotId {
293 pub fn principal() -> Self {
295 Self(ValidSlotId::Principal)
296 }
297
298 pub fn resource() -> Self {
300 Self(ValidSlotId::Resource)
301 }
302
303 pub fn is_principal(&self) -> bool {
305 matches!(self, Self(ValidSlotId::Principal))
306 }
307
308 pub fn is_resource(&self) -> bool {
310 matches!(self, Self(ValidSlotId::Resource))
311 }
312}
313
314impl From<PrincipalOrResource> for SlotId {
315 fn from(v: PrincipalOrResource) -> Self {
316 match v {
317 PrincipalOrResource::Principal => SlotId::principal(),
318 PrincipalOrResource::Resource => SlotId::resource(),
319 }
320 }
321}
322
323impl std::fmt::Display for SlotId {
324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325 write!(f, "{}", self.0)
326 }
327}
328
329#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
331pub(crate) enum ValidSlotId {
332 #[serde(rename = "?principal")]
333 Principal,
334 #[serde(rename = "?resource")]
335 Resource,
336}
337
338impl std::fmt::Display for ValidSlotId {
339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340 let s = match self {
341 ValidSlotId::Principal => "principal",
342 ValidSlotId::Resource => "resource",
343 };
344 write!(f, "?{s}")
345 }
346}
347
348#[derive(Educe, Debug, Clone)]
350#[educe(PartialEq, Eq, Hash)]
351pub struct Slot {
352 pub id: SlotId,
354 #[educe(PartialEq(ignore))]
356 #[educe(Hash(ignore))]
357 pub loc: Option<Loc>,
358}
359
360#[cfg(test)]
361mod vars_test {
362 use super::*;
363 #[test]
365 fn vars_correct() {
366 SlotId::principal();
367 SlotId::resource();
368 }
369
370 #[test]
371 fn display() {
372 assert_eq!(format!("{}", SlotId::principal()), "?principal")
373 }
374}
375
376#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, RefCast)]
382#[repr(transparent)]
383#[serde(transparent)]
384pub struct Name(pub(crate) InternalName);
385
386impl From<UnreservedId> for Name {
387 fn from(value: UnreservedId) -> Self {
388 Self::unqualified_name(value)
389 }
390}
391
392impl TryFrom<Name> for UnreservedId {
393 type Error = ();
394 fn try_from(value: Name) -> Result<Self, Self::Error> {
395 if value.0.is_unqualified() {
396 Ok(value.basename())
397 } else {
398 Err(())
399 }
400 }
401}
402
403impl Display for Name {
404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405 self.0.fmt(f)
406 }
407}
408
409impl FromStr for Name {
410 type Err = ParseErrors;
411 fn from_str(s: &str) -> Result<Self, Self::Err> {
412 let n: InternalName = s.parse()?;
413 n.try_into().map_err(ParseErrors::singleton)
414 }
415}
416
417impl FromNormalizedStr for Name {
418 fn describe_self() -> &'static str {
419 "Name"
420 }
421}
422
423impl<'de> Deserialize<'de> for Name {
426 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
427 where
428 D: Deserializer<'de>,
429 {
430 deserializer
431 .deserialize_str(NameVisitor)
432 .and_then(|n| n.try_into().map_err(serde::de::Error::custom))
433 }
434}
435
436impl Name {
437 pub fn parse_unqualified_name(s: &str) -> Result<Self, ParseErrors> {
440 InternalName::parse_unqualified_name(s)
441 .and_then(|n| n.try_into().map_err(ParseErrors::singleton))
442 }
443
444 pub fn unqualified_name(id: UnreservedId) -> Self {
446 Self(InternalName::unqualified_name(id.0))
448 }
449
450 pub fn basename_as_ref(&self) -> &Id {
453 self.0.basename()
454 }
455
456 pub fn basename(&self) -> UnreservedId {
459 #![allow(clippy::unwrap_used)]
461 self.0.basename().clone().try_into().unwrap()
462 }
463
464 pub fn is_unqualified(&self) -> bool {
466 self.0.is_unqualified()
467 }
468
469 pub fn qualify_with(&self, namespace: Option<&InternalName>) -> InternalName {
473 self.0.qualify_with(namespace)
474 }
475
476 pub fn qualify_with_name(&self, namespace: Option<&Self>) -> Self {
481 Self(self.as_ref().qualify_with(namespace.map(|n| n.as_ref())))
484 }
485
486 pub fn loc(&self) -> Option<&Loc> {
488 self.0.loc()
489 }
490}
491
492#[derive(Debug, Clone, PartialEq, Eq, Error, Diagnostic, Hash)]
494#[error("The name `{0}` contains `__cedar`, which is reserved")]
495pub struct ReservedNameError(pub(crate) InternalName);
496
497impl ReservedNameError {
498 pub fn name(&self) -> &InternalName {
500 &self.0
501 }
502}
503
504impl From<ReservedNameError> for ParseError {
505 fn from(value: ReservedNameError) -> Self {
506 ParseError::ToAST(ToASTError::new(
507 value.clone().into(),
508 match &value.0.loc {
509 Some(loc) => loc.clone(),
510 None => {
511 let name_str = value.0.to_string();
512 Loc::new(0..(name_str.len()), name_str.into())
513 }
514 },
515 ))
516 }
517}
518
519impl TryFrom<InternalName> for Name {
520 type Error = ReservedNameError;
521 fn try_from(value: InternalName) -> Result<Self, Self::Error> {
522 if value.is_reserved() {
523 Err(ReservedNameError(value))
524 } else {
525 Ok(Self(value))
526 }
527 }
528}
529
530impl<'a> TryFrom<&'a InternalName> for &'a Name {
531 type Error = ReservedNameError;
532 fn try_from(value: &'a InternalName) -> Result<&'a Name, ReservedNameError> {
533 if value.is_reserved() {
534 Err(ReservedNameError(value.clone()))
535 } else {
536 Ok(<Name as RefCast>::ref_cast(value))
537 }
538 }
539}
540
541impl From<Name> for InternalName {
542 fn from(value: Name) -> Self {
543 value.0
544 }
545}
546
547impl AsRef<InternalName> for Name {
548 fn as_ref(&self) -> &InternalName {
549 &self.0
550 }
551}
552
553#[cfg(feature = "arbitrary")]
554impl<'a> arbitrary::Arbitrary<'a> for Name {
555 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
556 let path_size = u.int_in_range(0..=8)?;
560 let basename: UnreservedId = u.arbitrary()?;
561 let path: Vec<UnreservedId> = (0..path_size)
562 .map(|_| u.arbitrary())
563 .collect::<Result<Vec<_>, _>>()?;
564 let name = InternalName::new(basename.into(), path.into_iter().map(|id| id.into()), None);
565 #[allow(clippy::unwrap_used)]
567 Ok(name.try_into().unwrap())
568 }
569
570 fn size_hint(depth: usize) -> (usize, Option<usize>) {
571 <InternalName as arbitrary::Arbitrary>::size_hint(depth)
572 }
573}
574
575#[cfg(test)]
576mod test {
577 use super::*;
578
579 #[test]
580 fn normalized_name() {
581 InternalName::from_normalized_str("foo").expect("should be OK");
582 InternalName::from_normalized_str("foo::bar").expect("should be OK");
583 InternalName::from_normalized_str(r#"foo::"bar""#).expect_err("shouldn't be OK");
584 InternalName::from_normalized_str(" foo").expect_err("shouldn't be OK");
585 InternalName::from_normalized_str("foo ").expect_err("shouldn't be OK");
586 InternalName::from_normalized_str("foo\n").expect_err("shouldn't be OK");
587 InternalName::from_normalized_str("foo//comment").expect_err("shouldn't be OK");
588 }
589
590 #[test]
591 fn qualify_with() {
592 assert_eq!(
593 "foo::bar::baz",
594 InternalName::from_normalized_str("baz")
595 .unwrap()
596 .qualify_with(Some(&"foo::bar".parse().unwrap()))
597 .to_smolstr()
598 );
599 assert_eq!(
600 "C::D",
601 InternalName::from_normalized_str("C::D")
602 .unwrap()
603 .qualify_with(Some(&"A::B".parse().unwrap()))
604 .to_smolstr()
605 );
606 assert_eq!(
607 "A::B::C::D",
608 InternalName::from_normalized_str("D")
609 .unwrap()
610 .qualify_with(Some(&"A::B::C".parse().unwrap()))
611 .to_smolstr()
612 );
613 assert_eq!(
614 "B::C::D",
615 InternalName::from_normalized_str("B::C::D")
616 .unwrap()
617 .qualify_with(Some(&"A".parse().unwrap()))
618 .to_smolstr()
619 );
620 assert_eq!(
621 "A",
622 InternalName::from_normalized_str("A")
623 .unwrap()
624 .qualify_with(None)
625 .to_smolstr()
626 )
627 }
628
629 #[test]
630 fn test_reserved() {
631 for n in [
632 "__cedar",
633 "__cedar::A",
634 "__cedar::A::B",
635 "A::__cedar",
636 "A::__cedar::B",
637 ] {
638 assert!(InternalName::from_normalized_str(n).unwrap().is_reserved());
639 }
640
641 for n in ["__cedarr", "A::_cedar", "A::___cedar::B"] {
642 assert!(!InternalName::from_normalized_str(n).unwrap().is_reserved());
643 }
644 }
645}