Skip to content

Commit

Permalink
Add serde_with
Browse files Browse the repository at this point in the history
  • Loading branch information
ModProg committed Jan 17, 2023
1 parent 5268080 commit 47f6280
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
override: true
- if: matrix.rust == '1.45.0'
# work-around https://github.com/serde-rs/serde/issues/2255 and similar crate/rustc compatibility issues
run: cargo update -p serde --precise 1.0.142 && cargo update -p once_cell --precise 1.10.0 && cargo update -p pretty_assertions --precise 1.2.1
run: sed -i '/serde_with =/d' schemars/Cargo.toml && cargo update -p serde --precise 1.0.142 && cargo update -p once_cell --precise 1.10.0 && cargo update -p pretty_assertions --precise 1.2.1
- name: Check with no feature flags
run: cargo check --verbose --no-default-features
continue-on-error: ${{ matrix.allow_failure }}
Expand Down
1 change: 1 addition & 0 deletions schemars/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ bytes = { version = "1.0", optional = true }
rust_decimal = { version = "1", default-features = false, optional = true }
bigdecimal = { version = "0.3", default-features = false, optional = true }
enumset = { version = "1.0", optional = true }
serde_with = { version = "2.2", optional = true, default-features = false, features = ["alloc"] }

[dev-dependencies]
pretty_assertions = "1.2.1"
Expand Down
2 changes: 2 additions & 0 deletions schemars/src/json_schema_impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ mod nonzero_signed;
mod nonzero_unsigned;
mod primitives;
mod sequences;
#[cfg(feature = "serde_with")]
mod serde_with;
mod serdejson;
#[cfg(feature = "smallvec")]
mod smallvec;
Expand Down
139 changes: 139 additions & 0 deletions schemars/src/json_schema_impls/serde_with.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use serde_with::formats::Format;
use serde_with::*;

use crate::gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;

macro_rules! schema_impl {
($ident:ident$(<$($T:ident$(:$Bound:path)?),+>)?, $name:block, $schema:block, $gen:ident) => {
impl$(<$($T: JsonSchema $(+ $Bound)?),+>)? JsonSchema for $ident $(<$($T),+>)? {
fn schema_name() -> String $name

fn json_schema($gen: &mut SchemaGenerator) -> Schema $schema
}
};
($ident:ident$(<$($T:ident$(:$Bound:path)?),+>)? = $alias:ty) => {
schema_impl!($ident$(<$($T$(:$Bound)?),+>)?, { <$alias>::schema_name() }, { <$alias>::json_schema(gen) }, gen);
};
}

schema_impl!(As<T> = T);
schema_impl!(BoolFromInt = u8);
schema_impl!(Bytes = Vec<u8>);
schema_impl!(
BytesOrString,
{ "BytesOrString".to_owned() },
{
let mut schema = SchemaObject::default();
schema.subschemas().any_of = Some(vec![
gen.subschema_for::<Vec<u8>>(),
gen.subschema_for::<String>(),
]);
schema.into()
},
gen
);
schema_impl!(DefaultOnError<T> = T);
schema_impl!(DefaultOnNull<T> = T);
schema_impl!(DisplayFromStr = String);

macro_rules! with_format {
($($ident:ident),*) => {
$( schema_impl!($ident<FORMAT: Format> = FORMAT);)*
};
}
with_format!(
DurationMicroSeconds,
DurationMicroSecondsWithFrac,
DurationMilliSeconds,
DurationMilliSecondsWithFrac,
DurationNanoSeconds,
DurationNanoSecondsWithFrac,
DurationSeconds,
DurationSecondsWithFrac
);

schema_impl!(FromInto<T> = T);
schema_impl!(Map<K, V>,
{ format!("Map_of_{}_to_{}", K::schema_name(), V::schema_name()) },
{
let subschema = gen.subschema_for::<V>();
SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(Box::new(ObjectValidation {
additional_properties: Some(Box::new(subschema)),
..Default::default()
})),
..Default::default()
}
.into()
},
gen
);
schema_impl!(NoneAsEmptyString = Option<String>);
schema_impl!(
OneOrMany<T>,
{ format!("OneOrMany_of_{}", T::schema_name()) },
{
let mut schema = SchemaObject::default();
schema.subschemas().any_of = Some(vec![
gen.subschema_for::<Vec<T>>(),
gen.subschema_for::<T>(),
]);
schema.into()
},
gen
);

impl<T: JsonSchema> JsonSchema for PickFirst<(T,)> {
fn schema_name() -> String {
T::schema_name()
}
fn json_schema(gen: &mut crate::gen::SchemaGenerator) -> Schema {
T::json_schema(gen)
}
}
macro_rules! pick_first {
($($Ts:ident),+, $fmt:literal) => {
impl<$($Ts: JsonSchema),+> JsonSchema for PickFirst<($($Ts),+)> {
fn schema_name() -> String {
format!($fmt, $($Ts::schema_name()),+)
}
fn json_schema(gen: &mut crate::gen::SchemaGenerator) -> Schema {
let mut schema = SchemaObject::default();
schema.subschemas().any_of =
Some(vec![$(gen.subschema_for::<$Ts>()),+]);
schema.into()
}
}
};
}
// only implemented for 1-4
pick_first!(A, B, "{}_or_{}");
pick_first!(A, B, C, "{}_or_{}_or_{}");
pick_first!(A, B, C, D, "{}_or_{}_or_{}_or_{}");

schema_impl!(Seq<T> = Vec<T>);
impl<Sep, T: JsonSchema> JsonSchema for StringWithSeparator<Sep, T> {
fn schema_name() -> String {
T::schema_name()
}
fn json_schema(gen: &mut crate::gen::SchemaGenerator) -> Schema {
T::json_schema(gen)
}
}

with_format!(
TimestampMicroSeconds,
TimestampMicroSecondsWithFrac,
TimestampMilliSeconds,
TimestampMilliSecondsWithFrac,
TimestampNanoSeconds,
TimestampNanoSecondsWithFrac,
TimestampSeconds,
TimestampSecondsWithFrac
);

schema_impl!(TryFromInto<T> = T);
schema_impl!(VecSkipError<T> = Vec<T>);

0 comments on commit 47f6280

Please sign in to comment.