use super::id::Id;
use itertools::Itertools;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use smol_str::ToSmolStr;
use std::sync::Arc;
use crate::parser::err::ParseErrors;
use crate::parser::Loc;
use crate::FromNormalizedStr;
use super::PrincipalOrResource;
#[derive(Debug, Clone)]
pub struct Name {
pub(crate) id: Id,
pub(crate) path: Arc<Vec<Id>>,
pub(crate) loc: Option<Loc>,
}
impl PartialEq for Name {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.path == other.path
}
}
impl Eq for Name {}
impl std::hash::Hash for Name {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.path.hash(state);
}
}
impl PartialOrd for Name {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Name {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id).then(self.path.cmp(&other.path))
}
}
impl From<Id> for Name {
fn from(value: Id) -> Self {
Self::unqualified_name(value)
}
}
impl TryFrom<Name> for Id {
type Error = ();
fn try_from(value: Name) -> Result<Self, Self::Error> {
if value.is_unqualified() {
Ok(value.id)
} else {
Err(())
}
}
}
impl Name {
pub fn new(basename: Id, path: impl IntoIterator<Item = Id>, loc: Option<Loc>) -> Self {
Self {
id: basename,
path: Arc::new(path.into_iter().collect()),
loc,
}
}
pub fn unqualified_name(id: Id) -> Self {
Self {
id,
path: Arc::new(vec![]),
loc: None,
}
}
pub fn parse_unqualified_name(s: &str) -> Result<Self, ParseErrors> {
Ok(Self {
id: s.parse()?,
path: Arc::new(vec![]),
loc: None,
})
}
pub fn type_in_namespace(basename: Id, namespace: Name, loc: Option<Loc>) -> Name {
let mut path = Arc::unwrap_or_clone(namespace.path);
path.push(namespace.id);
Name::new(basename, path, loc)
}
pub fn loc(&self) -> Option<&Loc> {
self.loc.as_ref()
}
pub fn basename(&self) -> &Id {
&self.id
}
pub fn namespace_components(&self) -> impl Iterator<Item = &Id> {
self.path.iter()
}
pub fn namespace(&self) -> String {
self.path.iter().join("::")
}
pub fn prefix_namespace_if_unqualified(&self, namespace: Option<Name>) -> Name {
if self.is_unqualified() {
match namespace {
Some(namespace) => Self::new(
self.basename().clone(),
namespace
.namespace_components()
.chain(std::iter::once(namespace.basename()))
.cloned(),
self.loc().cloned(),
),
None => self.clone(),
}
} else {
self.clone()
}
}
pub fn is_unqualified(&self) -> bool {
self.path.is_empty()
}
}
impl std::fmt::Display for Name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for elem in self.path.as_ref() {
write!(f, "{}::", elem)?;
}
write!(f, "{}", self.id)?;
Ok(())
}
}
impl Serialize for Name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_smolstr().serialize(serializer)
}
}
impl std::str::FromStr for Name {
type Err = ParseErrors;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::parser::parse_name(s)
}
}
impl FromNormalizedStr for Name {
fn describe_self() -> &'static str {
"Name"
}
}
struct NameVisitor;
impl<'de> serde::de::Visitor<'de> for NameVisitor {
type Value = Name;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a name consisting of an optional namespace and id")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Name::from_normalized_str(value)
.map_err(|err| serde::de::Error::custom(format!("invalid name `{value}`: {err}")))
}
}
impl<'de> Deserialize<'de> for Name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(NameVisitor)
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Name {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self {
id: u.arbitrary()?,
path: u.arbitrary()?,
loc: None,
})
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SlotId(pub(crate) ValidSlotId);
impl SlotId {
pub fn principal() -> Self {
Self(ValidSlotId::Principal)
}
pub fn resource() -> Self {
Self(ValidSlotId::Resource)
}
pub fn is_principal(&self) -> bool {
matches!(self, Self(ValidSlotId::Principal))
}
pub fn is_resource(&self) -> bool {
matches!(self, Self(ValidSlotId::Resource))
}
}
impl From<PrincipalOrResource> for SlotId {
fn from(v: PrincipalOrResource) -> Self {
match v {
PrincipalOrResource::Principal => SlotId::principal(),
PrincipalOrResource::Resource => SlotId::resource(),
}
}
}
impl std::fmt::Display for SlotId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub(crate) enum ValidSlotId {
#[serde(rename = "?principal")]
Principal,
#[serde(rename = "?resource")]
Resource,
}
impl std::fmt::Display for ValidSlotId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
ValidSlotId::Principal => "principal",
ValidSlotId::Resource => "resource",
};
write!(f, "?{s}")
}
}
#[derive(Debug, Clone)]
pub struct Slot {
pub id: SlotId,
pub loc: Option<Loc>,
}
impl PartialEq for Slot {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Slot {}
impl std::hash::Hash for Slot {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
#[cfg(test)]
mod vars_test {
use super::*;
#[test]
fn vars_correct() {
SlotId::principal();
SlotId::resource();
}
#[test]
fn display() {
assert_eq!(format!("{}", SlotId::principal()), "?principal")
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn normalized_name() {
Name::from_normalized_str("foo").expect("should be OK");
Name::from_normalized_str("foo::bar").expect("should be OK");
Name::from_normalized_str(r#"foo::"bar""#).expect_err("shouldn't be OK");
Name::from_normalized_str(" foo").expect_err("shouldn't be OK");
Name::from_normalized_str("foo ").expect_err("shouldn't be OK");
Name::from_normalized_str("foo\n").expect_err("shouldn't be OK");
Name::from_normalized_str("foo//comment").expect_err("shouldn't be OK");
}
#[test]
fn prefix_namespace() {
assert_eq!(
"foo::bar::baz",
Name::from_normalized_str("baz")
.unwrap()
.prefix_namespace_if_unqualified(Some("foo::bar".parse().unwrap()))
.to_smolstr()
);
assert_eq!(
"C::D",
Name::from_normalized_str("C::D")
.unwrap()
.prefix_namespace_if_unqualified(Some("A::B".parse().unwrap()))
.to_smolstr()
);
assert_eq!(
"A::B::C::D",
Name::from_normalized_str("D")
.unwrap()
.prefix_namespace_if_unqualified(Some("A::B::C".parse().unwrap()))
.to_smolstr()
);
assert_eq!(
"B::C::D",
Name::from_normalized_str("B::C::D")
.unwrap()
.prefix_namespace_if_unqualified(Some("A".parse().unwrap()))
.to_smolstr()
);
}
}