diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 123c84d..a895147 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -37,10 +37,10 @@ jobs: with: components: clippy - name: Clippy - uses: actions-rs/clippy-check@v1 + uses: giraffate/clippy-action@v1 with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all-features --all-targets + clippy_flags: '' + reporter: 'github-pr-check' publish-dry-run: name: Publish (dry run) @@ -68,6 +68,8 @@ jobs: override: true - name: Install cargo-nextest uses: taiki-e/install-action@nextest + - name: Install Protoc + uses: arduino/setup-protoc@v2 - name: Build tests uses: actions-rs/cargo@v1 with: diff --git a/pbjson-build/Cargo.toml b/pbjson-build/Cargo.toml index 72d3ee5..7e816e5 100644 --- a/pbjson-build/Cargo.toml +++ b/pbjson-build/Cargo.toml @@ -16,7 +16,7 @@ name = "pbjson_build" heck = "0.4" prost = "0.11" prost-types = "0.11" -itertools = "0.11" +itertools = "0.10" [dev-dependencies] -tempfile = "3" +tempfile = "3.1" diff --git a/pbjson-build/src/escape.rs b/pbjson-build/src/escape.rs index 554ac85..08a55e0 100644 --- a/pbjson-build/src/escape.rs +++ b/pbjson-build/src/escape.rs @@ -1,4 +1,4 @@ -///! Contains code to escape strings to avoid collisions with reserved Rust keywords +//! Contains code to escape strings to avoid collisions with reserved Rust keywords pub fn escape_ident(mut ident: String) -> String { // Copied from prost-build::ident diff --git a/pbjson-build/src/generator.rs b/pbjson-build/src/generator.rs index 1f450c2..7ca617d 100644 --- a/pbjson-build/src/generator.rs +++ b/pbjson-build/src/generator.rs @@ -39,7 +39,7 @@ fn write_serialize_start(indent: usize, rust_type: &str, writer: &mut writer, r#"{indent}impl serde::Serialize for {rust_type} {{ {indent} #[allow(deprecated)] -{indent} fn serialize(&self, serializer: S) -> std::result::Result +{indent} fn serialize(&self, serializer: S) -> core::result::Result {indent} where {indent} S: serde::Serializer, {indent} {{"#, @@ -62,7 +62,7 @@ fn write_deserialize_start(indent: usize, rust_type: &str, writer: &mu writer, r#"{indent}impl<'de> serde::Deserialize<'de> for {rust_type} {{ {indent} #[allow(deprecated)] -{indent} fn deserialize(deserializer: D) -> std::result::Result +{indent} fn deserialize(deserializer: D) -> core::result::Result {indent} where {indent} D: serde::Deserializer<'de>, {indent} {{"#, diff --git a/pbjson-build/src/generator/enumeration.rs b/pbjson-build/src/generator/enumeration.rs index 05850af..c03d930 100644 --- a/pbjson-build/src/generator/enumeration.rs +++ b/pbjson-build/src/generator/enumeration.rs @@ -96,15 +96,15 @@ fn write_visitor( {indent}impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {{ {indent} type Value = {rust_type}; -{indent} fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ +{indent} fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{ {indent} write!(formatter, "expected one of: {{:?}}", &FIELDS) {indent} }} -{indent} fn visit_i64(self, v: i64) -> std::result::Result +{indent} fn visit_i64(self, v: i64) -> core::result::Result {indent} where {indent} E: serde::de::Error, {indent} {{ -{indent} use std::convert::TryFrom; +{indent} use core::convert::TryFrom; {indent} i32::try_from(v) {indent} .ok() {indent} .and_then({rust_type}::from_i32) @@ -113,11 +113,11 @@ fn write_visitor( {indent} }}) {indent} }} -{indent} fn visit_u64(self, v: u64) -> std::result::Result +{indent} fn visit_u64(self, v: u64) -> core::result::Result {indent} where {indent} E: serde::de::Error, {indent} {{ -{indent} use std::convert::TryFrom; +{indent} use core::convert::TryFrom; {indent} i32::try_from(v) {indent} .ok() {indent} .and_then({rust_type}::from_i32) @@ -126,7 +126,7 @@ fn write_visitor( {indent} }}) {indent} }} -{indent} fn visit_str(self, value: &str) -> std::result::Result +{indent} fn visit_str(self, value: &str) -> core::result::Result {indent} where {indent} E: serde::de::Error, {indent} {{"#, diff --git a/pbjson-build/src/generator/message.rs b/pbjson-build/src/generator/message.rs index c7cb46e..e0fb96a 100644 --- a/pbjson-build/src/generator/message.rs +++ b/pbjson-build/src/generator/message.rs @@ -210,7 +210,7 @@ fn write_decode_variant( writeln!(writer, "{}::from_i32({})", resolver.rust_type(path), value)?; write!( writer, - "{}.ok_or_else(|| serde::ser::Error::custom(format!(\"Invalid variant {{}}\", {})))", + "{}.ok_or_else(|| serde::ser::Error::custom(::alloc::format!(\"Invalid variant {{}}\", {})))", Indent(indent), value ) @@ -261,7 +261,7 @@ fn write_serialize_variable( writeln!(writer)?; write!( writer, - "{}}}).collect::, _>>()", + "{}}}).collect::, _>>()", Indent(indent + 1) ) } @@ -287,7 +287,7 @@ fn write_serialize_variable( { writeln!( writer, - "{}let v: std::collections::HashMap<_, _> = {}.iter()", + "{}let v: alloc::collections::BTreeMap<_, _> = {}.iter()", Indent(indent), variable.raw )?; @@ -296,7 +296,7 @@ fn write_serialize_variable( FieldType::Scalar(ScalarType::I64) | FieldType::Scalar(ScalarType::U64) => { writeln!( writer, - "{}.map(|(k, v)| (k, v.to_string())).collect();", + "{}.map(|(k, v)| (k, ::alloc::string::ToString::to_string(v))).collect();", Indent(indent + 1) )?; } @@ -349,7 +349,7 @@ fn write_serialize_scalar_variable( writer: &mut W, ) -> Result<()> { let conversion = match scalar { - ScalarType::I64 | ScalarType::U64 => "ToString::to_string", + ScalarType::I64 | ScalarType::U64 => "::alloc::string::ToString::to_string", ScalarType::Bytes => "pbjson::private::base64::encode", _ => { return writeln!( @@ -366,7 +366,7 @@ fn write_serialize_scalar_variable( FieldModifier::Repeated => { writeln!( writer, - "{}struct_ser.serialize_field(\"{}\", &{}.iter().map({}).collect::>())?;", + "{}struct_ser.serialize_field(\"{}\", &{}.iter().map({}).collect::<::alloc::vec::Vec<_>>())?;", Indent(indent), field_name, variable.raw, @@ -513,11 +513,11 @@ fn write_deserialize_message( r#"{indent}impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {{ {indent} type Value = {rust_type}; -{indent} fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ +{indent} fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{ {indent} formatter.write_str("struct {name}") {indent} }} -{indent} fn visit_map(self, mut map: V) -> std::result::Result<{rust_type}, V::Error> +{indent} fn visit_map(self, mut map: V) -> core::result::Result<{rust_type}, V::Error> {indent} where {indent} V: serde::de::MapAccess<'de>, {indent} {{"#, @@ -689,7 +689,7 @@ fn write_deserialize_field_name( writeln!( writer, r#"{indent}impl<'de> serde::Deserialize<'de> for GeneratedField {{ -{indent} fn deserialize(deserializer: D) -> std::result::Result +{indent} fn deserialize(deserializer: D) -> core::result::Result {indent} where {indent} D: serde::Deserializer<'de>, {indent} {{ @@ -698,12 +698,12 @@ fn write_deserialize_field_name( {indent} impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {{ {indent} type Value = GeneratedField; -{indent} fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ +{indent} fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{ {indent} write!(formatter, "expected one of: {{:?}}", &FIELDS) {indent} }} {indent} #[allow(unused_variables)] -{indent} fn visit_str(self, value: &str) -> std::result::Result +{indent} fn visit_str(self, value: &str) -> core::result::Result {indent} where {indent} E: serde::de::Error, {indent} {{"#, @@ -837,7 +837,7 @@ fn write_deserialize_field( Some(deserializer) => { write!( writer, - "map.next_value::<::std::option::Option<{}>>()?.map(|x| {}::{}(x.0))", + "map.next_value::<::core::option::Option<{}>>()?.map(|x| {}::{}(x.0))", deserializer, resolver.rust_type(&one_of.path), field.rust_type_name() @@ -846,7 +846,7 @@ fn write_deserialize_field( None => { write!( writer, - "map.next_value::<::std::option::Option<_>>()?.map({}::{})", + "map.next_value::<::core::option::Option<_>>()?.map({}::{})", resolver.rust_type(&one_of.path), field.rust_type_name() )?; @@ -855,7 +855,7 @@ fn write_deserialize_field( FieldType::Enum(path) => { write!( writer, - "map.next_value::<::std::option::Option<{}>>()?.map(|x| {}::{}(x as i32))", + "map.next_value::<::core::option::Option<{}>>()?.map(|x| {}::{}(x as i32))", resolver.rust_type(path), resolver.rust_type(&one_of.path), field.rust_type_name() @@ -863,7 +863,7 @@ fn write_deserialize_field( } FieldType::Message(_) => writeln!( writer, - "map.next_value::<::std::option::Option<_>>()?.map({}::{})", + "map.next_value::<::core::option::Option<_>>()?.map({}::{})", resolver.rust_type(&one_of.path), field.rust_type_name() )?, @@ -878,14 +878,14 @@ fn write_deserialize_field( FieldModifier::Optional => { write!( writer, - "map.next_value::<::std::option::Option<{}>>()?.map(|x| x as i32)", + "map.next_value::<::core::option::Option<{}>>()?.map(|x| x as i32)", resolver.rust_type(path) )?; } FieldModifier::Repeated => { write!( writer, - "Some(map.next_value::>()?.into_iter().map(|x| x as i32).collect())", + "Some(map.next_value::<::alloc::vec::Vec<{}>>()?.into_iter().map(|x| x as i32).collect())", resolver.rust_type(path) )?; } @@ -903,7 +903,7 @@ fn write_deserialize_field( match btree_map { true => write!( writer, - "{}map.next_value:: write!( @@ -1012,7 +1012,7 @@ fn write_encode_scalar_field( FieldModifier::Optional => { writeln!( writer, - "{}map.next_value::<::std::option::Option<{}>>()?.map(|x| x.0)", + "{}map.next_value::<::core::option::Option<{}>>()?.map(|x| x.0)", Indent(indent + 1), deserializer )?; @@ -1020,7 +1020,7 @@ fn write_encode_scalar_field( FieldModifier::Repeated => { writeln!( writer, - "{}Some(map.next_value::>()?", + "{}Some(map.next_value::<::alloc::vec::Vec<{}>>()?", Indent(indent + 1), deserializer )?; diff --git a/pbjson-test/build.rs b/pbjson-test/build.rs index 02bd410..b8907a7 100644 --- a/pbjson-test/build.rs +++ b/pbjson-test/build.rs @@ -28,7 +28,7 @@ fn main() -> Result<()> { .compile_well_known_types() .extern_path(".google.protobuf", "::pbjson_types") .extern_path(".test.external", "crate") - .bytes(&[".test"]) + .bytes([".test"]) .protoc_arg("--experimental_allow_proto3_optional"); if cfg!(feature = "btree") { diff --git a/pbjson-test/src/lib.rs b/pbjson-test/src/lib.rs index a2f604a..031077e 100644 --- a/pbjson-test/src/lib.rs +++ b/pbjson-test/src/lib.rs @@ -1,7 +1,9 @@ use serde::{Deserialize, Serialize}; +extern crate alloc; + /// A test of an externally defined message -#[derive(Clone, PartialEq, ::prost::Message, Serialize, Deserialize)] +#[derive(Clone, Eq, PartialEq, ::prost::Message, Serialize, Deserialize)] pub struct ExternMessage {} /// A test of an externally defined enumeration @@ -211,6 +213,8 @@ mod tests { #[test] #[cfg(not(any(feature = "emit-fields", feature = "use-integers-for-enums")))] fn test_kitchen_sink() { + use chrono::Timelike; + let mut decoded: KitchenSink = serde_json::from_str("{}").unwrap(); verify(&decoded, "{}"); @@ -547,13 +551,20 @@ mod tests { decoded.optional_string = None; verify_decode(&decoded, "{}"); - let date = chrono::Utc.ymd(2072, 3, 1).and_hms_milli(5, 2, 5, 30); + let date = chrono::Utc + .with_ymd_and_hms(2072, 3, 1, 5, 2, 5) + .unwrap() + .with_nanosecond(30) + .unwrap(); decoded.timestamp = Some(Timestamp { seconds: date.timestamp(), nanos: date.timestamp_subsec_nanos() as i32, }); - verify(&decoded, r#"{"timestamp":"2072-03-01T05:02:05.030+00:00"}"#); + verify( + &decoded, + r#"{"timestamp":"2072-03-01T05:02:05.000000030+00:00"}"#, + ); decoded.timestamp = None; verify_decode(&decoded, "{}"); diff --git a/pbjson-types/Cargo.toml b/pbjson-types/Cargo.toml index 05687d8..e2fba0e 100644 --- a/pbjson-types/Cargo.toml +++ b/pbjson-types/Cargo.toml @@ -13,12 +13,16 @@ exclude = ["protos/*"] [lib] name = "pbjson_types" +[features] +default = ["std"] +std = ["prost/std", "serde/std"] + [dependencies] # In alphabetical order bytes = "1.0" chrono = { version = "0.4", default-features = false, features = ["alloc"] } informalsystems-pbjson = { path = "../pbjson", version = "0.5" } -prost = "0.11" -serde = { version = "1.0", features = ["derive"] } +prost = { version = "0.11", default-features = false } +serde = { version = "1.0", features = ["derive"], default-features = false } [dev-dependencies] serde_json = "1.0" diff --git a/pbjson-types/build.rs b/pbjson-types/build.rs index 99bebcc..6dfcb02 100644 --- a/pbjson-types/build.rs +++ b/pbjson-types/build.rs @@ -16,10 +16,16 @@ fn main() -> Result<()> { config .file_descriptor_set_path(&descriptor_path) .compile_well_known_types() - .disable_comments(&["."]) - .bytes(&[".google"]) + .disable_comments(["."]) + .bytes([".google"]) .skip_protoc_run(); + let std = std::env::var("CARGO_FEATURE_STD").map_or(false, |_| true); + + if !std { + config.btree_map([".google"]); + } + let empty: &[&str] = &[]; config.compile_protos(empty, empty)?; diff --git a/pbjson-types/src/duration.rs b/pbjson-types/src/duration.rs index bc2801d..23066d7 100644 --- a/pbjson-types/src/duration.rs +++ b/pbjson-types/src/duration.rs @@ -2,8 +2,11 @@ use crate::Duration; use serde::de::Visitor; use serde::Serialize; -impl TryFrom for std::time::Duration { - type Error = std::num::TryFromIntError; +use alloc::format; +use alloc::string::ToString; + +impl TryFrom for core::time::Duration { + type Error = core::num::TryFromIntError; fn try_from(value: Duration) -> Result { Ok(Self::new( @@ -13,8 +16,8 @@ impl TryFrom for std::time::Duration { } } -impl From for Duration { - fn from(value: std::time::Duration) -> Self { +impl From for Duration { + fn from(value: core::time::Duration) -> Self { Self { seconds: value.as_secs() as _, nanos: value.subsec_nanos() as _, @@ -43,7 +46,7 @@ impl Serialize for Duration { if self.nanos != 0 { s.push('.'); - let f = match split_nanos(self.nanos.abs() as u32) { + let f = match split_nanos(self.nanos.unsigned_abs()) { (millis, 0, 0) => format!("{:03}", millis), (millis, micros, 0) => format!("{:03}{:03}", millis, micros), (millis, micros, nanos) => format!("{:03}{:03}{:03}", millis, micros, nanos), @@ -61,7 +64,7 @@ struct DurationVisitor; impl<'de> Visitor<'de> for DurationVisitor { type Value = Duration; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("a duration string") } diff --git a/pbjson-types/src/lib.rs b/pbjson-types/src/lib.rs index caf506a..82af122 100644 --- a/pbjson-types/src/lib.rs +++ b/pbjson-types/src/lib.rs @@ -9,6 +9,7 @@ //! [2]: https://docs.rs/serde/1.0.130/serde/trait.Deserialize.html //! [3]: https://developers.google.com/protocol-buffers/docs/proto3#json +#![cfg_attr(not(feature = "std"), no_std)] #![deny(rustdoc::broken_intra_doc_links, rustdoc::bare_urls, rust_2018_idioms)] #![warn( missing_debug_implementations, @@ -18,6 +19,8 @@ clippy::future_not_send )] +extern crate alloc; + #[allow( unused_imports, clippy::redundant_static_lifetimes, diff --git a/pbjson-types/src/list_value.rs b/pbjson-types/src/list_value.rs index 35062f8..99b425b 100644 --- a/pbjson-types/src/list_value.rs +++ b/pbjson-types/src/list_value.rs @@ -1,5 +1,7 @@ use crate::ListValue; +use alloc::vec::Vec; + impl From> for ListValue { fn from(values: Vec) -> Self { Self { values } @@ -59,7 +61,7 @@ struct ListValueVisitor; impl<'de> serde::de::Visitor<'de> for ListValueVisitor { type Value = ListValue; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("google.protobuf.ListValue") } diff --git a/pbjson-types/src/null_value.rs b/pbjson-types/src/null_value.rs index bd0f9af..fda3652 100644 --- a/pbjson-types/src/null_value.rs +++ b/pbjson-types/src/null_value.rs @@ -29,7 +29,7 @@ struct NullValueVisitor; impl<'de> serde::de::Visitor<'de> for NullValueVisitor { type Value = NullValue; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("google.protobuf.NullValue") } diff --git a/pbjson-types/src/struct.rs b/pbjson-types/src/struct.rs index 428dcaa..d5c539a 100644 --- a/pbjson-types/src/struct.rs +++ b/pbjson-types/src/struct.rs @@ -1,11 +1,21 @@ use crate::Struct; +use alloc::string::String; + +#[cfg(feature = "std")] impl From> for Struct { fn from(fields: std::collections::HashMap) -> Self { Self { fields } } } +#[cfg(not(feature = "std"))] +impl From> for Struct { + fn from(fields: alloc::collections::BTreeMap) -> Self { + Self { fields } + } +} + impl FromIterator<(String, crate::Value)> for Struct { fn from_iter(iter: T) -> Self where @@ -40,7 +50,7 @@ struct StructVisitor; impl<'de> serde::de::Visitor<'de> for StructVisitor { type Value = Struct; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("google.protobuf.Struct") } @@ -48,8 +58,12 @@ impl<'de> serde::de::Visitor<'de> for StructVisitor { where A: serde::de::MapAccess<'de>, { + #[cfg(feature = "std")] let mut map = std::collections::HashMap::new(); + #[cfg(not(feature = "std"))] + let mut map = alloc::collections::BTreeMap::new(); + while let Some((key, value)) = map_access.next_entry()? { map.insert(key, value); } @@ -60,6 +74,8 @@ impl<'de> serde::de::Visitor<'de> for StructVisitor { #[cfg(test)] mod tests { + use super::*; + #[test] fn it_works() { let map: crate::Struct = std::collections::HashMap::from([ diff --git a/pbjson-types/src/timestamp.rs b/pbjson-types/src/timestamp.rs index 7f2d22f..d6904d7 100644 --- a/pbjson-types/src/timestamp.rs +++ b/pbjson-types/src/timestamp.rs @@ -4,12 +4,15 @@ use serde::de::Visitor; use serde::Serialize; impl TryFrom for chrono::DateTime { - type Error = std::num::TryFromIntError; + type Error = core::num::TryFromIntError; fn try_from(value: Timestamp) -> Result { let Timestamp { seconds, nanos } = value; + let dt = NaiveDateTime::from_timestamp_opt(seconds, nanos.try_into()?); - let dt = NaiveDateTime::from_timestamp(seconds, nanos.try_into()?); - Ok(Self::from_utc(dt, Utc)) + Ok(Self::from_utc( + dt.expect("invalid or out-of-range datetime"), + Utc, + )) } } @@ -37,7 +40,7 @@ struct TimestampVisitor; impl<'de> Visitor<'de> for TimestampVisitor { type Value = Timestamp; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("a date string") } @@ -69,9 +72,10 @@ mod tests { #[test] fn test_date() { - let datetime = FixedOffset::east(5 * 3600) - .ymd(2016, 11, 8) - .and_hms(21, 7, 9); + let datetime = FixedOffset::east_opt(5 * 3600) + .unwrap() + .with_ymd_and_hms(2016, 11, 8, 21, 7, 9) + .unwrap(); let encoded = datetime.to_rfc3339(); assert_eq!(&encoded, "2016-11-08T21:07:09+05:00"); diff --git a/pbjson-types/src/value.rs b/pbjson-types/src/value.rs index 9ea05ad..11d5297 100644 --- a/pbjson-types/src/value.rs +++ b/pbjson-types/src/value.rs @@ -1,5 +1,8 @@ pub use crate::pb::google::protobuf::value::Kind; +use alloc::string::String; +use alloc::vec::Vec; + use serde::{ de::{self, MapAccess, SeqAccess}, ser, Deserialize, Deserializer, Serialize, Serializer, @@ -30,7 +33,6 @@ from! { crate::ListValue => Kind::from(value).into(), crate::Struct => Kind::from(value).into(), f64 => Kind::from(value).into(), - std::collections::HashMap => Kind::from(value).into(), } Kind[value] => { @@ -42,10 +44,31 @@ from! { crate::ListValue => Self::ListValue(value), crate::Struct => Self::StructValue(value), f64 => Self::NumberValue(value), + } +} + +#[cfg(feature = "std")] +from! { + crate::Value[value] => { + std::collections::HashMap => Kind::from(value).into(), + } + + Kind[value] => { std::collections::HashMap => Self::StructValue(value.into()), } } +#[cfg(not(feature = "std"))] +from! { + crate::Value[value] => { + alloc::collections::BTreeMap => Kind::from(value).into(), + } + + Kind[value] => { + alloc::collections::BTreeMap => Self::StructValue(value.into()), + } +} + impl From<[Self; N]> for crate::Value { fn from(value: [Self; N]) -> Self { crate::ListValue::from(value).into() @@ -116,7 +139,7 @@ struct KindVisitor; impl<'de> serde::de::Visitor<'de> for KindVisitor { type Value = Kind; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("google.protobuf.Value") } @@ -277,8 +300,12 @@ impl<'de> serde::de::Visitor<'de> for KindVisitor { where A: MapAccess<'de>, { + #[cfg(feature = "std")] let mut map = std::collections::HashMap::new(); + #[cfg(not(feature = "std"))] + let mut map = alloc::collections::BTreeMap::new(); + while let Some((key, value)) = map_access.next_entry()? { map.insert(key, value); } diff --git a/pbjson-types/src/wrappers.rs b/pbjson-types/src/wrappers.rs index 38a5b0a..3ce6fc7 100644 --- a/pbjson-types/src/wrappers.rs +++ b/pbjson-types/src/wrappers.rs @@ -1,5 +1,8 @@ use prost::bytes::Bytes; +use alloc::string::String; +use alloc::string::ToString; + macro_rules! ser_scalar_value { ($typ: ty) => { impl serde::Serialize for $typ { @@ -32,11 +35,7 @@ macro_rules! ser_bytes_value { where S: serde::Serializer, { - use pbjson::private::base64::engine::Engine; - - let value = - pbjson::private::base64::engine::general_purpose::STANDARD.encode(&self.value); - + let value = pbjson::private::base64::encode(&self.value); value.serialize(ser) } } diff --git a/pbjson/Cargo.toml b/pbjson/Cargo.toml index 51399e4..b689864 100644 --- a/pbjson/Cargo.toml +++ b/pbjson/Cargo.toml @@ -14,7 +14,7 @@ name = "pbjson" [dependencies] serde = { version = "1.0", features = ["derive"] } -base64 = "0.21" +base64 = "0.13" [dev-dependencies] bytes = "1.0" diff --git a/pbjson/src/lib.rs b/pbjson/src/lib.rs index a09cccb..640c8b8 100644 --- a/pbjson/src/lib.rs +++ b/pbjson/src/lib.rs @@ -8,6 +8,7 @@ //! [2]: https://developers.google.com/protocol-buffers/docs/proto3#json //! [3]: https://docs.rs/pbjson-build //! +#![no_std] #![deny(rustdoc::broken_intra_doc_links, rustdoc::bare_urls, rust_2018_idioms)] #![warn( missing_debug_implementations, @@ -17,16 +18,19 @@ clippy::future_not_send )] +extern crate alloc; + #[doc(hidden)] pub mod private { /// Re-export base64 pub use base64; - use base64::Engine; + use alloc::borrow::Cow; + use alloc::str::FromStr; + use alloc::vec::Vec; + use serde::de::Visitor; use serde::Deserialize; - use std::borrow::Cow; - use std::str::FromStr; /// Used to parse a number from either a string or its raw representation #[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash, Ord, Eq)] @@ -43,7 +47,7 @@ pub mod private { impl<'de, T> serde::Deserialize<'de> for NumberDeserialize where T: FromStr + serde::Deserialize<'de>, - ::Err: std::error::Error, + ::Err: core::fmt::Display, // std::error::Error, { fn deserialize(deserializer: D) -> Result where @@ -62,7 +66,7 @@ pub mod private { impl<'de> Visitor<'de> for Base64Visitor { type Value = Vec; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("a base64 string") } @@ -70,8 +74,7 @@ pub mod private { where E: serde::de::Error, { - let decoded = base64::engine::general_purpose::STANDARD - .decode(s) + let decoded = base64::decode_config(s, base64::STANDARD) .or_else(|e| match e { // Either standard or URL-safe base64 encoding are accepted // @@ -80,7 +83,7 @@ pub mod private { // Therefore if we error out on those characters, try again with // the URL-safe character set base64::DecodeError::InvalidByte(_, c) if c == b'-' || c == b'_' => { - base64::engine::general_purpose::URL_SAFE.decode(s) + base64::decode_config(s, base64::URL_SAFE) } _ => Err(e), }) @@ -116,15 +119,15 @@ pub mod private { for _ in 0..20 { let mut rng = thread_rng(); let len = rng.gen_range(50..100); - let raw: Vec<_> = std::iter::from_fn(|| Some(rng.gen())).take(len).collect(); + let raw: Vec<_> = core::iter::from_fn(|| Some(rng.gen())).take(len).collect(); for config in [ - base64::engine::general_purpose::STANDARD, - base64::engine::general_purpose::STANDARD_NO_PAD, - base64::engine::general_purpose::URL_SAFE, - base64::engine::general_purpose::URL_SAFE_NO_PAD, + base64::STANDARD, + base64::STANDARD_NO_PAD, + base64::URL_SAFE, + base64::URL_SAFE_NO_PAD, ] { - let encoded = config.encode(&raw); + let encoded = base64::encode_config(&raw, config); let deserializer = BorrowedStrDeserializer::<'_, Error>::new(&encoded); let a: Bytes = BytesDeserialize::deserialize(deserializer).unwrap().0;