use proc_macro2::{TokenStream, TokenTree};
use crate::TypespaceTraitSet;
use crate::error::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JsonValue(pub serde_json::Value);
impl JsonValue {
pub fn new(value: serde_json::Value) -> Self {
Self(value)
}
}
impl From<serde_json::Value> for JsonValue {
fn from(value: serde_json::Value) -> Self {
Self(value)
}
}
impl Ord for JsonValue {
fn cmp(&self, _: &Self) -> std::cmp::Ordering {
std::cmp::Ordering::Equal
}
}
impl PartialOrd for JsonValue {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Clone, Default)]
pub struct TypeCommon {
pub(crate) name: Option<String>,
pub(crate) description: Option<String>,
pub(crate) default: Option<JsonValue>,
pub(crate) extra_derives: Vec<String>,
pub(crate) extra_attrs: Vec<String>,
pub(crate) built: Option<TypeCommonBuilt>,
}
impl TypeCommon {
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn default(&self) -> Option<&serde_json::Value> {
self.default.as_ref().map(|JsonValue(value)| value)
}
pub fn extra_derives(&self) -> &[String] {
&self.extra_derives
}
pub fn extra_attrs(&self) -> &[String] {
&self.extra_attrs
}
pub(crate) fn built_name(&self) -> &str {
self.name.as_deref().expect("validated type has a name")
}
pub(crate) fn validate_name<Id>(&self, kind: &'static str) -> Result<(), Error<Id>>
where
Id: std::fmt::Debug + std::fmt::Display,
{
let Some(name) = self.name.as_deref() else {
return Err(Error::MissingTypeName { kind });
};
validate_ident(kind, name)
}
}
pub(crate) fn validate_ident<Id>(kind: &'static str, name: &str) -> Result<(), Error<Id>>
where
Id: std::fmt::Debug + std::fmt::Display,
{
if name == "gen" {
return Err(Error::InvalidName {
kind,
name: name.to_string(),
message: "is a Rust keyword",
});
}
if !name.starts_with("r#") && syn::parse_str::<syn::Ident>(name).is_ok() {
return Ok(());
}
let is_keyword = !name.starts_with("r#")
&& name.parse::<TokenStream>().is_ok_and(|ts| {
let mut trees = ts.into_iter();
matches!(
(trees.next(), trees.next()),
(Some(TokenTree::Ident(ident)), None) if ident == name
)
});
if is_keyword {
return Err(Error::InvalidName {
kind,
name: name.to_string(),
message: "is a Rust keyword",
});
}
Err(Error::InvalidName {
kind,
name: name.to_string(),
message: "is not a valid Rust identifier (raw identifiers are not supported)",
})
}
#[derive(Debug, Clone)]
pub(crate) struct TypeCommonBuilt {
pub traits: TypespaceTraitSet,
pub from_string_irrefutable: bool,
}