diff --git a/Cargo.toml b/Cargo.toml index caa401b..c8158e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,20 @@ edition = "2018" [features] default = ["std"] std = [] -# Implements "schemars::JsonSchema". Also implies "serde". -json = ["serde", "schemars"] -ser_as_str = ["heapless"] +serde = ["dep:serde"] # Implements "Serialize" and "Deserialize" using "serde@1.*". +heapless = ["dep:heapless", "serde"] # Avoids dynamic allocations during serialization using "heapless@0.*". Also implies "serde". +schemars08 = ["dep:schemars08"] # Implements "JsonSchema" using "schemars@0.8.*" +schemars1 = ["dep:schemars1"] # Implements "JsonSchema" using "schemars@1.*" + +# Legacy features: +json = ["schemars08", "serde"] # Alias for "schemars08" and "serde" kept for backwards compatibility. To be removed in "ipnet@3". +schemars = ["schemars08"] # Alias for "schemars08" kept for backwards compatibility. To be removed in "ipnet@3". +ser_as_str = ["dep:heapless"] # Avoids dynamic allocations during serialization using "heapless@0.*". Only active when feature "serde" is active as well, but does not imply it due to compatibility reasons. To be removed in "ipnet@3". [dependencies] -serde = { package = "serde", version = "1", features = ["derive"], optional = true, default-features=false } -schemars = { version = "0.8", optional = true } +serde = { package = "serde", version = "1", features = ["derive"], optional = true, default-features = false } +schemars08 = { package = "schemars", version = "0.8", optional = true, default-features = false } +schemars1 = { package = "schemars", version = "1", optional = true, default-features = false } heapless = { version = "0", optional = true } [dev-dependencies] diff --git a/src/ipnet_schemars.rs b/src/ipnet_schemars_08.rs similarity index 82% rename from src/ipnet_schemars.rs rename to src/ipnet_schemars_08.rs index 4e648f5..45fe20c 100644 --- a/src/ipnet_schemars.rs +++ b/src/ipnet_schemars_08.rs @@ -1,17 +1,22 @@ +use crate::IpNet; use crate::Ipv4Net; use crate::Ipv6Net; -use crate::IpNet; use alloc::{ boxed::Box, - string::{ - String, - ToString - }, + string::{String, ToString}, vec, }; -use schemars::{JsonSchema, gen::SchemaGenerator, schema::{SubschemaValidation, Schema, SchemaObject, StringValidation, Metadata, SingleOrVec, InstanceType}}; +use schemars08::{ + self as schemars, + gen::SchemaGenerator, + schema::{ + InstanceType, Metadata, Schema, SchemaObject, SingleOrVec, StringValidation, + SubschemaValidation, + }, + JsonSchema, +}; impl JsonSchema for Ipv4Net { fn schema_name() -> String { @@ -37,7 +42,7 @@ impl JsonSchema for Ipv4Net { ..Default::default() })), ..Default::default() - }) + }) } } impl JsonSchema for Ipv6Net { @@ -60,11 +65,13 @@ impl JsonSchema for Ipv6Net { string: Some(Box::new(StringValidation { max_length: Some(43), min_length: None, - pattern: Some(r#"^[0-9A-Fa-f:\.]+\/(?:[0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$"#.to_string()), + pattern: Some( + r#"^[0-9A-Fa-f:\.]+\/(?:[0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$"#.to_string(), + ), ..Default::default() })), ..Default::default() - }) + }) } } impl JsonSchema for IpNet { @@ -83,13 +90,11 @@ impl JsonSchema for IpNet { ], ..Default::default() })), - subschemas: Some(Box::new( - SubschemaValidation { - one_of: Some(vec![Ipv4Net::json_schema(gen), Ipv6Net::json_schema(gen)]), - ..Default::default() - } - )), + subschemas: Some(Box::new(SubschemaValidation { + one_of: Some(vec![Ipv4Net::json_schema(gen), Ipv6Net::json_schema(gen)]), + ..Default::default() + })), ..Default::default() - }) + }) } } diff --git a/src/ipnet_schemars_1.rs b/src/ipnet_schemars_1.rs new file mode 100644 index 0000000..efdd268 --- /dev/null +++ b/src/ipnet_schemars_1.rs @@ -0,0 +1,69 @@ +use crate::IpNet; +use crate::Ipv4Net; +use crate::Ipv6Net; + +#[cfg(not(feature = "std"))] +use alloc::borrow::Cow; +#[cfg(feature = "std")] +use std::borrow::Cow; + +use schemars1::{json_schema, JsonSchema, Schema, SchemaGenerator}; + +impl JsonSchema for Ipv4Net { + fn schema_name() -> Cow<'static, str> { + "Ipv4Net".into() + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "title": "IPv4 network", + "description": "An IPv4 address with prefix length", + "examples": [ + "0.0.0.0/0", + "192.168.0.0/24" + ], + "type": "string", + "maxLength": 18, + "pattern": r#"^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(?:3[0-2]|[1-2][0-9]|[0-9])$"# + }) + } +} +impl JsonSchema for Ipv6Net { + fn schema_name() -> Cow<'static, str> { + "Ipv6Net".into() + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + json_schema!({ + "title": "IPv6 network", + "description": "An IPv6 address with prefix length", + "examples": [ + "::/0", + "fd00::/32" + ], + "type": "string", + "maxLength": 43, + "pattern": r#"^[0-9A-Fa-f:\.]+\/(?:[0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$"# + }) + } +} +impl JsonSchema for IpNet { + fn schema_name() -> Cow<'static, str> { + "IpNet".into() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + json_schema!({ + "title": "IP network", + "description": "An IPv4 or IPv6 address with prefix length", + "examples": [ + "192.168.0.0/24", + "fd00::/32" + ], + "oneOf": [ + gen.subschema_for::(), + gen.subschema_for::() + ] + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index cb32370..8459af2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ //! [`IpAddr`], [`Ipv4Addr`], and [`Ipv6Addr`] types already provided in //! Rust's standard library and align to their design to stay //! consistent. -//! +//! //! The module also provides the [`IpSubnets`], [`Ipv4Subnets`], and //! [`Ipv6Subnets`] types for iterating over the subnets contained in //! an IP address range. The [`IpAddrRange`], [`Ipv4AddrRange`], and @@ -57,26 +57,71 @@ //! [`IpBitAnd`]: trait.IpBitAnd.html //! [`IpBitOr`]: trait.IpBitOr.html //! -//! # Serde support +//! # Features +//! +//! These flags can be used to extend functionality using third-party +//! dependencies or optional libraries. See the [features] reference +//! for more information. +//! +//! [features]: https://doc.rust-lang.org/cargo/reference/features.html#the-features-section +//! +//! ## "std" +//! +//! Enabled by default. Disabling this feature will mandate the use of the +//! [core] and [alloc] crates where applicable instead of [std]. +//! +//! [core]: https://doc.rust-lang.org/core/ +//! [alloc]: https://doc.rust-lang.org/alloc/ +//! [std]: https://doc.rust-lang.org/std/ +//! +//! ## "serde" //! -//! This library comes with support for [serde](https://serde.rs) but -//! it's not enabled by default. Use the `serde` [feature] to enable. -//! -//! ```toml -//! [dependencies] -//! ipnet = { version = "2", features = ["serde"] } -//! ``` +//! Uses [`serde`] to implement the `Serialize` and +//! `Deserialize` traits. //! //! For human readable formats (e.g. JSON) the `IpNet`, `Ipv4Net`, and //! `Ipv6Net` types will serialize to their `Display` strings. -//! +//! //! For compact binary formats (e.g. Bincode) the `Ipv4Net` and //! `Ipv6Net` types will serialize to a string of 5 and 17 bytes that //! consist of the network address octects followed by the prefix //! length. The `IpNet` type will serialize to an Enum with the V4 or V6 //! variant index prepending the above string of 5 or 17 bytes. //! -//! [feature]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section +//! [`serde`]: https://serde.rs +//! +//! ## "heapless" [^1] +//! +//! Uses [`heapless`] to optimize serialization performance by avoiding +//! dynamic allocations. +//! +//! [`heapless`]: https://docs.rs/heapless/latest/heapless/ +//! +//! ## "schemars08" +//! +//! Uses [`schemars@0.8.*`] to implement the `JsonSchema` trait. +//! +//! [`schemars@0.8.*`]: https://docs.rs/schemars/0.8/schemars/ +//! +//! ## "schemars1" +//! +//! Uses [`schemars@1.*`] to implement the `JsonSchema` trait. +//! +//! [`schemars@1.*`]: https://docs.rs/schemars/1/schemars/ +//! +//! ## Legacy features +//! +//! The following features are set to be removed in the next major +//! release. Use the provided analogs instead. +//! +//! | Name | Analog | Reason for removal | +//! | ------------ | ------------------------ | --------------------------------------------------------------- | +//! | `json` [^1] | `schemars08` and `serde` | Unconventional naming. | +//! | `ser_as_str` | `heapless` [^1] | Doesn't enable the `serde` feature but does nothing on its own. | +//! | `schemars` | `schemars08` | Replaced by `schemars08`. | +//! +//! [^1]: Enabling these features will also enable the `serde` feature. +//! #![no_std] @@ -86,21 +131,28 @@ extern crate std; #[cfg_attr(test, macro_use)] extern crate alloc; +#[cfg(feature = "schemars08")] +extern crate schemars08; +#[cfg(feature = "schemars1")] +extern crate schemars1; #[cfg(feature = "serde")] extern crate serde; -#[cfg(feature = "schemars")] -extern crate schemars; -pub use self::ipext::{IpAdd, IpSub, IpBitAnd, IpBitOr, IpAddrRange, Ipv4AddrRange, Ipv6AddrRange}; -pub use self::ipnet::{IpNet, Ipv4Net, Ipv6Net, PrefixLenError, IpSubnets, Ipv4Subnets, Ipv6Subnets}; -pub use self::parser::AddrParseError; +pub use self::ipext::{IpAdd, IpAddrRange, IpBitAnd, IpBitOr, IpSub, Ipv4AddrRange, Ipv6AddrRange}; +pub use self::ipnet::{ + IpNet, IpSubnets, Ipv4Net, Ipv4Subnets, Ipv6Net, Ipv6Subnets, PrefixLenError, +}; pub use self::mask::{ip_mask_to_prefix, ipv4_mask_to_prefix, ipv6_mask_to_prefix}; +pub use self::parser::AddrParseError; mod ipext; mod ipnet; -mod parser; mod mask; +mod parser; + +#[cfg(feature = "schemars08")] +mod ipnet_schemars_08; +#[cfg(feature = "schemars1")] +mod ipnet_schemars_1; #[cfg(feature = "serde")] mod ipnet_serde; -#[cfg(feature = "schemars")] -mod ipnet_schemars;