Repository: GREsau/schemars (rust)

Issue:
Controlling referenceability in derived schemas
Currently, if I want to make a particular schema appear inline I must implement the entirety `JsonSchema` myself, even though all I want to do is overriding `fn is_referenceable() -> bool { false }`. It would be great if there was an attribute that would let me control referenceability like there are for other options.

Pull request:
Add support for inlining schemas with derive macros (`#[schemars(always_inline)]`)
Closes #211.

This PR allows configuring `JsonSchema::always_inline_schema` through the derive API, without needing to manually implement the trait.

Also adds a brief description of this attribute to the docs.

```rust
#[derive(JsonSchema)]
struct Foo { ... }

#[derive(JsonSchema)]
#[schemars(always_inline)]
struct Bar { ... }

assert_eq!(Foo::always_inline_schema(), false);
assert_eq!(Bar::always_inline_schema(), true);
```

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 972cdfe..f2d55ae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -139,9 +139,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
 
 [[package]]
 name = "bytes"
-version = "1.9.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
+checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
 dependencies = [
  "serde",
 ]
@@ -245,9 +245,9 @@ dependencies = [
 
 [[package]]
 name = "dyn-clone"
-version = "1.0.17"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35"
 
 [[package]]
 name = "either"
@@ -537,6 +537,35 @@ version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
 
+[[package]]
+name = "jiff"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba926fdd8e5b5e7f9700355b0831d8c416afe94b014b1023424037a187c9c582"
+dependencies = [
+ "jiff-tzdb-platform",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+ "windows-sys",
+]
+
+[[package]]
+name = "jiff-tzdb"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf2cec2f5d266af45a071ece48b1fb89f3b00b2421ac3a5fe10285a6caaa60d3"
+
+[[package]]
+name = "jiff-tzdb-platform"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a63c62e404e7b92979d2792352d885a7f8f83fd1d0d31eea582d77b2ceca697e"
+dependencies = [
+ "jiff-tzdb",
+]
+
 [[package]]
 name = "jsonschema"
 version = "0.28.3"
@@ -585,6 +614,12 @@ version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
 
+[[package]]
+name = "log"
+version = "0.4.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
+
 [[package]]
 name = "memchr"
 version = "2.7.4"
@@ -678,9 +713,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.20.2"
+version = "1.20.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
 
 [[package]]
 name = "outref"
@@ -694,6 +729,21 @@ version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
+[[package]]
+name = "portable-atomic"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
 [[package]]
 name = "pretty_assertions"
 version = "1.4.1"
@@ -825,9 +875,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
 
 [[package]]
 name = "ryu"
-version = "1.0.18"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
 
 [[package]]
 name = "schemars"
@@ -841,6 +891,7 @@ dependencies = [
  "either",
  "garde",
  "indexmap",
+ "jiff",
  "jsonschema",
  "pretty_assertions",
  "ref-cast",
@@ -913,9 +964,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.137"
+version = "1.0.138"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
+checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
 dependencies = [
  "indexmap",
  "itoa",
@@ -1012,9 +1063,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.96"
+version = "2.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1059,9 +1110,9 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.8.19"
+version = "0.8.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -1080,9 +1131,9 @@ dependencies = [
 
 [[package]]
 name = "toml_edit"
-version = "0.22.22"
+version = "0.22.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee"
 dependencies = [
  "indexmap",
  "serde",
@@ -1108,9 +1159,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.15"
+version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
 
 [[package]]
 name = "url"
@@ -1144,9 +1195,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "uuid"
-version = "1.12.1"
+version = "1.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
+checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0"
 dependencies = [
  "serde",
 ]
@@ -1294,9 +1345,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winnow"
-version = "0.6.24"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
+checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f"
 dependencies = [
  "memchr",
 ]
diff --git a/README.md b/README.md
index fc68efc..c441b22 100644
--- a/README.md
+++ b/README.md
@@ -269,6 +269,7 @@ Schemars can implement `JsonSchema` on types from several popular crates, enable
 - `chrono04` - [chrono](https://crates.io/crates/chrono) (^0.4)
 - `either1` - [either](https://crates.io/crates/either) (^1.3)
 - `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0)
+- `jiff02` - [jiff](https://crates.io/crates/jiff) (^0.2)
 - `rust_decimal1` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0)
 - `semver1` - [semver](https://crates.io/crates/semver) (^1.0.9)
 - `smallvec1` - [smallvec](https://crates.io/crates/smallvec) (^1.0)
diff --git a/docs/4-features.md b/docs/4-features.md
index 1141154..246dbc2 100644
--- a/docs/4-features.md
+++ b/docs/4-features.md
@@ -19,6 +19,7 @@ Schemars can implement `JsonSchema` on types from several popular crates, enable
 - `chrono04` - [chrono](https://crates.io/crates/chrono) (^0.4)
 - `either1` - [either](https://crates.io/crates/either) (^1.3)
 - `indexmap2` - [indexmap](https://crates.io/crates/indexmap) (^2.0)
+- `jiff02` - [jiff](https://crates.io/crates/jiff) (^0.2)
 - `rust_decimal1` - [rust_decimal](https://crates.io/crates/rust_decimal) (^1.0)
 - `semver1` - [semver](https://crates.io/crates/semver) (^1.0.9)
 - `smallvec1` - [smallvec](https://crates.io/crates/smallvec) (^1.0)
diff --git a/docs/_includes/attributes.md b/docs/_includes/attributes.md
index 1497b0f..0d23efd 100644
--- a/docs/_includes/attributes.md
+++ b/docs/_includes/attributes.md
@@ -36,6 +36,7 @@ TABLE OF CONTENTS
    - [`title` / `description`](#title-description)
    - [`example`](#example)
    - [`deprecated`](#deprecated)
+   - [`inline`](#inline)
    - [`crate`](#crate)
    - [`extend`](#extend)
    - [`transform`](#transform)
@@ -316,6 +317,14 @@ Alternatively, to directly set multiple examples without repeating `example = ..
 
 Set the Rust built-in [`deprecated`](https://doc.rust-lang.org/edition-guide/rust-2018/the-compiler/an-attribute-for-deprecation.html) attribute on a struct, enum, field or variant to set the generated schema's `deprecated` keyword to `true`.
 
+<h3 id="inline">
+
+`#[schemars(inline)]`
+
+</h3>
+
+Set the return value of [`always_inline_schema`](trait.JsonSchema.html#method.always_inline_schema) to `true` to include JSON schemas generated for this type directly in parent schemas, rather than being re-used where possible using the `$ref` keyword.
+
 <h3 id="crate">
 
 `#[schemars(crate = "other_crate::schemars")]`
diff --git a/docs/_includes/example_v0.md b/docs/_includes/example_v0.md
index 8868524..eef7724 100644
--- a/docs/_includes/example_v0.md
+++ b/docs/_includes/example_v0.md
@@ -1,5 +1,5 @@
-{% capture input %}examples/{{ include.name }}.rs{% endcapture %}
-{% capture output %}examples/{{ include.name }}.schema.json{% endcapture %}
+{% capture input %}examples_v0/{{ include.name }}.rs{% endcapture %}
+{% capture output %}examples_v0/{{ include.name }}.schema.json{% endcapture %}
 
 ```rust
 {% include {{ input }} %}
@@ -11,4 +11,5 @@
 ```json
 {% include {{ output }} -%}
 ```
+
 </details>
diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml
index 49b7092..c0e04ca 100644
--- a/schemars/Cargo.toml
+++ b/schemars/Cargo.toml
@@ -26,6 +26,7 @@ bytes1 = { version = "1.0", default-features = false, optional = true, package =
 chrono04 = { version = "0.4", default-features = false, optional = true, package = "chrono" }
 either1 = { version = "1.3", default-features = false, optional = true, package = "either" }
 indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" }
+jiff02 = { version = "0.2", default-features = false, optional = true, package = "jiff" }
 rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal" }
 semver1 = { version = "1.0.9", default-features = false, optional = true, package = "semver" }
 smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" }
@@ -50,6 +51,7 @@ bytes1 = { version = "1.0", default-features = false, features = ["serde"], pack
 chrono04 = { version = "0.4", default-features = false, features = ["serde"], package = "chrono" }
 either1 = { version = "1.3", default-features = false, features = ["serde"], package = "either" }
 indexmap2 = { version = "2.0", default-features = false, features = ["serde"], package = "indexmap" }
+jiff02 = { version = "0.2", features = ["serde"], package = "jiff" }
 rust_decimal1 = { version = "1", default-features = false, features = ["serde"], package = "rust_decimal" }
 semver1 = { version = "1.0.9", default-features = false, features = ["serde"], package = "semver" }
 smallvec1 = { version = "1.0", default-features = false, features = ["serde"], package = "smallvec" }
diff --git a/schemars/src/_private/mod.rs b/schemars/src/_private/mod.rs
index aa5c6fe..baf90f9 100644
--- a/schemars/src/_private/mod.rs
+++ b/schemars/src/_private/mod.rs
@@ -279,7 +279,7 @@ pub fn flatten(schema: &mut Schema, other: Schema) {
                     _ => {
                         // leave the original value as it is (don't modify `schema`)
                     }
-                };
+                }
             }
         }
     }
diff --git a/schemars/src/json_schema_impls/core.rs b/schemars/src/json_schema_impls/core.rs
index 07fbce5..489c6f4 100644
--- a/schemars/src/json_schema_impls/core.rs
+++ b/schemars/src/json_schema_impls/core.rs
@@ -57,7 +57,7 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
             schema
                 .ensure_object()
                 .insert("nullable".into(), true.into());
-        };
+        }
 
         schema
     }
diff --git a/schemars/src/json_schema_impls/jiff02.rs b/schemars/src/json_schema_impls/jiff02.rs
new file mode 100644
index 0000000..1b43fbc
--- /dev/null
+++ b/schemars/src/json_schema_impls/jiff02.rs
@@ -0,0 +1,38 @@
+use crate::{json_schema, JsonSchema, Schema, SchemaGenerator};
+use alloc::borrow::Cow;
+use jiff02::civil::{Date, DateTime, Time};
+use jiff02::{SignedDuration, Span, Timestamp, Zoned};
+
+macro_rules! formatted_string_impl {
+    ($ty:ident, $format:literal) => {
+        formatted_string_impl!($ty, $format, JsonSchema for $ty);
+    };
+    ($ty:ident, $format:literal, $($desc:tt)+) => {
+        impl $($desc)+ {
+            always_inline!();
+
+            fn schema_name() -> Cow<'static, str> {
+                stringify!($ty).into()
+            }
+
+            fn schema_id() -> Cow<'static, str>  {
+                stringify!(jiff::$ty).into()
+            }
+
+            fn json_schema(_: &mut SchemaGenerator) -> Schema {
+                json_schema!({
+                    "type": "string",
+                    "format": $format
+                })
+            }
+        }
+    };
+}
+
+formatted_string_impl!(SignedDuration, "duration");
+formatted_string_impl!(Span, "duration");
+formatted_string_impl!(Timestamp, "date-time");
+formatted_string_impl!(Zoned, "zoned-date-time");
+formatted_string_impl!(Date, "date");
+formatted_string_impl!(Time, "partial-time");
+formatted_string_impl!(DateTime, "partial-date-time");
diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs
index cbe1a07..089620b 100644
--- a/schemars/src/json_schema_impls/mod.rs
+++ b/schemars/src/json_schema_impls/mod.rs
@@ -74,6 +74,9 @@ mod either1;
 #[cfg(feature = "indexmap2")]
 mod indexmap2;
 
+#[cfg(feature = "jiff02")]
+mod jiff02;
+
 #[cfg(feature = "semver1")]
 mod semver1;
 
diff --git a/schemars/src/ser.rs b/schemars/src/ser.rs
index 079c566..1227af0 100644
--- a/schemars/src/ser.rs
+++ b/schemars/src/ser.rs
@@ -167,7 +167,7 @@ impl<'a> serde::Serializer for Serializer<'a> {
             schema
                 .ensure_object()
                 .insert("nullable".into(), true.into());
-        };
+        }
 
         Ok(schema)
     }
diff --git a/schemars_derive/src/attr/mod.rs b/schemars_derive/src/attr/mod.rs
index 37350d3..1342130 100644
--- a/schemars_derive/src/attr/mod.rs
+++ b/schemars_derive/src/attr/mod.rs
@@ -3,7 +3,9 @@ mod parse_meta;
 mod schemars_to_serde;
 mod validation;
 
-use parse_meta::{parse_extensions, parse_name_value_expr, parse_name_value_lit_str};
+use parse_meta::{
+    parse_extensions, parse_name_value_expr, parse_name_value_lit_str, require_path_only,
+};
 use proc_macro2::TokenStream;
 use quote::ToTokens;
 use serde_derive_internals::Ctxt;
@@ -39,6 +41,7 @@ pub struct ContainerAttrs {
     pub repr: Option<Type>,
     pub crate_name: Option<Path>,
     pub is_renamed: bool,
+    pub inline: bool,
 }
 
 #[derive(Debug, Default)]
@@ -299,6 +302,14 @@ impl ContainerAttrs {
             // The actual parsing of `rename` is done by serde
             "rename" => self.is_renamed = true,
 
+            "inline" => {
+                if self.inline {
+                    cx.duplicate_error(&meta);
+                } else if require_path_only(meta, cx).is_ok() {
+                    self.inline = true;
+                }
+            }
+
             _ => return Some(meta),
         };
 
diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs
index 8d8ccd0..2ac7278 100644
--- a/schemars_derive/src/lib.rs
+++ b/schemars_derive/src/lib.rs
@@ -178,6 +178,8 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result<To
         schema_exprs::expr_for_container(&cont)
     };
 
+    let inline = cont.attrs.inline;
+
     Ok(quote! {
         const _: () = {
             #crate_alias
@@ -196,6 +198,10 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result<To
                 fn json_schema(#GENERATOR: &mut schemars::SchemaGenerator) -> schemars::Schema {
                     #schema_expr
                 }
+
+                fn always_inline_schema() -> bool {
+                    #inline
+                }
             };
         };
     })


Test patch:
diff --git a/schemars/tests/integration/garde.rs b/schemars/tests/integration/garde.rs
index e7ca2a5..54c3786 100644
--- a/schemars/tests/integration/garde.rs
+++ b/schemars/tests/integration/garde.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::incompatible_msrv)]
+
 use crate::prelude::*;
 use garde::Validate;
 
diff --git a/schemars/tests/integration/jiff.rs b/schemars/tests/integration/jiff.rs
new file mode 100644
index 0000000..441dd5e
--- /dev/null
+++ b/schemars/tests/integration/jiff.rs
@@ -0,0 +1,62 @@
+use crate::prelude::*;
+use jiff02::civil::{Date, DateTime, Time};
+use jiff02::{SignedDuration, Span, Timestamp, Zoned};
+
+#[derive(JsonSchema, Serialize, Deserialize)]
+struct JiffTypes {
+    date_time_ts: Timestamp,
+    date_time_zoned: Zoned,
+    naive_date: Date,
+    naive_date_time: DateTime,
+    naive_time: Time,
+    duration: SignedDuration,
+    span: Span,
+}
+
+#[test]
+fn jiff() {
+    test!(JiffTypes).assert_snapshot();
+
+    test!(Timestamp)
+        .assert_allows_ser_roundtrip_default()
+        .assert_matches_de_roundtrip(arbitrary_values());
+
+    test!(Zoned)
+        .assert_allows_ser_roundtrip_default()
+        .assert_matches_de_roundtrip(arbitrary_values_except(
+            Value::is_string,
+            "Custom format 'zoned-date-time', so arbitrary strings technically allowed by schema",
+        ));
+
+    test!(Date)
+        .assert_allows_ser_roundtrip_default()
+        .assert_matches_de_roundtrip(arbitrary_values());
+
+    test!(DateTime)
+        .assert_allows_ser_roundtrip_default()
+        .assert_matches_de_roundtrip(arbitrary_values_except(
+            Value::is_string,
+            "Custom format 'partial-date-time', so arbitrary strings technically allowed by schema",
+        ));
+
+    test!(Time)
+        .assert_allows_ser_roundtrip_default()
+        .assert_matches_de_roundtrip(arbitrary_values_except(
+            Value::is_string,
+            "Custom format 'date-time', so arbitrary strings technically allowed by schema",
+        ));
+
+    test!(SignedDuration)
+        .assert_allows_ser_roundtrip_default()
+        .assert_matches_de_roundtrip(arbitrary_values_except(
+            Value::is_string,
+            "jsonschema 0.20 does not handle durations correctly - this can be removed once MSRV is increased",
+        ));
+
+    test!(Span)
+        .assert_allows_ser_roundtrip_default()
+        .assert_matches_de_roundtrip(arbitrary_values_except(
+            Value::is_string,
+            "jsonschema 0.20 does not handle durations correctly - this can be removed once MSRV is increased",
+        ));
+}
diff --git a/schemars/tests/integration/main.rs b/schemars/tests/integration/main.rs
index eb44526..0cbacb9 100644
--- a/schemars/tests/integration/main.rs
+++ b/schemars/tests/integration/main.rs
@@ -28,6 +28,8 @@ mod garde;
 #[cfg(feature = "indexmap2")]
 mod indexmap;
 mod inline_subschemas;
+#[cfg(feature = "jiff02")]
+mod jiff;
 mod macros;
 mod remote_derive;
 mod same_name;
diff --git a/schemars/tests/integration/snapshots/schemars/tests/integration/jiff.rs~jiff.json b/schemars/tests/integration/snapshots/schemars/tests/integration/jiff.rs~jiff.json
new file mode 100644
index 0000000..b7c89d4
--- /dev/null
+++ b/schemars/tests/integration/snapshots/schemars/tests/integration/jiff.rs~jiff.json
@@ -0,0 +1,44 @@
+{
+  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "title": "JiffTypes",
+  "type": "object",
+  "properties": {
+    "date_time_ts": {
+      "type": "string",
+      "format": "date-time"
+    },
+    "date_time_zoned": {
+      "type": "string",
+      "format": "zoned-date-time"
+    },
+    "naive_date": {
+      "type": "string",
+      "format": "date"
+    },
+    "naive_date_time": {
+      "type": "string",
+      "format": "partial-date-time"
+    },
+    "naive_time": {
+      "type": "string",
+      "format": "partial-time"
+    },
+    "duration": {
+      "type": "string",
+      "format": "duration"
+    },
+    "span": {
+      "type": "string",
+      "format": "duration"
+    }
+  },
+  "required": [
+    "date_time_ts",
+    "date_time_zoned",
+    "naive_date",
+    "naive_date_time",
+    "naive_time",
+    "duration",
+    "span"
+  ]
+}
\ No newline at end of file
diff --git a/schemars/tests/ui/invalid_attrs.rs b/schemars/tests/ui/invalid_attrs.rs
index 0eea93b..dc6a689 100644
--- a/schemars/tests/ui/invalid_attrs.rs
+++ b/schemars/tests/ui/invalid_attrs.rs
@@ -1,11 +1,27 @@
 use schemars::JsonSchema;
 
 #[derive(JsonSchema)]
-#[serde(default = 0, foo, deny_unknown_fields, deny_unknown_fields)]
+#[serde(
+    default = 0,
+    foo,
+    deny_unknown_fields,
+    deny_unknown_fields,
+    inline = 1,
+    inline,
+    inline
+)]
 pub struct Struct1;
 
 #[derive(JsonSchema)]
-#[schemars(default = 0, foo, deny_unknown_fields, deny_unknown_fields)]
+#[schemars(
+    default = 0,
+    foo,
+    deny_unknown_fields,
+    deny_unknown_fields,
+    inline = 1,
+    inline,
+    inline
+)]
 pub struct Struct2;
 
 fn main() {}
diff --git a/schemars/tests/ui/invalid_attrs.stderr b/schemars/tests/ui/invalid_attrs.stderr
index 4823859..9c8a3c7 100644
--- a/schemars/tests/ui/invalid_attrs.stderr
+++ b/schemars/tests/ui/invalid_attrs.stderr
@@ -1,29 +1,41 @@
 error: expected serde default attribute to be a string: `default = "..."`
- --> $DIR/invalid_attrs.rs:4:19
+ --> tests/ui/invalid_attrs.rs:5:15
   |
-4 | #[serde(default = 0, foo, deny_unknown_fields, deny_unknown_fields)]
-  |                   ^
+5 |     default = 0,
+  |               ^
 
 error: duplicate serde attribute `deny_unknown_fields`
- --> $DIR/invalid_attrs.rs:4:48
+ --> tests/ui/invalid_attrs.rs:8:5
   |
-4 | #[serde(default = 0, foo, deny_unknown_fields, deny_unknown_fields)]
-  |                                                ^^^^^^^^^^^^^^^^^^^
+8 |     deny_unknown_fields,
+  |     ^^^^^^^^^^^^^^^^^^^
 
 error: expected serde default attribute to be a string: `default = "..."`
- --> $DIR/invalid_attrs.rs:8:22
-  |
-8 | #[schemars(default = 0, foo, deny_unknown_fields, deny_unknown_fields)]
-  |                      ^
+  --> tests/ui/invalid_attrs.rs:17:15
+   |
+17 |     default = 0,
+   |               ^
 
 error: duplicate serde attribute `deny_unknown_fields`
- --> $DIR/invalid_attrs.rs:8:51
-  |
-8 | #[schemars(default = 0, foo, deny_unknown_fields, deny_unknown_fields)]
-  |                                                   ^^^^^^^^^^^^^^^^^^^
+  --> tests/ui/invalid_attrs.rs:20:5
+   |
+20 |     deny_unknown_fields,
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: unexpected value of schemars inline attribute item
+  --> tests/ui/invalid_attrs.rs:21:12
+   |
+21 |     inline = 1,
+   |            ^^^
+
+error: duplicate schemars attribute item `inline`
+  --> tests/ui/invalid_attrs.rs:23:5
+   |
+23 |     inline
+   |     ^^^^^^
 
 error: unknown schemars attribute `foo`
- --> $DIR/invalid_attrs.rs:8:25
-  |
-8 | #[schemars(default = 0, foo, deny_unknown_fields, deny_unknown_fields)]
-  |                         ^^^
+  --> tests/ui/invalid_attrs.rs:18:5
+   |
+18 |     foo,
+   |     ^^^