use crate::ast::{Id, InternalName, Name, UnreservedId};
use crate::parser::Loc;
use crate::validator::schema::AllDefs;
use crate::validator::schema_errors::TypeNotDefinedError;
use itertools::Itertools;
use nonempty::{nonempty, NonEmpty};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
#[serde(transparent)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct RawName(InternalName);
impl RawName {
pub fn new(id: Id, loc: Option<Loc>) -> Self {
Self(InternalName::unqualified_name(id, loc))
}
pub fn new_from_unreserved(id: UnreservedId, loc: Option<Loc>) -> Self {
Self::new(id.into(), loc)
}
pub fn from_name(name: InternalName) -> Self {
Self(name)
}
pub fn parse_unqualified_name(s: &str) -> Result<Self, crate::parser::err::ParseErrors> {
InternalName::parse_unqualified_name(s).map(RawName)
}
pub fn from_normalized_str(s: &str) -> Result<Self, crate::parser::err::ParseErrors> {
use crate::FromNormalizedStr;
InternalName::from_normalized_str(s).map(RawName)
}
pub fn is_unqualified(&self) -> bool {
self.0.is_unqualified()
}
pub fn loc(&self) -> Option<&Loc> {
self.0.loc()
}
pub fn qualify_with(&self, ns: Option<&InternalName>) -> InternalName {
self.0.qualify_with(ns)
}
pub fn qualify_with_name(&self, ns: Option<&Name>) -> InternalName {
self.0.qualify_with_name(ns)
}
pub fn conditionally_qualify_with(
self,
ns: Option<&InternalName>,
reference_type: ReferenceType,
) -> ConditionalName {
let possibilities = if self.is_unqualified() {
match ns {
Some(ns) => {
nonempty![self.qualify_with(Some(ns)), self.qualify_with(None)]
}
None => {
nonempty![self.qualify_with(None)]
}
}
} else {
nonempty![self.qualify_with(None)]
};
ConditionalName {
possibilities,
reference_type,
raw: self,
}
}
}
impl std::fmt::Display for RawName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for RawName {
type Err = <InternalName as std::str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
InternalName::from_str(s).map(RawName)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ConditionalName {
possibilities: NonEmpty<InternalName>,
reference_type: ReferenceType,
raw: RawName,
}
impl ConditionalName {
pub fn unconditional(name: InternalName, reference_type: ReferenceType) -> Self {
ConditionalName {
possibilities: nonempty!(name.clone()),
reference_type,
raw: RawName(name),
}
}
pub fn raw(&self) -> &RawName {
&self.raw
}
pub(crate) fn possibilities(&self) -> impl Iterator<Item = &InternalName> {
self.possibilities.iter()
}
pub fn loc(&self) -> Option<&Loc> {
self.raw.loc()
}
pub fn resolve(self, all_defs: &AllDefs) -> Result<InternalName, TypeNotDefinedError> {
for possibility in &self.possibilities {
if matches!(
self.reference_type,
ReferenceType::Common | ReferenceType::CommonOrEntity
) && all_defs.is_defined_as_common(possibility)
{
return Ok(possibility.clone());
}
if matches!(
self.reference_type,
ReferenceType::Entity | ReferenceType::CommonOrEntity
) && all_defs.is_defined_as_entity(possibility)
{
return Ok(possibility.clone());
}
}
Err(TypeNotDefinedError {
undefined_types: nonempty![self],
})
}
pub(crate) fn resolution_failure_help(&self) -> String {
let entity_or_common_text = match self.reference_type {
ReferenceType::Common => "as a common type",
ReferenceType::Entity => "as an entity type",
ReferenceType::CommonOrEntity => "as a common or entity type",
};
#[expect(
clippy::indexing_slicing,
reason = "indexing is safe because we first check the `.len()`"
)]
match self.possibilities.len() {
1 => format!(
"`{}` has not been declared {}",
self.possibilities[0], entity_or_common_text
),
2 => format!(
"neither `{}` nor `{}` refers to anything that has been declared {}",
self.possibilities[0], self.possibilities[1], entity_or_common_text,
),
_ => format!(
"none of these have been declared {}: {}",
entity_or_common_text,
self.possibilities
.iter()
.map(|p| format!("`{p}`"))
.join(", ")
),
}
}
}
impl Serialize for ConditionalName {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.raw().serialize(serializer)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ReferenceType {
Common,
Entity,
CommonOrEntity,
}