From 89a34e7a632a7d5aed987e1297725755390f9461 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sat, 17 Aug 2024 19:46:11 +0100 Subject: [PATCH] Add `no_std` support via disabling the new default feature `std` (#319) --- CHANGELOG.md | 6 ++ Cargo.lock | 23 +++--- schemars/Cargo.toml | 31 ++++++-- schemars/src/_private.rs | 1 + schemars/src/gen.rs | 47 +++++++------ schemars/src/json_schema_impls/array.rs | 3 +- schemars/src/json_schema_impls/arrayvec07.rs | 3 +- schemars/src/json_schema_impls/atomic.rs | 2 +- schemars/src/json_schema_impls/chrono04.rs | 2 +- schemars/src/json_schema_impls/core.rs | 15 ++-- schemars/src/json_schema_impls/decimal.rs | 2 +- schemars/src/json_schema_impls/either1.rs | 3 +- schemars/src/json_schema_impls/ffi.rs | 3 +- schemars/src/json_schema_impls/indexmap2.rs | 6 +- schemars/src/json_schema_impls/maps.rs | 7 +- schemars/src/json_schema_impls/mod.rs | 20 +++--- .../src/json_schema_impls/nonzero_signed.rs | 5 +- .../src/json_schema_impls/nonzero_unsigned.rs | 5 +- schemars/src/json_schema_impls/primitives.rs | 28 +++++--- schemars/src/json_schema_impls/semver1.rs | 2 +- schemars/src/json_schema_impls/sequences.rs | 15 ++-- schemars/src/json_schema_impls/serdejson.rs | 5 +- .../{time.rs => std_time.rs} | 8 +-- schemars/src/json_schema_impls/tuple.rs | 3 +- schemars/src/json_schema_impls/url2.rs | 2 +- schemars/src/json_schema_impls/uuid1.rs | 2 +- schemars/src/json_schema_impls/wrapper.rs | 21 +++--- schemars/src/lib.rs | 22 +++++- schemars/src/schema.rs | 11 +-- schemars/src/ser.rs | 9 ++- schemars/src/transform.rs | 8 +-- schemars/tests/expected/no_std.json | 70 +++++++++++++++++++ schemars/tests/no_std.rs | 25 +++++++ schemars/tests/remote_derive_generic.rs | 4 +- schemars/tests/{time.rs => std_time.rs} | 0 schemars/tests/util/mod.rs | 4 ++ schemars/tests/validate.rs | 6 +- schemars_derive/src/attr/validation.rs | 12 ++-- schemars_derive/src/lib.rs | 38 +++++----- schemars_derive/src/schema_exprs.rs | 28 ++++---- 40 files changed, 343 insertions(+), 164 deletions(-) rename schemars/src/json_schema_impls/{time.rs => std_time.rs} (88%) create mode 100644 schemars/tests/expected/no_std.json create mode 100644 schemars/tests/no_std.rs rename schemars/tests/{time.rs => std_time.rs} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84cdc1e..998deaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [1.0.0-alpha.5] - _in-dev_ + +### Added + +- Schemars can now be used in `no_std` environments by disabling the new `std` feature flag (which is enabled by default). Schemars still requires an allocator to be available. + ## [1.0.0-alpha.4] - 2024-08-17 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 4833077..39e20b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown", @@ -195,6 +195,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "num-bigint" version = "0.4.4" @@ -346,18 +352,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", @@ -377,12 +383,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "indexmap", "itoa", + "memchr", "ryu", "serde", ] diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index 74f26da..0c384a5 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -14,8 +14,8 @@ rust-version = "1.60" [dependencies] schemars_derive = { version = "=1.0.0-alpha.4", optional = true, path = "../schemars_derive" } -serde = "1.0" -serde_json = "1.0.25" +serde = { version = "1.0", default-features = false, features = ["alloc"]} +serde_json = { version = "1.0.122", default-features = false, features = ["alloc"] } dyn-clone = "1.0" ref-cast = "1.0.22" @@ -40,18 +40,37 @@ trybuild = "1.0" serde = { version = "1.0", features = ["derive"] } [features] -default = ["derive"] +default = ["derive", "std"] +# Provide impls for common standard library types like `HashMap`. +# Requires a dependency on the Rust standard library. +std = [] + +# Provide `derive(JsonSchema)` macro. derive = ["schemars_derive"] + +# Preserves order of properties inserted into a `Schema`. +# When deriving `JsonSchema`, this ensures that the `properties` entires match +# the order of the fields in the struct definition. preserve_order = ["serde_json/preserve_order"] +# Implements `JsonSchema` on `serde_json::value::RawValue` raw_value = ["serde_json/raw_value"] -ui_test = [] +# For internal/CI use only +_ui_test = [] + +[[test]] +name = "std_time" +required-features = ["std"] + +[[test]] +name = "ffi" +required-features = ["std"] [[test]] name = "ui" -required-features = ["ui_test"] +required-features = ["_ui_test"] [[test]] name = "chrono" @@ -103,4 +122,4 @@ required-features = ["rust_decimal1", "bigdecimal04"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--extend-css", "docs-rs-custom.css "] +rustdoc-args = ["--extend-css", "docs-rs-custom.css"] diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index f641c02..dc867d9 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -1,3 +1,4 @@ +use crate::_alloc_prelude::*; use crate::{JsonSchema, Schema, SchemaGenerator}; use serde::Serialize; use serde_json::{json, map::Entry, Map, Value}; diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 5c1d77c..505baa6 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -8,14 +8,15 @@ There are two main types in this module: */ use crate::Schema; +use crate::_alloc_prelude::*; use crate::{transform::*, JsonSchema}; +use alloc::collections::{BTreeMap, BTreeSet}; +use core::{any::Any, fmt::Debug}; use dyn_clone::DynClone; use serde::Serialize; -use serde_json::{Map, Value}; -use std::collections::{HashMap, HashSet}; -use std::{any::Any, fmt::Debug}; +use serde_json::{Map as JsonMap, Value}; -type CowStr = std::borrow::Cow<'static, str>; +type CowStr = alloc::borrow::Cow<'static, str>; /// Settings to customize how Schemas are generated. /// @@ -168,10 +169,10 @@ impl SchemaSettings { #[derive(Debug, Default)] pub struct SchemaGenerator { settings: SchemaSettings, - definitions: Map, - pending_schema_ids: HashSet, - schema_id_to_name: HashMap, - used_schema_names: HashSet, + definitions: JsonMap, + pending_schema_ids: BTreeSet, + schema_id_to_name: BTreeMap, + used_schema_names: BTreeSet, } impl Clone for SchemaGenerator { @@ -179,9 +180,9 @@ impl Clone for SchemaGenerator { Self { settings: self.settings.clone(), definitions: self.definitions.clone(), - pending_schema_ids: HashSet::new(), - schema_id_to_name: HashMap::new(), - used_schema_names: HashSet::new(), + pending_schema_ids: BTreeSet::new(), + schema_id_to_name: BTreeMap::new(), + used_schema_names: BTreeSet::new(), } } } @@ -276,7 +277,7 @@ impl SchemaGenerator { /// /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn definitions(&self) -> &Map { + pub fn definitions(&self) -> &JsonMap { &self.definitions } @@ -284,7 +285,7 @@ impl SchemaGenerator { /// /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn definitions_mut(&mut self) -> &mut Map { + pub fn definitions_mut(&mut self) -> &mut JsonMap { &mut self.definitions } @@ -293,8 +294,8 @@ impl SchemaGenerator { /// /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas /// themselves. - pub fn take_definitions(&mut self) -> Map { - std::mem::take(&mut self.definitions) + pub fn take_definitions(&mut self) -> JsonMap { + core::mem::take(&mut self.definitions) } /// Returns an iterator over the [transforms](SchemaSettings::transforms) being used by this `SchemaGenerator`. @@ -338,7 +339,7 @@ impl SchemaGenerator { .entry("title") .or_insert_with(|| T::schema_name().into()); - if let Some(meta_schema) = std::mem::take(&mut self.settings.meta_schema) { + if let Some(meta_schema) = core::mem::take(&mut self.settings.meta_schema) { object.insert("$schema".into(), meta_schema.into()); } @@ -401,7 +402,7 @@ impl SchemaGenerator { object.insert("examples".into(), vec![example].into()); } - if let Some(meta_schema) = std::mem::take(&mut self.settings.meta_schema) { + if let Some(meta_schema) = core::mem::take(&mut self.settings.meta_schema) { object.insert("$schema".into(), meta_schema.into()); } @@ -440,8 +441,8 @@ impl SchemaGenerator { fn add_definitions( &mut self, - schema_object: &mut Map, - mut definitions: Map, + schema_object: &mut JsonMap, + mut definitions: JsonMap, ) { if definitions.is_empty() { return; @@ -472,10 +473,10 @@ impl SchemaGenerator { } fn json_pointer_mut<'a>( - mut object: &'a mut Map, + mut object: &'a mut JsonMap, pointer: &str, create_if_missing: bool, -) -> Option<&'a mut Map> { +) -> Option<&'a mut JsonMap> { let pointer = pointer.strip_prefix('/')?; if pointer.is_empty() { return Some(object); @@ -491,7 +492,7 @@ fn json_pointer_mut<'a>( use serde_json::map::Entry; let next_value = match object.entry(segment) { Entry::Occupied(o) => o.into_mut(), - Entry::Vacant(v) if create_if_missing => v.insert(Value::Object(Map::default())), + Entry::Vacant(v) if create_if_missing => v.insert(Value::Object(JsonMap::default())), Entry::Vacant(_) => return None, }; @@ -580,7 +581,7 @@ where } impl Debug for Box { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self._debug_type_name(f) } } diff --git a/schemars/src/json_schema_impls/array.rs b/schemars/src/json_schema_impls/array.rs index 7dccbfb..18e8b76 100644 --- a/schemars/src/json_schema_impls/array.rs +++ b/schemars/src/json_schema_impls/array.rs @@ -1,6 +1,7 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; // Does not require T: JsonSchema. impl JsonSchema for [T; 0] { diff --git a/schemars/src/json_schema_impls/arrayvec07.rs b/schemars/src/json_schema_impls/arrayvec07.rs index cd5f154..a5511ab 100644 --- a/schemars/src/json_schema_impls/arrayvec07.rs +++ b/schemars/src/json_schema_impls/arrayvec07.rs @@ -1,3 +1,4 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; use arrayvec07::{ArrayString, ArrayVec}; @@ -12,7 +13,7 @@ where { always_inline!(); - fn schema_name() -> std::borrow::Cow<'static, str> { + fn schema_name() -> alloc::borrow::Cow<'static, str> { format!("Array_up_to_size_{}_of_{}", CAP, T::schema_name()).into() } diff --git a/schemars/src/json_schema_impls/atomic.rs b/schemars/src/json_schema_impls/atomic.rs index 19d681c..e871e76 100644 --- a/schemars/src/json_schema_impls/atomic.rs +++ b/schemars/src/json_schema_impls/atomic.rs @@ -1,4 +1,4 @@ -use std::sync::atomic::*; +use core::sync::atomic::*; #[cfg(target_has_atomic = "8")] forward_impl!(AtomicBool => bool); diff --git a/schemars/src/json_schema_impls/chrono04.rs b/schemars/src/json_schema_impls/chrono04.rs index 2635e56..1dfc2b1 100644 --- a/schemars/src/json_schema_impls/chrono04.rs +++ b/schemars/src/json_schema_impls/chrono04.rs @@ -1,7 +1,7 @@ use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; use chrono04::prelude::*; -use std::borrow::Cow; +use alloc::borrow::Cow; impl JsonSchema for Weekday { always_inline!(); diff --git a/schemars/src/json_schema_impls/core.rs b/schemars/src/json_schema_impls/core.rs index d49f392..3e0d994 100644 --- a/schemars/src/json_schema_impls/core.rs +++ b/schemars/src/json_schema_impls/core.rs @@ -1,8 +1,9 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; +use alloc::borrow::Cow; +use core::ops::{Bound, Range, RangeInclusive}; use serde_json::Value; -use std::borrow::Cow; -use std::ops::{Bound, Range, RangeInclusive}; impl JsonSchema for Option { always_inline!(); @@ -32,8 +33,10 @@ impl JsonSchema for Option { } Some(Value::String(string)) => { if string != "null" { - *instance_type.unwrap() = - Value::Array(vec![std::mem::take(string).into(), "null".into()]) + *instance_type.unwrap() = Value::Array(vec![ + core::mem::take(string).into(), + "null".into(), + ]) } obj.into() } @@ -158,6 +161,6 @@ impl JsonSchema for Range { forward_impl!(( JsonSchema for RangeInclusive) => Range); -forward_impl!(( JsonSchema for std::marker::PhantomData) => ()); +forward_impl!(( JsonSchema for core::marker::PhantomData) => ()); -forward_impl!((<'a> JsonSchema for std::fmt::Arguments<'a>) => String); +forward_impl!((<'a> JsonSchema for core::fmt::Arguments<'a>) => String); diff --git a/schemars/src/json_schema_impls/decimal.rs b/schemars/src/json_schema_impls/decimal.rs index 3266ccc..0f55ba9 100644 --- a/schemars/src/json_schema_impls/decimal.rs +++ b/schemars/src/json_schema_impls/decimal.rs @@ -1,6 +1,6 @@ use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; macro_rules! decimal_impl { ($type:ty) => { diff --git a/schemars/src/json_schema_impls/either1.rs b/schemars/src/json_schema_impls/either1.rs index fcc3479..3822faa 100644 --- a/schemars/src/json_schema_impls/either1.rs +++ b/schemars/src/json_schema_impls/either1.rs @@ -1,7 +1,8 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; +use alloc::borrow::Cow; use either1::Either; -use std::borrow::Cow; impl JsonSchema for Either { always_inline!(); diff --git a/schemars/src/json_schema_impls/ffi.rs b/schemars/src/json_schema_impls/ffi.rs index 026d5ae..524c49f 100644 --- a/schemars/src/json_schema_impls/ffi.rs +++ b/schemars/src/json_schema_impls/ffi.rs @@ -1,6 +1,7 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; impl JsonSchema for OsString { diff --git a/schemars/src/json_schema_impls/indexmap2.rs b/schemars/src/json_schema_impls/indexmap2.rs index 9473dea..355864e 100644 --- a/schemars/src/json_schema_impls/indexmap2.rs +++ b/schemars/src/json_schema_impls/indexmap2.rs @@ -1,6 +1,6 @@ use crate::JsonSchema; +use alloc::collections::{BTreeMap, BTreeSet}; use indexmap2::{IndexMap, IndexSet}; -use std::collections::{HashMap, HashSet}; -forward_impl!(( JsonSchema for IndexMap) => HashMap); -forward_impl!(( JsonSchema for IndexSet) => HashSet); +forward_impl!(( JsonSchema for IndexMap) => BTreeMap); +forward_impl!(( JsonSchema for IndexSet) => BTreeSet); diff --git a/schemars/src/json_schema_impls/maps.rs b/schemars/src/json_schema_impls/maps.rs index 19ae5cf..af43a43 100644 --- a/schemars/src/json_schema_impls/maps.rs +++ b/schemars/src/json_schema_impls/maps.rs @@ -1,6 +1,7 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; macro_rules! map_impl { ($($desc:tt)+) => { @@ -28,5 +29,7 @@ macro_rules! map_impl { }; } -map_impl!( JsonSchema for std::collections::BTreeMap); +map_impl!( JsonSchema for alloc::collections::BTreeMap); + +#[cfg(feature = "std")] map_impl!( JsonSchema for std::collections::HashMap); diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs index 6260b2b..a0287f7 100644 --- a/schemars/src/json_schema_impls/mod.rs +++ b/schemars/src/json_schema_impls/mod.rs @@ -13,11 +13,11 @@ macro_rules! forward_impl { <$target>::always_inline_schema() } - fn schema_name() -> std::borrow::Cow<'static, str> { + fn schema_name() -> alloc::borrow::Cow<'static, str> { <$target>::schema_name() } - fn schema_id() -> std::borrow::Cow<'static, str> { + fn schema_id() -> alloc::borrow::Cow<'static, str> { <$target>::schema_id() } @@ -41,27 +41,29 @@ macro_rules! forward_impl { mod array; mod core; -mod ffi; mod maps; mod nonzero_signed; mod nonzero_unsigned; mod primitives; mod sequences; mod serdejson; -mod time; +mod std_time; mod tuple; mod wrapper; #[cfg(target_has_atomic)] mod atomic; +#[cfg(feature = "std")] +mod ffi; + #[cfg(feature = "arrayvec07")] mod arrayvec07; #[cfg(feature = "bytes1")] mod bytes1 { - forward_impl!(bytes1::Bytes => Vec); - forward_impl!(bytes1::BytesMut => Vec); + forward_impl!(bytes1::Bytes => alloc::vec::Vec); + forward_impl!(bytes1::BytesMut => alloc::vec::Vec); } #[cfg(feature = "chrono04")] @@ -74,7 +76,7 @@ mod decimal; mod either1; #[cfg(feature = "enumset1")] -forward_impl!(( crate::JsonSchema for enumset1::EnumSet) => std::collections::BTreeSet); +forward_impl!(( crate::JsonSchema for enumset1::EnumSet) => alloc::collections::BTreeSet); #[cfg(feature = "indexmap2")] mod indexmap2; @@ -83,10 +85,10 @@ mod indexmap2; mod semver1; #[cfg(feature = "smallvec1")] -forward_impl!(( crate::JsonSchema for smallvec1::SmallVec where A::Item: crate::JsonSchema) => Vec); +forward_impl!(( crate::JsonSchema for smallvec1::SmallVec where A::Item: crate::JsonSchema) => alloc::vec::Vec); #[cfg(feature = "smol_str02")] -forward_impl!(smol_str02::SmolStr => String); +forward_impl!(smol_str02::SmolStr => alloc::string::String); #[cfg(feature = "url2")] mod url2; diff --git a/schemars/src/json_schema_impls/nonzero_signed.rs b/schemars/src/json_schema_impls/nonzero_signed.rs index c9d0919..37fef86 100644 --- a/schemars/src/json_schema_impls/nonzero_signed.rs +++ b/schemars/src/json_schema_impls/nonzero_signed.rs @@ -1,7 +1,8 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{JsonSchema, Schema}; -use std::borrow::Cow; -use std::num::*; +use alloc::borrow::Cow; +use core::num::*; macro_rules! nonzero_unsigned_impl { ($type:ty => $primitive:ty) => { diff --git a/schemars/src/json_schema_impls/nonzero_unsigned.rs b/schemars/src/json_schema_impls/nonzero_unsigned.rs index afabc05..8c07fb3 100644 --- a/schemars/src/json_schema_impls/nonzero_unsigned.rs +++ b/schemars/src/json_schema_impls/nonzero_unsigned.rs @@ -1,8 +1,9 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::JsonSchema; use crate::Schema; -use std::borrow::Cow; -use std::num::*; +use alloc::borrow::Cow; +use core::num::*; macro_rules! nonzero_unsigned_impl { ($type:ty => $primitive:ty) => { diff --git a/schemars/src/json_schema_impls/primitives.rs b/schemars/src/json_schema_impls/primitives.rs index a3bcce6..4b87961 100644 --- a/schemars/src/json_schema_impls/primitives.rs +++ b/schemars/src/json_schema_impls/primitives.rs @@ -1,8 +1,7 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use std::path::{Path, PathBuf}; +use alloc::borrow::Cow; macro_rules! simple_impl { ($type:ty => $instance_type:literal) => { @@ -51,16 +50,23 @@ simple_impl!(i128 => "integer", "int128"); simple_impl!(isize => "integer", "int"); simple_impl!(() => "null"); -simple_impl!(Path => "string"); -simple_impl!(PathBuf => "string"); +#[cfg(feature = "std")] +mod std_types { + use super::*; + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; + use std::path::{Path, PathBuf}; -simple_impl!(Ipv4Addr => "string", "ipv4"); -simple_impl!(Ipv6Addr => "string", "ipv6"); -simple_impl!(IpAddr => "string", "ip"); + simple_impl!(Path => "string"); + simple_impl!(PathBuf => "string"); -simple_impl!(SocketAddr => "string"); -simple_impl!(SocketAddrV4 => "string"); -simple_impl!(SocketAddrV6 => "string"); + simple_impl!(Ipv4Addr => "string", "ipv4"); + simple_impl!(Ipv6Addr => "string", "ipv6"); + simple_impl!(IpAddr => "string", "ip"); + + simple_impl!(SocketAddr => "string"); + simple_impl!(SocketAddrV4 => "string"); + simple_impl!(SocketAddrV6 => "string"); +} macro_rules! unsigned_impl { ($type:ty => $instance_type:literal, $format:literal) => { diff --git a/schemars/src/json_schema_impls/semver1.rs b/schemars/src/json_schema_impls/semver1.rs index eb121fb..d693bd6 100644 --- a/schemars/src/json_schema_impls/semver1.rs +++ b/schemars/src/json_schema_impls/semver1.rs @@ -1,7 +1,7 @@ use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; use semver1::Version; -use std::borrow::Cow; +use alloc::borrow::Cow; impl JsonSchema for Version { always_inline!(); diff --git a/schemars/src/json_schema_impls/sequences.rs b/schemars/src/json_schema_impls/sequences.rs index c131339..8241dac 100644 --- a/schemars/src/json_schema_impls/sequences.rs +++ b/schemars/src/json_schema_impls/sequences.rs @@ -1,6 +1,7 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; macro_rules! seq_impl { ($($desc:tt)+) => { @@ -55,11 +56,13 @@ macro_rules! set_impl { }; } -seq_impl!( JsonSchema for std::collections::BinaryHeap); -seq_impl!( JsonSchema for std::collections::LinkedList); +seq_impl!( JsonSchema for alloc::collections::BinaryHeap); +seq_impl!( JsonSchema for alloc::collections::LinkedList); seq_impl!( JsonSchema for [T]); -seq_impl!( JsonSchema for Vec); -seq_impl!( JsonSchema for std::collections::VecDeque); +seq_impl!( JsonSchema for alloc::vec::Vec); +seq_impl!( JsonSchema for alloc::collections::VecDeque); -set_impl!( JsonSchema for std::collections::BTreeSet); +set_impl!( JsonSchema for alloc::collections::BTreeSet); + +#[cfg(feature = "std")] set_impl!( JsonSchema for std::collections::HashSet); diff --git a/schemars/src/json_schema_impls/serdejson.rs b/schemars/src/json_schema_impls/serdejson.rs index cd25326..25f6e78 100644 --- a/schemars/src/json_schema_impls/serdejson.rs +++ b/schemars/src/json_schema_impls/serdejson.rs @@ -1,8 +1,9 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; +use alloc::borrow::Cow; +use alloc::collections::BTreeMap; use serde_json::{Map, Number, Value}; -use std::borrow::Cow; -use std::collections::BTreeMap; impl JsonSchema for Value { always_inline!(); diff --git a/schemars/src/json_schema_impls/time.rs b/schemars/src/json_schema_impls/std_time.rs similarity index 88% rename from schemars/src/json_schema_impls/time.rs rename to schemars/src/json_schema_impls/std_time.rs index 0f1eb74..1766e93 100644 --- a/schemars/src/json_schema_impls/time.rs +++ b/schemars/src/json_schema_impls/std_time.rs @@ -1,9 +1,8 @@ use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; -use std::time::{Duration, SystemTime}; +use alloc::borrow::Cow; -impl JsonSchema for Duration { +impl JsonSchema for core::time::Duration { fn schema_name() -> Cow<'static, str> { "Duration".into() } @@ -24,7 +23,8 @@ impl JsonSchema for Duration { } } -impl JsonSchema for SystemTime { +#[cfg(feature = "std")] +impl JsonSchema for std::time::SystemTime { fn schema_name() -> Cow<'static, str> { "SystemTime".into() } diff --git a/schemars/src/json_schema_impls/tuple.rs b/schemars/src/json_schema_impls/tuple.rs index c05a5f8..bb57c0e 100644 --- a/schemars/src/json_schema_impls/tuple.rs +++ b/schemars/src/json_schema_impls/tuple.rs @@ -1,6 +1,7 @@ +use crate::_alloc_prelude::*; use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; macro_rules! tuple_impls { ($($len:expr => ($($name:ident)+))+) => { diff --git a/schemars/src/json_schema_impls/url2.rs b/schemars/src/json_schema_impls/url2.rs index 5a467d9..d34e949 100644 --- a/schemars/src/json_schema_impls/url2.rs +++ b/schemars/src/json_schema_impls/url2.rs @@ -1,6 +1,6 @@ use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; use url2::Url; impl JsonSchema for Url { diff --git a/schemars/src/json_schema_impls/uuid1.rs b/schemars/src/json_schema_impls/uuid1.rs index a56c10c..2628f00 100644 --- a/schemars/src/json_schema_impls/uuid1.rs +++ b/schemars/src/json_schema_impls/uuid1.rs @@ -1,6 +1,6 @@ use crate::gen::SchemaGenerator; use crate::{json_schema, JsonSchema, Schema}; -use std::borrow::Cow; +use alloc::borrow::Cow; use uuid1::Uuid; impl JsonSchema for Uuid { diff --git a/schemars/src/json_schema_impls/wrapper.rs b/schemars/src/json_schema_impls/wrapper.rs index e7e233c..ea09504 100644 --- a/schemars/src/json_schema_impls/wrapper.rs +++ b/schemars/src/json_schema_impls/wrapper.rs @@ -1,4 +1,5 @@ use crate::JsonSchema; +use crate::_alloc_prelude::*; macro_rules! wrapper_impl { ($($desc:tt)+) => { @@ -9,14 +10,16 @@ macro_rules! wrapper_impl { wrapper_impl!(<'a, T: ?Sized> JsonSchema for &'a T); wrapper_impl!(<'a, T: ?Sized> JsonSchema for &'a mut T); wrapper_impl!( JsonSchema for Box); -wrapper_impl!( JsonSchema for std::rc::Rc); -wrapper_impl!( JsonSchema for std::rc::Weak); -wrapper_impl!( JsonSchema for std::sync::Arc); -wrapper_impl!( JsonSchema for std::sync::Weak); +wrapper_impl!( JsonSchema for alloc::rc::Rc); +wrapper_impl!( JsonSchema for alloc::rc::Weak); +wrapper_impl!( JsonSchema for alloc::sync::Arc); +wrapper_impl!( JsonSchema for alloc::sync::Weak); +#[cfg(feature = "std")] wrapper_impl!( JsonSchema for std::sync::Mutex); +#[cfg(feature = "std")] wrapper_impl!( JsonSchema for std::sync::RwLock); -wrapper_impl!( JsonSchema for std::cell::Cell); -wrapper_impl!( JsonSchema for std::cell::RefCell); -wrapper_impl!(<'a, T: ?Sized + ToOwned> JsonSchema for std::borrow::Cow<'a, T>); -wrapper_impl!( JsonSchema for std::num::Wrapping); -wrapper_impl!( JsonSchema for std::cmp::Reverse); +wrapper_impl!( JsonSchema for core::cell::Cell); +wrapper_impl!( JsonSchema for core::cell::RefCell); +wrapper_impl!(<'a, T: ?Sized + ToOwned> JsonSchema for alloc::borrow::Cow<'a, T>); +wrapper_impl!( JsonSchema for core::num::Wrapping); +wrapper_impl!( JsonSchema for core::cmp::Reverse); diff --git a/schemars/src/lib.rs b/schemars/src/lib.rs index c0c316a..186a53f 100644 --- a/schemars/src/lib.rs +++ b/schemars/src/lib.rs @@ -1,5 +1,10 @@ #![deny(unsafe_code)] #![doc = include_str!("../README.md")] +#![no_std] + +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; mod json_schema_impls; mod schema; @@ -16,18 +21,29 @@ pub mod transform; #[cfg(feature = "schemars_derive")] extern crate schemars_derive; -use std::borrow::Cow; +use alloc::borrow::Cow; #[cfg(feature = "schemars_derive")] pub use schemars_derive::*; -// Export serde_json so schemars_derive can use it +// Export crates so schemars_derive can use them #[doc(hidden)] -pub use serde_json as _serde_json; +pub extern crate alloc as _alloc; +#[doc(hidden)] +pub extern crate serde_json as _serde_json; pub use gen::SchemaGenerator; pub use schema::Schema; +mod _alloc_prelude { + pub use alloc::borrow::ToOwned; + pub use alloc::boxed::Box; + pub use alloc::format; + pub use alloc::string::{String, ToString}; + pub use alloc::vec; + pub use alloc::vec::Vec; +} + /// A type which can be described as a JSON Schema document. /// /// This is implemented for many Rust primitive and standard library types. diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index dd9e866..c3882d0 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -2,6 +2,7 @@ JSON Schema types. */ +use crate::_alloc_prelude::*; use ref_cast::{ref_cast_custom, RefCastCustom}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; @@ -186,7 +187,7 @@ impl From for Value { } } -impl std::convert::TryFrom for Schema { +impl core::convert::TryFrom for Schema { type Error = serde_json::Error; fn try_from(value: Value) -> serde_json::Result { @@ -195,7 +196,7 @@ impl std::convert::TryFrom for Schema { } } -impl<'a> std::convert::TryFrom<&'a Value> for &'a Schema { +impl<'a> core::convert::TryFrom<&'a Value> for &'a Schema { type Error = serde_json::Error; fn try_from(value: &Value) -> serde_json::Result<&Schema> { @@ -204,7 +205,7 @@ impl<'a> std::convert::TryFrom<&'a Value> for &'a Schema { } } -impl<'a> std::convert::TryFrom<&'a mut Value> for &'a mut Schema { +impl<'a> core::convert::TryFrom<&'a mut Value> for &'a mut Schema { type Error = serde_json::Error; fn try_from(value: &mut Value) -> serde_json::Result<&mut Schema> { @@ -232,11 +233,11 @@ impl From for Schema { } impl crate::JsonSchema for Schema { - fn schema_name() -> std::borrow::Cow<'static, str> { + fn schema_name() -> alloc::borrow::Cow<'static, str> { "Schema".into() } - fn schema_id() -> std::borrow::Cow<'static, str> { + fn schema_id() -> alloc::borrow::Cow<'static, str> { "schemars::Schema".into() } diff --git a/schemars/src/ser.rs b/schemars/src/ser.rs index f42c187..c0bcc21 100644 --- a/schemars/src/ser.rs +++ b/schemars/src/ser.rs @@ -1,6 +1,7 @@ +use crate::_alloc_prelude::*; use crate::{json_schema, JsonSchema, Schema, SchemaGenerator}; +use core::fmt::Display; use serde_json::{Error, Map, Value}; -use std::fmt::Display; pub(crate) struct Serializer<'a> { pub(crate) gen: &'a mut SchemaGenerator, @@ -142,8 +143,10 @@ impl<'a> serde::Serializer for Serializer<'a> { } Some(Value::String(string)) => { if string != "null" { - *value.unwrap() = - Value::Array(vec![std::mem::take(string).into(), "null".into()]) + *value.unwrap() = Value::Array(vec![ + core::mem::take(string).into(), + "null".into(), + ]) } obj.into() } diff --git a/schemars/src/transform.rs b/schemars/src/transform.rs index 7482b31..5529e06 100644 --- a/schemars/src/transform.rs +++ b/schemars/src/transform.rs @@ -112,9 +112,9 @@ assert_eq!( ``` */ -use serde_json::{json, Value}; - use crate::Schema; +use crate::_alloc_prelude::*; +use serde_json::{json, Value}; /// Trait used to modify a constructed schema and optionally its subschemas. /// @@ -128,8 +128,8 @@ pub trait Transform { // Not public API // Hack to enable implementing Debug on Box even though closures don't implement Debug #[doc(hidden)] - fn _debug_type_name(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(std::any::type_name::()) + fn _debug_type_name(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(core::any::type_name::()) } } diff --git a/schemars/tests/expected/no_std.json b/schemars/tests/expected/no_std.json new file mode 100644 index 0000000..68da890 --- /dev/null +++ b/schemars/tests/expected/no_std.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "MyStruct", + "type": "object", + "properties": { + "my_int": { + "type": "integer", + "format": "int32" + }, + "my_bool": { + "type": "boolean" + }, + "my_nullable_enum": { + "anyOf": [ + { + "$ref": "#/$defs/MyEnum" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "my_int", + "my_bool" + ], + "$defs": { + "MyEnum": { + "oneOf": [ + { + "type": "object", + "properties": { + "StringNewType": { + "type": "string" + } + }, + "required": [ + "StringNewType" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "StructVariant": { + "type": "object", + "properties": { + "floats": { + "type": "array", + "items": { + "type": "number", + "format": "float" + } + } + }, + "required": [ + "floats" + ] + } + }, + "required": [ + "StructVariant" + ], + "additionalProperties": false + } + ] + } + } +} \ No newline at end of file diff --git a/schemars/tests/no_std.rs b/schemars/tests/no_std.rs new file mode 100644 index 0000000..c011aa2 --- /dev/null +++ b/schemars/tests/no_std.rs @@ -0,0 +1,25 @@ +#![no_std] + +mod util; +use schemars::JsonSchema; +use util::*; + +extern crate alloc as test_alloc; + +#[derive(JsonSchema)] +pub struct MyStruct { + pub my_int: i32, + pub my_bool: bool, + pub my_nullable_enum: Option, +} + +#[derive(JsonSchema)] +pub enum MyEnum { + StringNewType(test_alloc::string::String), + StructVariant { floats: test_alloc::vec::Vec }, +} + +#[test] +fn no_std() -> TestResult { + test_default_generated_schema::("no_std") +} diff --git a/schemars/tests/remote_derive_generic.rs b/schemars/tests/remote_derive_generic.rs index 8599266..53fb7d2 100644 --- a/schemars/tests/remote_derive_generic.rs +++ b/schemars/tests/remote_derive_generic.rs @@ -2,7 +2,7 @@ mod util; use schemars::JsonSchema; use serde::Serialize; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; use util::*; #[allow(dead_code)] @@ -39,7 +39,7 @@ struct MyStruct<'a, T: Serialize> { s: Str<'a>, // #[schemars(with = "HashMap::<_, HashSet<_>>")] // map: BTreeMap>, - #[schemars(with = "HashMap::>")] + #[schemars(with = "BTreeMap::>")] fake_map: (), } diff --git a/schemars/tests/time.rs b/schemars/tests/std_time.rs similarity index 100% rename from schemars/tests/time.rs rename to schemars/tests/std_time.rs diff --git a/schemars/tests/util/mod.rs b/schemars/tests/util/mod.rs index 3376c02..2c735ef 100644 --- a/schemars/tests/util/mod.rs +++ b/schemars/tests/util/mod.rs @@ -1,7 +1,11 @@ use pretty_assertions::assert_eq; use schemars::{gen::SchemaSettings, schema_for, JsonSchema, Schema}; use std::error::Error; +use std::format; use std::fs; +use std::prelude::rust_2021::*; + +extern crate std; pub type TestResult = Result<(), Box>; diff --git a/schemars/tests/validate.rs b/schemars/tests/validate.rs index 9770eec..560fd57 100644 --- a/schemars/tests/validate.rs +++ b/schemars/tests/validate.rs @@ -1,6 +1,6 @@ mod util; use schemars::JsonSchema; -use std::collections::HashMap; +use std::collections::BTreeMap; use util::*; // In real code, this would typically be a Regex, potentially created in a `lazy_static!`. @@ -39,7 +39,7 @@ pub struct Struct { #[validate(length(equal = 2))] pair: Vec, #[validate(contains = "map_key")] - map_contains: HashMap, + map_contains: BTreeMap, #[validate(required)] required_option: Option, #[validate(required)] @@ -91,7 +91,7 @@ pub struct Struct2 { #[schemars(length(equal = 2))] pair: Vec, #[schemars(contains = "map_key")] - map_contains: HashMap, + map_contains: BTreeMap, #[schemars(required)] required_option: Option, #[schemars(required)] diff --git a/schemars_derive/src/attr/validation.rs b/schemars_derive/src/attr/validation.rs index 8d90302..095590a 100644 --- a/schemars_derive/src/attr/validation.rs +++ b/schemars_derive/src/attr/validation.rs @@ -155,7 +155,7 @@ impl ValidationAttrs { if !ignore_errors { errors.error_spanned_by( meta, - "unknown item in schemars length attribute".to_string(), + "unknown item in schemars length attribute", ); } } @@ -184,7 +184,7 @@ impl ValidationAttrs { if !ignore_errors { errors.error_spanned_by( meta, - "unknown item in schemars range attribute".to_string(), + "unknown item in schemars range attribute", ); } } @@ -252,8 +252,7 @@ impl ValidationAttrs { if !ignore_errors { errors.error_spanned_by( meta, - "unknown item in schemars regex attribute" - .to_string(), + "unknown item in schemars regex attribute", ); } } @@ -294,8 +293,7 @@ impl ValidationAttrs { if !ignore_errors { errors.error_spanned_by( meta, - "unknown item in schemars contains attribute" - .to_string(), + "unknown item in schemars contains attribute", ); } } @@ -390,7 +388,7 @@ impl ValidationAttrs { if let Some(format) = &self.format { let f = format.schema_str(); result.push(quote! { - schema.ensure_object().insert("format".to_owned(), #f.into()); + schema.ensure_object().insert("format".into(), #f.into()); }) }; diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index bd427bd..1e9de01 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -60,11 +60,11 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result::always_inline_schema() } - fn schema_name() -> std::borrow::Cow<'static, str> { + fn schema_name() -> schemars::_alloc::borrow::Cow<'static, str> { <#ty as schemars::JsonSchema>::schema_name() } - fn schema_id() -> std::borrow::Cow<'static, str> { + fn schema_id() -> schemars::_alloc::borrow::Cow<'static, str> { <#ty as schemars::JsonSchema>::schema_id() } @@ -104,11 +104,11 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result syn::Result syn::Result syn::Result std::borrow::Cow<'static, str> { + fn schema_name() -> schemars::_alloc::borrow::Cow<'static, str> { #schema_name } - fn schema_id() -> std::borrow::Cow<'static, str> { + fn schema_id() -> schemars::_alloc::borrow::Cow<'static, str> { #schema_id } diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 3278e99..58aaded 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -51,11 +51,11 @@ pub fn expr_for_repr(cont: &Container) -> Result { let mut schema_expr = quote!({ let mut map = schemars::_serde_json::Map::new(); - map.insert("type".to_owned(), "integer".into()); + map.insert("type".into(), "integer".into()); map.insert( - "enum".to_owned(), + "enum".into(), schemars::_serde_json::Value::Array({ - let mut enum_values = Vec::new(); + let mut enum_values = schemars::_alloc::vec::Vec::new(); #(enum_values.push((#enum_ident::#variant_idents as #repr_type).into());)* enum_values }), @@ -114,14 +114,14 @@ fn type_for_schema(with_attr: &WithAttr) -> (syn::Type, Option) { true } - fn schema_name() -> std::borrow::Cow<'static, str> { - std::borrow::Cow::Borrowed(#fn_name) + fn schema_name() -> schemars::_alloc::borrow::Cow<'static, str> { + schemars::_alloc::borrow::Cow::Borrowed(#fn_name) } - fn schema_id() -> std::borrow::Cow<'static, str> { - std::borrow::Cow::Borrowed(std::concat!( + fn schema_id() -> schemars::_alloc::borrow::Cow<'static, str> { + schemars::_alloc::borrow::Cow::Borrowed(::core::concat!( "_SchemarsSchemaWithFunction/", - std::module_path!(), + ::core::module_path!(), "/", #fn_name )) @@ -171,11 +171,11 @@ fn expr_for_external_tagged_enum<'a>( let unit_names = unit_variants.iter().map(|v| v.name()); let unit_schema = quote!({ let mut map = schemars::_serde_json::Map::new(); - map.insert("type".to_owned(), "string".into()); + map.insert("type".into(), "string".into()); map.insert( - "enum".to_owned(), + "enum".into(), schemars::_serde_json::Value::Array({ - let mut enum_values = Vec::new(); + let mut enum_values = schemars::_alloc::vec::Vec::new(); #(enum_values.push((#unit_names).into());)* enum_values }), @@ -347,9 +347,9 @@ fn variant_subschemas(unique: bool, schemas: Vec) -> TokenStream { quote!({ let mut map = schemars::_serde_json::Map::new(); map.insert( - #keyword.to_owned(), + #keyword.into(), schemars::_serde_json::Value::Array({ - let mut enum_values = Vec::new(); + let mut enum_values = schemars::_alloc::vec::Vec::new(); #(enum_values.push(#schemas.to_value());)* enum_values }), @@ -556,7 +556,7 @@ fn field_default_expr(field: &Field, container_has_default: bool) -> Option { - fn serialize(&self, serializer: S) -> core::result::Result + fn serialize(&self, serializer: S) -> ::core::result::Result where S: serde::Serializer {