use thiserror::Error;
use crate::*;
#[macro_use]
mod macros;
mod impls;
mod post_process;
pub use post_process::*;
pub enum TypeCategory {
Inline(DataType),
Reference(DataTypeReference),
}
#[derive(Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[allow(missing_docs)]
pub enum ExportError {
#[error("Atemmpted to export type defined at '{}' but encountered error: {1}", .0.as_str())]
InvalidType(ImplLocation, &'static str),
}
pub trait Type {
fn inline(opts: DefOpts, generics: &[DataType]) -> Result<DataType, ExportError>;
fn definition_generics() -> Vec<GenericType> {
vec![]
}
fn definition(opts: DefOpts) -> Result<DataType, ExportError> {
Self::inline(
opts,
&Self::definition_generics()
.into_iter()
.map(Into::into)
.collect::<Vec<_>>(),
)
}
fn category_impl(opts: DefOpts, generics: &[DataType]) -> Result<TypeCategory, ExportError> {
Self::inline(opts, generics).map(TypeCategory::Inline)
}
fn reference(opts: DefOpts, generics: &[DataType]) -> Result<DataType, ExportError> {
let category = Self::category_impl(
DefOpts {
parent_inline: opts.parent_inline,
type_map: opts.type_map,
},
generics,
)?;
Ok(match category {
TypeCategory::Inline(inline) => inline,
TypeCategory::Reference(def) => {
if opts.type_map.get(&def.sid).is_none() {
opts.type_map.entry(def.sid).or_default();
let definition = Self::definition(DefOpts {
parent_inline: opts.parent_inline,
type_map: opts.type_map,
})?;
let definition = match definition {
DataType::Named(definition) => definition,
_ => unreachable!(),
};
opts.type_map.insert(def.sid, Some(definition));
}
DataType::Reference(def)
}
})
}
}
pub trait NamedType: Type {
fn named_data_type(opts: DefOpts, generics: &[DataType]) -> Result<NamedDataType, ExportError>;
fn definition_named_data_type(opts: DefOpts) -> Result<NamedDataType, ExportError> {
Self::named_data_type(
opts,
&Self::definition_generics()
.into_iter()
.map(Into::into)
.collect::<Vec<_>>(),
)
}
}
pub trait Flatten: Type {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[doc(hidden)]
pub struct TypeSid(u64);
#[doc(hidden)]
pub const fn internal_sid_hash(
module_path: &'static str,
file: &'static str,
type_name: &'static str,
) -> TypeSid {
let mut hash = 0xcbf29ce484222325;
let prime = 0x00000100000001B3;
let mut bytes = module_path.as_bytes();
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(prime);
i += 1;
}
bytes = file.as_bytes();
i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(prime);
i += 1;
}
bytes = type_name.as_bytes();
i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(prime);
i += 1;
}
TypeSid(hash)
}
#[macro_export]
#[doc(hidden)]
macro_rules! sid {
($name:expr) => {
$crate::sid!($name, $crate::impl_location!().as_str())
};
(@with_specta_path; $name:expr; $first:ident$(::$rest:ident)*) => {{
use $first$(::$rest)*::{internal_sid_hash, impl_location};
internal_sid_hash(
module_path!(),
impl_location!().as_str(),
$name,
)
}};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[doc(hidden)]
pub struct ImplLocation(&'static str);
impl ImplLocation {
#[doc(hidden)]
pub const fn internal_new(s: &'static str) -> Self {
Self(s)
}
pub const fn as_str(&self) -> &'static str {
self.0
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! impl_location {
() => {
$crate::ImplLocation::internal_new(concat!(file!(), ":", line!(), ":", column!()))
};
(@with_specta_path; $first:ident$(::$rest:ident)*) => {
$first$(::$rest)*::ImplLocation::internal_new(concat!(file!(), ":", line!(), ":", column!()))
};
}