use crate::{
formats::{Flexible, Format, PreferMany, PreferOne, Separator, Strict},
prelude::{Schema as WrapSchema, *},
};
use ::schemars_0_8::{
gen::SchemaGenerator,
schema::{
ArrayValidation, InstanceType, Metadata, NumberValidation, ObjectValidation, Schema,
SchemaObject, SingleOrVec, SubschemaValidation,
},
JsonSchema,
};
use core::{
mem::ManuallyDrop,
ops::{Deref, DerefMut},
};
pub trait JsonSchemaAs<T: ?Sized> {
fn is_referenceable() -> bool {
true
}
fn schema_name() -> String;
fn schema_id() -> Cow<'static, str> {
Cow::Owned(Self::schema_name())
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema;
}
impl<T, TA> JsonSchema for WrapSchema<T, TA>
where
T: ?Sized,
TA: JsonSchemaAs<T>,
{
fn schema_name() -> String {
TA::schema_name()
}
fn schema_id() -> Cow<'static, str> {
TA::schema_id()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
TA::json_schema(gen)
}
fn is_referenceable() -> bool {
TA::is_referenceable()
}
}
macro_rules! forward_schema {
($fwd:ty) => {
fn schema_name() -> String {
<$fwd as JsonSchema>::schema_name()
}
fn schema_id() -> Cow<'static, str> {
<$fwd as JsonSchema>::schema_id()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
<$fwd as JsonSchema>::json_schema(gen)
}
fn is_referenceable() -> bool {
<$fwd as JsonSchema>::is_referenceable()
}
};
}
impl<'a, T: 'a, TA: 'a> JsonSchemaAs<&'a T> for &'a TA
where
T: ?Sized,
TA: JsonSchemaAs<T>,
{
forward_schema!(&'a WrapSchema<T, TA>);
}
impl<'a, T: 'a, TA: 'a> JsonSchemaAs<&'a mut T> for &'a mut TA
where
T: ?Sized,
TA: JsonSchemaAs<T>,
{
forward_schema!(&'a mut WrapSchema<T, TA>);
}
impl<T, TA> JsonSchemaAs<Option<T>> for Option<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(Option<WrapSchema<T, TA>>);
}
impl<T, TA> JsonSchemaAs<Box<T>> for Box<TA>
where
T: ?Sized,
TA: JsonSchemaAs<T>,
{
forward_schema!(Box<WrapSchema<T, TA>>);
}
impl<T, TA> JsonSchemaAs<Rc<T>> for Rc<TA>
where
T: ?Sized,
TA: JsonSchemaAs<T>,
{
forward_schema!(Rc<WrapSchema<T, TA>>);
}
impl<T, TA> JsonSchemaAs<Arc<T>> for Arc<TA>
where
T: ?Sized,
TA: JsonSchemaAs<T>,
{
forward_schema!(Arc<WrapSchema<T, TA>>);
}
impl<T, TA> JsonSchemaAs<Vec<T>> for Vec<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(Vec<WrapSchema<T, TA>>);
}
impl<T, TA> JsonSchemaAs<VecDeque<T>> for VecDeque<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(VecDeque<WrapSchema<T, TA>>);
}
impl<K, V, KA, VA> JsonSchemaAs<BTreeMap<K, V>> for BTreeMap<KA, VA>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(BTreeMap<WrapSchema<K, KA>, WrapSchema<V, VA>>);
}
impl<K, V, S, KA, VA> JsonSchemaAs<HashMap<K, V, S>> for HashMap<KA, VA, S>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(HashMap<WrapSchema<K, KA>, WrapSchema<V, VA>, S>);
}
impl<T, TA> JsonSchemaAs<BTreeSet<T>> for BTreeSet<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(BTreeSet<WrapSchema<T, TA>>);
}
impl<T, TA, S> JsonSchemaAs<T> for HashSet<TA, S>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(HashSet<WrapSchema<T, TA>, S>);
}
impl<T, TA, const N: usize> JsonSchemaAs<[T; N]> for [TA; N]
where
TA: JsonSchemaAs<T>,
{
fn schema_name() -> String {
std::format!("[{}; {}]", <WrapSchema<T, TA>>::schema_name(), N)
}
fn schema_id() -> Cow<'static, str> {
std::format!("[{}; {}]", <WrapSchema<T, TA>>::schema_id(), N).into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let (max, min) = match N.try_into() {
Ok(len) => (Some(len), Some(len)),
Err(_) => (None, Some(u32::MAX)),
};
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(gen.subschema_for::<WrapSchema<T, TA>>().into()),
max_items: max,
min_items: min,
..Default::default()
})),
..Default::default()
}
.into()
}
fn is_referenceable() -> bool {
false
}
}
macro_rules! schema_for_tuple {
(
( $( $ts:ident )+ )
( $( $as:ident )+ )
) => {
impl<$($ts,)+ $($as,)+> JsonSchemaAs<($($ts,)+)> for ($($as,)+)
where
$( $as: JsonSchemaAs<$ts>, )+
{
forward_schema!(( $( WrapSchema<$ts, $as>, )+ ));
}
}
}
impl JsonSchemaAs<()> for () {
forward_schema!(());
}
schema_for_tuple!((T0)(A0));
schema_for_tuple!((T0 T1) (A0 A1));
schema_for_tuple!((T0 T1 T2) (A0 A1 A2));
schema_for_tuple!((T0 T1 T2 T3) (A0 A1 A2 A3));
schema_for_tuple!((T0 T1 T2 T3 T4) (A0 A1 A2 A3 A4));
schema_for_tuple!((T0 T1 T2 T3 T4 T5) (A0 A1 A2 A3 A4 A5));
schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6) (A0 A1 A2 A3 A4 A5 A6));
schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7) (A0 A1 A2 A3 A4 A5 A6 A7));
schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8) (A0 A1 A2 A3 A4 A5 A6 A7 A8));
schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9));
schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10));
schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11));
schema_for_tuple!(
(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12)
(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12)
);
schema_for_tuple!(
(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13)
(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13)
);
schema_for_tuple!(
(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14)
(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14)
);
schema_for_tuple!(
(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15)
(A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15)
);
impl<T: JsonSchema> JsonSchemaAs<T> for Same {
forward_schema!(T);
}
impl<T> JsonSchemaAs<T> for DisplayFromStr {
forward_schema!(String);
}
impl JsonSchemaAs<bool> for BoolFromInt<Strict> {
fn schema_name() -> String {
"BoolFromInt<Strict>".into()
}
fn schema_id() -> Cow<'static, str> {
"serde_with::BoolFromInt<Strict>".into()
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Integer.into()),
number: Some(Box::new(NumberValidation {
minimum: Some(0.0),
maximum: Some(1.0),
..Default::default()
})),
..Default::default()
}
.into()
}
fn is_referenceable() -> bool {
false
}
}
impl JsonSchemaAs<bool> for BoolFromInt<Flexible> {
fn schema_name() -> String {
"BoolFromInt<Flexible>".into()
}
fn schema_id() -> Cow<'static, str> {
"serde_with::BoolFromInt<Flexible>".into()
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Integer.into()),
..Default::default()
}
.into()
}
fn is_referenceable() -> bool {
false
}
}
impl<'a, T: 'a> JsonSchemaAs<Cow<'a, T>> for BorrowCow
where
T: ?Sized + ToOwned,
Cow<'a, T>: JsonSchema,
{
forward_schema!(Cow<'a, T>);
}
impl<T> JsonSchemaAs<T> for Bytes {
forward_schema!(Vec<u8>);
}
impl JsonSchemaAs<Vec<u8>> for BytesOrString {
fn schema_name() -> String {
"BytesOrString".into()
}
fn schema_id() -> Cow<'static, str> {
"serde_with::BytesOrString".into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(std::vec![
gen.subschema_for::<Vec<u8>>(),
SchemaObject {
instance_type: Some(InstanceType::String.into()),
metadata: Some(Box::new(Metadata {
write_only: true,
..Default::default()
})),
..Default::default()
}
.into()
]),
..Default::default()
})),
..Default::default()
}
.into()
}
fn is_referenceable() -> bool {
false
}
}
impl<T, TA> JsonSchemaAs<T> for DefaultOnError<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(WrapSchema<T, TA>);
}
impl<T, TA> JsonSchemaAs<T> for DefaultOnNull<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(Option<WrapSchema<T, TA>>);
}
impl<O, T: JsonSchema> JsonSchemaAs<O> for FromInto<T> {
forward_schema!(T);
}
impl<O, T: JsonSchema> JsonSchemaAs<O> for FromIntoRef<T> {
forward_schema!(T);
}
impl<T, U: JsonSchema> JsonSchemaAs<T> for TryFromInto<U> {
forward_schema!(U);
}
impl<T, U: JsonSchema> JsonSchemaAs<T> for TryFromIntoRef<U> {
forward_schema!(U);
}
impl<T, TA, FA> JsonSchemaAs<T> for IfIsHumanReadable<TA, FA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(WrapSchema<T, TA>);
}
macro_rules! schema_for_map {
($type:ty) => {
impl<K, V, KA, VA> JsonSchemaAs<$type> for Map<KA, VA>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(WrapSchema<BTreeMap<K, V>, BTreeMap<KA, VA>>);
}
};
}
schema_for_map!([(K, V)]);
schema_for_map!(BTreeSet<(K, V)>);
schema_for_map!(BinaryHeap<(K, V)>);
schema_for_map!(Box<[(K, V)]>);
schema_for_map!(LinkedList<(K, V)>);
schema_for_map!(Vec<(K, V)>);
schema_for_map!(VecDeque<(K, V)>);
impl<K, V, S, KA, VA> JsonSchemaAs<HashSet<(K, V), S>> for Map<KA, VA>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(WrapSchema<BTreeMap<K, V>, BTreeMap<KA, VA>>);
}
impl<T> JsonSchemaAs<Vec<T>> for EnumMap
where
T: JsonSchema,
{
fn schema_name() -> String {
std::format!("EnumMap<{}>", T::schema_name())
}
fn schema_id() -> Cow<'static, str> {
std::format!("serde_with::EnumMap<{}>", T::schema_id()).into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut object = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let inner = T::json_schema(gen).into_object();
let one_of = match inner.subschemas {
Some(subschemas) => match subschemas.one_of {
Some(one_of) => one_of,
None => return object.into(),
},
None => return object.into(),
};
let properties = &mut object.object().properties;
for schema in one_of {
let mut schema = schema.into_object();
if let Some(object) = &mut schema.object {
properties.append(&mut object.properties);
}
}
object.object().additional_properties = Some(Box::new(Schema::Bool(false)));
object.into()
}
fn is_referenceable() -> bool {
true
}
}
impl<T, TA> WrapSchema<Vec<T>, KeyValueMap<TA>>
where
TA: JsonSchemaAs<T>,
{
fn kvmap_transform_schema(gen: &mut SchemaGenerator, schema: &mut Schema) {
let mut parents = Vec::new();
Self::kvmap_transform_schema_impl(gen, schema, &mut parents, 0);
}
fn kvmap_transform_schema_impl(
gen: &mut SchemaGenerator,
schema: &mut Schema,
parents: &mut Vec<String>,
depth: u32,
) {
if depth > 8 {
return;
}
let mut done = false;
let schema = match schema {
Schema::Object(schema) => schema,
_ => return,
};
let mut parents = if let Some(reference) = &schema.reference {
let name = match reference.strip_prefix(&gen.settings().definitions_path) {
Some(name) => name,
None => return,
};
if parents.iter().any(|parent| parent == name) {
return;
}
let name = name.to_owned();
*schema = match gen.definitions().get(&name) {
Some(Schema::Object(schema)) => schema.clone(),
_ => return,
};
parents.push(name);
DropGuard::new(parents, |parents| drop(parents.pop()))
} else {
DropGuard::unguarded(parents)
};
if let Some(object) = &mut schema.object {
done |= object.properties.remove("$key$").is_some();
done |= object.required.remove("$key$");
if let Some(max) = &mut object.max_properties {
*max = max.saturating_sub(1);
}
if let Some(min) = &mut object.max_properties {
*min = min.saturating_sub(1);
}
}
if let Some(array) = &mut schema.array {
if let Some(SingleOrVec::Vec(items)) = &mut array.items {
if !items.is_empty() {
items.remove(0);
done = true;
}
}
if let Some(max) = &mut array.max_items {
*max = max.saturating_sub(1);
}
if let Some(min) = &mut array.min_items {
*min = min.saturating_sub(1);
}
}
if done {
return;
}
let subschemas = match &mut schema.subschemas {
Some(subschemas) => subschemas,
None => return,
};
if let Some(one_of) = &mut subschemas.one_of {
for subschema in one_of {
Self::kvmap_transform_schema_impl(gen, subschema, &mut parents, depth + 1);
}
}
if let Some(any_of) = &mut subschemas.any_of {
for subschema in any_of {
Self::kvmap_transform_schema_impl(gen, subschema, &mut parents, depth + 1);
}
}
if let Some(all_of) = &mut subschemas.all_of {
for subschema in all_of {
Self::kvmap_transform_schema_impl(gen, subschema, &mut parents, depth + 1);
}
}
}
}
impl<T, TA> JsonSchemaAs<Vec<T>> for KeyValueMap<TA>
where
TA: JsonSchemaAs<T>,
{
fn schema_name() -> String {
std::format!("KeyValueMap<{}>", <WrapSchema<T, TA>>::schema_name())
}
fn schema_id() -> Cow<'static, str> {
std::format!(
"serde_with::KeyValueMap<{}>",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut value = <WrapSchema<T, TA>>::json_schema(gen);
<WrapSchema<Vec<T>, KeyValueMap<TA>>>::kvmap_transform_schema(gen, &mut value);
SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(Box::new(ObjectValidation {
additional_properties: Some(Box::new(value)),
..Default::default()
})),
..Default::default()
}
.into()
}
fn is_referenceable() -> bool {
true
}
}
impl<K, V, KA, VA, const N: usize> JsonSchemaAs<[(K, V); N]> for Map<KA, VA>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(WrapSchema<BTreeMap<K, V>, BTreeMap<KA, VA>>);
}
macro_rules! map_first_last_wins_schema {
($(=> $extra:ident)? $type:ty) => {
impl<K, V, $($extra,)? KA, VA> JsonSchemaAs<$type> for MapFirstKeyWins<KA, VA>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(BTreeMap<WrapSchema<K, KA>, WrapSchema<V, VA>>);
}
impl<K, V, $($extra,)? KA, VA> JsonSchemaAs<$type> for MapPreventDuplicates<KA, VA>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(BTreeMap<WrapSchema<K, KA>, WrapSchema<V, VA>>);
}
}
}
map_first_last_wins_schema!(BTreeMap<K, V>);
map_first_last_wins_schema!(=> S HashMap<K, V, S>);
#[cfg(feature = "hashbrown_0_14")]
map_first_last_wins_schema!(=> S hashbrown_0_14::HashMap<K, V, S>);
#[cfg(feature = "indexmap_1")]
map_first_last_wins_schema!(=> S indexmap_1::IndexMap<K, V, S>);
#[cfg(feature = "indexmap_2")]
map_first_last_wins_schema!(=> S indexmap_2::IndexMap<K, V, S>);
impl<T, TA> JsonSchema for WrapSchema<Vec<T>, OneOrMany<TA, PreferOne>>
where
WrapSchema<T, TA>: JsonSchema,
{
fn schema_name() -> String {
std::format!(
"OneOrMany<{}, PreferOne>",
<WrapSchema<T, TA>>::schema_name()
)
}
fn schema_id() -> Cow<'static, str> {
std::format!(
"serde_with::OneOrMany<{}, PreferOne>",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let single = gen.subschema_for::<WrapSchema<T, TA>>();
let array = SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(single.clone().into()),
..Default::default()
})),
..Default::default()
};
SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(std::vec![single, array.into()]),
..Default::default()
})),
..Default::default()
}
.into()
}
}
impl<T, TA> JsonSchema for WrapSchema<Vec<T>, OneOrMany<TA, PreferMany>>
where
WrapSchema<T, TA>: JsonSchema,
{
fn schema_name() -> String {
std::format!(
"OneOrMany<{}, PreferMany>",
<WrapSchema<T, TA>>::schema_name()
)
}
fn schema_id() -> Cow<'static, str> {
std::format!(
"serde_with::OneOrMany<{}, PreferMany>",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let inner = gen.subschema_for::<WrapSchema<T, TA>>();
let single = SchemaObject {
metadata: Some(Box::new(Metadata {
write_only: true,
..Default::default()
})),
subschemas: Some(Box::new(SubschemaValidation {
all_of: Some(std::vec![inner.clone()]),
..Default::default()
})),
..Default::default()
};
let array = SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(Schema::from(single.clone()).into()),
..Default::default()
})),
..Default::default()
};
SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(std::vec![single.into(), array.into()]),
..Default::default()
})),
..Default::default()
}
.into()
}
}
macro_rules! schema_for_pickfirst {
($( $param:ident )+) => {
impl<T, $($param,)+> JsonSchemaAs<T> for PickFirst<($( $param, )+)>
where
$( $param: JsonSchemaAs<T>, )+
{
fn schema_name() -> String {
std::format!(
concat!(
"PickFirst<(",
$( "{", stringify!($param), "}", )+
")>"
),
$( $param = <WrapSchema<T, $param>>::schema_name(), )+
)
}
fn schema_id() -> Cow<'static, str> {
std::format!(
concat!(
"serde_with::PickFirst<(",
$( "{", stringify!($param), "}", )+
")>"
),
$( $param = <WrapSchema<T, $param>>::schema_id(), )+
)
.into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut first = true;
let subschemas = std::vec![$(
{
let is_first = std::mem::replace(&mut first, false);
let schema = gen.subschema_for::<WrapSchema<T, $param>>();
if !is_first {
SchemaObject {
metadata: Some(Box::new(Metadata {
write_only: true,
..Default::default()
})),
subschemas: Some(Box::new(SubschemaValidation {
all_of: Some(std::vec![schema]),
..Default::default()
})),
..Default::default()
}
.into()
} else {
schema
}
}
),+];
SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(subschemas),
..Default::default()
})),
..Default::default()
}
.into()
}
}
}
}
schema_for_pickfirst!(A);
schema_for_pickfirst!(A B);
schema_for_pickfirst!(A B C);
schema_for_pickfirst!(A B C D);
impl<T, TA> JsonSchemaAs<T> for SetLastValueWins<TA>
where
TA: JsonSchemaAs<T>,
{
fn schema_id() -> Cow<'static, str> {
std::format!(
"serde_with::SetLastValueWins<{}>",
<WrapSchema<T, TA> as JsonSchema>::schema_id()
)
.into()
}
fn schema_name() -> String {
std::format!(
"SetLastValueWins<{}>",
<WrapSchema<T, TA> as JsonSchema>::schema_name()
)
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let schema = <WrapSchema<T, TA> as JsonSchema>::json_schema(gen);
let mut schema = schema.into_object();
if let Some(array) = &mut schema.array {
array.unique_items = None;
}
schema.into()
}
fn is_referenceable() -> bool {
false
}
}
impl<T, TA> JsonSchemaAs<T> for SetPreventDuplicates<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(WrapSchema<T, TA>);
}
impl<SEP, T, TA> JsonSchemaAs<T> for StringWithSeparator<SEP, TA>
where
SEP: Separator,
{
forward_schema!(String);
}
impl<T, TA> JsonSchemaAs<Vec<T>> for VecSkipError<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(Vec<WrapSchema<T, TA>>);
}
mod timespan {
use super::*;
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TimespanTargetType {
String,
F64,
U64,
I64,
}
impl TimespanTargetType {
pub const fn is_signed(self) -> bool {
!matches!(self, Self::U64)
}
}
pub trait TimespanSchemaTarget<F> {
const TYPE: TimespanTargetType;
const SIGNED: bool = true;
}
macro_rules! timespan_type_of {
(String) => {
TimespanTargetType::String
};
(f64) => {
TimespanTargetType::F64
};
(i64) => {
TimespanTargetType::I64
};
(u64) => {
TimespanTargetType::U64
};
}
macro_rules! declare_timespan_target {
( $target:ty { $($format:ident),* $(,)? } ) => {
$(
impl TimespanSchemaTarget<$format> for $target {
const TYPE: TimespanTargetType = timespan_type_of!($format);
}
)*
}
}
impl TimespanSchemaTarget<u64> for Duration {
const TYPE: TimespanTargetType = TimespanTargetType::U64;
const SIGNED: bool = false;
}
impl TimespanSchemaTarget<f64> for Duration {
const TYPE: TimespanTargetType = TimespanTargetType::F64;
const SIGNED: bool = false;
}
impl TimespanSchemaTarget<String> for Duration {
const TYPE: TimespanTargetType = TimespanTargetType::String;
const SIGNED: bool = false;
}
declare_timespan_target!(SystemTime { i64, f64, String });
#[cfg(feature = "chrono_0_4")]
declare_timespan_target!(::chrono_0_4::Duration { i64, f64, String });
#[cfg(feature = "chrono_0_4")]
declare_timespan_target!(::chrono_0_4::DateTime<::chrono_0_4::Utc> { i64, f64, String });
#[cfg(feature = "chrono_0_4")]
declare_timespan_target!(::chrono_0_4::DateTime<::chrono_0_4::Local> { i64, f64, String });
#[cfg(feature = "chrono_0_4")]
declare_timespan_target!(::chrono_0_4::NaiveDateTime { i64, f64, String });
#[cfg(feature = "time_0_3")]
declare_timespan_target!(::time_0_3::Duration { i64, f64, String });
#[cfg(feature = "time_0_3")]
declare_timespan_target!(::time_0_3::OffsetDateTime { i64, f64, String });
#[cfg(feature = "time_0_3")]
declare_timespan_target!(::time_0_3::PrimitiveDateTime { i64, f64, String });
}
use self::timespan::{TimespanSchemaTarget, TimespanTargetType};
struct Timespan<Format, Strictness>(PhantomData<(Format, Strictness)>);
impl<T, F> JsonSchemaAs<T> for Timespan<F, Strict>
where
T: TimespanSchemaTarget<F>,
F: Format + JsonSchema,
{
forward_schema!(F);
}
impl TimespanTargetType {
pub(crate) fn to_flexible_schema(self, signed: bool) -> Schema {
use ::schemars_0_8::schema::StringValidation;
let mut number = SchemaObject {
instance_type: Some(InstanceType::Number.into()),
number: (!signed).then(|| {
Box::new(NumberValidation {
minimum: Some(0.0),
..Default::default()
})
}),
..Default::default()
};
let regex = r#"[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?"#;
let mut string = SchemaObject {
instance_type: Some(InstanceType::String.into()),
string: Some(Box::new(StringValidation {
pattern: Some(match signed {
true => std::format!("^-?{regex}$"),
false => std::format!("^{regex}$"),
}),
..Default::default()
})),
..Default::default()
};
if self == Self::String {
number.metadata().write_only = true;
} else {
string.metadata().write_only = true;
}
SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
one_of: Some(std::vec![number.into(), string.into()]),
..Default::default()
})),
..Default::default()
}
.into()
}
pub(crate) fn schema_id(self) -> &'static str {
match self {
Self::String => "serde_with::FlexibleStringTimespan",
Self::F64 => "serde_with::FlexibleF64Timespan",
Self::U64 => "serde_with::FlexibleU64Timespan",
Self::I64 => "serde_with::FlexibleI64Timespan",
}
}
}
impl<T, F> JsonSchemaAs<T> for Timespan<F, Flexible>
where
T: TimespanSchemaTarget<F>,
F: Format + JsonSchema,
{
fn schema_name() -> String {
<T as TimespanSchemaTarget<F>>::TYPE
.schema_id()
.strip_prefix("serde_with::")
.expect("schema id did not start with `serde_with::` - this is a bug")
.into()
}
fn schema_id() -> Cow<'static, str> {
<T as TimespanSchemaTarget<F>>::TYPE.schema_id().into()
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
<T as TimespanSchemaTarget<F>>::TYPE
.to_flexible_schema(<T as TimespanSchemaTarget<F>>::SIGNED)
}
fn is_referenceable() -> bool {
false
}
}
macro_rules! forward_duration_schema {
($ty:ident) => {
impl<T, F> JsonSchemaAs<T> for $ty<F, Strict>
where
T: TimespanSchemaTarget<F>,
F: Format + JsonSchema
{
forward_schema!(WrapSchema<T, Timespan<F, Strict>>);
}
impl<T, F> JsonSchemaAs<T> for $ty<F, Flexible>
where
T: TimespanSchemaTarget<F>,
F: Format + JsonSchema
{
forward_schema!(WrapSchema<T, Timespan<F, Flexible>>);
}
};
}
forward_duration_schema!(DurationSeconds);
forward_duration_schema!(DurationMilliSeconds);
forward_duration_schema!(DurationMicroSeconds);
forward_duration_schema!(DurationNanoSeconds);
forward_duration_schema!(DurationSecondsWithFrac);
forward_duration_schema!(DurationMilliSecondsWithFrac);
forward_duration_schema!(DurationMicroSecondsWithFrac);
forward_duration_schema!(DurationNanoSecondsWithFrac);
forward_duration_schema!(TimestampSeconds);
forward_duration_schema!(TimestampMilliSeconds);
forward_duration_schema!(TimestampMicroSeconds);
forward_duration_schema!(TimestampNanoSeconds);
forward_duration_schema!(TimestampSecondsWithFrac);
forward_duration_schema!(TimestampMilliSecondsWithFrac);
forward_duration_schema!(TimestampMicroSecondsWithFrac);
forward_duration_schema!(TimestampNanoSecondsWithFrac);
struct DropGuard<T, F: FnOnce(T)> {
value: ManuallyDrop<T>,
guard: Option<F>,
}
impl<T, F: FnOnce(T)> DropGuard<T, F> {
pub fn new(value: T, guard: F) -> Self {
Self {
value: ManuallyDrop::new(value),
guard: Some(guard),
}
}
pub fn unguarded(value: T) -> Self {
Self {
value: ManuallyDrop::new(value),
guard: None,
}
}
}
impl<T, F: FnOnce(T)> Deref for DropGuard<T, F> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T, F: FnOnce(T)> DerefMut for DropGuard<T, F> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T, F: FnOnce(T)> Drop for DropGuard<T, F> {
fn drop(&mut self) {
let value = unsafe { ManuallyDrop::take(&mut self.value) };
if let Some(guard) = self.guard.take() {
guard(value);
}
}
}