diff --git a/Cargo.lock b/Cargo.lock index 84627c9..13cb41d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -108,9 +108,9 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bstr" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" +checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" dependencies = [ "memchr", "serde", @@ -265,25 +265,14 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "errno" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "eyre" version = "0.6.8" @@ -354,9 +343,9 @@ checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" [[package]] name = "insta" -version = "1.33.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa511b2e298cd49b1856746f6bb73e17036bcd66b25f5e92cdcdbec9bd75686" +checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" dependencies = [ "console", "globset", @@ -392,9 +381,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libmimalloc-sys" @@ -414,18 +403,18 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linkme" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f948366ad5bb46b5514ba7a7a80643726eef08b06632592699676748c8bc33b" +checksum = "4ad5707f9d2423042c4321155b22d5257c2f140e3f30d0a42dce882a6010010d" dependencies = [ "linkme-impl", ] [[package]] name = "linkme-impl" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc28438cad73dcc90ff3466fc329a9252b1b8ba668eb0d5668ba97088cf4eef0" +checksum = "b43a5344be08996a47cb02b1ddc737119578a1cd01ae60d91541864df5926db9" dependencies = [ "proc-macro2", "quote", @@ -434,9 +423,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" @@ -560,9 +549,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -578,9 +567,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.6" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" dependencies = [ "aho-corasick", "memchr", @@ -590,9 +579,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" dependencies = [ "aho-corasick", "memchr", @@ -601,15 +590,15 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f96ede7f386ba6e910092e7ccdc04176cface62abebea07ed6b46d870ed95ca2" +checksum = "83aa2b788f84cf0e43e6ae57bb0fcdbbe7604414a7d2bf997a311b062a6f4291" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c3cbb081b9784b07cceb8824c8583f86db4814d172ab043f3c23f7dc600bf83d" [[package]] name = "rustc-demangle" @@ -619,9 +608,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.17" +version = "0.38.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +checksum = "5a74ee2d7c2581cd139b42447d7d9389b889bdaad3a73f1ebb16f2a3237bb19c" dependencies = [ "bitflags", "errno", @@ -661,9 +650,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" [[package]] name = "smawk" diff --git a/Cargo.toml b/Cargo.toml index 10db40a..8f6c80e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,10 @@ license = "MIT" [workspace.dependencies] color-eyre = { version = "0.6.2", default-features = false } indoc = "2.0.4" -insta = { version = "1.33.0", features = ["glob"] } +insta = { version = "1.34.0", features = ["glob"] } miette = "5.10.0" mimalloc = "0.1.39" -proc-macro2 = { version = "1.0.67", default-features = false } +proc-macro2 = { version = "1.0.69", default-features = false } quote = { version = "1.0.33", default-features = false } syn = "2.0.38" thiserror = "1.0.49" diff --git a/Justfile b/Justfile index 770ae74..742e59a 100644 --- a/Justfile +++ b/Justfile @@ -15,6 +15,10 @@ bench: check: cargo hack clippy --workspace --feature-powerset --no-dev-deps +# Format the code of all Rust crates +fmt: + cargo +nightly fmt --all + # Start up the local server for the book @book: cd book && just dev diff --git a/crates/stef-build/src/decode.rs b/crates/stef-build/src/decode.rs index 78f6992..b2fdffe 100644 --- a/crates/stef-build/src/decode.rs +++ b/crates/stef-build/src/decode.rs @@ -144,7 +144,11 @@ fn compile_field_matches(fields: &Fields<'_>) -> TokenStream { }| { let id = proc_macro2::Literal::u32_unsuffixed(id.0); let name = proc_macro2::Ident::new(name, Span::call_site()); - let ty = compile_data_type(ty); + let ty = compile_data_type(if let DataType::Option(ty) = ty { + ty + } else { + ty + }); quote! { #id => #name = Some(#ty?) } }, @@ -159,7 +163,11 @@ fn compile_field_matches(fields: &Fields<'_>) -> TokenStream { .map(|(idx, UnnamedField { ty, id })| { let id = proc_macro2::Literal::u32_unsuffixed(id.0); let name = Ident::new(&format!("n{idx}"), Span::call_site()); - let ty = compile_data_type(ty); + let ty = compile_data_type(if let DataType::Option(ty) = ty { + ty + } else { + ty + }); quote! { #id => #name = Some(#ty?) } }); @@ -246,7 +254,7 @@ fn compile_data_type(ty: &DataType<'_>) -> TokenStream { DataType::Vec(_ty) => quote! { ::stef::buf::decode_vec(r) }, DataType::HashMap(_kv) => quote! { ::stef::buf::decode_hash_map(r) }, DataType::HashSet(_ty) => quote! { ::stef::buf::decode_hash_set(r) }, - DataType::Option(ty) => compile_data_type(ty), + DataType::Option(_ty) => quote! { ::stef::buf::decode_option(r) }, DataType::NonZero(ty) => match **ty { DataType::U8 => quote! { NonZeroU8::decode(r) }, DataType::U16 => quote! { NonZeroU16::decode(r) }, diff --git a/crates/stef-build/src/definition.rs b/crates/stef-build/src/definition.rs index 552534b..d77648a 100644 --- a/crates/stef-build/src/definition.rs +++ b/crates/stef-build/src/definition.rs @@ -140,6 +140,7 @@ fn compile_alias( quote! { #comment + #[allow(dead_code)] pub type #alias = #target; } } @@ -583,6 +584,7 @@ mod tests { "#}; let expect = indoc! {r#" /// Hello world! + #[allow(dead_code)] pub type Sample = String; "#}; diff --git a/crates/stef-build/src/encode.rs b/crates/stef-build/src/encode.rs index 55a20f1..218609d 100644 --- a/crates/stef-build/src/encode.rs +++ b/crates/stef-build/src/encode.rs @@ -37,9 +37,13 @@ fn compile_struct_fields(fields: &Fields<'_>) -> TokenStream { }| { let id = proc_macro2::Literal::u32_unsuffixed(id.0); let name = proc_macro2::Ident::new(name, Span::call_site()); - let ty = compile_data_type(ty, quote! { self.#name }); - quote! { ::stef::buf::encode_field(w, #id, |w| { #ty }); } + if matches!(ty, DataType::Option(_)) { + quote! { ::stef::buf::encode_field_option(w, #id, &self.#name); } + } else { + let ty = compile_data_type(ty, quote! { self.#name }); + quote! { ::stef::buf::encode_field(w, #id, |w| { #ty }); } + } }, ); @@ -154,9 +158,13 @@ fn compile_variant_fields(fields: &Fields<'_>) -> TokenStream { }| { let id = proc_macro2::Literal::u32_unsuffixed(id.0); let name = proc_macro2::Ident::new(name, Span::call_site()); - let ty = compile_data_type(ty, quote! { *#name }); - quote! { ::stef::buf::encode_field(w, #id, |w| { #ty }); } + if matches!(ty, DataType::Option(_)) { + quote! { ::stef::buf::encode_field_option(w, #id, &#name); } + } else { + let ty = compile_data_type(ty, quote! { *#name }); + quote! { ::stef::buf::encode_field(w, #id, |w| { #ty }); } + } }, ); @@ -221,7 +229,7 @@ fn compile_data_type(ty: &DataType<'_>, name: TokenStream) -> TokenStream { DataType::Vec(_ty) => quote! { ::stef::buf::encode_vec(w, &#name) }, DataType::HashMap(_kv) => quote! { ::stef::buf::encode_hash_map(w, #name) }, DataType::HashSet(_ty) => quote! { ::stef::buf::encode_hash_set(w, #name) }, - DataType::Option(_ty) => quote! { ::stef::buf::encode_option(w, #name) }, + DataType::Option(_ty) => quote! { ::stef::buf::encode_option(w, &#name) }, DataType::BoxString => quote! { ::stef::buf::encode_string(w, &*#name) }, DataType::BoxBytes => quote! { ::stef::buf::encode_bytes(w, &*#name) }, DataType::Tuple(types) => match types.len() { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@alias-basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@alias-basic.stef.snap index 911bfbc..f1a90fe 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@alias-basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@alias-basic.stef.snap @@ -4,5 +4,6 @@ expression: "/// Sample type alias.\ntype Sample = u32;" input_file: crates/stef-parser/tests/inputs/alias-basic.stef --- /// Sample type alias. +#[allow(dead_code)] pub type Sample = u32; 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 9a09c43..fbefd8d 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 @@ -25,7 +25,7 @@ impl ::stef::Encode for Sample { 3, |w| { ::stef::buf::encode_hash_set(w, self.f3) }, ); - ::stef::buf::encode_field(w, 4, |w| { ::stef::buf::encode_option(w, self.f4) }); + ::stef::buf::encode_field_option(w, 4, &self.f4); ::stef::buf::encode_field(w, 5, |w| { (self.f5).encode(w) }); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } diff --git a/crates/stef-playground/src/lib.rs b/crates/stef-playground/src/lib.rs new file mode 100644 index 0000000..c12eb64 --- /dev/null +++ b/crates/stef-playground/src/lib.rs @@ -0,0 +1,93 @@ +mod sample { + include!(concat!(env!("OUT_DIR"), "/sample.rs")); +} + +#[cfg(test)] +mod tests { + use std::fmt::Debug; + + use stef::{Decode, Encode}; + + use super::sample; + + fn roundtrip(value: T) { + let mut buf = Vec::new(); + value.encode(&mut buf); + println!("{}: {buf:?}", std::any::type_name::()); + + let value2 = T::decode(&mut &*buf).unwrap(); + assert_eq!(value, value2); + } + + #[test] + fn sample() { + roundtrip(sample::Sample { + a: 5, + b: true, + c: ("Test".into(), -2), + }); + } + + #[test] + fn sample2_unit() { + roundtrip(sample::Sample2::Unit); + } + + #[test] + fn sample2_tuple() { + roundtrip(sample::Sample2::Tuple(7, 8)); + } + + #[test] + fn sample2_fields() { + roundtrip(sample::Sample2::Fields { + name: "this".into(), + valid: true, + dates: vec![ + (2023, 1, 1), + (2023, 10, 5), + (2023, 12, sample::CHRISTMAS_DAY), + ], + }); + } + + #[test] + fn sample_gen() { + roundtrip(sample::gens::SampleGen { + raw: vec![5, 6, 7, 8], + array: [9_i16; 4], + value: 9, + }); + } + + #[test] + fn sample_gen2() { + roundtrip(sample::gens::SampleGen2::Value(sample::SampleAlias { + a: 50, + b: false, + c: (String::new(), -10), + })); + } + + #[test] + fn specials_options_some() { + roundtrip(sample::specials::SomeOptions { + maybe_int: Some(5), + maybe_text: Some("hi".into()), + maybe_tuple: Some((20, 30)), + nested: Some(Some(8)), + vec_maybe: vec![Some(true), None, Some(false)], + }); + } + + #[test] + fn specials_options_none() { + roundtrip(sample::specials::SomeOptions { + maybe_int: None, + maybe_text: None, + maybe_tuple: None, + nested: None, + vec_maybe: vec![None, None], + }); + } +} diff --git a/crates/stef-playground/src/main.rs b/crates/stef-playground/src/main.rs deleted file mode 100644 index b899bfa..0000000 --- a/crates/stef-playground/src/main.rs +++ /dev/null @@ -1,63 +0,0 @@ -use stef::{Decode, Encode}; - -mod sample { - include!(concat!(env!("OUT_DIR"), "/sample.rs")); -} - -fn main() { - let mut buf = Vec::new(); - let v = sample::Sample { - a: 5, - b: true, - c: ("Test".into(), -2), - }; - v.encode(&mut buf); - println!("{buf:?}"); - - let v2 = sample::Sample::decode(&mut &*buf).unwrap(); - assert_eq!(v, v2); - - buf.clear(); - let v = sample::Sample2::Unit; - v.encode(&mut buf); - println!("{buf:?}"); - - let v2 = sample::Sample2::decode(&mut &*buf).unwrap(); - assert_eq!(v, v2); - - buf.clear(); - let v = sample::Sample2::Tuple(7, 8); - v.encode(&mut buf); - println!("{buf:?}"); - - let v2 = sample::Sample2::decode(&mut &*buf).unwrap(); - assert_eq!(v, v2); - - buf.clear(); - let v = sample::Sample2::Fields { - name: "this".into(), - valid: true, - dates: vec![ - (2023, 1, 1), - (2023, 10, 5), - (2023, 12, sample::CHRISTMAS_DAY), - ], - }; - v.encode(&mut buf); - println!("{buf:?}"); - - let v2 = sample::Sample2::decode(&mut &*buf).unwrap(); - assert_eq!(v, v2); - - buf.clear(); - let v = sample::SampleGen { - raw: vec![5, 6, 7, 8], - array: [9_i16; 4], - value: 9, - }; - v.encode(&mut buf); - println!("{buf:?}"); - - let v2 = sample::SampleGen::decode(&mut &*buf).unwrap(); - assert_eq!(v, v2); -} diff --git a/crates/stef-playground/src/sample.stef b/crates/stef-playground/src/sample.stef index 367ac14..5e74576 100644 --- a/crates/stef-playground/src/sample.stef +++ b/crates/stef-playground/src/sample.stef @@ -19,13 +19,34 @@ enum Sample2 { const CHRISTMAS_MONTH: u8 = 12; const CHRISTMAS_DAY: u8 = 24; -/// Using generics -struct SampleGen { - raw: bytes @1, - array: [T; 4] @2, - value: T @3, +/// Data structures with generics. +mod gens { + /// Using generics + struct SampleGen { + raw: bytes @1, + array: [T; 4] @2, + value: T @3, + } + + enum SampleGen2 { + Value(T @1) @1, + } + + struct Nested { + one: A @1, + two: SampleGen @2, + } } -enum SampleGen2 { - Value(T @1) @1, +/// Alias for another type. +type SampleAlias = Sample; + +mod specials { + struct SomeOptions { + maybe_int: option @1, + maybe_text: option @2, + maybe_tuple: option<(u8, u8)> @3, + nested: option> @4, + vec_maybe: vec> @5, + } } diff --git a/crates/stef/src/buf/decode.rs b/crates/stef/src/buf/decode.rs index 811b0ae..7c6e54f 100644 --- a/crates/stef/src/buf/decode.rs +++ b/crates/stef/src/buf/decode.rs @@ -117,6 +117,15 @@ pub fn decode_hash_set(r: &mut impl Buf) -> Result(r: &mut impl Buf) -> Result> { + let some = decode_u8(r)? == 1; + if some { + T::decode(r).map(Some) + } else { + Ok(None) + } +} + pub fn decode_array(r: &mut impl Buf) -> Result<[T; N]> { let len = decode_u64(r)?; if (len as usize) < N { @@ -454,6 +463,16 @@ where } } +impl Decode for Option +where + T: Decode, +{ + #[inline(always)] + fn decode(r: &mut impl Buf) -> Result { + decode_option(r) + } +} + impl Decode for [T; N] where T: Debug + Decode, diff --git a/crates/stef/src/buf/encode.rs b/crates/stef/src/buf/encode.rs index c2befb4..92331a2 100644 --- a/crates/stef/src/buf/encode.rs +++ b/crates/stef/src/buf/encode.rs @@ -83,7 +83,10 @@ pub fn encode_hash_set(w: &mut impl BufMut, set: &HashSet) { pub fn encode_option(w: &mut impl BufMut, option: &Option) { if let Some(value) = option { + w.put_u8(1); value.encode(w); + } else { + w.put_u8(0); } } @@ -332,6 +335,18 @@ where encode(w); } +#[inline(always)] +pub fn encode_field_option(w: &mut W, id: u32, option: &Option) +where + W: BufMut, + T: Encode, +{ + if let Some(value) = option { + encode_id(w, id); + value.encode(w); + } +} + pub trait Encode { fn encode(&self, w: &mut impl BufMut); }