From e0fc43ea968a9ca1586642eff937005648eed346 Mon Sep 17 00:00:00 2001 From: Dominik Nakamura Date: Sat, 21 Oct 2023 00:43:32 +0900 Subject: [PATCH] feat: add non-zero collection types This expands from non-zero integer values to non-zero collections, meaning `non_zero` specializations where `T` is one of `string`, `bytes`, `vec`, `hash_map` or `hash_set`. Any other `non_zero` types are now refused as there is no clear way of defining what non-zero means for other types (or at least not a single _correct_ way). Also, snapshot tests includes missing adjustments from the last commits that introduced further `#[allow(...)]` attributes in the Rust implementation. --- crates/stef-build/src/decode.rs | 26 +- crates/stef-build/src/definition.rs | 25 +- crates/stef-build/src/encode.rs | 39 +-- ...ompiler__compile@attribute-multi.stef.snap | 6 +- ...mpiler__compile@attribute-single.stef.snap | 6 +- ...compiler__compile@attribute-unit.stef.snap | 6 +- ...piler__compile@attributes-min-ws.stef.snap | 6 +- .../compiler__compile@attributes.stef.snap | 6 +- .../compiler__compile@module-basic.stef.snap | 6 +- .../compiler__compile@schema-basic.stef.snap | 6 +- .../compiler__compile@struct-basic.stef.snap | 6 +- ...ompiler__compile@struct-generics.stef.snap | 6 +- ...compiler__compile@struct-many-ws.stef.snap | 6 +- .../compiler__compile@struct-min-ws.stef.snap | 6 +- .../compiler__compile@struct-tuple.stef.snap | 6 +- .../compiler__compile@types-basic.stef.snap | 6 +- .../compiler__compile@types-generic.stef.snap | 6 +- .../compiler__compile@types-nested.stef.snap | 19 +- ...compiler__compile@types-non-zero.stef.snap | 304 ++++++++++++++++++ .../compiler__compile@types-ref.stef.snap | 12 +- .../tests/inputs/types-non-zero.stef | 17 + .../parser__parse@types-non-zero.stef.snap | 216 +++++++++++++ .../parser__print@types-non-zero.stef.snap | 24 ++ crates/stef-playground/src/lib.rs | 4 + crates/stef/src/buf/decode.rs | 78 ++++- crates/stef/src/buf/encode.rs | 12 +- crates/stef/src/buf/mod.rs | 104 ++++++ crates/stef/src/lib.rs | 53 +++ 28 files changed, 963 insertions(+), 54 deletions(-) create mode 100644 crates/stef-build/tests/snapshots/compiler__compile@types-non-zero.stef.snap create mode 100644 crates/stef-parser/tests/inputs/types-non-zero.stef create mode 100644 crates/stef-parser/tests/snapshots/parser__parse@types-non-zero.stef.snap create mode 100644 crates/stef-parser/tests/snapshots/parser__print@types-non-zero.stef.snap diff --git a/crates/stef-build/src/decode.rs b/crates/stef-build/src/decode.rs index 44a7527..f854e18 100644 --- a/crates/stef-build/src/decode.rs +++ b/crates/stef-build/src/decode.rs @@ -269,7 +269,7 @@ fn compile_data_type(ty: &DataType<'_>) -> TokenStream { let ty = compile_data_type(ty); quote! { ::stef::buf::decode_option(r, |r| { #ty }) } } - DataType::NonZero(ty) => match **ty { + DataType::NonZero(ty) => match &**ty { DataType::U8 => quote! { ::std::num::NonZeroU8::decode(r) }, DataType::U16 => quote! { ::std::num::NonZeroU16::decode(r) }, DataType::U32 => quote! { ::std::num::NonZeroU32::decode(r) }, @@ -280,10 +280,26 @@ fn compile_data_type(ty: &DataType<'_>) -> TokenStream { DataType::I32 => quote! { ::std::num::NonZeroI32::decode(r) }, DataType::I64 => quote! { ::std::num::NonZeroI64::decode(r) }, DataType::I128 => quote! { ::std::num::NonZeroI128::decode(r) }, - _ => quote! { - let _r = r; - todo!(); - }, + DataType::String | DataType::StringRef => { + quote! { ::stef::buf::decode_non_zero_string(r) } + } + DataType::Bytes | DataType::BytesRef => { + quote! { ::stef::buf::decode_non_zero_bytes(r) } + } + DataType::Vec(ty) => { + let ty = compile_data_type(ty); + quote! { ::stef::buf::decode_non_zero_vec(r, |r| { #ty }) } + } + DataType::HashMap(kv) => { + let ty_k = compile_data_type(&kv.0); + let ty_v = compile_data_type(&kv.1); + quote! { ::stef::buf::decode_non_zero_hash_map(r, |r| { #ty_k }, |r| { #ty_v }) } + } + DataType::HashSet(ty) => { + let ty = compile_data_type(ty); + quote! { ::stef::buf::decode_non_zero_hash_set(r, |r| { #ty }) } + } + ty => todo!("compiler should catch invalid {ty:?} type"), }, DataType::BoxString => quote! { Box::::decode(r) }, DataType::BoxBytes => quote! { Box::<[u8]>::decode(r) }, diff --git a/crates/stef-build/src/definition.rs b/crates/stef-build/src/definition.rs index 3660183..f7ec59a 100644 --- a/crates/stef-build/src/definition.rs +++ b/crates/stef-build/src/definition.rs @@ -285,7 +285,7 @@ pub(super) fn compile_data_type(ty: &DataType<'_>) -> TokenStream { let ty = compile_data_type(ty); quote! { Option<#ty> } } - DataType::NonZero(ty) => match **ty { + DataType::NonZero(ty) => match &**ty { DataType::U8 => quote! { ::std::num::NonZeroU8 }, DataType::U16 => quote! { ::std::num::NonZeroU16 }, DataType::U32 => quote! { ::std::num::NonZeroU32 }, @@ -296,7 +296,22 @@ pub(super) fn compile_data_type(ty: &DataType<'_>) -> TokenStream { DataType::I32 => quote! { ::std::num::NonZeroI32 }, DataType::I64 => quote! { ::std::num::NonZeroI64 }, DataType::I128 => quote! { ::std::num::NonZeroI128 }, - _ => compile_data_type(ty), + DataType::String | DataType::StringRef => quote! { ::stef::NonZeroString }, + DataType::Bytes | DataType::BytesRef => quote! { ::stef::NonZeroBytes }, + DataType::Vec(ty) => { + let ty = compile_data_type(ty); + quote! { ::stef::NonZeroVec<#ty> } + } + DataType::HashMap(kv) => { + let k = compile_data_type(&kv.0); + let v = compile_data_type(&kv.1); + quote! { ::stef::NonZeroHashMap<#k, #v> } + } + DataType::HashSet(ty) => { + let ty = compile_data_type(ty); + quote! { ::stef::NonZeroHashSet<#ty> } + } + ty => todo!("compiler should catch invalid {ty:?} type"), }, DataType::BoxString => quote! { Box }, DataType::BoxBytes => quote! { Box<[u8]> }, @@ -421,7 +436,11 @@ mod tests { } #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/src/encode.rs b/crates/stef-build/src/encode.rs index 338cd16..bffb65a 100644 --- a/crates/stef-build/src/encode.rs +++ b/crates/stef-build/src/encode.rs @@ -307,25 +307,26 @@ fn compile_data_type(ty: &DataType<'_>, name: TokenStream) -> TokenStream { ); quote! { ::stef::buf::encode_option(w, &#name, |w, v| { #ty; }) } } - DataType::NonZero(ty) => { - if matches!( - **ty, - DataType::U8 - | DataType::U16 - | DataType::U32 - | DataType::U64 - | DataType::U128 - | DataType::I8 - | DataType::I16 - | DataType::I32 - | DataType::I64 - | DataType::I128 - ) { - quote! { (#name).encode(w) } - } else { - compile_data_type(ty, name) - } - } + DataType::NonZero(ty) => match &**ty { + DataType::U8 + | DataType::U16 + | DataType::U32 + | DataType::U64 + | DataType::U128 + | DataType::I8 + | DataType::I16 + | DataType::I32 + | DataType::I64 + | DataType::I128 => quote! { (#name).encode(w) }, + DataType::String + | DataType::StringRef + | DataType::Bytes + | DataType::BytesRef + | DataType::Vec(_) + | DataType::HashMap(_) + | DataType::HashSet(_) => compile_data_type(ty, quote! { #name.get() }), + ty => todo!("compiler should catch invalid {ty:?} type"), + }, DataType::BoxString => quote! { ::stef::buf::encode_string(w, &*#name) }, DataType::BoxBytes => quote! { ::stef::buf::encode_bytes(w, &*#name) }, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@attribute-multi.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@attribute-multi.stef.snap index 4d89385..9a34ee1 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@attribute-multi.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@attribute-multi.stef.snap @@ -9,7 +9,11 @@ use ::stef::buf::{Decode, Encode}; pub struct Sample; #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) {} } #[automatically_derived] diff --git a/crates/stef-build/tests/snapshots/compiler__compile@attribute-single.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@attribute-single.stef.snap index b4c521f..1be237e 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@attribute-single.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@attribute-single.stef.snap @@ -9,7 +9,11 @@ use ::stef::buf::{Decode, Encode}; pub struct Sample; #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) {} } #[automatically_derived] diff --git a/crates/stef-build/tests/snapshots/compiler__compile@attribute-unit.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@attribute-unit.stef.snap index a150655..fb18a08 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@attribute-unit.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@attribute-unit.stef.snap @@ -9,7 +9,11 @@ use ::stef::buf::{Decode, Encode}; pub struct Sample; #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) {} } #[automatically_derived] diff --git a/crates/stef-build/tests/snapshots/compiler__compile@attributes-min-ws.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@attributes-min-ws.stef.snap index 085d26d..bc63f69 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@attributes-min-ws.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@attributes-min-ws.stef.snap @@ -9,7 +9,11 @@ use ::stef::buf::{Decode, Encode}; pub struct Sample; #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) {} } #[automatically_derived] diff --git a/crates/stef-build/tests/snapshots/compiler__compile@attributes.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@attributes.stef.snap index 26291b3..aaf2668 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@attributes.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@attributes.stef.snap @@ -9,7 +9,11 @@ use ::stef::buf::{Decode, Encode}; pub struct Sample; #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) {} } #[automatically_derived] diff --git a/crates/stef-build/tests/snapshots/compiler__compile@module-basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@module-basic.stef.snap index 66e27bf..97202d1 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@module-basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@module-basic.stef.snap @@ -43,7 +43,11 @@ pub mod a { } #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@schema-basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@schema-basic.stef.snap index 723ed4c..423122b 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@schema-basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@schema-basic.stef.snap @@ -13,7 +13,11 @@ pub struct SampleStruct { } #[automatically_derived] impl ::stef::Encode for SampleStruct { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct-basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct-basic.stef.snap index 3914138..a4b4764 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct-basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct-basic.stef.snap @@ -14,7 +14,11 @@ pub struct Sample { } #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct-generics.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct-generics.stef.snap index 6947216..bf1865b 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct-generics.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct-generics.stef.snap @@ -17,7 +17,11 @@ where K: ::stef::buf::Encode, V: ::stef::buf::Encode, { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct-many-ws.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct-many-ws.stef.snap index 1486080..94a87f1 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct-many-ws.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct-many-ws.stef.snap @@ -17,7 +17,11 @@ where A: ::stef::buf::Encode, B: ::stef::buf::Encode, { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct-min-ws.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct-min-ws.stef.snap index 86cb27f..5ea9a68 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct-min-ws.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct-min-ws.stef.snap @@ -16,7 +16,11 @@ impl ::stef::Encode for Sample where T: ::stef::buf::Encode, { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct-tuple.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct-tuple.stef.snap index d5d9d87..905ce5e 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct-tuple.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct-tuple.stef.snap @@ -10,7 +10,11 @@ use ::stef::buf::{Decode, Encode}; pub struct Sample(u32, bool); #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, self.0) }); ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_bool(w, self.1) }); diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types-basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types-basic.stef.snap index a6fce57..4b5aff5 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types-basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types-basic.stef.snap @@ -31,7 +31,11 @@ pub struct Sample { } #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types-generic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types-generic.stef.snap index d3569a6..2f98e74 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types-generic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types-generic.stef.snap @@ -15,7 +15,11 @@ pub struct Sample { } #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types-nested.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types-nested.stef.snap index 4851967..2880019 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types-nested.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types-nested.stef.snap @@ -7,11 +7,15 @@ input_file: crates/stef-parser/tests/inputs/types-nested.stef use ::stef::buf::{Decode, Encode}; #[derive(Clone, Debug, PartialEq)] pub struct Sample { - pub value: Vec>>>, + pub value: Vec>>>, } #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, @@ -27,7 +31,7 @@ impl ::stef::Encode for Sample { |w, v| { ::stef::buf::encode_hash_map( w, - &v, + &v.get(), |w, k| { ::stef::buf::encode_i64(w, *k); }, @@ -48,7 +52,7 @@ impl ::stef::Encode for Sample { impl ::stef::Decode for Sample { #[allow(clippy::type_complexity)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - let mut value: Option>>>> = None; + let mut value: Option>>>> = None; loop { match ::stef::buf::decode_id(r)? { ::stef::buf::END_MARKER => break, @@ -60,8 +64,11 @@ impl ::stef::Decode for Sample { ::stef::buf::decode_option( r, |r| { - let _r = r; - todo!(); + ::stef::buf::decode_non_zero_hash_map( + r, + |r| { ::stef::buf::decode_i64(r) }, + |r| { Box::::decode(r) }, + ) }, ) }, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types-non-zero.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types-non-zero.stef.snap new file mode 100644 index 0000000..d24eb64 --- /dev/null +++ b/crates/stef-build/tests/snapshots/compiler__compile@types-non-zero.stef.snap @@ -0,0 +1,304 @@ +--- +source: crates/stef-build/tests/compiler.rs +expression: "struct Sample {\n f01: non_zero @1,\n f02: non_zero @2,\n f03: non_zero @3,\n f04: non_zero @4,\n f05: non_zero @5,\n f06: non_zero @6,\n f07: non_zero @7,\n f08: non_zero @8,\n f09: non_zero @9,\n f10: non_zero @10,\n f11: non_zero @11,\n f12: non_zero @12,\n f13: non_zero> @13,\n f14: non_zero> @14,\n f15: non_zero> @15,\n}" +input_file: crates/stef-parser/tests/inputs/types-non-zero.stef +--- +#[allow(unused_imports)] +use ::stef::buf::{Decode, Encode}; +#[derive(Clone, Debug, PartialEq)] +pub struct Sample { + pub f01: ::std::num::NonZeroU8, + pub f02: ::std::num::NonZeroU16, + pub f03: ::std::num::NonZeroU32, + pub f04: ::std::num::NonZeroU64, + pub f05: ::std::num::NonZeroU128, + pub f06: ::std::num::NonZeroI8, + pub f07: ::std::num::NonZeroI16, + pub f08: ::std::num::NonZeroI32, + pub f09: ::std::num::NonZeroI64, + pub f10: ::std::num::NonZeroI128, + pub f11: ::stef::NonZeroString, + pub f12: ::stef::NonZeroBytes, + pub f13: ::stef::NonZeroVec, + pub f14: ::stef::NonZeroHashMap>, + pub f15: ::stef::NonZeroHashSet, +} +#[automatically_derived] +impl ::stef::Encode for Sample { + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field( + w, + 1, + |w| { + (self.f01).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 2, + |w| { + (self.f02).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 3, + |w| { + (self.f03).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 4, + |w| { + (self.f04).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 5, + |w| { + (self.f05).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 6, + |w| { + (self.f06).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 7, + |w| { + (self.f07).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 8, + |w| { + (self.f08).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 9, + |w| { + (self.f09).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 10, + |w| { + (self.f10).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 11, + |w| { + ::stef::buf::encode_string(w, &self.f11.get()); + }, + ); + ::stef::buf::encode_field( + w, + 12, + |w| { + ::stef::buf::encode_bytes(w, &self.f12.get()); + }, + ); + ::stef::buf::encode_field( + w, + 13, + |w| { + ::stef::buf::encode_vec( + w, + &self.f13.get(), + |w, v| { + ::stef::buf::encode_string(w, &v); + }, + ); + }, + ); + ::stef::buf::encode_field( + w, + 14, + |w| { + ::stef::buf::encode_hash_map( + w, + &self.f14.get(), + |w, k| { + ::stef::buf::encode_string(w, &k); + }, + |w, v| { + ::stef::buf::encode_bytes(w, &v); + }, + ); + }, + ); + ::stef::buf::encode_field( + w, + 15, + |w| { + ::stef::buf::encode_hash_set( + w, + &self.f15.get(), + |w, v| { + ::stef::buf::encode_string(w, &v); + }, + ); + }, + ); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } +} +#[automatically_derived] +impl ::stef::Decode for Sample { + #[allow(clippy::type_complexity)] + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut f01: Option<::std::num::NonZeroU8> = None; + let mut f02: Option<::std::num::NonZeroU16> = None; + let mut f03: Option<::std::num::NonZeroU32> = None; + let mut f04: Option<::std::num::NonZeroU64> = None; + let mut f05: Option<::std::num::NonZeroU128> = None; + let mut f06: Option<::std::num::NonZeroI8> = None; + let mut f07: Option<::std::num::NonZeroI16> = None; + let mut f08: Option<::std::num::NonZeroI32> = None; + let mut f09: Option<::std::num::NonZeroI64> = None; + let mut f10: Option<::std::num::NonZeroI128> = None; + let mut f11: Option<::stef::NonZeroString> = None; + let mut f12: Option<::stef::NonZeroBytes> = None; + let mut f13: Option<::stef::NonZeroVec> = None; + let mut f14: Option<::stef::NonZeroHashMap>> = None; + let mut f15: Option<::stef::NonZeroHashSet> = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => f01 = Some(::std::num::NonZeroU8::decode(r)?), + 2 => f02 = Some(::std::num::NonZeroU16::decode(r)?), + 3 => f03 = Some(::std::num::NonZeroU32::decode(r)?), + 4 => f04 = Some(::std::num::NonZeroU64::decode(r)?), + 5 => f05 = Some(::std::num::NonZeroU128::decode(r)?), + 6 => f06 = Some(::std::num::NonZeroI8::decode(r)?), + 7 => f07 = Some(::std::num::NonZeroI16::decode(r)?), + 8 => f08 = Some(::std::num::NonZeroI32::decode(r)?), + 9 => f09 = Some(::std::num::NonZeroI64::decode(r)?), + 10 => f10 = Some(::std::num::NonZeroI128::decode(r)?), + 11 => f11 = Some(::stef::buf::decode_non_zero_string(r)?), + 12 => f12 = Some(::stef::buf::decode_non_zero_bytes(r)?), + 13 => { + f13 = Some( + ::stef::buf::decode_non_zero_vec( + r, + |r| { ::stef::buf::decode_string(r) }, + )?, + ); + } + 14 => { + f14 = Some( + ::stef::buf::decode_non_zero_hash_map( + r, + |r| { ::stef::buf::decode_string(r) }, + |r| { ::stef::buf::decode_bytes(r) }, + )?, + ); + } + 15 => { + f15 = Some( + ::stef::buf::decode_non_zero_hash_set( + r, + |r| { ::stef::buf::decode_string(r) }, + )?, + ); + } + _ => continue, + } + } + Ok(Self { + f01: f01 + .ok_or(::stef::buf::Error::MissingField { + id: 1, + name: Some("f01"), + })?, + f02: f02 + .ok_or(::stef::buf::Error::MissingField { + id: 2, + name: Some("f02"), + })?, + f03: f03 + .ok_or(::stef::buf::Error::MissingField { + id: 3, + name: Some("f03"), + })?, + f04: f04 + .ok_or(::stef::buf::Error::MissingField { + id: 4, + name: Some("f04"), + })?, + f05: f05 + .ok_or(::stef::buf::Error::MissingField { + id: 5, + name: Some("f05"), + })?, + f06: f06 + .ok_or(::stef::buf::Error::MissingField { + id: 6, + name: Some("f06"), + })?, + f07: f07 + .ok_or(::stef::buf::Error::MissingField { + id: 7, + name: Some("f07"), + })?, + f08: f08 + .ok_or(::stef::buf::Error::MissingField { + id: 8, + name: Some("f08"), + })?, + f09: f09 + .ok_or(::stef::buf::Error::MissingField { + id: 9, + name: Some("f09"), + })?, + f10: f10 + .ok_or(::stef::buf::Error::MissingField { + id: 10, + name: Some("f10"), + })?, + f11: f11 + .ok_or(::stef::buf::Error::MissingField { + id: 11, + name: Some("f11"), + })?, + f12: f12 + .ok_or(::stef::buf::Error::MissingField { + id: 12, + name: Some("f12"), + })?, + f13: f13 + .ok_or(::stef::buf::Error::MissingField { + id: 13, + name: Some("f13"), + })?, + f14: f14 + .ok_or(::stef::buf::Error::MissingField { + id: 14, + name: Some("f14"), + })?, + f15: f15 + .ok_or(::stef::buf::Error::MissingField { + id: 15, + name: Some("f15"), + })?, + }) + } +} + diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types-ref.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types-ref.stef.snap index ab3ab40..6898b34 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types-ref.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types-ref.stef.snap @@ -12,7 +12,11 @@ pub struct Sample { } #[automatically_derived] impl ::stef::Encode for Sample { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, @@ -94,7 +98,11 @@ where K: ::stef::buf::Encode, V: ::stef::buf::Encode, { - #[allow(clippy::needless_borrow, clippy::explicit_auto_deref)] + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + )] fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, diff --git a/crates/stef-parser/tests/inputs/types-non-zero.stef b/crates/stef-parser/tests/inputs/types-non-zero.stef new file mode 100644 index 0000000..f18379b --- /dev/null +++ b/crates/stef-parser/tests/inputs/types-non-zero.stef @@ -0,0 +1,17 @@ +struct Sample { + f01: non_zero @1, + f02: non_zero @2, + f03: non_zero @3, + f04: non_zero @4, + f05: non_zero @5, + f06: non_zero @6, + f07: non_zero @7, + f08: non_zero @8, + f09: non_zero @9, + f10: non_zero @10, + f11: non_zero @11, + f12: non_zero @12, + f13: non_zero> @13, + f14: non_zero> @14, + f15: non_zero> @15, +} diff --git a/crates/stef-parser/tests/snapshots/parser__parse@types-non-zero.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@types-non-zero.stef.snap new file mode 100644 index 0000000..7b93932 --- /dev/null +++ b/crates/stef-parser/tests/snapshots/parser__parse@types-non-zero.stef.snap @@ -0,0 +1,216 @@ +--- +source: crates/stef-parser/tests/parser.rs +expression: "Schema :: parse(input.as_str()).unwrap()" +input_file: crates/stef-parser/tests/inputs/types-non-zero.stef +--- +Schema { + definitions: [ + Struct( + Struct { + comment: Comment( + [], + ), + attributes: Attributes( + [], + ), + name: "Sample", + generics: Generics( + [], + ), + fields: Named( + [ + NamedField { + comment: Comment( + [], + ), + name: "f01", + ty: NonZero( + U8, + ), + id: Id( + 1, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f02", + ty: NonZero( + U16, + ), + id: Id( + 2, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f03", + ty: NonZero( + U32, + ), + id: Id( + 3, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f04", + ty: NonZero( + U64, + ), + id: Id( + 4, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f05", + ty: NonZero( + U128, + ), + id: Id( + 5, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f06", + ty: NonZero( + I8, + ), + id: Id( + 6, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f07", + ty: NonZero( + I16, + ), + id: Id( + 7, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f08", + ty: NonZero( + I32, + ), + id: Id( + 8, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f09", + ty: NonZero( + I64, + ), + id: Id( + 9, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f10", + ty: NonZero( + I128, + ), + id: Id( + 10, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f11", + ty: NonZero( + String, + ), + id: Id( + 11, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f12", + ty: NonZero( + Bytes, + ), + id: Id( + 12, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f13", + ty: NonZero( + Vec( + String, + ), + ), + id: Id( + 13, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f14", + ty: NonZero( + HashMap( + ( + String, + Bytes, + ), + ), + ), + id: Id( + 14, + ), + }, + NamedField { + comment: Comment( + [], + ), + name: "f15", + ty: NonZero( + HashSet( + String, + ), + ), + id: Id( + 15, + ), + }, + ], + ), + }, + ), + ], +} diff --git a/crates/stef-parser/tests/snapshots/parser__print@types-non-zero.stef.snap b/crates/stef-parser/tests/snapshots/parser__print@types-non-zero.stef.snap new file mode 100644 index 0000000..15367bf --- /dev/null +++ b/crates/stef-parser/tests/snapshots/parser__print@types-non-zero.stef.snap @@ -0,0 +1,24 @@ +--- +source: crates/stef-parser/tests/parser.rs +expression: "Schema :: parse(input.as_str()).unwrap()" +input_file: crates/stef-parser/tests/inputs/types-non-zero.stef +--- +struct Sample { + f01: non_zero @1, + f02: non_zero @2, + f03: non_zero @3, + f04: non_zero @4, + f05: non_zero @5, + f06: non_zero @6, + f07: non_zero @7, + f08: non_zero @8, + f09: non_zero @9, + f10: non_zero @10, + f11: non_zero @11, + f12: non_zero @12, + f13: non_zero> @13, + f14: non_zero> @14, + f15: non_zero> @15, +} + + diff --git a/crates/stef-playground/src/lib.rs b/crates/stef-playground/src/lib.rs index 95e86b6..7aa9a80 100644 --- a/crates/stef-playground/src/lib.rs +++ b/crates/stef-playground/src/lib.rs @@ -97,6 +97,10 @@ mod schemas { include!(concat!(env!("OUT_DIR"), "/types-nested.rs")); } + mod types_non_zero { + include!(concat!(env!("OUT_DIR"), "/types-non-zero.rs")); + } + mod types_ref { include!(concat!(env!("OUT_DIR"), "/types-ref.rs")); } diff --git a/crates/stef/src/buf/decode.rs b/crates/stef/src/buf/decode.rs index a09f655..dc921e4 100644 --- a/crates/stef/src/buf/decode.rs +++ b/crates/stef/src/buf/decode.rs @@ -8,7 +8,7 @@ use std::{ pub use bytes::Buf; -use crate::varint; +use crate::{varint, NonZero}; pub type Result = std::result::Result; @@ -92,10 +92,9 @@ pub fn decode_string(r: &mut impl Buf) -> Result { } pub fn decode_bytes(r: &mut impl Buf) -> Result> { - let (len, consumed) = varint::decode_u64(r.chunk())?; - r.advance(consumed); - + let len = decode_u64(r)?; ensure_size!(r, len as usize); + Ok(r.copy_to_bytes(len as usize).to_vec()) } @@ -172,6 +171,77 @@ where Ok(buf.try_into().unwrap()) } +macro_rules! ensure_not_empty { + ($size:ident) => { + if $size == 0 { + return Err(Error::Zero); + } + }; +} + +pub fn decode_non_zero_string(r: &mut impl Buf) -> Result> { + String::from_utf8(decode_non_zero_bytes(r)?.into_inner()) + .map(|v| NonZero::::new(v).unwrap()) + .map_err(Into::into) +} + +pub fn decode_non_zero_bytes(r: &mut impl Buf) -> Result>> { + let len = decode_u64(r)?; + ensure_not_empty!(len); + ensure_size!(r, len as usize); + + Ok(NonZero::>::new(r.copy_to_bytes(len as usize).to_vec()).unwrap()) +} + +pub fn decode_non_zero_vec(r: &mut R, decode: D) -> Result>> +where + R: Buf, + D: Fn(&mut R) -> Result, +{ + let len = decode_u64(r)?; + ensure_not_empty!(len); + + (0..len) + .map(|_| decode(r)) + .collect::>() + .map(|v| NonZero::>::new(v).unwrap()) +} + +pub fn decode_non_zero_hash_map( + r: &mut R, + decode_key: DK, + decode_value: DV, +) -> Result>> +where + R: Buf, + K: Hash + Eq, + DK: Fn(&mut R) -> Result, + DV: Fn(&mut R) -> Result, +{ + let len = decode_u64(r)?; + ensure_not_empty!(len); + + (0..len) + .map(|_| Ok((decode_key(r)?, decode_value(r)?))) + .collect::>() + .map(|v| NonZero::>::new(v).unwrap()) +} + +pub fn decode_non_zero_hash_set(r: &mut R, decode: D) -> Result>> +where + R: Buf, + T: Hash + Eq, + D: Fn(&mut R) -> Result, +{ + let len = decode_u64(r)?; + ensure_not_empty!(len); + + (0..len) + .map(|_| decode(r)) + .collect::>() + .map(|v| NonZero::>::new(v).unwrap()) +} + #[inline(always)] pub fn decode_id(r: &mut impl Buf) -> Result { decode_u32(r) diff --git a/crates/stef/src/buf/encode.rs b/crates/stef/src/buf/encode.rs index a52beca..913808a 100644 --- a/crates/stef/src/buf/encode.rs +++ b/crates/stef/src/buf/encode.rs @@ -8,7 +8,7 @@ use std::{ pub use bytes::BufMut; -use crate::varint; +use crate::{varint, NonZero}; pub fn encode_bool(w: &mut impl BufMut, value: bool) { w.put_u8(value.into()); @@ -317,6 +317,16 @@ impl Encode for NonZeroI128 { } } +impl Encode for NonZero +where + T: Encode, +{ + #[inline(always)] + fn encode(&self, w: &mut impl BufMut) { + self.0.encode(w) + } +} + impl<'a, T> Encode for std::borrow::Cow<'a, T> where T: Clone + Encode, diff --git a/crates/stef/src/buf/mod.rs b/crates/stef/src/buf/mod.rs index 97b3c8f..fa08543 100644 --- a/crates/stef/src/buf/mod.rs +++ b/crates/stef/src/buf/mod.rs @@ -5,3 +5,107 @@ pub use encode::*; mod decode; mod encode; + +#[cfg(test)] +mod tests { + use std::collections::{HashMap, HashSet}; + + use super::*; + + #[test] + fn non_zero_string_valid() { + let mut buf = Vec::new(); + encode_string(&mut buf, "test"); + assert!(decode_non_zero_string(&mut &*buf).is_ok()); + } + + #[test] + fn non_zero_string_invalid() { + let mut buf = Vec::new(); + encode_string(&mut buf, ""); + assert!(matches!( + decode_non_zero_string(&mut &*buf), + Err(Error::Zero), + )); + } + + #[test] + fn non_zero_bytes_valid() { + let mut buf = Vec::new(); + encode_bytes(&mut buf, &[1, 2, 3]); + assert!(decode_non_zero_bytes(&mut &*buf).is_ok()); + } + + #[test] + fn non_zero_bytes_invalid() { + let mut buf = Vec::new(); + encode_bytes(&mut buf, &[]); + assert!(matches!( + decode_non_zero_bytes(&mut &*buf), + Err(Error::Zero), + )); + } + + #[test] + fn non_zero_vec_valid() { + let mut buf = Vec::new(); + encode_vec(&mut buf, &[1, 2, 3], |w, v| encode_u32(w, *v)); + assert!(decode_non_zero_vec(&mut &*buf, decode_u32).is_ok()); + } + + #[test] + fn non_zero_vec_invalid() { + let mut buf = Vec::new(); + encode_vec(&mut buf, &[], |w, v| encode_u32(w, *v)); + assert!(matches!( + decode_non_zero_vec(&mut &*buf, decode_u32), + Err(Error::Zero), + )); + } + + #[test] + fn non_zero_hash_map_valid() { + let mut buf = Vec::new(); + encode_hash_map( + &mut buf, + &HashMap::from_iter([(1, true), (2, false)]), + |w, k| encode_u32(w, *k), + |w, v| encode_bool(w, *v), + ); + assert!(decode_non_zero_hash_map(&mut &*buf, decode_u32, decode_bool).is_ok()); + } + + #[test] + fn non_zero_hash_map_invalid() { + let mut buf = Vec::new(); + encode_hash_map( + &mut buf, + &HashMap::new(), + |w, k| encode_u32(w, *k), + |w, v| encode_bool(w, *v), + ); + assert!(matches!( + decode_non_zero_hash_map(&mut &*buf, decode_u32, decode_bool), + Err(Error::Zero), + )); + } + + #[test] + fn non_zero_hash_set_valid() { + let mut buf = Vec::new(); + encode_hash_set(&mut buf, &HashSet::from_iter([1, 2, 3]), |w, v| { + encode_u32(w, *v) + }); + assert!(decode_non_zero_hash_set(&mut &*buf, decode_u32).is_ok()); + } + + #[test] + fn non_zero_hash_set_invalid() { + let mut buf = Vec::new(); + encode_hash_set(&mut buf, &HashSet::new(), |w, v| encode_u32(w, *v)); + assert!(matches!( + decode_non_zero_hash_set(&mut &*buf, decode_u32), + Err(Error::Zero), + )); + } +} diff --git a/crates/stef/src/lib.rs b/crates/stef/src/lib.rs index e8422de..2cc36c5 100644 --- a/crates/stef/src/lib.rs +++ b/crates/stef/src/lib.rs @@ -1,4 +1,57 @@ +use std::{ + collections::{HashMap, HashSet}, + ops::Deref, +}; + pub use buf::{Buf, BufMut, Decode, Encode}; pub mod buf; pub mod varint; + +#[derive(Clone, Debug, PartialEq)] +pub struct NonZero(T); + +impl NonZero { + /// ``` + /// let value = stef::NonZeroString::new("hello".to_owned()).unwrap(); + /// assert_eq!("hello", value.get()); + /// ``` + pub fn get(&self) -> &T { + &self.0 + } + + pub fn into_inner(self) -> T { + self.0 + } +} + +impl Deref for NonZero { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +macro_rules! non_zero_collection { + ($name:ident $(< $($gens:tt),+ >)?) => { + impl $(< $($gens),+ >)? NonZero<$name $(< $($gens),+ >)?> { + /// Try to create a new non-zero instance, which will succeed if the given collection + /// contains in fact some elements. Otherwise `None` is returned. + pub fn new(value: $name $(< $($gens),+ >)?) -> Option { + (!value.is_empty()).then_some(Self(value)) + } + } + }; +} + +non_zero_collection!(String); +non_zero_collection!(Vec); +non_zero_collection!(HashMap); +non_zero_collection!(HashSet); + +pub type NonZeroString = NonZero; +pub type NonZeroBytes = NonZero>; +pub type NonZeroVec = NonZero>; +pub type NonZeroHashMap = NonZero>; +pub type NonZeroHashSet = NonZero>;