use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Span {
pub line: usize,
pub col: usize,
pub end_line: usize,
pub end_col: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeField {
pub name: String,
pub ty: TypeRef,
pub docs: Option<String>,
pub span: Span,
#[serde(default)]
pub constraints: Vec<FieldConstraint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FieldConstraint {
Tier1 {
phrase: String,
args: serde_json::Value,
span: Span,
},
ProseRule { text: String, span: Span },
ValidateWith { name: String, span: Span },
}
impl FieldConstraint {
pub fn span(&self) -> &Span {
match self {
FieldConstraint::Tier1 { span, .. } => span,
FieldConstraint::ProseRule { span, .. } => span,
FieldConstraint::ValidateWith { span, .. } => span,
}
}
}
pub const MAX_UNION_ARMS: usize = 8;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeRef {
pub name: String,
pub inner: Option<Box<TypeRef>>,
pub choices: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub variants: Option<Vec<TypeRef>>,
}
pub const VARIANT_UNION_SENTINEL: &str = "variant_union";
pub const OPTIONAL_SENTINEL: &str = "optional";
impl TypeRef {
pub fn primitive(name: impl Into<String>) -> Self {
Self {
name: name.into(),
inner: None,
choices: None,
variants: None,
}
}
pub fn optional(inner: TypeRef) -> Self {
if inner.is_optional() {
return inner;
}
Self {
name: OPTIONAL_SENTINEL.to_string(),
inner: Some(Box::new(inner)),
choices: None,
variants: None,
}
}
pub fn is_optional(&self) -> bool {
self.name == OPTIONAL_SENTINEL && self.inner.is_some()
}
pub fn optional_inner(&self) -> Option<&TypeRef> {
if self.is_optional() {
self.inner.as_deref()
} else {
None
}
}
pub fn variant_union(arms: Vec<TypeRef>) -> Self {
debug_assert!(arms.len() >= 2, "variant union requires >= 2 arms");
Self {
name: VARIANT_UNION_SENTINEL.to_string(),
inner: None,
choices: None,
variants: Some(arms),
}
}
pub fn union_with_unable(success: TypeRef) -> Self {
Self::variant_union(vec![success, TypeRef::primitive("Unable")])
}
pub fn is_variant_union(&self) -> bool {
self.name == VARIANT_UNION_SENTINEL && self.variants.is_some()
}
pub fn union_arms(&self) -> Option<&[TypeRef]> {
self.variants.as_deref()
}
pub fn is_union_with_unable(&self) -> bool {
match self.variants.as_deref() {
Some(arms) if arms.len() == 2 => {
(arms[0].name == "Unable") ^ (arms[1].name == "Unable")
}
_ => false,
}
}
pub fn unwrap_union_success(&self) -> Option<&TypeRef> {
match self.variants.as_deref() {
Some(arms) if arms.len() == 2 => {
if arms[0].name == "Unable" && arms[1].name != "Unable" {
Some(&arms[1])
} else if arms[1].name == "Unable" && arms[0].name != "Unable" {
Some(&arms[0])
} else {
None
}
}
_ => None,
}
}
pub fn union_success_arm(&self) -> Option<&TypeRef> {
self.variants.as_deref().and_then(|arms| arms.first())
}
pub fn display(&self) -> String {
if let Some(inner) = self.optional_inner() {
return format!("{}?", inner.display());
}
if let Some(arms) = &self.variants {
return arms
.iter()
.map(|a| a.display())
.collect::<Vec<_>>()
.join(" | ");
}
if let Some(choices) = &self.choices {
choices
.iter()
.map(|c| format!("\"{}\"", c))
.collect::<Vec<_>>()
.join(" | ")
} else if let Some(inner) = &self.inner {
format!("{}[{}]", self.name, inner.display())
} else {
self.name.clone()
}
}
}
impl fmt::Display for TypeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.display())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ActorHint {
Human,
Any,
Client(String),
}