mod arrays;
mod bools;
mod enums;
mod externals;
mod literals;
mod numbers;
mod objects;
mod strings;
mod structs;
mod tuples;
mod unions;
pub use arrays::*;
pub use bools::*;
pub use enums::*;
pub use literals::*;
pub use numbers::*;
pub use objects::*;
pub use strings::*;
pub use structs::*;
pub use tuples::*;
pub use unions::*;
#[derive(Clone, Debug, Default)]
pub enum SchemaType {
Null,
#[default]
Unknown,
Array(ArrayType),
Boolean(BooleanType),
Enum(EnumType),
Float(FloatType),
Integer(IntegerType),
Literal(LiteralType),
Object(ObjectType),
Struct(StructType),
String(StringType),
Tuple(TupleType),
Union(UnionType),
}
impl SchemaType {
pub fn infer<T: Schematic>() -> SchemaType {
T::generate_schema()
}
pub fn infer_with_default<T: Schematic>(default: LiteralValue) -> SchemaType {
let mut schema = T::generate_schema();
schema.set_default(default);
schema
}
pub fn infer_partial<T: Schematic>() -> SchemaType {
let mut schema = T::generate_schema();
schema.set_partial(true);
schema
}
pub fn array(items_type: SchemaType) -> SchemaType {
SchemaType::Array(ArrayType {
items_type: Box::new(items_type),
..ArrayType::default()
})
}
pub fn boolean() -> SchemaType {
SchemaType::Boolean(BooleanType::default())
}
pub fn enumerable<I>(values: I) -> SchemaType
where
I: IntoIterator<Item = LiteralValue>,
{
SchemaType::Enum(EnumType {
values: values.into_iter().collect(),
..EnumType::default()
})
}
pub fn float(kind: FloatKind) -> SchemaType {
SchemaType::Float(FloatType {
kind,
..FloatType::default()
})
}
pub fn integer(kind: IntegerKind) -> SchemaType {
SchemaType::Integer(IntegerType {
kind,
..IntegerType::default()
})
}
pub fn literal(value: LiteralValue) -> SchemaType {
SchemaType::Literal(LiteralType {
value: Some(value),
..LiteralType::default()
})
}
pub fn nullable(mut schema: SchemaType) -> SchemaType {
if let SchemaType::Union(inner) = &mut schema {
if inner.name.is_none() {
if !inner
.variants_types
.iter()
.any(|t| matches!(**t, SchemaType::Null))
{
inner.variants_types.push(Box::new(SchemaType::Null));
}
return schema;
}
}
SchemaType::union([schema, SchemaType::Null])
}
pub fn object(key_type: SchemaType, value_type: SchemaType) -> SchemaType {
SchemaType::Object(ObjectType {
key_type: Box::new(key_type),
value_type: Box::new(value_type),
..ObjectType::default()
})
}
pub fn string() -> SchemaType {
SchemaType::String(StringType::default())
}
pub fn structure<I>(fields: I) -> SchemaType
where
I: IntoIterator<Item = SchemaField>,
{
SchemaType::Struct(StructType {
fields: fields.into_iter().collect(),
..StructType::default()
})
}
pub fn tuple<I>(items_types: I) -> SchemaType
where
I: IntoIterator<Item = SchemaType>,
{
SchemaType::Tuple(TupleType {
items_types: items_types.into_iter().map(Box::new).collect(),
..TupleType::default()
})
}
pub fn union<I>(variants_types: I) -> SchemaType
where
I: IntoIterator<Item = SchemaType>,
{
SchemaType::Union(UnionType {
variants_types: variants_types.into_iter().map(Box::new).collect(),
..UnionType::default()
})
}
pub fn union_one<I>(variants_types: I) -> SchemaType
where
I: IntoIterator<Item = SchemaType>,
{
SchemaType::Union(UnionType {
operator: UnionOperator::OneOf,
variants_types: variants_types.into_iter().map(Box::new).collect(),
..UnionType::default()
})
}
pub fn get_default(&self) -> Option<&LiteralValue> {
match self {
SchemaType::Boolean(BooleanType { default, .. }) => default.as_ref(),
SchemaType::Enum(EnumType {
default_index,
values,
..
}) => {
if let Some(index) = default_index {
if let Some(value) = values.get(*index) {
return Some(value);
}
}
None
}
SchemaType::Float(FloatType { default, .. }) => default.as_ref(),
SchemaType::Integer(IntegerType { default, .. }) => default.as_ref(),
SchemaType::String(StringType { default, .. }) => default.as_ref(),
SchemaType::Union(UnionType {
default_index,
variants_types,
..
}) => {
if let Some(index) = default_index {
if let Some(value) = variants_types.get(*index) {
return value.get_default();
}
}
for variant in variants_types {
if let Some(value) = variant.get_default() {
return Some(value);
}
}
None
}
_ => None,
}
}
pub fn get_name(&self) -> Option<&String> {
match self {
SchemaType::Null => None,
SchemaType::Unknown => None,
SchemaType::Array(ArrayType { name, .. }) => name.as_ref(),
SchemaType::Boolean(BooleanType { name, .. }) => name.as_ref(),
SchemaType::Enum(EnumType { name, .. }) => name.as_ref(),
SchemaType::Float(FloatType { name, .. }) => name.as_ref(),
SchemaType::Integer(IntegerType { name, .. }) => name.as_ref(),
SchemaType::Literal(LiteralType { name, .. }) => name.as_ref(),
SchemaType::Object(ObjectType { name, .. }) => name.as_ref(),
SchemaType::Struct(StructType { name, .. }) => name.as_ref(),
SchemaType::String(StringType { name, .. }) => name.as_ref(),
SchemaType::Tuple(TupleType { name, .. }) => name.as_ref(),
SchemaType::Union(UnionType { name, .. }) => name.as_ref(),
}
}
pub fn is_null(&self) -> bool {
matches!(self, SchemaType::Null)
}
pub fn is_nullable(&self) -> bool {
if let SchemaType::Union(uni) = self {
return uni.is_nullable();
}
false
}
pub fn set_default(&mut self, default: LiteralValue) {
match self {
SchemaType::Boolean(ref mut inner) => {
inner.default = Some(default);
}
SchemaType::Float(ref mut inner) => {
inner.default = Some(default);
}
SchemaType::Integer(ref mut inner) => {
inner.default = Some(default);
}
SchemaType::String(ref mut inner) => {
inner.default = Some(default);
}
_ => {}
};
}
pub fn set_name<S: AsRef<str>>(&mut self, name: S) {
let name = Some(name.as_ref().to_owned());
match self {
SchemaType::Array(ref mut inner) => {
inner.name = name;
}
SchemaType::Enum(ref mut inner) => {
inner.name = name;
}
SchemaType::Float(ref mut inner) => {
inner.name = name;
}
SchemaType::Integer(ref mut inner) => {
inner.name = name;
}
SchemaType::Literal(ref mut inner) => {
inner.name = name;
}
SchemaType::Object(ref mut inner) => {
inner.name = name;
}
SchemaType::Struct(ref mut inner) => {
inner.name = name;
}
SchemaType::String(ref mut inner) => {
inner.name = name;
}
SchemaType::Tuple(ref mut inner) => {
inner.name = name;
}
SchemaType::Union(ref mut inner) => {
inner.name = name;
}
_ => {}
};
}
pub fn set_partial(&mut self, state: bool) {
match self {
SchemaType::Array(ref mut inner) => inner.items_type.set_partial(state),
SchemaType::Object(ref mut inner) => inner.value_type.set_partial(state),
SchemaType::Struct(ref mut inner) => {
inner.partial = state;
}
SchemaType::Union(ref mut inner) => {
inner.partial = state;
let is_nullable = inner
.variants_types
.iter()
.any(|t| matches!(**t, SchemaType::Null));
if is_nullable {
for item in inner.variants_types.iter_mut() {
item.set_partial(state);
}
}
}
_ => {}
};
}
pub fn add_field(&mut self, field: SchemaField) {
match self {
SchemaType::Enum(ref mut inner) => {
inner.variants.get_or_insert(vec![]).push(field);
}
SchemaType::Struct(ref mut inner) => {
inner.fields.push(field);
}
SchemaType::Union(ref mut inner) => {
inner.variants.get_or_insert(vec![]).push(field);
}
_ => {}
};
}
}
#[derive(Clone, Debug, Default)]
pub struct SchemaField {
pub name: String,
pub description: Option<String>,
pub type_of: SchemaType,
pub deprecated: Option<String>,
pub env_var: Option<String>,
pub hidden: bool,
pub nullable: bool,
pub optional: bool,
pub read_only: bool,
pub write_only: bool,
}
impl SchemaField {
pub fn new(name: &str, type_of: SchemaType) -> SchemaField {
SchemaField {
name: name.to_owned(),
type_of,
..SchemaField::default()
}
}
}
pub trait Schematic {
fn generate_schema() -> SchemaType {
SchemaType::Unknown
}
}
impl Schematic for () {
fn generate_schema() -> SchemaType {
SchemaType::Null
}
}
impl<T: Schematic> Schematic for &T {
fn generate_schema() -> SchemaType {
T::generate_schema()
}
}
impl<T: Schematic> Schematic for &mut T {
fn generate_schema() -> SchemaType {
T::generate_schema()
}
}
impl<T: Schematic> Schematic for Box<T> {
fn generate_schema() -> SchemaType {
T::generate_schema()
}
}
impl<T: Schematic> Schematic for Option<T> {
fn generate_schema() -> SchemaType {
SchemaType::nullable(T::generate_schema())
}
}