use std::sync::Arc;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use crate::parser::err::ParseErrors;
use crate::FromNormalizedStr;
use super::PrincipalOrResource;
pub fn unwrap_or_clone<T: Clone>(arc: Arc<T>) -> T {
Arc::try_unwrap(arc).unwrap_or_else(|arc| (*arc).clone())
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Name {
pub(crate) id: Id,
pub(crate) path: Arc<Vec<Id>>,
}
impl Name {
pub fn new(basename: Id, path: impl IntoIterator<Item = Id>) -> Self {
Self {
id: basename,
path: Arc::new(path.into_iter().collect()),
}
}
pub fn unqualified_name(id: Id) -> Self {
Self {
id,
path: Arc::new(vec![]),
}
}
pub fn parse_unqualified_name(s: &str) -> Result<Self, ParseErrors> {
Ok(Self {
id: s.parse()?,
path: Arc::new(vec![]),
})
}
pub fn type_in_namespace(basename: Id, namespace: Name) -> Name {
let mut path = unwrap_or_clone(namespace.path);
path.push(namespace.id);
Name::new(basename, path)
}
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("::")
}
}
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 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"
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SlotId(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)]
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}")
}
}
#[cfg(test)]
mod vars_test {
use super::*;
#[test]
fn vars_correct() {
SlotId::principal();
SlotId::resource();
}
#[test]
fn display() {
assert_eq!(format!("{}", SlotId::principal()), "?principal")
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
pub struct Id(SmolStr);
impl Id {
pub(crate) fn new_unchecked(s: impl Into<SmolStr>) -> Id {
Id(s.into())
}
pub fn to_smolstr(self) -> SmolStr {
self.0
}
}
impl AsRef<str> for Id {
fn as_ref(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl std::str::FromStr for Id {
type Err = ParseErrors;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::parser::parse_ident(s)
}
}
impl FromNormalizedStr for Id {
fn describe_self() -> &'static str {
"Id"
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Id {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let construct_list = |s: &str| s.chars().collect::<Vec<char>>();
let list_concat = |s1: &[char], s2: &[char]| [s1, s2].concat();
let head_letters = construct_list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_");
let tail_letters = list_concat(&construct_list("0123456789"), &head_letters);
let remaining_length = u.int_in_range(0..=16)?;
let mut cs = vec![*u.choose(&head_letters)?];
cs.extend(
(0..remaining_length)
.map(|_| u.choose(&tail_letters))
.collect::<Result<Vec<&char>, _>>()?,
);
let mut s: String = cs.into_iter().collect();
if crate::parser::parse_ident(&s).is_err() {
s.push('_');
}
Ok(Self::new_unchecked(s))
}
fn size_hint(depth: usize) -> (usize, Option<usize>) {
arbitrary::size_hint::and_all(&[
<usize as arbitrary::Arbitrary>::size_hint(depth),
<Vec<u8> as arbitrary::Arbitrary>::size_hint(depth),
])
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn normalized_id() {
Id::from_normalized_str("foo").expect("should be OK");
Id::from_normalized_str("foo::bar").expect_err("shouldn't be OK");
Id::from_normalized_str(r#"foo::"bar""#).expect_err("shouldn't be OK");
Id::from_normalized_str(" foo").expect_err("shouldn't be OK");
Id::from_normalized_str("foo ").expect_err("shouldn't be OK");
Id::from_normalized_str("foo\n").expect_err("shouldn't be OK");
Id::from_normalized_str("foo//comment").expect_err("shouldn't be OK");
}
#[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");
}
}