From 4da96aef81d9b29552f299685f627dbd09a7551d Mon Sep 17 00:00:00 2001 From: dzmitry-lahoda Date: Tue, 5 Dec 2023 22:35:43 +0000 Subject: [PATCH] improvement(primitive-types): better json-schema, allow to build for serde_no_std/json-schema feature/targets combinations (#801) * more std * fixes of std and improvement of schema * better alloc support --- primitive-types/Cargo.toml | 2 + primitive-types/src/json_schema.rs | 75 ++++++++++++++++++++++++++++++ primitive-types/src/lib.rs | 23 +++------ 3 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 primitive-types/src/json_schema.rs diff --git a/primitive-types/Cargo.toml b/primitive-types/Cargo.toml index 126b17dc..1c861d36 100644 --- a/primitive-types/Cargo.toml +++ b/primitive-types/Cargo.toml @@ -20,6 +20,8 @@ schemars = { version = ">=0.8.12", default-features = true, optional = true } [dev-dependencies] num-traits = "0.2" +serde_json = { version = "1.0", default-features = false } +jsonschema = { version = "0.17", default-features = false } [features] default = ["std"] diff --git a/primitive-types/src/json_schema.rs b/primitive-types/src/json_schema.rs new file mode 100644 index 00000000..948bf86c --- /dev/null +++ b/primitive-types/src/json_schema.rs @@ -0,0 +1,75 @@ +use super::*; +#[cfg(not(feature = "std"))] +use alloc::{ + borrow::ToOwned, + string::{String, ToString}, +}; + +use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; + +impl JsonSchema for H160 { + fn schema_name() -> String { + "HexEncoded20Bytes".to_owned() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let mut schema = gen.subschema_for::().into_object(); + schema.metadata().description = Some("Hex encoded 20 bytes".to_string()); + schema.string().pattern = Some("^0(x|X)[a-fA-F0-9]{40}$".to_string()); + schema.into() + } +} + +impl JsonSchema for U256 { + fn schema_name() -> String { + "U256String".to_string() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let mut schema = gen.subschema_for::().into_object(); + schema.metadata().description = Some("256-bit Unsigned Integer".to_string()); + schema.string().pattern = Some("^(0|[1-9][0-9]{0,77})$".to_string()); + schema.into() + } +} + +#[cfg(test)] +#[cfg(any(feature = "serde", feature = "serde_no_std"))] +mod tests { + use crate::{H160, U256}; + #[cfg(not(feature = "std"))] + use alloc::string::String; + use jsonschema::Draft; + use schemars::JsonSchema; + + #[test] + fn hex_encoded_20_bytes() { + let schema = H160::json_schema(&mut schemars::gen::SchemaGenerator::default()); + let schema_json = serde_json::to_value(&schema).unwrap(); + let schema = jsonschema::JSONSchema::options() + .with_draft(Draft::Draft7) + .compile(&schema_json) + .unwrap(); + let value = serde_json::to_value("0x55086adeca661185c437d92b9818e6eda6d0d047").unwrap(); + assert!(schema.validate(&value).is_ok()); + let value = serde_json::to_value("0X0E9C8DA9FD4BDD3281879D9E328D8D74D02558CC").unwrap(); + assert!(schema.validate(&value).is_ok()); + + let value = serde_json::to_value("42").unwrap(); + assert!(schema.validate(&value).is_err()); + } + + #[test] + fn u256() { + let schema = U256::json_schema(&mut schemars::gen::SchemaGenerator::default()); + let schema_json = serde_json::to_value(&schema).unwrap(); + let schema = jsonschema::JSONSchema::options() + .with_draft(Draft::Draft7) + .compile(&schema_json) + .unwrap(); + let addr = serde_json::to_value("42").unwrap(); + assert!(schema.validate(&addr).is_ok()); + let addr = serde_json::to_value(['1'; 79].into_iter().collect::()).unwrap(); + assert!(schema.validate(&addr).is_err()); + } +} diff --git a/primitive-types/src/lib.rs b/primitive-types/src/lib.rs index d80a312f..41b74029 100644 --- a/primitive-types/src/lib.rs +++ b/primitive-types/src/lib.rs @@ -14,8 +14,14 @@ #![cfg_attr(not(feature = "std"), no_std)] +// serde_no_std leads to alloc via impl, json-schema without std requires alloc +#[cfg(all(not(feature = "std"), any(feature = "serde_no_std", feature = "json-schema")))] +extern crate alloc; + #[cfg(feature = "fp-conversion")] mod fp_conversion; +#[cfg(feature = "json-schema")] +mod json_schema; use core::convert::TryFrom; use fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions}; @@ -105,23 +111,6 @@ mod serde { impl_fixed_hash_serde!(H768, 96); } -// true that no need std, but need to do no_std alloc than, so simplified for now -// also no macro, but easy to create -#[cfg(all(feature = "std", feature = "json-schema"))] -mod json_schema { - use super::*; - - impl schemars::JsonSchema for H160 { - fn schema_name() -> String { - "0xPrefixedHexString".to_string() - } - - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - } -} - #[cfg(feature = "impl-codec")] mod codec { use super::*;