use alloc::{
borrow::Cow,
collections::{BTreeMap, BTreeSet, LinkedList, VecDeque},
rc::Rc,
sync::Arc,
};
use core::{
cell::{Cell, RefCell},
hash::BuildHasher,
marker::PhantomData,
};
use std::collections::{HashMap, HashSet};
#[cfg(feature = "derive")]
use std::path::{Path, PathBuf};
#[cfg(feature = "derive")]
use crate::schema::SchemaError;
#[cfg(feature = "derive")]
use crate::schema::error::StorageError;
use crate::schema::{Schema, TypeKind, collect::SchemaEntry};
pub trait RonSchema {
fn type_kind() -> TypeKind;
#[must_use]
fn type_doc() -> Option<&'static str> {
None
}
#[must_use]
fn schema() -> Schema {
Schema {
doc: Self::type_doc().map(str::to_string),
kind: Self::type_kind(),
}
}
#[must_use]
fn type_path() -> Option<&'static str> {
None
}
#[must_use]
fn child_schemas() -> &'static [&'static SchemaEntry] {
&[]
}
#[cfg(feature = "derive")]
fn write_schema(output_dir: Option<&Path>) -> Result<PathBuf, SchemaError> {
let type_path = Self::type_path().ok_or_else(|| {
SchemaError::Storage(StorageError::Io(
"type does not support schema storage".to_string(),
))
})?;
let schema = Self::schema();
super::write_schema(type_path, &schema, output_dir)
}
#[cfg(feature = "derive")]
fn write_schemas(output_dir: Option<&str>) -> Result<Vec<PathBuf>, SchemaError>
where
Self: Sized,
{
super::write_schemas::<Self>(output_dir)
}
}
pub trait RonList: RonSchema {
type Element: RonSchema;
#[must_use]
fn element_type_kind() -> TypeKind {
Self::Element::type_kind()
}
}
pub trait RonMap: RonSchema {
type Key: RonSchema;
type Value: RonSchema;
#[must_use]
fn key_type_kind() -> TypeKind {
Self::Key::type_kind()
}
#[must_use]
fn value_type_kind() -> TypeKind {
Self::Value::type_kind()
}
}
pub trait RonOptional: RonSchema {
type Inner: RonSchema;
#[must_use]
fn inner_type_kind() -> TypeKind {
Self::Inner::type_kind()
}
}
macro_rules! impl_primitive_schema {
($($ty:ty => $variant:ident),* $(,)?) => {
$(
impl RonSchema for $ty {
fn type_kind() -> TypeKind {
TypeKind::$variant
}
}
)*
};
}
macro_rules! impl_transparent_schema {
($($ty:ident),* $(,)?) => {
$(
impl<T: RonSchema> RonSchema for $ty<T> {
fn type_kind() -> TypeKind {
T::type_kind()
}
}
)*
};
}
macro_rules! impl_list_schema {
($($ty:ty),* $(,)?) => {
$(
impl<T: RonSchema> RonSchema for $ty {
fn type_kind() -> TypeKind {
TypeKind::List(Box::new(T::type_kind()))
}
}
impl<T: RonSchema> RonList for $ty {
type Element = T;
}
)*
};
}
macro_rules! impl_tuple_schema {
($($T:ident),+) => {
impl<$($T: RonSchema),+> RonSchema for ($($T,)+) {
fn type_kind() -> TypeKind {
TypeKind::Tuple(alloc::vec![$($T::type_kind()),+])
}
}
};
}
macro_rules! impl_tuple_schema_all {
() => {};
($first:ident $(, $rest:ident)*) => {
impl_tuple_schema!($first $(, $rest)*);
impl_tuple_schema_all!($($rest),*);
};
}
impl_primitive_schema! {
bool => Bool,
i8 => I8, i16 => I16, i32 => I32, i64 => I64, i128 => I128, isize => I64,
u8 => U8, u16 => U16, u32 => U32, u64 => U64, u128 => U128, usize => U64,
f32 => F32, f64 => F64,
char => Char,
String => String,
() => Unit,
std::path::PathBuf => String,
std::path::Path => String,
std::ffi::OsString => String,
std::ffi::OsStr => String,
}
impl RonSchema for &str {
fn type_kind() -> TypeKind {
TypeKind::String
}
}
impl<T: RonSchema> RonSchema for Option<T> {
fn type_kind() -> TypeKind {
TypeKind::Option(Box::new(T::type_kind()))
}
}
impl<T: RonSchema> RonOptional for Option<T> {
type Inner = T;
}
impl_list_schema! { Vec<T>, VecDeque<T>, LinkedList<T>, BTreeSet<T> }
impl<T: RonSchema> RonSchema for [T] {
fn type_kind() -> TypeKind {
TypeKind::List(Box::new(T::type_kind()))
}
}
impl<T: RonSchema, const N: usize> RonSchema for [T; N] {
fn type_kind() -> TypeKind {
TypeKind::List(Box::new(T::type_kind()))
}
}
impl<T: RonSchema, const N: usize> RonList for [T; N] {
type Element = T;
}
impl<K: RonSchema, V: RonSchema, S: BuildHasher> RonSchema for HashMap<K, V, S> {
fn type_kind() -> TypeKind {
TypeKind::Map {
key: Box::new(K::type_kind()),
value: Box::new(V::type_kind()),
}
}
}
impl<K: RonSchema, V: RonSchema, S: BuildHasher> RonMap for HashMap<K, V, S> {
type Key = K;
type Value = V;
}
impl<K: RonSchema, V: RonSchema> RonSchema for BTreeMap<K, V> {
fn type_kind() -> TypeKind {
TypeKind::Map {
key: Box::new(K::type_kind()),
value: Box::new(V::type_kind()),
}
}
}
impl<K: RonSchema, V: RonSchema> RonMap for BTreeMap<K, V> {
type Key = K;
type Value = V;
}
impl<T: RonSchema, S: BuildHasher> RonSchema for HashSet<T, S> {
fn type_kind() -> TypeKind {
TypeKind::List(Box::new(T::type_kind()))
}
}
impl<T: RonSchema, S: BuildHasher> RonList for HashSet<T, S> {
type Element = T;
}
impl_transparent_schema! { Box, Rc, Arc, Cell, RefCell }
impl<T: RonSchema> RonSchema for std::sync::Mutex<T> {
fn type_kind() -> TypeKind {
T::type_kind()
}
}
impl<T: RonSchema> RonSchema for std::sync::RwLock<T> {
fn type_kind() -> TypeKind {
T::type_kind()
}
}
impl<T: RonSchema + ToOwned + ?Sized> RonSchema for Cow<'_, T> {
fn type_kind() -> TypeKind {
T::type_kind()
}
}
impl_tuple_schema_all!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl<T: ?Sized> RonSchema for PhantomData<T> {
fn type_kind() -> TypeKind {
TypeKind::Unit
}
}
impl<T: RonSchema> RonSchema for crate::convert::Spanned<T> {
fn type_kind() -> TypeKind {
T::type_kind()
}
fn type_doc() -> Option<&'static str> {
T::type_doc()
}
fn schema() -> Schema {
T::schema()
}
fn type_path() -> Option<&'static str> {
T::type_path()
}
}
impl<T: RonOptional> RonOptional for crate::convert::Spanned<T> {
type Inner = T;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_primitive_type_kinds() {
assert_eq!(bool::type_kind(), TypeKind::Bool);
assert_eq!(i32::type_kind(), TypeKind::I32);
assert_eq!(u64::type_kind(), TypeKind::U64);
assert_eq!(f64::type_kind(), TypeKind::F64);
assert_eq!(char::type_kind(), TypeKind::Char);
assert_eq!(String::type_kind(), TypeKind::String);
assert_eq!(<()>::type_kind(), TypeKind::Unit);
}
#[test]
fn test_option_type_kind() {
assert_eq!(
Option::<i32>::type_kind(),
TypeKind::Option(Box::new(TypeKind::I32))
);
}
#[test]
fn test_vec_type_kind() {
assert_eq!(
Vec::<String>::type_kind(),
TypeKind::List(Box::new(TypeKind::String))
);
}
#[test]
fn test_hashmap_type_kind() {
assert_eq!(
std::collections::HashMap::<String, i32>::type_kind(),
TypeKind::Map {
key: Box::new(TypeKind::String),
value: Box::new(TypeKind::I32),
}
);
}
#[test]
fn test_tuple_type_kind() {
assert_eq!(
<(i32, String)>::type_kind(),
TypeKind::Tuple(vec![TypeKind::I32, TypeKind::String])
);
}
#[test]
fn test_box_transparent() {
assert_eq!(Box::<i32>::type_kind(), TypeKind::I32);
}
#[test]
fn test_custom_list_type() {
#[allow(dead_code)]
struct MyVec<T>(Vec<T>);
impl<T: RonSchema> RonSchema for MyVec<T> {
fn type_kind() -> TypeKind {
TypeKind::List(Box::new(T::type_kind()))
}
}
impl<T: RonSchema> RonList for MyVec<T> {
type Element = T;
}
assert_eq!(
MyVec::<i32>::type_kind(),
TypeKind::List(Box::new(TypeKind::I32))
);
assert_eq!(MyVec::<i32>::element_type_kind(), TypeKind::I32);
}
#[test]
fn test_custom_map_type() {
#[allow(dead_code)]
struct MyMap<K, V>(std::collections::HashMap<K, V>);
impl<K: RonSchema, V: RonSchema> RonSchema for MyMap<K, V> {
fn type_kind() -> TypeKind {
TypeKind::Map {
key: Box::new(K::type_kind()),
value: Box::new(V::type_kind()),
}
}
}
impl<K: RonSchema, V: RonSchema> RonMap for MyMap<K, V> {
type Key = K;
type Value = V;
}
assert_eq!(
MyMap::<String, i32>::type_kind(),
TypeKind::Map {
key: Box::new(TypeKind::String),
value: Box::new(TypeKind::I32),
}
);
assert_eq!(MyMap::<String, i32>::key_type_kind(), TypeKind::String);
assert_eq!(MyMap::<String, i32>::value_type_kind(), TypeKind::I32);
}
#[test]
fn test_spanned_schema_is_transparent() {
use crate::convert::Spanned;
assert_eq!(Spanned::<i32>::type_kind(), i32::type_kind());
assert_eq!(Spanned::<String>::type_kind(), String::type_kind());
assert_eq!(
Spanned::<Option<bool>>::type_kind(),
Option::<bool>::type_kind()
);
assert_eq!(Spanned::<Vec<i32>>::type_kind(), Vec::<i32>::type_kind());
}
#[test]
fn test_spanned_nested_transparency() {
use crate::convert::Spanned;
assert_eq!(Spanned::<Spanned<i32>>::type_kind(), i32::type_kind());
}
#[test]
fn test_spanned_option_combinations() {
use crate::convert::Spanned;
assert_eq!(
Option::<Spanned<i32>>::type_kind(),
Option::<i32>::type_kind()
);
assert_eq!(
Spanned::<Option<i32>>::type_kind(),
Option::<i32>::type_kind()
);
}
}