use crate::{
formats::{Flexible, Format, PreferMany, PreferOne, Separator, Strict},
prelude::{Schema as WrapSchema, *},
utils::NumberExt as _,
};
use ::schemars_0_9::{json_schema, JsonSchema, Schema, SchemaGenerator};
use alloc::{
borrow::Cow,
collections::{BTreeMap, BTreeSet},
format,
rc::Rc,
vec::Vec,
};
use serde_json::Value;
pub trait JsonSchemaAs<T: ?Sized> {
fn inline_schema() -> bool {
false
}
fn schema_name() -> Cow<'static, str>;
fn schema_id() -> Cow<'static, str> {
Self::schema_name()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema;
}
impl<T, TA> JsonSchema for WrapSchema<T, TA>
where
T: ?Sized,
TA: JsonSchemaAs<T>,
{
fn schema_name() -> Cow<'static, str> {
TA::schema_name()
}
fn schema_id() -> Cow<'static, str> {
TA::schema_id()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
TA::json_schema(generator)
}
fn inline_schema() -> bool {
TA::inline_schema()
}
}
macro_rules! forward_schema {
($fwd:ty) => {
fn schema_name() -> Cow<'static, str> {
<$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 inline_schema() -> bool {
<$fwd as JsonSchema>::inline_schema()
}
};
}
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>>);
}
#[cfg(feature = "std")]
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>>);
}
#[cfg(feature = "std")]
impl<T, TA, S> JsonSchemaAs<T> for HashSet<TA, S>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(HashSet<WrapSchema<T, TA>, S>);
}
impl<T, TA> JsonSchemaAs<Bound<T>> for Bound<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(Bound<WrapSchema<T, TA>>);
}
impl<T, TA> JsonSchemaAs<Range<T>> for Range<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(Range<WrapSchema<T, TA>>);
}
impl<T, TA> JsonSchemaAs<RangeInclusive<T>> for RangeInclusive<TA>
where
TA: JsonSchemaAs<T>,
{
forward_schema!(RangeInclusive<WrapSchema<T, TA>>);
}
impl<T, TA, const N: usize> JsonSchemaAs<[T; N]> for [TA; N]
where
TA: JsonSchemaAs<T>,
{
fn schema_name() -> Cow<'static, str> {
format!("[{}; {}]", <WrapSchema<T, TA>>::schema_name(), N).into()
}
fn schema_id() -> Cow<'static, str> {
format!("[{}; {}]", <WrapSchema<T, TA>>::schema_id(), N).into()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let (max, min) = match N.try_into() {
Ok(len) => (Some(len), Some(len)),
Err(_) => (None, Some(u32::MAX)),
};
json_schema!({
"type": "array",
"items": generator.subschema_for::<WrapSchema<T, TA>>(),
"maxItems": max,
"minItems": min
})
}
fn inline_schema() -> bool {
true
}
}
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);
}
#[cfg(feature = "hex")]
impl<T, F: formats::Format> JsonSchemaAs<T> for hex::Hex<F> {
fn schema_name() -> Cow<'static, str> {
"Hex<F>".into()
}
fn schema_id() -> Cow<'static, str> {
"serde_with::hex::Hex<F>".into()
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "string",
"pattern": r"^(?:[0-9A-Fa-f]{2})*$",
})
}
fn inline_schema() -> bool {
true
}
}
impl JsonSchemaAs<bool> for BoolFromInt<Strict> {
fn schema_name() -> Cow<'static, str> {
"BoolFromInt<Strict>".into()
}
fn schema_id() -> Cow<'static, str> {
"serde_with::BoolFromInt<Strict>".into()
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "integer",
"minimum": 0.0,
"maximum": 1.0
})
}
fn inline_schema() -> bool {
true
}
}
impl JsonSchemaAs<bool> for BoolFromInt<Flexible> {
fn schema_name() -> Cow<'static, str> {
"BoolFromInt<Flexible>".into()
}
fn schema_id() -> Cow<'static, str> {
"serde_with::BoolFromInt<Flexible>".into()
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
json_schema!({
"type": "integer",
})
}
fn inline_schema() -> bool {
true
}
}
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() -> Cow<'static, str> {
"BytesOrString".into()
}
fn schema_id() -> Cow<'static, str> {
"serde_with::BytesOrString".into()
}
fn json_schema(g: &mut SchemaGenerator) -> Schema {
json_schema!({
"anyOf": [
g.subschema_for::<Vec<u8>>(),
{
"type": "string",
"writeOnly": true
}
]
})
}
fn inline_schema() -> bool {
true
}
}
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)>);
#[cfg(feature = "std")]
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() -> Cow<'static, str> {
format!("EnumMap({})", T::schema_name()).into()
}
fn schema_id() -> Cow<'static, str> {
format!("serde_with::EnumMap({})", T::schema_id()).into()
}
fn json_schema(g: &mut SchemaGenerator) -> Schema {
let mut inner_schema = T::json_schema(g);
let inner = inner_schema.ensure_object();
let one_of = match inner.get_mut("oneOf") {
Some(Value::Array(one_of)) => one_of,
_ => return inner_schema,
};
let mut properties = serde_json::Map::new();
for schema in one_of {
let schema = match schema {
Value::Object(schema) => schema,
_ => continue,
};
if let Some(Value::Object(props)) = schema.get_mut("properties") {
properties.extend(core::mem::take(props));
}
}
json_schema!({
"type": "object",
"properties": properties,
"additionalProperties": false
})
}
fn inline_schema() -> bool {
false
}
}
impl<T, TA> WrapSchema<Vec<T>, KeyValueMap<TA>>
where
TA: JsonSchemaAs<T>,
{
fn kvmap_transform_schema_0_9(g: &mut SchemaGenerator, schema: &mut Schema) {
let mut parents = Vec::new();
let mut value = if let Some(object) = schema.as_object_mut() {
Value::Object(core::mem::take(object))
} else if let Some(value) = schema.as_bool() {
Value::Bool(value)
} else {
unreachable!()
};
Self::kvmap_transform_schema_impl_0_9(g, &mut value, &mut parents, 0);
*schema = Schema::try_from(value).expect("modified value was not an object or boolean");
}
fn kvmap_transform_schema_impl_0_9(
g: &mut SchemaGenerator,
schema: &mut Value,
parents: &mut Vec<String>,
depth: u32,
) {
if depth > 8 {
return;
}
let mut done = false;
let schema = match schema.as_object_mut() {
Some(schema) => schema,
_ => return,
};
let mut parents = if let Some(reference) = &schema.get("$ref") {
let reference = match reference {
Value::String(reference) => &**reference,
_ => return,
};
let name = match Self::resolve_reference_0_9(g, reference) {
Some(name) => name,
None => return,
};
if parents.iter().any(|parent| parent == name) {
return;
}
let name = name.to_owned();
*schema = match g.definitions().get(&name) {
Some(Value::Object(schema)) => schema.clone(),
_ => return,
};
parents.push(name);
utils::DropGuard::new(parents, |parents| drop(parents.pop()))
} else {
utils::DropGuard::unguarded(parents)
};
let ty = match schema.get("type") {
Some(Value::String(ty)) if ty == "object" => Some("object"),
Some(Value::String(ty)) if ty == "array" => Some("array"),
_ => None,
};
if ty == Some("object") {
if let Some(Value::Object(properties)) = schema.get_mut("properties") {
done |= properties.remove("$key$").is_some();
}
if let Some(Value::Array(required)) = schema.get_mut("required") {
required.retain(|req| match req {
Value::String(key) if key == "$key$" => {
done = true;
false
}
_ => true,
});
}
if let Some(Value::Number(max)) = schema.get_mut("maxProperties") {
*max = max.saturating_sub(1);
}
if let Some(Value::Number(min)) = schema.get_mut("minProperties") {
*min = min.saturating_sub(1);
}
}
if ty == Some("array") {
if let Some(Value::Array(items)) = schema.get_mut("prefixItems") {
if !items.is_empty() {
items.remove(0);
done = true;
}
}
if let Some(Value::Array(items)) = schema.get_mut("items") {
if !items.is_empty() {
items.remove(0);
done = true;
}
}
if let Some(Value::Number(max)) = schema.get_mut("maxItems") {
*max = max.saturating_sub(1);
}
if let Some(Value::Number(min)) = schema.get_mut("minItems") {
*min = min.saturating_sub(1);
}
}
if done {
return;
}
if let Some(Value::Array(one_of)) = schema.get_mut("oneOf") {
for subschema in one_of {
Self::kvmap_transform_schema_impl_0_9(g, subschema, &mut parents, depth + 1);
}
}
if let Some(Value::Array(any_of)) = schema.get_mut("anyOf") {
for subschema in any_of {
Self::kvmap_transform_schema_impl_0_9(g, subschema, &mut parents, depth + 1);
}
}
if let Some(Value::Array(all_of)) = schema.get_mut("allOf") {
for subschema in all_of {
Self::kvmap_transform_schema_impl_0_9(g, subschema, &mut parents, depth + 1);
}
}
}
fn resolve_reference_0_9<'a>(g: &mut SchemaGenerator, reference: &'a str) -> Option<&'a str> {
let reference = reference.strip_prefix('#')?;
let defpath: &str = &g.settings().definitions_path;
let defpath = defpath.strip_prefix("#").unwrap_or(defpath);
let mut reference = reference.strip_prefix(defpath)?;
if !defpath.ends_with('/') {
reference = reference.strip_prefix('/').unwrap_or(reference);
}
Some(reference)
}
}
impl<T, TA> JsonSchemaAs<Vec<T>> for KeyValueMap<TA>
where
TA: JsonSchemaAs<T>,
{
fn schema_name() -> Cow<'static, str> {
format!("KeyValueMap({})", <WrapSchema<T, TA>>::schema_name()).into()
}
fn schema_id() -> Cow<'static, str> {
format!(
"serde_with::KeyValueMap({})",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}
fn json_schema(g: &mut SchemaGenerator) -> Schema {
let mut value = <WrapSchema<T, TA>>::json_schema(g);
<WrapSchema<Vec<T>, KeyValueMap<TA>>>::kvmap_transform_schema_0_9(g, &mut value);
json_schema!({
"type": "object",
"additionalProperties": value
})
}
}
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>);
#[cfg(feature = "std")]
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 = "hashbrown_0_15")]
map_first_last_wins_schema!(=> S hashbrown_0_15::HashMap<K, V, S>);
#[cfg(feature = "hashbrown_0_16")]
map_first_last_wins_schema!(=> S hashbrown_0_16::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> JsonSchemaAs<Vec<T>> for OneOrMany<TA, PreferOne>
where
TA: JsonSchemaAs<T>,
{
fn schema_name() -> Cow<'static, str> {
format!(
"OneOrMany({},PreferOne)",
<WrapSchema<T, TA>>::schema_name()
)
.into()
}
fn schema_id() -> Cow<'static, str> {
format!(
"serde_with::OneOrMany({},PreferOne)",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}
fn json_schema(g: &mut SchemaGenerator) -> Schema {
let single = g.subschema_for::<WrapSchema<T, TA>>();
json_schema!({
"anyOf": [
single,
{
"type": "array",
"items": single
}
]
})
}
}
impl<T, TA> JsonSchemaAs<Vec<T>> for OneOrMany<TA, PreferMany>
where
TA: JsonSchemaAs<T>,
{
fn schema_name() -> Cow<'static, str> {
format!(
"OneOrMany<{}, PreferMany>",
<WrapSchema<T, TA>>::schema_name()
)
.into()
}
fn schema_id() -> Cow<'static, str> {
format!(
"serde_with::OneOrMany<{}, PreferMany>",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}
fn json_schema(g: &mut SchemaGenerator) -> Schema {
let inner = g.subschema_for::<WrapSchema<T, TA>>();
json_schema!({
"anyOf": [
{
"writeOnly": true,
"allOf": [
inner
],
},
{
"type": "array",
"items": inner
}
]
})
}
}
macro_rules! schema_for_pickfirst {
($( $param:ident )+) => {
impl<T, $($param,)+> JsonSchemaAs<T> for PickFirst<($( $param, )+)>
where
$( $param: JsonSchemaAs<T>, )+
{
fn schema_name() -> Cow<'static, str> {
format!(
concat!(
"PickFirst(",
$( "{", stringify!($param), "}", )+
")"
),
$( $param = <WrapSchema<T, $param>>::schema_name(), )+
)
.into()
}
fn schema_id() -> Cow<'static, str> {
format!(
concat!(
"serde_with::PickFirst(",
$( "{", stringify!($param), "}", )+
")"
),
$( $param = <WrapSchema<T, $param>>::schema_id(), )+
)
.into()
}
fn json_schema(g: &mut SchemaGenerator) -> Schema {
let mut first = true;
let subschemas = alloc::vec![$(
{
let is_first = core::mem::replace(&mut first, false);
let schema = g.subschema_for::<WrapSchema<T, $param>>();
if !is_first {
json_schema!({
"writeOnly": true,
"allOf": [schema]
})
} else {
schema
}
}
),+];
json_schema!({
"anyOf": subschemas
})
}
}
}
}
schema_for_pickfirst!(A);
schema_for_pickfirst!(A B);
schema_for_pickfirst!(A B C);
schema_for_pickfirst!(A B C D);
macro_rules! map_first_last_wins_schema {
($(=> $extra:ident)? $type:ty) => {
impl<V, $($extra,)? VA> JsonSchemaAs<$type> for SetLastValueWins<VA>
where
VA: JsonSchemaAs<V>,
{
fn schema_id() -> Cow<'static, str> {
format!(
"serde_with::SetLastValueWins<{}>",
<WrapSchema<V, VA> as JsonSchema>::schema_id()
)
.into()
}
fn schema_name() -> Cow<'static, str> {
format!(
"SetLastValueWins<{}>",
<WrapSchema<V, VA> as JsonSchema>::schema_name()
)
.into()
}
fn json_schema(g: &mut SchemaGenerator) -> Schema {
let mut schema = <BTreeSet<WrapSchema<V, VA>> as JsonSchema>::json_schema(g);
let object = schema.ensure_object();
object.remove("uniqueItems");
schema
}
fn inline_schema() -> bool {
<WrapSchema<V, VA> as JsonSchema>::inline_schema()
}
}
impl<V, $($extra,)? VA> JsonSchemaAs<$type> for SetPreventDuplicates<VA>
where
VA: JsonSchemaAs<V>,
{
forward_schema!(BTreeSet<WrapSchema<V, VA>>);
}
}
}
map_first_last_wins_schema!(BTreeSet<V>);
#[cfg(feature = "std")]
map_first_last_wins_schema!(=> S HashSet<V, S>);
#[cfg(feature = "hashbrown_0_14")]
map_first_last_wins_schema!(=> S hashbrown_0_14::HashSet<V, S>);
#[cfg(feature = "hashbrown_0_15")]
map_first_last_wins_schema!(=> S hashbrown_0_15::HashSet<V, S>);
#[cfg(feature = "hashbrown_0_16")]
map_first_last_wins_schema!(=> S hashbrown_0_16::HashSet<V, S>);
#[cfg(feature = "indexmap_1")]
map_first_last_wins_schema!(=> S indexmap_1::IndexSet<V, S>);
#[cfg(feature = "indexmap_2")]
map_first_last_wins_schema!(=> S indexmap_2::IndexSet<V, S>);
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]
pub enum TimespanTargetType {
String,
F64,
U64,
I64,
}
pub trait TimespanSchemaTarget<F> {
const TYPE: TimespanTargetType;
const SIGNED: bool = true;
}
#[cfg_attr(not(feature = "std"), allow(unused_macros))]
macro_rules! timespan_type_of {
(String) => {
TimespanTargetType::String
};
(f64) => {
TimespanTargetType::F64
};
(i64) => {
TimespanTargetType::I64
};
(u64) => {
TimespanTargetType::U64
};
}
#[cfg_attr(not(feature = "std"), allow(unused_macros))]
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;
}
#[cfg(feature = "std")]
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(all(feature = "chrono_0_4", feature = "std"))]
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 into_flexible_schema(self, signed: bool) -> Schema {
let mut number = json_schema!({
"type": "number"
});
if !signed {
number
.ensure_object()
.insert("minimum".into(), serde_json::json!(0.0));
}
let regex = r#"[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?"#;
let mut string = json_schema!({
"type": "string",
"pattern": match signed {
true => format!("^-?{regex}$"),
false => format!("^{regex}$"),
}
});
if matches!(self, Self::String) {
number
.ensure_object()
.insert("writeOnly".into(), true.into());
} else {
string
.ensure_object()
.insert("writeOnly".into(), true.into());
}
json_schema!({
"oneOf": [number, string]
})
}
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() -> Cow<'static, str> {
<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
.into_flexible_schema(<T as TimespanSchemaTarget<F>>::SIGNED)
}
fn inline_schema() -> bool {
true
}
}
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);
#[cfg(feature = "json")]
impl<T> JsonSchemaAs<T> for json::JsonString {
forward_schema!(String);
}