diff --git a/.gitignore b/.gitignore index 5bbb3ea..15fc76b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules /target .lycheecache +*.snap.new diff --git a/Cargo.lock b/Cargo.lock index 85d46ad..7bc6c97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,9 +108,9 @@ checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bstr" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" dependencies = [ "memchr", "serde", @@ -139,9 +139,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.7" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", @@ -217,22 +217,21 @@ dependencies = [ [[package]] name = "divan" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fab20f5802e0b897093184f5dc5d23447fa715604f238dc798f6da188b230019" +checksum = "1a2f0b89afd851ac3372f22144e66157058999ffe695a337711d5511acb91504" dependencies = [ "clap", "condtype", "divan-macros", - "linkme", "regex-lite", ] [[package]] name = "divan-macros" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c5d6354551e0b5c451a948814fc47fe745a14eac7835c087d60162661019db4" +checksum = "016a705a288faf1e8d45113e54ffcc7b566e1572ff829b8455546362b0c902f5" dependencies = [ "proc-macro2", "quote", @@ -247,35 +246,29 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "80f656be11ddf91bd709454d15d5bd896fbaf4cc3314e69349e4d1569f5b46cd" dependencies = [ "indenter", "once_cell", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -285,15 +278,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", "bstr", - "fnv", "log", - "regex", + "regex-automata", + "regex-syntax", ] [[package]] @@ -369,9 +362,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libmimalloc-sys" @@ -389,31 +382,11 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "linkme" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ed2ee9464ff9707af8e9ad834cffa4802f072caad90639c583dd3c62e6e608" -dependencies = [ - "linkme-impl", -] - -[[package]] -name = "linkme-impl" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125974b109d512fccbc6c0244e7580143e460895dfd6ea7f8bbb692fd94396" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" @@ -517,9 +490,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -533,18 +506,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - [[package]] name = "regex-automata" version = "0.4.3" @@ -576,15 +537,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -598,18 +559,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.190" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -696,6 +657,18 @@ dependencies = [ "syn", ] +[[package]] +name = "stef-go" +version = "0.1.0" +dependencies = [ + "clap", + "heck", + "insta", + "miette", + "stef-compiler", + "stef-parser", +] + [[package]] name = "stef-parser" version = "0.1.0" @@ -762,9 +735,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -905,6 +878,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -935,6 +917,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -947,6 +944,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -959,6 +962,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -971,6 +980,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -983,6 +998,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -995,6 +1016,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -1007,6 +1034,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -1019,6 +1052,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.19" diff --git a/Cargo.toml b/Cargo.toml index c1f995e..ff07db4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,9 +29,9 @@ insta = { version = "1.34.0", features = ["glob"] } miette = "5.10.0" mimalloc = "0.1.39" owo-colors = { version = "3.5.0", features = ["supports-colors"] } -proc-macro2 = { version = "1.0.69", default-features = false } +proc-macro2 = { version = "1.0.70", default-features = false } quote = { version = "1.0.33", default-features = false } -syn = "2.0.38" +syn = "2.0.39" thiserror = "1.0.50" [profile.release] diff --git a/book/src/schema/enums/advanced.go b/book/src/schema/enums/advanced.go index 6d8422b..63e7fbb 100644 --- a/book/src/schema/enums/advanced.go +++ b/book/src/schema/enums/advanced.go @@ -8,18 +8,18 @@ type SampleVariant interface { type Sample SampleVariant -type SampleVariant1 struct { +type Sample_Variant1 struct { F1 uint32 F2 uint16 } -func (v SampleVariant1) sealed() {} +func (v Sample_Variant1) sealed() {} -type SampleVariant2 struct { +type Sample_Variant2 struct { Field1 int64 Field2 bool } -func (v SampleVariant2) sealed() {} +func (v Sample_Variant2) sealed() {} // N variants... diff --git a/crates/stef-benches/Cargo.toml b/crates/stef-benches/Cargo.toml index fc2b484..7435692 100644 --- a/crates/stef-benches/Cargo.toml +++ b/crates/stef-benches/Cargo.toml @@ -28,7 +28,7 @@ stef-compiler = { path = "../stef-compiler" } stef-parser = { path = "../stef-parser" } [dev-dependencies] -divan = "0.1.2" +divan = "0.1.3" indoc.workspace = true [lints] diff --git a/crates/stef-build/src/decode.rs b/crates/stef-build/src/decode.rs index 7420a19..278d5b1 100644 --- a/crates/stef-build/src/decode.rs +++ b/crates/stef-build/src/decode.rs @@ -1,7 +1,7 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; use stef_parser::{ - DataType, Enum, Fields, Generics, NamedField, Struct, Type, UnnamedField, Variant, + DataType, Enum, ExternalType, Fields, Generics, NamedField, Struct, Type, UnnamedField, Variant, }; use crate::{BytesType, Opts}; @@ -252,7 +252,7 @@ fn compile_generics(Generics(types): &Generics<'_>) -> (TokenStream, TokenStream .unwrap_or_default() } -#[allow(clippy::needless_pass_by_value)] +#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)] fn compile_data_type(opts: &Opts, ty: &Type<'_>) -> TokenStream { match &ty.value { DataType::Bool => quote! { ::stef::buf::decode_bool(r) }, @@ -334,17 +334,28 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>) -> TokenStream { let types = types.iter().map(|ty| compile_data_type(opts, ty)); quote! { { Ok::<_, ::stef::buf::Error>((#(#types?,)*)) } } } - 0 => panic!("tuple with zero elements"), - 1 => panic!("tuple with single element"), - _ => panic!("tuple with more than 12 elements"), + n => todo!("compiler should catch invalid tuple with {n} elements"), }, DataType::Array(ty, _size) => { let ty = compile_data_type(opts, ty); quote! { ::stef::buf::decode_array(r, |r| { #ty }) } } - DataType::External(ty) => { - let ty = Ident::new(ty.name.get(), Span::call_site()); - quote! { #ty::decode(r) } + DataType::External(ExternalType { + path, + name, + generics, + }) => { + let path = path + .iter() + .map(|part| Ident::new(part.get(), Span::call_site())); + let ty = Ident::new(name.get(), Span::call_site()); + let generics = (!generics.is_empty()).then(|| { + let types = generics + .iter() + .map(|ty| super::definition::compile_data_type(opts, ty)); + quote! { ::<#(#types,)*> } + }); + quote! { #(#path::)* #ty #generics::decode(r) } } } } diff --git a/crates/stef-build/src/definition.rs b/crates/stef-build/src/definition.rs index 2a0157b..7330479 100644 --- a/crates/stef-build/src/definition.rs +++ b/crates/stef-build/src/definition.rs @@ -2,7 +2,7 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; use stef_parser::{ Comment, Const, DataType, Definition, Enum, ExternalType, Fields, Generics, Import, Literal, - Module, Name, NamedField, Schema, Struct, Type, TypeAlias, UnnamedField, Variant, + Module, NamedField, Schema, Struct, Type, TypeAlias, UnnamedField, Variant, }; use super::{decode, encode}; @@ -345,7 +345,9 @@ pub(super) fn compile_data_type(opts: &Opts, ty: &Type<'_>) -> TokenStream { name, generics, }) => { - let path = path.iter().map(Name::get); + let path = path + .iter() + .map(|part| Ident::new(part.get(), Span::call_site())); let name = Ident::new(name.get(), Span::call_site()); let generics = (!generics.is_empty()).then(|| { let types = generics.iter().map(|ty| compile_data_type(opts, ty)); 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 954dc83..65b682e 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 @@ -1,6 +1,6 @@ --- source: crates/stef-build/tests/compiler.rs -description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n }\n}" +description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n inner: b::Sample @2,\n }\n}" input_file: crates/stef-parser/tests/inputs/module_basic.stef --- #[allow(unused_imports)] @@ -47,6 +47,7 @@ pub mod a { #[allow(clippy::module_name_repetitions, clippy::option_option)] pub struct Sample { pub value: u32, + pub inner: b::Sample, } #[automatically_derived] impl ::stef::Encode for Sample { @@ -64,6 +65,13 @@ pub mod a { ::stef::buf::encode_u32(w, self.value); }, ); + ::stef::buf::encode_field( + w, + 2, + |w| { + (self.inner).encode(w); + }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } } @@ -72,10 +80,12 @@ pub mod a { #[allow(clippy::type_complexity, clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { let mut value: Option = None; + let mut inner: Option = None; loop { match ::stef::buf::decode_id(r)? { ::stef::buf::END_MARKER => break, 1 => value = Some(::stef::buf::decode_u32(r)?), + 2 => inner = Some(b::Sample::decode(r)?), _ => continue, } } @@ -85,6 +95,11 @@ pub mod a { id: 1, name: Some("value"), })?, + inner: inner + .ok_or(::stef::buf::Error::MissingField { + id: 2, + name: Some("inner"), + })?, }) } } 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 57f8a6d..d5b5a71 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 @@ -47,7 +47,7 @@ impl ::stef::Decode for Sample { match ::stef::buf::decode_id(r)? { ::stef::buf::END_MARKER => break, 1 => basic = Some(Test123::decode(r)?), - 2 => with_generics = Some(KeyValue::decode(r)?), + 2 => with_generics = Some(KeyValue::::decode(r)?), _ => continue, } } diff --git a/crates/stef-cli/Cargo.toml b/crates/stef-cli/Cargo.toml index de9c767..7ef3a30 100644 --- a/crates/stef-cli/Cargo.toml +++ b/crates/stef-cli/Cargo.toml @@ -14,7 +14,7 @@ name = "stef" path = "src/main.rs" [dependencies] -clap = { version = "4.4.7", features = ["derive", "wrap_help"] } +clap = { version = "4.4.10", features = ["derive", "wrap_help"] } color-eyre.workspace = true glob.workspace = true miette = { workspace = true, features = ["fancy-no-backtrace"] } diff --git a/crates/stef-go/Cargo.toml b/crates/stef-go/Cargo.toml new file mode 100644 index 0000000..2587bc7 --- /dev/null +++ b/crates/stef-go/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "stef-go" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +readme.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +clap = { version = "4.4.10", features = ["derive"] } +heck = "0.4.1" +miette = { workspace = true, features = ["fancy-no-backtrace"] } +stef-compiler = { path = "../stef-compiler" } +stef-parser = { path = "../stef-parser" } + +[dev-dependencies] +insta.workspace = true + +[lints] +workspace = true diff --git a/crates/stef-go/src/cli.rs b/crates/stef-go/src/cli.rs new file mode 100644 index 0000000..7ec608f --- /dev/null +++ b/crates/stef-go/src/cli.rs @@ -0,0 +1,25 @@ +use clap::Parser; + +#[derive(Debug, Parser)] +#[command(about, author, version, propagate_version = true)] +pub struct Cli { + #[arg(num_args(1..))] + files: Vec, +} + +impl Cli { + pub fn parse() -> Self { + ::parse() + } +} + +#[cfg(test)] +mod tests { + use super::Cli; + + #[test] + fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert(); + } +} diff --git a/crates/stef-go/src/decode.rs b/crates/stef-go/src/decode.rs new file mode 100644 index 0000000..70f3b2d --- /dev/null +++ b/crates/stef-go/src/decode.rs @@ -0,0 +1,503 @@ +#![allow(clippy::too_many_lines)] + +use std::fmt::{self, Display}; + +use stef_parser::{DataType, Fields, Generics, Struct, Type, Variant}; + +use crate::definition::{self, RenderGenericNames}; + +pub(super) struct RenderStruct<'a>(pub(super) &'a Struct<'a>); + +impl Display for RenderStruct<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "var _ buf.Decode = (*{}{})(nil)\n", + heck::AsUpperCamelCase(&self.0.name), + RenderGenericNames { + generics: &self.0.generics, + fields_filter: None, + } + )?; + + writeln!( + f, + "func (v *{}{}) Decode(r []byte) ([]byte, error) {{", + heck::AsUpperCamelCase(&self.0.name), + RenderGenericNames { + generics: &self.0.generics, + fields_filter: None, + } + )?; + writeln!(f, "{}", RenderFieldVars(&self.0.fields))?; + writeln!(f, "\tfor len(r) > 0 {{")?; + writeln!(f, "\t\tr2, id, err := buf.DecodeID(r)")?; + writeln!(f, "\t\tif err != nil {{")?; + writeln!(f, "\t\t\treturn nil, err")?; + writeln!(f, "\t\t}}")?; + writeln!(f, "\t\tr = r2\n")?; + writeln!(f, "\t\tswitch id {{")?; + write!(f, "{}", RenderFields(&self.0.fields))?; + writeln!(f, "\t\t\tcase buf.EndMarker:")?; + writeln!(f, "\t\t\t\tbreak")?; + writeln!(f, "\t\t}}")?; + writeln!(f, "\t}}\n")?; + write!(f, "{}", RenderFoundChecks(&self.0.fields))?; + writeln!(f, "\n\treturn r, nil\n}}") + } +} + +pub(super) struct RenderEnumVariant<'a> { + pub(super) enum_name: &'a str, + pub(super) generics: &'a Generics<'a>, + pub(super) variant: &'a Variant<'a>, +} + +impl Display for RenderEnumVariant<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "var _ buf.Decode = (*{}_{}{})(nil)\n", + heck::AsUpperCamelCase(self.enum_name), + heck::AsUpperCamelCase(&self.variant.name), + RenderGenericNames { + generics: self.generics, + fields_filter: Some(&self.variant.fields), + } + )?; + + writeln!( + f, + "func (v *{}_{}{}) Decode(r []byte) ([]byte, error) {{", + heck::AsUpperCamelCase(self.enum_name), + heck::AsUpperCamelCase(&self.variant.name), + RenderGenericNames { + generics: self.generics, + fields_filter: Some(&self.variant.fields), + } + )?; + writeln!(f, "{}", RenderFieldVars(&self.variant.fields))?; + writeln!(f, "\tfor len(r) > 0 {{")?; + writeln!(f, "\t\tr2, id, err := buf.DecodeID(r)")?; + writeln!(f, "\t\tif err != nil {{")?; + writeln!(f, "\t\t\treturn nil, err")?; + writeln!(f, "\t\t}}")?; + writeln!(f, "\t\tr = r2\n")?; + writeln!(f, "\t\tswitch id {{")?; + write!(f, "{}", RenderFields(&self.variant.fields))?; + writeln!(f, "\t\t\tcase buf.EndMarker:")?; + writeln!(f, "\t\t\t\tbreak")?; + writeln!(f, "\t\t}}")?; + writeln!(f, "\t}}\n")?; + write!(f, "{}", RenderFoundChecks(&self.variant.fields))?; + writeln!(f, "\n\treturn r, nil\n}}") + } +} + +struct RenderFieldVars<'a>(&'a Fields<'a>); + +impl Display for RenderFieldVars<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Fields::Named(named) => { + for field in named { + writeln!(f, "\tfound{} := false", heck::AsUpperCamelCase(&field.name))?; + } + } + Fields::Unnamed(unnamed) => { + for (idx, _) in unnamed.iter().enumerate() { + writeln!(f, "\tfoundF{idx} := false")?; + } + } + Fields::Unit => {} + } + + Ok(()) + } +} + +struct RenderFoundChecks<'a>(&'a Fields<'a>); + +impl Display for RenderFoundChecks<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Fields::Named(named) => { + for field in named { + writeln!(f, "\tif !found{} {{", heck::AsUpperCamelCase(&field.name))?; + writeln!(f, "\t\treturn nil, buf.MissingFieldError{{")?; + writeln!(f, "\t\t\tID: {}", field.id.get())?; + writeln!(f, "\t\t\tField: \"{}\"", &field.name)?; + writeln!(f, "\t\t}}")?; + writeln!(f, "\t}}")?; + } + } + Fields::Unnamed(unnamed) => { + for (idx, field) in unnamed.iter().enumerate() { + writeln!(f, "\tif !foundF{idx} {{")?; + writeln!(f, "\t\treturn nil, buf.MissingFieldError{{")?; + writeln!(f, "\t\t\tID: {}", field.id.get())?; + writeln!(f, "\t\t\tField: \"\"")?; + writeln!(f, "\t\t}}")?; + writeln!(f, "\t}}")?; + } + } + Fields::Unit => {} + } + + Ok(()) + } +} + +struct RenderFields<'a>(&'a Fields<'a>); + +impl Display for RenderFields<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Fields::Named(named) => { + for field in named { + writeln!(f, "\t\t\tcase {}:", field.id.get())?; + writeln!( + f, + "\t\t\t\tr2, value, err := {}", + RenderType { + ty: &field.ty, + indent: 4 + } + )?; + writeln!(f, "\t\t\t\tif err != nil {{")?; + writeln!(f, "\t\t\t\t\treturn nil, err")?; + writeln!(f, "\t\t\t\t}}")?; + writeln!(f, "\t\t\t\tr = r2")?; + writeln!( + f, + "\t\t\t\tv.{} = value", + heck::AsUpperCamelCase(&field.name) + )?; + writeln!( + f, + "\t\t\t\tfound{} = true", + heck::AsUpperCamelCase(&field.name) + )?; + } + } + Fields::Unnamed(unnamed) => { + for (idx, field) in unnamed.iter().enumerate() { + writeln!(f, "\t\t\tcase {}:", field.id.get())?; + writeln!( + f, + "\t\t\t\tr2, value, err := {}", + RenderType { + ty: &field.ty, + indent: 4 + } + )?; + writeln!(f, "\t\t\t\tif err != nil {{")?; + writeln!(f, "\t\t\t\t\treturn nil, err")?; + writeln!(f, "\t\t\t\t}}")?; + writeln!(f, "\t\t\t\tr = r2")?; + writeln!(f, "\t\t\t\tv.F{idx} = value")?; + writeln!(f, "\t\t\t\tfoundF{idx} = true")?; + } + } + Fields::Unit => {} + } + + Ok(()) + } +} + +struct RenderType<'a> { + ty: &'a Type<'a>, + indent: usize, +} + +impl Display for RenderType<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.ty.value { + DataType::Bool => write!(f, "buf.DecodeBool(r)"), + DataType::U8 => write!(f, "buf.DecodeU8(r)"), + DataType::U16 => write!(f, "buf.DecodeU16(r)"), + DataType::U32 => write!(f, "buf.DecodeU32(r)"), + DataType::U64 => write!(f, "buf.DecodeU64(r)"), + DataType::U128 => write!(f, "buf.DecodeU128(r)"), + DataType::I8 => write!(f, "buf.DecodeI8(r)"), + DataType::I16 => write!(f, "buf.DecodeI16(r)"), + DataType::I32 => write!(f, "buf.DecodeI32(r)"), + DataType::I64 => write!(f, "buf.DecodeI64(r)"), + DataType::I128 => write!(f, "buf.DecodeI128(r)"), + DataType::F32 => write!(f, "buf.DecodeF32(r)"), + DataType::F64 => write!(f, "buf.DecodeF64(r)"), + DataType::String | DataType::StringRef | DataType::BoxString => { + write!(f, "buf.DecodeString(r)") + } + DataType::Bytes | DataType::BytesRef | DataType::BoxBytes => { + write!(f, "buf.DecodeBytes(r)") + } + DataType::Vec(ty) => { + write!( + f, + "{}", + DecodeGenericSingle { + name: "DecodeVec", + ty, + indent: self.indent, + } + ) + } + DataType::HashMap(kv) => { + write!( + f, + "{}", + DecodeGenericPair { + name: "DecodeHashMap", + pair: kv, + indent: self.indent + } + ) + } + DataType::HashSet(ty) => { + write!( + f, + "{}", + DecodeGenericSingle { + name: "DecodeHashSet", + ty, + indent: self.indent, + } + ) + } + DataType::Option(ty) => { + write!( + f, + "{}", + DecodeGenericSingle { + name: "DecodeOption", + ty, + indent: self.indent, + } + ) + } + DataType::NonZero(ty) => match &ty.value { + DataType::U8 => write!(f, "buf.DecodeNonZeroU8(r)"), + DataType::U16 => write!(f, "buf.DecodeNonZeroU16(r)"), + DataType::U32 => write!(f, "buf.DecodeNonZeroU32(r)"), + DataType::U64 => write!(f, "buf.DecodeNonZeroU64(r)"), + DataType::U128 => write!(f, "buf.DecodeNonZeroU128(r)"), + DataType::I8 => write!(f, "buf.DecodeNonZeroI8(r)"), + DataType::I16 => write!(f, "buf.DecodeNonZeroI16(r)"), + DataType::I32 => write!(f, "buf.DecodeNonZeroI32(r)"), + DataType::I64 => write!(f, "buf.DecodeNonZeroI64(r)"), + DataType::I128 => write!(f, "buf.DecodeNonZeroI128(r)"), + DataType::String | DataType::StringRef => write!(f, "buf.DecodeNonZeroString(r)"), + DataType::Bytes | DataType::BytesRef => write!(f, "buf.DecodeNonZeroBytes(r)"), + DataType::Vec(ty) => { + write!( + f, + "{}", + DecodeGenericSingle { + name: "DecodeNonZeroVec", + ty, + indent: self.indent, + } + ) + } + DataType::HashMap(kv) => { + write!( + f, + "{}", + DecodeGenericPair { + name: "DecodeNonZeroHashMap", + pair: kv, + indent: self.indent + } + ) + } + DataType::HashSet(ty) => { + write!( + f, + "{}", + DecodeGenericSingle { + name: "DecodeNonZeroHashSet", + ty, + indent: self.indent, + } + ) + } + ty => todo!("compiler should catch invalid {ty:?} type"), + }, + DataType::Tuple(types) => match types.len() { + n @ 2..=12 => { + writeln!(f, "func (r []byte) ([]byte, buf.Tuple{n}, error) {{")?; + for (idx, ty) in types.iter().enumerate() { + writeln!( + f, + "{:\t todo!("compiler should catch invalid tuple with {n} elements"), + }, + DataType::Array(ty, size) => match *size { + 1..=32 => { + writeln!( + f, + "buf.DecodeArray{size}[{}](r, func(r []byte) ([]byte, {0}, error) {{", + definition::RenderType(ty), + )?; + writeln!( + f, + "{:\t todo!("arrays with larger ({n}) sizes"), + }, + DataType::External(_) => { + writeln!( + f, + "func(r []byte) ([]byte, {}, error) {{", + definition::RenderType(self.ty) + )?; + writeln!( + f, + "{:\t { + name: &'static str, + ty: &'a Type<'a>, + indent: usize, +} + +impl Display for DecodeGenericSingle<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "buf.{}[{}](r, func(r []byte) ([]byte, {1}, error) {{", + self.name, + definition::RenderType(self.ty), + )?; + writeln!( + f, + "{:\t { + name: &'static str, + pair: &'a (Type<'a>, Type<'a>), + indent: usize, +} + +impl Display for DecodeGenericPair<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "buf.{}[{}, {}](", + self.name, + definition::RenderType(&self.pair.0), + definition::RenderType(&self.pair.1) + )?; + writeln!(f, "{:\t( + opts: &'a Opts<'_>, + Schema { definitions, .. }: &'a Schema<'_>, +) -> Output<'a> { + let mut content = format!( + "{}{}{}", + RenderHeader, + RenderImports, + RenderPackage(opts.package, None) + ); + + let modules = definitions + .iter() + .filter_map(|def| render_definition(&mut content, def)) + .collect(); + + Output { + name: opts.package, + content, + modules, + } +} + +fn render_definition<'a>(buf: &mut String, definition: &'a Definition<'_>) -> Option> { + match definition { + Definition::Module(m) => { + let mut content = format!( + "{}{}", + RenderHeader, + RenderPackage(m.name.get(), Some(&m.comment)) + ); + + let modules = m + .definitions + .iter() + .filter_map(|def| render_definition(&mut content, def)) + .collect(); + + return Some(Output { + name: m.name.get(), + content, + modules, + }); + } + Definition::Struct(s) => { + writeln!(buf, "{}", RenderStruct(s)).unwrap(); + writeln!( + buf, + "{}", + RenderNewFunc { + name: heck::AsUpperCamelCase(&s.name), + generics: &s.generics, + fields: &s.fields, + filter_generics: false, + } + ) + .unwrap(); + writeln!( + buf, + "\n{}\n{}", + encode::RenderStruct(s), + decode::RenderStruct(s) + ) + .unwrap(); + } + Definition::Enum(e) => writeln!(buf, "{}", RenderEnum(e)).unwrap(), + Definition::TypeAlias(a) => write!(buf, "{}", RenderAlias(a)).unwrap(), + Definition::Const(c) => write!(buf, "{}", RenderConst(c)).unwrap(), + Definition::Import(_) => {} + } + + None +} + +struct RenderHeader; + +impl Display for RenderHeader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "// Code generated by {} (v{}). DO NOT EDIT.\n", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + ) + } +} + +struct RenderPackage<'a>(&'a str, Option<&'a Comment<'a>>); + +impl Display for RenderPackage<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(comment) = self.1 { + write!(f, "{}", RenderComment { indent: 0, comment })?; + } + + writeln!(f, "package {}\n", heck::AsSnakeCase(self.0)) + } +} + +struct RenderImports; + +impl Display for RenderImports { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "import (")?; + writeln!(f, "\t\"github.com/dnaka91/stef-go\"")?; + writeln!(f, "\t\"github.com/dnaka91/stef-go/buf\"")?; + writeln!(f, ")\n") + } +} + +struct RenderStruct<'a>(&'a Struct<'a>); + +impl Display for RenderStruct<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "{}type {}{} {}", + RenderComment { + indent: 0, + comment: &self.0.comment + }, + heck::AsUpperCamelCase(&self.0.name), + RenderGenerics { + generics: &self.0.generics, + fields_filter: None + }, + RenderFields(&self.0.fields) + ) + } +} + +struct RenderNewFunc<'a, T> { + name: T, + generics: &'a Generics<'a>, + fields: &'a Fields<'a>, + filter_generics: bool, +} + +impl Display for RenderNewFunc<'_, T> +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "func New{}{}({}) {0}{} {{", + self.name, + RenderGenerics { + generics: self.generics, + fields_filter: self.filter_generics.then_some(self.fields) + }, + RenderParameters(self.fields), + RenderGenericNames { + generics: self.generics, + fields_filter: self.filter_generics.then_some(self.fields) + }, + )?; + writeln!( + f, + "\treturn {}{}{}", + self.name, + RenderGenericNames { + generics: self.generics, + fields_filter: self.filter_generics.then_some(self.fields) + }, + RenderConstructor(self.fields), + )?; + write!(f, "}}") + } +} + +struct RenderGenerics<'a> { + generics: &'a Generics<'a>, + fields_filter: Option<&'a Fields<'a>>, +} + +impl Display for RenderGenerics<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.fields_filter { + Some(fields) => { + if !self.generics.0.iter().any(|gen| uses_generic(gen, fields)) { + return Ok(()); + } + } + None => { + if self.generics.0.is_empty() { + return Ok(()); + } + } + } + + f.write_char('[')?; + for (i, value) in self + .generics + .0 + .iter() + .filter(|gen| match self.fields_filter { + Some(fields) => uses_generic(gen, fields), + None => true, + }) + .enumerate() + { + if i > 0 { + f.write_str(", ")?; + } + write!(f, "{value} any")?; + } + f.write_char(']') + } +} + +pub(super) struct RenderGenericNames<'a> { + pub(super) generics: &'a Generics<'a>, + pub(super) fields_filter: Option<&'a Fields<'a>>, +} + +impl Display for RenderGenericNames<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.fields_filter { + Some(fields) => { + if !self.generics.0.iter().any(|gen| uses_generic(gen, fields)) { + return Ok(()); + } + } + None => { + if self.generics.0.is_empty() { + return Ok(()); + } + } + } + + f.write_char('[')?; + for (i, value) in self + .generics + .0 + .iter() + .filter(|gen| match self.fields_filter { + Some(fields) => uses_generic(gen, fields), + None => true, + }) + .enumerate() + { + if i > 0 { + f.write_str(", ")?; + } + write!(f, "{value}")?; + } + f.write_char(']') + } +} + +struct RenderGenericTypes<'a>(&'a [Type<'a>]); + +impl Display for RenderGenericTypes<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0.is_empty() { + return Ok(()); + } + + f.write_char('[')?; + for (i, value) in self.0.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + write!(f, "{}", RenderType(value))?; + } + f.write_char(']') + } +} + +struct RenderFields<'a>(&'a Fields<'a>); + +impl Display for RenderFields<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Fields::Named(named) => { + if named.is_empty() { + write!(f, "struct{{}}") + } else { + writeln!(f, "struct {{")?; + + for field in named { + writeln!( + f, + "{}\t{} {}", + RenderComment { + indent: 1, + comment: &field.comment + }, + heck::AsUpperCamelCase(&field.name), + RenderType(&field.ty) + )?; + } + + write!(f, "}}") + } + } + Fields::Unnamed(unnamed) => { + if unnamed.is_empty() { + write!(f, "struct{{}}") + } else { + writeln!(f, "struct {{")?; + + for (i, field) in unnamed.iter().enumerate() { + writeln!(f, "\tF{i} {}", RenderType(&field.ty))?; + } + + write!(f, "}}") + } + } + Fields::Unit => write!(f, "struct{{}}"), + } + } +} + +struct RenderParameters<'a>(&'a Fields<'a>); + +impl Display for RenderParameters<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Fields::Named(named) => { + if !named.is_empty() { + writeln!(f)?; + } + + for field in named { + writeln!( + f, + "\t{} {},", + heck::AsLowerCamelCase(&field.name), + RenderType(&field.ty), + )?; + } + } + Fields::Unnamed(unnamed) => { + if !unnamed.is_empty() { + writeln!(f)?; + } + + for (i, field) in unnamed.iter().enumerate() { + writeln!(f, "\tf{i} {},", RenderType(&field.ty))?; + } + } + Fields::Unit => {} + } + + Ok(()) + } +} + +struct RenderConstructor<'a>(&'a Fields<'a>); + +impl Display for RenderConstructor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Fields::Named(named) => { + if named.is_empty() { + write!(f, "{{}}") + } else { + writeln!(f, "{{")?; + + for field in named { + writeln!( + f, + "\t\t{}: {},", + heck::AsUpperCamelCase(&field.name), + heck::AsLowerCamelCase(&field.name) + )?; + } + + write!(f, "\t}}") + } + } + Fields::Unnamed(unnamed) => { + if unnamed.is_empty() { + write!(f, "{{}}") + } else { + writeln!(f, "{{")?; + + for (i, _) in unnamed.iter().enumerate() { + writeln!(f, "\t\tF{i}: f{i},")?; + } + + write!(f, "\t}}") + } + } + Fields::Unit => write!(f, "{{}}"), + } + } +} + +struct RenderAlias<'a>(&'a TypeAlias<'a>); + +impl Display for RenderAlias<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}type {} {}", + RenderComment { + indent: 0, + comment: &self.0.comment, + }, + heck::AsUpperCamelCase(&self.0.name), + RenderType(&self.0.target), + ) + } +} + +struct RenderComment<'a> { + indent: usize, + comment: &'a Comment<'a>, +} + +impl Display for RenderComment<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for line in &self.comment.0 { + writeln!(f, "{:\t(pub(super) &'a Type<'a>); + +impl Display for RenderType<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.0.value { + DataType::Bool => write!(f, "bool"), + DataType::U8 => write!(f, "uint8"), + DataType::U16 => write!(f, "uint16"), + DataType::U32 => write!(f, "uint32"), + DataType::U64 => write!(f, "uint64"), + DataType::U128 | DataType::I128 => write!(f, "*big.Int"), + DataType::I8 => write!(f, "int8"), + DataType::I16 => write!(f, "int16"), + DataType::I32 => write!(f, "int32"), + DataType::I64 => write!(f, "int64"), + DataType::F32 => write!(f, "float32"), + DataType::F64 => write!(f, "float64"), + DataType::String | DataType::StringRef | DataType::BoxString => write!(f, "string"), + DataType::Bytes | DataType::BytesRef | DataType::BoxBytes => write!(f, "[]byte"), + DataType::Vec(ty) => write!(f, "[]{}", RenderType(ty)), + DataType::HashMap(kv) => write!(f, "map[{}]{}", RenderType(&kv.0), RenderType(&kv.1)), + DataType::HashSet(ty) => write!(f, "map[{}]struct{{}}", RenderType(ty)), + DataType::Option(ty) => write!(f, "*{}", RenderType(ty)), + DataType::NonZero(ty) => match &ty.value { + DataType::U8 => write!(f, "stef.NonZeroU8"), + DataType::U16 => write!(f, "stef.NonZeroU16"), + DataType::U32 => write!(f, "stef.NonZeroU32"), + DataType::U64 => write!(f, "stef.NonZeroU64"), + DataType::U128 => write!(f, "stef.NonZeroU128"), + DataType::I8 => write!(f, "stef.NonZeroI8"), + DataType::I16 => write!(f, "stef.NonZeroI16"), + DataType::I32 => write!(f, "stef.NonZeroI32"), + DataType::I64 => write!(f, "stef.NonZeroI64"), + DataType::I128 => write!(f, "stef.NonZeroI128"), + DataType::F32 => write!(f, "stef.NonZeroF32"), + DataType::F64 => write!(f, "stef.NonZeroF64"), + DataType::String | DataType::StringRef => write!(f, "stef.NonZeroString"), + DataType::Bytes | DataType::BytesRef => write!(f, "stef.NonZeroBytes"), + DataType::Vec(ty) => write!(f, "stef.NonZeroVec[{}]", RenderType(ty)), + DataType::HashMap(kv) => write!( + f, + "stef.NonZeroHashMap[{}, {}]", + RenderType(&kv.0), + RenderType(&kv.1) + ), + DataType::HashSet(ty) => write!(f, "stef.NonZeroHashSet[{}]", RenderType(ty)), + ty => todo!("compiler should catch invalid {ty:?} type"), + }, + DataType::Tuple(types) => write!(f, "stef.Tuple{}{}", types.len(), Concat(types)), + DataType::Array(ty, size) => write!(f, "[{size}]{}", RenderType(ty)), + DataType::External(ExternalType { + path, + name, + generics, + }) => { + if let Some(path) = path.last() { + write!( + f, + "{path}.{}{}", + heck::AsUpperCamelCase(name), + RenderGenericTypes(generics), + ) + } else { + write!( + f, + "{}{}", + heck::AsUpperCamelCase(name), + RenderGenericTypes(generics), + ) + } + } + } + } +} + +struct RenderConstType<'a>(&'a Type<'a>); + +impl Display for RenderConstType<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.0.value { + DataType::Bool => write!(f, "bool"), + DataType::U8 => write!(f, "uint8"), + DataType::U16 => write!(f, "uint16"), + DataType::U32 => write!(f, "uint32"), + DataType::U64 => write!(f, "uint64"), + DataType::U128 | DataType::I128 => write!(f, "*big.Int"), + DataType::I8 => write!(f, "int8"), + DataType::I16 => write!(f, "int16"), + DataType::I32 => write!(f, "int32"), + DataType::I64 => write!(f, "int64"), + DataType::F32 => write!(f, "float32"), + DataType::F64 => write!(f, "float64"), + DataType::String | DataType::StringRef => write!(f, "string"), + DataType::Bytes | DataType::BytesRef => write!(f, "[]byte"), + _ => panic!("invalid data type for const"), + } + } +} + +struct Concat<'a, T>(&'a [T]); + +impl Display for Concat<'_, T> +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0.is_empty() { + return Ok(()); + } + + f.write_char('[')?; + for (i, value) in self.0.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + value.fmt(f)?; + } + f.write_char(']') + } +} + +struct RenderConst<'a>(&'a Const<'a>); + +impl Display for RenderConst<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let kind = if matches!( + self.0.ty.value, + DataType::Bool + | DataType::U8 + | DataType::U16 + | DataType::U32 + | DataType::U64 + | DataType::I8 + | DataType::I16 + | DataType::I32 + | DataType::I64 + | DataType::F32 + | DataType::F64 + | DataType::String + | DataType::StringRef + ) { + "const" + } else { + "var" + }; + + writeln!( + f, + "{}{kind} {} {} = {}", + RenderComment { + indent: 0, + comment: &self.0.comment + }, + heck::AsUpperCamelCase(&self.0.name), + RenderConstType(&self.0.ty), + RenderLiteral(&self.0.value), + ) + } +} + +struct RenderLiteral<'a>(&'a Literal); + +impl Display for RenderLiteral<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Literal::Bool(b) => write!(f, "{b}"), + Literal::Int(i) => write!(f, "{i}"), + Literal::Float(f2) => write!(f, "{f2}"), + Literal::String(s) => write!(f, "{s:?}"), + Literal::Bytes(b) => { + if b.is_empty() { + return Ok(()); + } + + f.write_str("[]byte{")?; + for (i, value) in b.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + value.fmt(f)?; + } + f.write_char('}') + } + } + } +} + +struct RenderEnum<'a>(&'a Enum<'a>); + +impl Display for RenderEnum<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "type {}Variant interface {{", + heck::AsUpperCamelCase(&self.0.name), + )?; + writeln!(f, "\t sealed()")?; + writeln!(f, "}}")?; + + writeln!( + f, + "\n{}type {} {1}Variant", + RenderComment { + indent: 0, + comment: &self.0.comment + }, + heck::AsUpperCamelCase(&self.0.name), + )?; + + for variant in &self.0.variants { + write!( + f, + "\n{}", + RenderEnumVariant { + enum_name: self.0.name.get(), + generics: &self.0.generics, + variant + } + )?; + } + + Ok(()) + } +} + +struct RenderEnumVariant<'a> { + enum_name: &'a str, + generics: &'a Generics<'a>, + variant: &'a Variant<'a>, +} + +impl Display for RenderEnumVariant<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "{}type {}_{}{} {}", + RenderComment { + indent: 0, + comment: &self.variant.comment + }, + heck::AsUpperCamelCase(self.enum_name), + heck::AsUpperCamelCase(&self.variant.name), + RenderGenerics { + generics: self.generics, + fields_filter: Some(&self.variant.fields) + }, + RenderFields(&self.variant.fields) + )?; + + writeln!( + f, + "\nfunc (v {}_{}{}) sealed() {{}}", + heck::AsUpperCamelCase(self.enum_name), + heck::AsUpperCamelCase(&self.variant.name), + RenderGenericNames { + generics: self.generics, + fields_filter: Some(&self.variant.fields), + } + )?; + + writeln!( + f, + "\n{}", + RenderNewFunc { + name: format_args!( + "{}_{}", + heck::AsUpperCamelCase(self.enum_name), + heck::AsUpperCamelCase(&self.variant.name) + ), + generics: self.generics, + fields: &self.variant.fields, + filter_generics: true, + }, + )?; + + write!( + f, + "\n{}\n{}", + encode::RenderEnumVariant { + enum_name: self.enum_name, + generics: self.generics, + variant: self.variant, + }, + decode::RenderEnumVariant { + enum_name: self.enum_name, + generics: self.generics, + variant: self.variant, + }, + ) + } +} + +fn uses_generic(generic: &Name<'_>, fields: &Fields<'_>) -> bool { + fn visit_external(ty: &Type<'_>, visit: &impl Fn(&ExternalType<'_>) -> bool) -> bool { + match &ty.value { + DataType::Bool + | DataType::U8 + | DataType::U16 + | DataType::U32 + | DataType::U64 + | DataType::U128 + | DataType::I8 + | DataType::I16 + | DataType::I32 + | DataType::I64 + | DataType::I128 + | DataType::F32 + | DataType::F64 + | DataType::String + | DataType::StringRef + | DataType::Bytes + | DataType::BytesRef + | DataType::BoxString + | DataType::BoxBytes => false, + DataType::Vec(ty) + | DataType::HashSet(ty) + | DataType::Option(ty) + | DataType::NonZero(ty) + | DataType::Array(ty, _) => visit_external(ty, visit), + DataType::HashMap(kv) => visit_external(&kv.0, visit) || visit_external(&kv.1, visit), + DataType::Tuple(types) => types.iter().any(|ty| visit_external(ty, visit)), + DataType::External(ty) => visit(ty), + } + } + + let matches = |ext: &ExternalType<'_>| { + ext.path.is_empty() && ext.generics.is_empty() && ext.name.get() == generic.get() + }; + + match fields { + Fields::Named(named) => named + .iter() + .any(|field| visit_external(&field.ty, &matches)), + Fields::Unnamed(unnamed) => unnamed + .iter() + .any(|field| visit_external(&field.ty, &matches)), + Fields::Unit => false, + } +} diff --git a/crates/stef-go/src/encode.rs b/crates/stef-go/src/encode.rs new file mode 100644 index 0000000..75e00ca --- /dev/null +++ b/crates/stef-go/src/encode.rs @@ -0,0 +1,368 @@ +#![allow(clippy::too_many_lines)] + +use std::fmt::{self, Display}; + +use stef_parser::{DataType, Fields, Generics, Struct, Type, Variant}; + +use crate::definition::{self, RenderGenericNames}; + +pub(super) struct RenderStruct<'a>(pub(super) &'a Struct<'a>); + +impl Display for RenderStruct<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "var _ buf.Encode = (*{}{})(nil)\n", + heck::AsUpperCamelCase(&self.0.name), + RenderGenericNames { + generics: &self.0.generics, + fields_filter: None, + } + )?; + + writeln!( + f, + "func (v *{}{}) Encode(w []byte) []byte {{", + heck::AsUpperCamelCase(&self.0.name), + RenderGenericNames { + generics: &self.0.generics, + fields_filter: None, + } + )?; + writeln!(f, "{}\treturn w\n}}", RenderFields(&self.0.fields)) + } +} + +pub(super) struct RenderEnumVariant<'a> { + pub(super) enum_name: &'a str, + pub(super) generics: &'a Generics<'a>, + pub(super) variant: &'a Variant<'a>, +} + +impl Display for RenderEnumVariant<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "var _ buf.Encode = (*{}_{}{})(nil)\n", + heck::AsUpperCamelCase(self.enum_name), + heck::AsUpperCamelCase(&self.variant.name), + RenderGenericNames { + generics: self.generics, + fields_filter: Some(&self.variant.fields), + } + )?; + + writeln!( + f, + "func (v *{}_{}{}) Encode(w []byte) []byte {{", + heck::AsUpperCamelCase(self.enum_name), + heck::AsUpperCamelCase(&self.variant.name), + RenderGenericNames { + generics: self.generics, + fields_filter: Some(&self.variant.fields), + } + )?; + writeln!(f, "{}\treturn nil\n}}", RenderFields(&self.variant.fields)) + } +} + +struct RenderFields<'a>(&'a Fields<'a>); + +impl Display for RenderFields<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Fields::Named(named) => { + for field in named { + if let DataType::Option(ty) = &field.ty.value { + writeln!( + f, + "\tw = buf.EncodeFieldOption[{}](w, {}, &v.{}, func (w []byte, v {0}) \ + []byte {{\n\t\treturn {}\n\t}})", + definition::RenderType(ty), + field.id.get(), + heck::AsUpperCamelCase(&field.name), + RenderType { + ty, + name: "v", + indent: 2, + }, + )?; + } else { + writeln!( + f, + "\tw = buf.EncodeField(w, {}, func (w []byte) []byte {{\n\t\treturn \ + {}\n\t}})", + field.id.get(), + RenderType { + ty: &field.ty, + name: format_args!("v.{}", heck::AsUpperCamelCase(&field.name)), + indent: 2, + }, + )?; + } + } + + writeln!(f, "\tw = buf.EncodeU32(w, buf.EndMarker)")?; + } + Fields::Unnamed(unnamed) => { + for (idx, field) in unnamed.iter().enumerate() { + if let DataType::Option(ty) = &field.ty.value { + writeln!( + f, + "\tw = buf.EncodeFieldOption[{}](w, {}, &v.F{idx}, func (w []byte, v \ + {0}) [] byte {{\n\t\treturn {}\n\t}})", + definition::RenderType(ty), + field.id.get(), + RenderType { + ty, + name: "v", + indent: 2, + }, + )?; + } else { + writeln!( + f, + "\tw = buf.EncodeField(w, {}, func (w []byte) []byte {{\n\t\treturn \ + {}\n\t}})", + field.id.get(), + RenderType { + ty: &field.ty, + name: format_args!("v.F{idx}"), + indent: 2, + }, + )?; + } + } + + writeln!(f, "\tw = buf.EncodeU32(w, buf.EndMarker)")?; + } + Fields::Unit => {} + } + + Ok(()) + } +} + +struct RenderType<'a, T> { + ty: &'a Type<'a>, + name: T, + indent: usize, +} + +impl Display for RenderType<'_, T> +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.ty.value { + DataType::Bool => write!(f, "buf.EncodeBool(w, {})", self.name), + DataType::U8 => write!(f, "buf.EncodeU8(w, {})", self.name), + DataType::U16 => write!(f, "buf.EncodeU16(w, {})", self.name), + DataType::U32 => write!(f, "buf.EncodeU32(w, {})", self.name), + DataType::U64 => write!(f, "buf.EncodeU64(w, {})", self.name), + DataType::U128 => write!(f, "buf.EncodeU128(w, {})", self.name), + DataType::I8 => write!(f, "buf.EncodeI8(w, {})", self.name), + DataType::I16 => write!(f, "buf.EncodeI16(w, {})", self.name), + DataType::I32 => write!(f, "buf.EncodeI32(w, {})", self.name), + DataType::I64 => write!(f, "buf.EncodeI64(w, {})", self.name), + DataType::I128 => write!(f, "buf.EncodeI128(w, {})", self.name), + DataType::F32 => write!(f, "buf.EncodeF32(w, {})", self.name), + DataType::F64 => write!(f, "buf.EncodeF64(w, {})", self.name), + DataType::String | DataType::StringRef | DataType::BoxString => { + write!(f, "buf.EncodeString(w, {})", self.name) + } + DataType::Bytes | DataType::BytesRef | DataType::BoxBytes => { + write!(f, "buf.EncodeBytes(w, {})", self.name) + } + DataType::Vec(ty) => { + writeln!( + f, + "buf.EncodeVec[{}](w, {}, func(w []byte, v {0}) []byte {{", + definition::RenderType(ty), + self.name + )?; + writeln!( + f, + "{:\t { + writeln!( + f, + "buf.EncodeHashMap[{}, {}](", + definition::RenderType(&kv.0), + definition::RenderType(&kv.1) + )?; + writeln!( + f, + "{:\t { + writeln!( + f, + "buf.EncodeHashSet[{}](w, {}, func(w []byte, v {0}) []byte {{", + definition::RenderType(ty), + self.name + )?; + writeln!( + f, + "{:\t { + writeln!( + f, + "buf.EncodeOption[{}](w, {}, func(w []byte, v {0}) []byte {{", + definition::RenderType(ty), + self.name + )?; + writeln!( + f, + "{:\t match &ty.value { + DataType::U8 => write!(f, "buf.EncodeU8(w, {}.Get())", self.name), + DataType::U16 => write!(f, "buf.EncodeU16(w, {}.Get())", self.name), + DataType::U32 => write!(f, "buf.EncodeU32(w, {}.Get())", self.name), + DataType::U64 => write!(f, "buf.EncodeU64(w, {}.Get())", self.name), + DataType::U128 => write!(f, "buf.EncodeU128(w, {}.Get())", self.name), + DataType::I8 => write!(f, "buf.EncodeI8(w, {}.Get())", self.name), + DataType::I16 => write!(f, "buf.EncodeI16(w, {}.Get())", self.name), + DataType::I32 => write!(f, "buf.EncodeI32(w, {}.Get())", self.name), + DataType::I64 => write!(f, "buf.EncodeI64(w, {}.Get())", self.name), + DataType::I128 => write!(f, "buf.EncodeI128(w, {}.Get())", self.name), + DataType::String + | DataType::StringRef + | DataType::Bytes + | DataType::BytesRef + | DataType::Vec(_) + | DataType::HashMap(_) + | DataType::HashSet(_) => write!( + f, + "{}", + RenderType { + ty, + name: format_args!("{}.Get()", self.name), + indent: self.indent, + } + ), + ty => todo!("compiler should catch invalid {ty:?} type"), + }, + DataType::Tuple(types) => match types.len() { + 2..=12 => { + writeln!(f, "func (w []byte) []byte {{")?; + for (idx, ty) in types.iter().enumerate() { + writeln!( + f, + "{:\t todo!("compiler should catch invalid tuple with {n} elements"), + }, + DataType::Array(ty, size) => match *size { + 1..=32 => { + writeln!( + f, + "buf.EncodeArray{size}[{}](w, {}, func(w []byte, v {0}) []byte {{", + definition::RenderType(ty), + self.name + )?; + writeln!( + f, + "{:\t todo!("arrays with larger ({n}) sizes"), + }, + DataType::External(_) => write!(f, "{}.Encode(w)", self.name), + } + } +} diff --git a/crates/stef-go/src/lib.rs b/crates/stef-go/src/lib.rs new file mode 100644 index 0000000..6293fbd --- /dev/null +++ b/crates/stef-go/src/lib.rs @@ -0,0 +1,31 @@ +//! Schema to source code converter for the _Go_ programming language. + +pub use definition::render_schema; + +mod decode; +mod definition; +mod encode; + +/// Options for the code generator that can modify the way the code is generated. +#[derive(Default)] +pub struct Opts<'a> { + /// Name of the package for the root schema. Eventual sub-modules will have their package name + /// the schema's module name. + pub package: &'a str, +} + +/// The output of generating converting a schema file into one or more Go source code files. The +/// files' content solely resides in this structure and still needs to be saved to the file system. +/// +/// As Go doesn't allow to directly define modules within a single file, a tree structure is formed +/// with each direct module located in the current module, their direct modules being located in +/// them, and so on. +#[derive(Debug)] +pub struct Output<'a> { + /// Name of this output as derived from the module name. + pub name: &'a str, + /// Final Go source code output of the module file. + pub content: String, + /// All modules that were defined as direct children of this module. + pub modules: Vec>, +} diff --git a/crates/stef-go/tests/inputs b/crates/stef-go/tests/inputs new file mode 120000 index 0000000..7089636 --- /dev/null +++ b/crates/stef-go/tests/inputs @@ -0,0 +1 @@ +../../stef-parser/tests/inputs \ No newline at end of file diff --git a/crates/stef-go/tests/render.rs b/crates/stef-go/tests/render.rs new file mode 100644 index 0000000..4616a8a --- /dev/null +++ b/crates/stef-go/tests/render.rs @@ -0,0 +1,44 @@ +use std::{ + fmt::Write, + fs, + path::{Path, PathBuf}, +}; + +use insta::{assert_snapshot, glob, with_settings}; +use stef_go::{Opts, Output}; +use stef_parser::Schema; + +fn strip_path(path: &Path) -> PathBuf { + path.strip_prefix(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/inputs")) + .or_else(|_| path.strip_prefix(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/inputs_extra"))) + .unwrap() + .to_owned() +} + +fn merge_output(buf: &mut String, output: Output<'_>, parent: &Path) { + let path = parent.join(output.name); + let _ = write!(buf, "--- {}.go\n\n{}", path.display(), output.content); + + for module in output.modules { + merge_output(buf, module, &path); + } +} + +#[test] +fn render_schema() { + glob!("inputs/*.stef", |path| { + let input = fs::read_to_string(path).unwrap(); + let value = Schema::parse(input.as_str(), Some(&strip_path(path))).unwrap(); + let value = stef_go::render_schema(&Opts { package: "sample" }, &value); + + let mut merged = String::new(); + merge_output(&mut merged, value, Path::new("")); + + with_settings!({ + description => input.trim(), + omit_expression => true, + }, { + assert_snapshot!("render", merged); + }); + }); +} diff --git a/crates/stef-go/tests/snapshots/render__render@alias_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@alias_basic.stef.snap new file mode 100644 index 0000000..b26f785 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@alias_basic.stef.snap @@ -0,0 +1,18 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Sample type alias.\ntype Sample = u32;" +input_file: crates/stef-parser/tests/inputs/alias_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +// Sample type alias. +type Sample uint32 diff --git a/crates/stef-go/tests/snapshots/render__render@attribute_multi.stef.snap b/crates/stef-go/tests/snapshots/render__render@attribute_multi.stef.snap new file mode 100644 index 0000000..965c847 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@attribute_multi.stef.snap @@ -0,0 +1,50 @@ +--- +source: crates/stef-go/tests/render.rs +description: "#[validate(min = 1, max = 100)]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute_multi.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct{} + +func NewSample() Sample { + return Sample{} +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@attribute_single.stef.snap b/crates/stef-go/tests/snapshots/render__render@attribute_single.stef.snap new file mode 100644 index 0000000..a1a6836 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@attribute_single.stef.snap @@ -0,0 +1,50 @@ +--- +source: crates/stef-go/tests/render.rs +description: "#[deprecated = \"don't use\"]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute_single.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct{} + +func NewSample() Sample { + return Sample{} +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@attribute_unit.stef.snap b/crates/stef-go/tests/snapshots/render__render@attribute_unit.stef.snap new file mode 100644 index 0000000..1f3ce5e --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@attribute_unit.stef.snap @@ -0,0 +1,50 @@ +--- +source: crates/stef-go/tests/render.rs +description: "#[deprecated]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute_unit.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct{} + +func NewSample() Sample { + return Sample{} +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@attributes.stef.snap b/crates/stef-go/tests/snapshots/render__render@attributes.stef.snap new file mode 100644 index 0000000..d0824c4 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@attributes.stef.snap @@ -0,0 +1,50 @@ +--- +source: crates/stef-go/tests/render.rs +description: "#[deprecated = \"don't use\", compress]\n#[validate(\n in_range(min = 100, max = 200),\n non_empty,\n)]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attributes.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct{} + +func NewSample() Sample { + return Sample{} +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@attributes_min_ws.stef.snap b/crates/stef-go/tests/snapshots/render__render@attributes_min_ws.stef.snap new file mode 100644 index 0000000..7879e83 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@attributes_min_ws.stef.snap @@ -0,0 +1,50 @@ +--- +source: crates/stef-go/tests/render.rs +description: "#[deprecated=\"don't use\",compress]\n#[validate(in_range(min=100,max=200),non_empty)]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attributes_min_ws.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct{} + +func NewSample() Sample { + return Sample{} +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@const_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@const_basic.stef.snap new file mode 100644 index 0000000..31d1710 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@const_basic.stef.snap @@ -0,0 +1,23 @@ +--- +source: crates/stef-go/tests/render.rs +description: "const BOOL_TRUE: bool = true;\nconst BOOL_FALSE: bool = false;\nconst INT: u32 = 100;\nconst FLOAT: f64 = 5.5;\nconst STRING: string = \"value\";\nconst BYTES: bytes = [1, 2, 3];" +input_file: crates/stef-parser/tests/inputs/const_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +const BoolTrue bool = true +const BoolFalse bool = false +const Int uint32 = 100 +const Float float64 = 5.5 +const String string = "value" +var Bytes []byte = []byte{1, 2, 3} + diff --git a/crates/stef-go/tests/snapshots/render__render@const_string.stef.snap b/crates/stef-go/tests/snapshots/render__render@const_string.stef.snap new file mode 100644 index 0000000..6458e81 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@const_string.stef.snap @@ -0,0 +1,21 @@ +--- +source: crates/stef-go/tests/render.rs +description: "const SIMPLE: string = \"value\";\n\nconst NEWLINE_ESCAPE: string = \"one \\\n two \\\n three\\\n\";\n\nconst ESCAPES: string = \"escape basics \\r\\n \\t \\b \\f \\\\ \\\"\\\n hello\\\" \\n\\\n unicode \\u{2764} \\\n emoji ❤ \\\n\";\n\nconst MULTILINE: string = \"a\n b\n c\n\";" +input_file: crates/stef-parser/tests/inputs/const_string.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +const Simple string = "value" +const NewlineEscape string = "one two three" +const Escapes string = "escape basics \r\n \t \u{8} \u{c} \\ \"hello\" \nunicode ❤ emoji ❤ " +const Multiline string = "a\n b\n c\n" + diff --git a/crates/stef-go/tests/snapshots/render__render@enum_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@enum_basic.stef.snap new file mode 100644 index 0000000..cf0bee4 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@enum_basic.stef.snap @@ -0,0 +1,223 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Sample enum.\nenum Sample {\n One @1,\n /// Second variant\n Two(u32 @1, u64 @2) @2,\n Three {\n field1: u32 @1,\n /// Second field of third variant\n field2: bool @2,\n } @3,\n}" +input_file: crates/stef-parser/tests/inputs/enum_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type SampleVariant interface { + sealed() +} + +// Sample enum. +type Sample SampleVariant + +type Sample_One struct{} + +func (v Sample_One) sealed() {} + +func NewSample_One() Sample_One { + return Sample_One{} +} + +var _ buf.Encode = (*Sample_One)(nil) + +func (v *Sample_One) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Sample_One)(nil) + +func (v *Sample_One) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +// Second variant +type Sample_Two struct { + F0 uint32 + F1 uint64 +} + +func (v Sample_Two) sealed() {} + +func NewSample_Two( + f0 uint32, + f1 uint64, +) Sample_Two { + return Sample_Two{ + F0: f0, + F1: f1, + } +} + +var _ buf.Encode = (*Sample_Two)(nil) + +func (v *Sample_Two) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.F0) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeU64(w, v.F1) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Two)(nil) + +func (v *Sample_Two) Decode(r []byte) ([]byte, error) { + foundF0 := false + foundF1 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case 2: + r2, value, err := buf.DecodeU64(r) + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "" + } + } + + return r, nil +} + +type Sample_Three struct { + Field1 uint32 + // Second field of third variant + Field2 bool +} + +func (v Sample_Three) sealed() {} + +func NewSample_Three( + field1 uint32, + field2 bool, +) Sample_Three { + return Sample_Three{ + Field1: field1, + Field2: field2, + } +} + +var _ buf.Encode = (*Sample_Three)(nil) + +func (v *Sample_Three) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.Field1) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.Field2) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Three)(nil) + +func (v *Sample_Three) Decode(r []byte) ([]byte, error) { + foundField1 := false + foundField2 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.Field1 = value + foundField1 = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.Field2 = value + foundField2 = true + case buf.EndMarker: + break + } + } + + if !foundField1 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "field1" + } + } + if !foundField2 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "field2" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@enum_generics.stef.snap b/crates/stef-go/tests/snapshots/render__render@enum_generics.stef.snap new file mode 100644 index 0000000..1cd4c9c --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@enum_generics.stef.snap @@ -0,0 +1,237 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Enum with generics.\nenum Sample {\n One @1,\n Two(A @1, B @2) @2,\n Three {\n field1: C @1,\n field2: D @2,\n } @3,\n}" +input_file: crates/stef-parser/tests/inputs/enum_generics.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type SampleVariant interface { + sealed() +} + +// Enum with generics. +type Sample SampleVariant + +type Sample_One struct{} + +func (v Sample_One) sealed() {} + +func NewSample_One() Sample_One { + return Sample_One{} +} + +var _ buf.Encode = (*Sample_One)(nil) + +func (v *Sample_One) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Sample_One)(nil) + +func (v *Sample_One) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +type Sample_Two[A any, B any] struct { + F0 A + F1 B +} + +func (v Sample_Two[A, B]) sealed() {} + +func NewSample_Two[A any, B any]( + f0 A, + f1 B, +) Sample_Two[A, B] { + return Sample_Two[A, B]{ + F0: f0, + F1: f1, + } +} + +var _ buf.Encode = (*Sample_Two[A, B])(nil) + +func (v *Sample_Two[A, B]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return v.F0.Encode(w) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.F1.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Two[A, B])(nil) + +func (v *Sample_Two[A, B]) Decode(r []byte) ([]byte, error) { + foundF0 := false + foundF1 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := func(r []byte) ([]byte, A, error) { + var value A + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case 2: + r2, value, err := func(r []byte) ([]byte, B, error) { + var value B + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "" + } + } + + return r, nil +} + +type Sample_Three[C any, D any] struct { + Field1 C + Field2 D +} + +func (v Sample_Three[C, D]) sealed() {} + +func NewSample_Three[C any, D any]( + field1 C, + field2 D, +) Sample_Three[C, D] { + return Sample_Three[C, D]{ + Field1: field1, + Field2: field2, + } +} + +var _ buf.Encode = (*Sample_Three[C, D])(nil) + +func (v *Sample_Three[C, D]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return v.Field1.Encode(w) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.Field2.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Three[C, D])(nil) + +func (v *Sample_Three[C, D]) Decode(r []byte) ([]byte, error) { + foundField1 := false + foundField2 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := func(r []byte) ([]byte, C, error) { + var value C + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Field1 = value + foundField1 = true + case 2: + r2, value, err := func(r []byte) ([]byte, D, error) { + var value D + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Field2 = value + foundField2 = true + case buf.EndMarker: + break + } + } + + if !foundField1 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "field1" + } + } + if !foundField2 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "field2" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@enum_many_ws.stef.snap b/crates/stef-go/tests/snapshots/render__render@enum_many_ws.stef.snap new file mode 100644 index 0000000..d336365 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@enum_many_ws.stef.snap @@ -0,0 +1,221 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Sample enum.\n enum Sample {\n\n One @1,\n\n Two ( u32 @1, u64 @2) @2,\n\n Three {\n\n field1: u32 @1,\n\n field2: bool @2,\n\n } @3,\n\n }" +input_file: crates/stef-parser/tests/inputs/enum_many_ws.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type SampleVariant interface { + sealed() +} + +// Sample enum. +type Sample SampleVariant + +type Sample_One struct{} + +func (v Sample_One) sealed() {} + +func NewSample_One() Sample_One { + return Sample_One{} +} + +var _ buf.Encode = (*Sample_One)(nil) + +func (v *Sample_One) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Sample_One)(nil) + +func (v *Sample_One) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +type Sample_Two struct { + F0 uint32 + F1 uint64 +} + +func (v Sample_Two) sealed() {} + +func NewSample_Two( + f0 uint32, + f1 uint64, +) Sample_Two { + return Sample_Two{ + F0: f0, + F1: f1, + } +} + +var _ buf.Encode = (*Sample_Two)(nil) + +func (v *Sample_Two) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.F0) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeU64(w, v.F1) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Two)(nil) + +func (v *Sample_Two) Decode(r []byte) ([]byte, error) { + foundF0 := false + foundF1 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case 2: + r2, value, err := buf.DecodeU64(r) + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "" + } + } + + return r, nil +} + +type Sample_Three struct { + Field1 uint32 + Field2 bool +} + +func (v Sample_Three) sealed() {} + +func NewSample_Three( + field1 uint32, + field2 bool, +) Sample_Three { + return Sample_Three{ + Field1: field1, + Field2: field2, + } +} + +var _ buf.Encode = (*Sample_Three)(nil) + +func (v *Sample_Three) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.Field1) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.Field2) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Three)(nil) + +func (v *Sample_Three) Decode(r []byte) ([]byte, error) { + foundField1 := false + foundField2 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.Field1 = value + foundField1 = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.Field2 = value + foundField2 = true + case buf.EndMarker: + break + } + } + + if !foundField1 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "field1" + } + } + if !foundField2 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "field2" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@enum_min_ws.stef.snap b/crates/stef-go/tests/snapshots/render__render@enum_min_ws.stef.snap new file mode 100644 index 0000000..533031e --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@enum_min_ws.stef.snap @@ -0,0 +1,270 @@ +--- +source: crates/stef-go/tests/render.rs +description: "enum Sample{One@1,Two(u32@1,u64@2,T@3)@2,Three{field1:u32@1,field2:bool@2,field3:T@3}@3}" +input_file: crates/stef-parser/tests/inputs/enum_min_ws.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type SampleVariant interface { + sealed() +} + +type Sample SampleVariant + +type Sample_One struct{} + +func (v Sample_One) sealed() {} + +func NewSample_One() Sample_One { + return Sample_One{} +} + +var _ buf.Encode = (*Sample_One)(nil) + +func (v *Sample_One) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Sample_One)(nil) + +func (v *Sample_One) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +type Sample_Two[T any] struct { + F0 uint32 + F1 uint64 + F2 T +} + +func (v Sample_Two[T]) sealed() {} + +func NewSample_Two[T any]( + f0 uint32, + f1 uint64, + f2 T, +) Sample_Two[T] { + return Sample_Two[T]{ + F0: f0, + F1: f1, + F2: f2, + } +} + +var _ buf.Encode = (*Sample_Two[T])(nil) + +func (v *Sample_Two[T]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.F0) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeU64(w, v.F1) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return v.F2.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Two[T])(nil) + +func (v *Sample_Two[T]) Decode(r []byte) ([]byte, error) { + foundF0 := false + foundF1 := false + foundF2 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case 2: + r2, value, err := buf.DecodeU64(r) + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case 3: + r2, value, err := func(r []byte) ([]byte, T, error) { + var value T + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.F2 = value + foundF2 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "" + } + } + if !foundF2 { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "" + } + } + + return r, nil +} + +type Sample_Three[T any] struct { + Field1 uint32 + Field2 bool + Field3 T +} + +func (v Sample_Three[T]) sealed() {} + +func NewSample_Three[T any]( + field1 uint32, + field2 bool, + field3 T, +) Sample_Three[T] { + return Sample_Three[T]{ + Field1: field1, + Field2: field2, + Field3: field3, + } +} + +var _ buf.Encode = (*Sample_Three[T])(nil) + +func (v *Sample_Three[T]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.Field1) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.Field2) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return v.Field3.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*Sample_Three[T])(nil) + +func (v *Sample_Three[T]) Decode(r []byte) ([]byte, error) { + foundField1 := false + foundField2 := false + foundField3 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.Field1 = value + foundField1 = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.Field2 = value + foundField2 = true + case 3: + r2, value, err := func(r []byte) ([]byte, T, error) { + var value T + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Field3 = value + foundField3 = true + case buf.EndMarker: + break + } + } + + if !foundField1 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "field1" + } + } + if !foundField2 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "field2" + } + } + if !foundField3 { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "field3" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@import_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@import_basic.stef.snap new file mode 100644 index 0000000..297503a --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@import_basic.stef.snap @@ -0,0 +1,17 @@ +--- +source: crates/stef-go/tests/render.rs +description: "use other::schema::Sample;\nuse second::submodule;" +input_file: crates/stef-parser/tests/inputs/import_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + + diff --git a/crates/stef-go/tests/snapshots/render__render@module_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@module_basic.stef.snap new file mode 100644 index 0000000..edca2ae --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@module_basic.stef.snap @@ -0,0 +1,154 @@ +--- +source: crates/stef-go/tests/render.rs +description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n inner: b::Sample @2,\n }\n}" +input_file: crates/stef-parser/tests/inputs/module_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +--- sample/a.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +package a + +type Sample struct { + Value uint32 + Inner b.Sample +} + +func NewSample( + value uint32, + inner b.Sample, +) Sample { + return Sample{ + Value: value, + Inner: inner, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.Value) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.Inner.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundValue := false + foundInner := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.Value = value + foundValue = true + case 2: + r2, value, err := func(r []byte) ([]byte, b.Sample, error) { + var value b.Sample + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Inner = value + foundInner = true + case buf.EndMarker: + break + } + } + + if !foundValue { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "value" + } + } + if !foundInner { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "inner" + } + } + + return r, nil +} + +--- sample/a/b.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +// Inner module +package b + +type SampleVariant interface { + sealed() +} + +type Sample SampleVariant + +type Sample_One struct{} + +func (v Sample_One) sealed() {} + +func NewSample_One() Sample_One { + return Sample_One{} +} + +var _ buf.Encode = (*Sample_One)(nil) + +func (v *Sample_One) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Sample_One)(nil) + +func (v *Sample_One) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@schema_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@schema_basic.stef.snap new file mode 100644 index 0000000..a41ca15 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@schema_basic.stef.snap @@ -0,0 +1,301 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Basic struct.\nstruct SampleStruct {\n a: u32 @1,\n b: bool @2,\n}\n\n/// Sample enum.\nenum SampleEnum {\n One @1,\n Two(u32 @1, u64 @2) @2,\n Three {\n field1: u32 @1,\n field2: bool @2,\n } @3,\n}" +input_file: crates/stef-parser/tests/inputs/schema_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +// Basic struct. +type SampleStruct struct { + A uint32 + B bool +} + +func NewSampleStruct( + a uint32, + b bool, +) SampleStruct { + return SampleStruct{ + A: a, + B: b, + } +} + +var _ buf.Encode = (*SampleStruct)(nil) + +func (v *SampleStruct) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.A) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.B) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*SampleStruct)(nil) + +func (v *SampleStruct) Decode(r []byte) ([]byte, error) { + foundA := false + foundB := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.A = value + foundA = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.B = value + foundB = true + case buf.EndMarker: + break + } + } + + if !foundA { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "a" + } + } + if !foundB { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "b" + } + } + + return r, nil +} + +type SampleEnumVariant interface { + sealed() +} + +// Sample enum. +type SampleEnum SampleEnumVariant + +type SampleEnum_One struct{} + +func (v SampleEnum_One) sealed() {} + +func NewSampleEnum_One() SampleEnum_One { + return SampleEnum_One{} +} + +var _ buf.Encode = (*SampleEnum_One)(nil) + +func (v *SampleEnum_One) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*SampleEnum_One)(nil) + +func (v *SampleEnum_One) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +type SampleEnum_Two struct { + F0 uint32 + F1 uint64 +} + +func (v SampleEnum_Two) sealed() {} + +func NewSampleEnum_Two( + f0 uint32, + f1 uint64, +) SampleEnum_Two { + return SampleEnum_Two{ + F0: f0, + F1: f1, + } +} + +var _ buf.Encode = (*SampleEnum_Two)(nil) + +func (v *SampleEnum_Two) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.F0) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeU64(w, v.F1) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*SampleEnum_Two)(nil) + +func (v *SampleEnum_Two) Decode(r []byte) ([]byte, error) { + foundF0 := false + foundF1 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case 2: + r2, value, err := buf.DecodeU64(r) + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "" + } + } + + return r, nil +} + +type SampleEnum_Three struct { + Field1 uint32 + Field2 bool +} + +func (v SampleEnum_Three) sealed() {} + +func NewSampleEnum_Three( + field1 uint32, + field2 bool, +) SampleEnum_Three { + return SampleEnum_Three{ + Field1: field1, + Field2: field2, + } +} + +var _ buf.Encode = (*SampleEnum_Three)(nil) + +func (v *SampleEnum_Three) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.Field1) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.Field2) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*SampleEnum_Three)(nil) + +func (v *SampleEnum_Three) Decode(r []byte) ([]byte, error) { + foundField1 := false + foundField2 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.Field1 = value + foundField1 = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.Field2 = value + foundField2 = true + case buf.EndMarker: + break + } + } + + if !foundField1 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "field1" + } + } + if !foundField2 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "field2" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@struct_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@struct_basic.stef.snap new file mode 100644 index 0000000..d611a39 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@struct_basic.stef.snap @@ -0,0 +1,98 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Basic struct.\nstruct Sample {\n a: u32 @1,\n /// Second field\n b: bool @2,\n}" +input_file: crates/stef-parser/tests/inputs/struct_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +// Basic struct. +type Sample struct { + A uint32 + // Second field + B bool +} + +func NewSample( + a uint32, + b bool, +) Sample { + return Sample{ + A: a, + B: b, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.A) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.B) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundA := false + foundB := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.A = value + foundA = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.B = value + foundB = true + case buf.EndMarker: + break + } + } + + if !foundA { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "a" + } + } + if !foundB { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "b" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@struct_generics.stef.snap b/crates/stef-go/tests/snapshots/render__render@struct_generics.stef.snap new file mode 100644 index 0000000..e03e7a5 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@struct_generics.stef.snap @@ -0,0 +1,105 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Generic key-value pair.\nstruct KeyValue {\n key: K @1,\n value: V @2,\n}" +input_file: crates/stef-parser/tests/inputs/struct_generics.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +// Generic key-value pair. +type KeyValue[K any, V any] struct { + Key K + Value V +} + +func NewKeyValue[K any, V any]( + key K, + value V, +) KeyValue[K, V] { + return KeyValue[K, V]{ + Key: key, + Value: value, + } +} + +var _ buf.Encode = (*KeyValue[K, V])(nil) + +func (v *KeyValue[K, V]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return v.Key.Encode(w) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.Value.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*KeyValue[K, V])(nil) + +func (v *KeyValue[K, V]) Decode(r []byte) ([]byte, error) { + foundKey := false + foundValue := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := func(r []byte) ([]byte, K, error) { + var value K + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Key = value + foundKey = true + case 2: + r2, value, err := func(r []byte) ([]byte, V, error) { + var value V + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Value = value + foundValue = true + case buf.EndMarker: + break + } + } + + if !foundKey { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "key" + } + } + if !foundValue { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "value" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@struct_many_ws.stef.snap b/crates/stef-go/tests/snapshots/render__render@struct_many_ws.stef.snap new file mode 100644 index 0000000..cd01dbe --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@struct_many_ws.stef.snap @@ -0,0 +1,122 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Some comment\n struct Sample<\n T\n > {\n\n a: u32 @1,\n b: bool @2,\n c: T @3,\n }" +input_file: crates/stef-parser/tests/inputs/struct_many_ws.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +// Some comment +type Sample[T any] struct { + A uint32 + B bool + C T +} + +func NewSample[T any]( + a uint32, + b bool, + c T, +) Sample[T] { + return Sample[T]{ + A: a, + B: b, + C: c, + } +} + +var _ buf.Encode = (*Sample[T])(nil) + +func (v *Sample[T]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.A) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.B) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return v.C.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample[T])(nil) + +func (v *Sample[T]) Decode(r []byte) ([]byte, error) { + foundA := false + foundB := false + foundC := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.A = value + foundA = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.B = value + foundB = true + case 3: + r2, value, err := func(r []byte) ([]byte, T, error) { + var value T + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.C = value + foundC = true + case buf.EndMarker: + break + } + } + + if !foundA { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "a" + } + } + if !foundB { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "b" + } + } + if !foundC { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "c" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@struct_min_ws.stef.snap b/crates/stef-go/tests/snapshots/render__render@struct_min_ws.stef.snap new file mode 100644 index 0000000..eabec00 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@struct_min_ws.stef.snap @@ -0,0 +1,121 @@ +--- +source: crates/stef-go/tests/render.rs +description: "struct Sample{a:u32@1,b:bool@2,c:T@3}" +input_file: crates/stef-parser/tests/inputs/struct_min_ws.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample[T any] struct { + A uint32 + B bool + C T +} + +func NewSample[T any]( + a uint32, + b bool, + c T, +) Sample[T] { + return Sample[T]{ + A: a, + B: b, + C: c, + } +} + +var _ buf.Encode = (*Sample[T])(nil) + +func (v *Sample[T]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.A) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.B) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return v.C.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample[T])(nil) + +func (v *Sample[T]) Decode(r []byte) ([]byte, error) { + foundA := false + foundB := false + foundC := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.A = value + foundA = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.B = value + foundB = true + case 3: + r2, value, err := func(r []byte) ([]byte, T, error) { + var value T + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.C = value + foundC = true + case buf.EndMarker: + break + } + } + + if !foundA { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "a" + } + } + if !foundB { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "b" + } + } + if !foundC { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "c" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@struct_tuple.stef.snap b/crates/stef-go/tests/snapshots/render__render@struct_tuple.stef.snap new file mode 100644 index 0000000..1f9a2d8 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@struct_tuple.stef.snap @@ -0,0 +1,97 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Basic struct.\nstruct Sample(u32 @1, bool @2)" +input_file: crates/stef-parser/tests/inputs/struct_tuple.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +// Basic struct. +type Sample struct { + F0 uint32 + F1 bool +} + +func NewSample( + f0 uint32, + f1 bool, +) Sample { + return Sample{ + F0: f0, + F1: f1, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU32(w, v.F0) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeBool(w, v.F1) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundF0 := false + foundF1 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case 2: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@types_basic.stef.snap b/crates/stef-go/tests/snapshots/render__render@types_basic.stef.snap new file mode 100644 index 0000000..c09a863 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@types_basic.stef.snap @@ -0,0 +1,524 @@ +--- +source: crates/stef-go/tests/render.rs +description: "struct Sample {\n f01: bool @1,\n f02: u8 @2,\n f03: u16 @3,\n f04: u32 @4,\n f05: u64 @5,\n f06: u128 @6,\n f07: i8 @7,\n f08: i16 @8,\n f09: i32 @9,\n f10: i64 @10,\n f11: i128 @11,\n f12: f32 @12,\n f13: f64 @13,\n f14: string @14,\n f15: &string @15,\n f16: bytes @16,\n f17: &bytes @17,\n f18: box @18,\n f19: box @19,\n f20: (u32, u32, u32) @20,\n f21: [u32; 12] @21,\n}" +input_file: crates/stef-parser/tests/inputs/types_basic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct { + F01 bool + F02 uint8 + F03 uint16 + F04 uint32 + F05 uint64 + F06 *big.Int + F07 int8 + F08 int16 + F09 int32 + F10 int64 + F11 *big.Int + F12 float32 + F13 float64 + F14 string + F15 string + F16 []byte + F17 []byte + F18 string + F19 []byte + F20 stef.Tuple3[u32, u32, u32] + F21 [12]uint32 +} + +func NewSample( + f01 bool, + f02 uint8, + f03 uint16, + f04 uint32, + f05 uint64, + f06 *big.Int, + f07 int8, + f08 int16, + f09 int32, + f10 int64, + f11 *big.Int, + f12 float32, + f13 float64, + f14 string, + f15 string, + f16 []byte, + f17 []byte, + f18 string, + f19 []byte, + f20 stef.Tuple3[u32, u32, u32], + f21 [12]uint32, +) Sample { + return Sample{ + F01: f01, + F02: f02, + F03: f03, + F04: f04, + F05: f05, + F06: f06, + F07: f07, + F08: f08, + F09: f09, + F10: f10, + F11: f11, + F12: f12, + F13: f13, + F14: f14, + F15: f15, + F16: f16, + F17: f17, + F18: f18, + F19: f19, + F20: f20, + F21: f21, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeBool(w, v.F01) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeU8(w, v.F02) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeU16(w, v.F03) + }) + w = buf.EncodeField(w, 4, func (w []byte) []byte { + return buf.EncodeU32(w, v.F04) + }) + w = buf.EncodeField(w, 5, func (w []byte) []byte { + return buf.EncodeU64(w, v.F05) + }) + w = buf.EncodeField(w, 6, func (w []byte) []byte { + return buf.EncodeU128(w, v.F06) + }) + w = buf.EncodeField(w, 7, func (w []byte) []byte { + return buf.EncodeI8(w, v.F07) + }) + w = buf.EncodeField(w, 8, func (w []byte) []byte { + return buf.EncodeI16(w, v.F08) + }) + w = buf.EncodeField(w, 9, func (w []byte) []byte { + return buf.EncodeI32(w, v.F09) + }) + w = buf.EncodeField(w, 10, func (w []byte) []byte { + return buf.EncodeI64(w, v.F10) + }) + w = buf.EncodeField(w, 11, func (w []byte) []byte { + return buf.EncodeI128(w, v.F11) + }) + w = buf.EncodeField(w, 12, func (w []byte) []byte { + return buf.EncodeF32(w, v.F12) + }) + w = buf.EncodeField(w, 13, func (w []byte) []byte { + return buf.EncodeF64(w, v.F13) + }) + w = buf.EncodeField(w, 14, func (w []byte) []byte { + return buf.EncodeString(w, v.F14) + }) + w = buf.EncodeField(w, 15, func (w []byte) []byte { + return buf.EncodeString(w, v.F15) + }) + w = buf.EncodeField(w, 16, func (w []byte) []byte { + return buf.EncodeBytes(w, v.F16) + }) + w = buf.EncodeField(w, 17, func (w []byte) []byte { + return buf.EncodeBytes(w, v.F17) + }) + w = buf.EncodeField(w, 18, func (w []byte) []byte { + return buf.EncodeString(w, v.F18) + }) + w = buf.EncodeField(w, 19, func (w []byte) []byte { + return buf.EncodeBytes(w, v.F19) + }) + w = buf.EncodeField(w, 20, func (w []byte) []byte { + return func (w []byte) []byte { + w = buf.EncodeU32(w, v.F20.F0) + w = buf.EncodeU32(w, v.F20.F1) + w = buf.EncodeU32(w, v.F20.F2) + return w + }(w) + }) + w = buf.EncodeField(w, 21, func (w []byte) []byte { + return buf.EncodeArray12[uint32](w, v.F21, func(w []byte, v uint32) []byte { + return buf.EncodeU32(w, v) + }) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundF01 := false + foundF02 := false + foundF03 := false + foundF04 := false + foundF05 := false + foundF06 := false + foundF07 := false + foundF08 := false + foundF09 := false + foundF10 := false + foundF11 := false + foundF12 := false + foundF13 := false + foundF14 := false + foundF15 := false + foundF16 := false + foundF17 := false + foundF18 := false + foundF19 := false + foundF20 := false + foundF21 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeBool(r) + if err != nil { + return nil, err + } + r = r2 + v.F01 = value + foundF01 = true + case 2: + r2, value, err := buf.DecodeU8(r) + if err != nil { + return nil, err + } + r = r2 + v.F02 = value + foundF02 = true + case 3: + r2, value, err := buf.DecodeU16(r) + if err != nil { + return nil, err + } + r = r2 + v.F03 = value + foundF03 = true + case 4: + r2, value, err := buf.DecodeU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F04 = value + foundF04 = true + case 5: + r2, value, err := buf.DecodeU64(r) + if err != nil { + return nil, err + } + r = r2 + v.F05 = value + foundF05 = true + case 6: + r2, value, err := buf.DecodeU128(r) + if err != nil { + return nil, err + } + r = r2 + v.F06 = value + foundF06 = true + case 7: + r2, value, err := buf.DecodeI8(r) + if err != nil { + return nil, err + } + r = r2 + v.F07 = value + foundF07 = true + case 8: + r2, value, err := buf.DecodeI16(r) + if err != nil { + return nil, err + } + r = r2 + v.F08 = value + foundF08 = true + case 9: + r2, value, err := buf.DecodeI32(r) + if err != nil { + return nil, err + } + r = r2 + v.F09 = value + foundF09 = true + case 10: + r2, value, err := buf.DecodeI64(r) + if err != nil { + return nil, err + } + r = r2 + v.F10 = value + foundF10 = true + case 11: + r2, value, err := buf.DecodeI128(r) + if err != nil { + return nil, err + } + r = r2 + v.F11 = value + foundF11 = true + case 12: + r2, value, err := buf.DecodeF32(r) + if err != nil { + return nil, err + } + r = r2 + v.F12 = value + foundF12 = true + case 13: + r2, value, err := buf.DecodeF64(r) + if err != nil { + return nil, err + } + r = r2 + v.F13 = value + foundF13 = true + case 14: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.F14 = value + foundF14 = true + case 15: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.F15 = value + foundF15 = true + case 16: + r2, value, err := buf.DecodeBytes(r) + if err != nil { + return nil, err + } + r = r2 + v.F16 = value + foundF16 = true + case 17: + r2, value, err := buf.DecodeBytes(r) + if err != nil { + return nil, err + } + r = r2 + v.F17 = value + foundF17 = true + case 18: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.F18 = value + foundF18 = true + case 19: + r2, value, err := buf.DecodeBytes(r) + if err != nil { + return nil, err + } + r = r2 + v.F19 = value + foundF19 = true + case 20: + r2, value, err := func (r []byte) ([]byte, buf.Tuple3, error) { + r2, value0, err := buf.DecodeU32(r) + if err != nil { + return nil, value, err + } + r = r2 + tuple.F0 = value0 + r2, value1, err := buf.DecodeU32(r) + if err != nil { + return nil, value, err + } + r = r2 + tuple.F1 = value1 + r2, value2, err := buf.DecodeU32(r) + if err != nil { + return nil, value, err + } + r = r2 + tuple.F2 = value2 + return r, tuple, nil + }(r) + if err != nil { + return nil, err + } + r = r2 + v.F20 = value + foundF20 = true + case 21: + r2, value, err := buf.DecodeArray12[uint32](r, func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F21 = value + foundF21 = true + case buf.EndMarker: + break + } + } + + if !foundF01 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "f01" + } + } + if !foundF02 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "f02" + } + } + if !foundF03 { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "f03" + } + } + if !foundF04 { + return nil, buf.MissingFieldError{ + ID: 4 + Field: "f04" + } + } + if !foundF05 { + return nil, buf.MissingFieldError{ + ID: 5 + Field: "f05" + } + } + if !foundF06 { + return nil, buf.MissingFieldError{ + ID: 6 + Field: "f06" + } + } + if !foundF07 { + return nil, buf.MissingFieldError{ + ID: 7 + Field: "f07" + } + } + if !foundF08 { + return nil, buf.MissingFieldError{ + ID: 8 + Field: "f08" + } + } + if !foundF09 { + return nil, buf.MissingFieldError{ + ID: 9 + Field: "f09" + } + } + if !foundF10 { + return nil, buf.MissingFieldError{ + ID: 10 + Field: "f10" + } + } + if !foundF11 { + return nil, buf.MissingFieldError{ + ID: 11 + Field: "f11" + } + } + if !foundF12 { + return nil, buf.MissingFieldError{ + ID: 12 + Field: "f12" + } + } + if !foundF13 { + return nil, buf.MissingFieldError{ + ID: 13 + Field: "f13" + } + } + if !foundF14 { + return nil, buf.MissingFieldError{ + ID: 14 + Field: "f14" + } + } + if !foundF15 { + return nil, buf.MissingFieldError{ + ID: 15 + Field: "f15" + } + } + if !foundF16 { + return nil, buf.MissingFieldError{ + ID: 16 + Field: "f16" + } + } + if !foundF17 { + return nil, buf.MissingFieldError{ + ID: 17 + Field: "f17" + } + } + if !foundF18 { + return nil, buf.MissingFieldError{ + ID: 18 + Field: "f18" + } + } + if !foundF19 { + return nil, buf.MissingFieldError{ + ID: 19 + Field: "f19" + } + } + if !foundF20 { + return nil, buf.MissingFieldError{ + ID: 20 + Field: "f20" + } + } + if !foundF21 { + return nil, buf.MissingFieldError{ + ID: 21 + Field: "f21" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@types_generic.stef.snap b/crates/stef-go/tests/snapshots/render__render@types_generic.stef.snap new file mode 100644 index 0000000..a1fc0e7 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@types_generic.stef.snap @@ -0,0 +1,353 @@ +--- +source: crates/stef-go/tests/render.rs +description: "struct Sample {\n f1: vec @1,\n f2: hash_map @2,\n f3: hash_set @3,\n f4: option @4,\n f5: non_zero @5,\n}\n\nstruct SampleUnnamed(\n vec @1,\n hash_map @2,\n hash_set @3,\n option @4,\n non_zero @5,\n)" +input_file: crates/stef-parser/tests/inputs/types_generic.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct { + F1 []uint32 + F2 map[uint32]string + F3 map[uint32]struct{} + F4 *uint32 + F5 stef.NonZeroU32 +} + +func NewSample( + f1 []uint32, + f2 map[uint32]string, + f3 map[uint32]struct{}, + f4 *uint32, + f5 stef.NonZeroU32, +) Sample { + return Sample{ + F1: f1, + F2: f2, + F3: f3, + F4: f4, + F5: f5, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeVec[uint32](w, v.F1, func(w []byte, v uint32) []byte { + return buf.EncodeU32(w, v) + }) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeHashMap[uint32, string]( + w, v.F2, + func(w []byte, k uint32) []byte { + return buf.EncodeU32(w, k) + }, + func(w []byte, v string) []byte { + return buf.EncodeString(w, v) + }, + ) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeHashSet[uint32](w, v.F3, func(w []byte, v uint32) []byte { + return buf.EncodeU32(w, v) + }) + }) + w = buf.EncodeFieldOption[uint32](w, 4, &v.F4, func (w []byte, v uint32) []byte { + return buf.EncodeU32(w, v) + }) + w = buf.EncodeField(w, 5, func (w []byte) []byte { + return buf.EncodeU32(w, v.F5.Get()) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundF1 := false + foundF2 := false + foundF3 := false + foundF4 := false + foundF5 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeVec[uint32](r, func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case 2: + r2, value, err := buf.DecodeHashMap[uint32, string]( + r, + func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }, + func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }, + ) + if err != nil { + return nil, err + } + r = r2 + v.F2 = value + foundF2 = true + case 3: + r2, value, err := buf.DecodeHashSet[uint32](r, func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F3 = value + foundF3 = true + case 4: + r2, value, err := buf.DecodeOption[uint32](r, func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F4 = value + foundF4 = true + case 5: + r2, value, err := buf.DecodeNonZeroU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F5 = value + foundF5 = true + case buf.EndMarker: + break + } + } + + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "f1" + } + } + if !foundF2 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "f2" + } + } + if !foundF3 { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "f3" + } + } + if !foundF4 { + return nil, buf.MissingFieldError{ + ID: 4 + Field: "f4" + } + } + if !foundF5 { + return nil, buf.MissingFieldError{ + ID: 5 + Field: "f5" + } + } + + return r, nil +} + +type SampleUnnamed struct { + F0 []uint32 + F1 map[uint32]string + F2 map[uint32]struct{} + F3 *uint32 + F4 stef.NonZeroU32 +} + +func NewSampleUnnamed( + f0 []uint32, + f1 map[uint32]string, + f2 map[uint32]struct{}, + f3 *uint32, + f4 stef.NonZeroU32, +) SampleUnnamed { + return SampleUnnamed{ + F0: f0, + F1: f1, + F2: f2, + F3: f3, + F4: f4, + } +} + +var _ buf.Encode = (*SampleUnnamed)(nil) + +func (v *SampleUnnamed) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeVec[uint32](w, v.F0, func(w []byte, v uint32) []byte { + return buf.EncodeU32(w, v) + }) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeHashMap[uint32, string]( + w, v.F1, + func(w []byte, k uint32) []byte { + return buf.EncodeU32(w, k) + }, + func(w []byte, v string) []byte { + return buf.EncodeString(w, v) + }, + ) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeHashSet[uint32](w, v.F2, func(w []byte, v uint32) []byte { + return buf.EncodeU32(w, v) + }) + }) + w = buf.EncodeFieldOption[uint32](w, 4, &v.F3, func (w []byte, v uint32) [] byte { + return buf.EncodeU32(w, v) + }) + w = buf.EncodeField(w, 5, func (w []byte) []byte { + return buf.EncodeU32(w, v.F4.Get()) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*SampleUnnamed)(nil) + +func (v *SampleUnnamed) Decode(r []byte) ([]byte, error) { + foundF0 := false + foundF1 := false + foundF2 := false + foundF3 := false + foundF4 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeVec[uint32](r, func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case 2: + r2, value, err := buf.DecodeHashMap[uint32, string]( + r, + func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }, + func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }, + ) + if err != nil { + return nil, err + } + r = r2 + v.F1 = value + foundF1 = true + case 3: + r2, value, err := buf.DecodeHashSet[uint32](r, func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F2 = value + foundF2 = true + case 4: + r2, value, err := buf.DecodeOption[uint32](r, func(r []byte) ([]byte, uint32, error) { + return buf.DecodeU32(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F3 = value + foundF3 = true + case 5: + r2, value, err := buf.DecodeNonZeroU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F4 = value + foundF4 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + if !foundF1 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "" + } + } + if !foundF2 { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "" + } + } + if !foundF3 { + return nil, buf.MissingFieldError{ + ID: 4 + Field: "" + } + } + if !foundF4 { + return nil, buf.MissingFieldError{ + ID: 5 + Field: "" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@types_nested.stef.snap b/crates/stef-go/tests/snapshots/render__render@types_nested.stef.snap new file mode 100644 index 0000000..c5f291a --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@types_nested.stef.snap @@ -0,0 +1,99 @@ +--- +source: crates/stef-go/tests/render.rs +description: "struct Sample {\n value: vec>>>> @1,\n}" +input_file: crates/stef-parser/tests/inputs/types_nested.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct { + Value []*stef.NonZeroHashMap[int64, string] +} + +func NewSample( + value []*stef.NonZeroHashMap[int64, string], +) Sample { + return Sample{ + Value: value, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeVec[*stef.NonZeroHashMap[int64, string]](w, v.Value, func(w []byte, v *stef.NonZeroHashMap[int64, string]) []byte { + return buf.EncodeOption[stef.NonZeroHashMap[int64, string]](w, v, func(w []byte, v stef.NonZeroHashMap[int64, string]) []byte { + return buf.EncodeHashMap[int64, string]( + w, v.Get(), + func(w []byte, k int64) []byte { + return buf.EncodeI64(w, k) + }, + func(w []byte, v string) []byte { + return buf.EncodeString(w, v) + }, + ) + }) + }) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundValue := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeVec[*stef.NonZeroHashMap[int64, string]](r, func(r []byte) ([]byte, *stef.NonZeroHashMap[int64, string], error) { + return buf.DecodeOption[stef.NonZeroHashMap[int64, string]](r, func(r []byte) ([]byte, stef.NonZeroHashMap[int64, string], error) { + return buf.DecodeNonZeroHashMap[int64, string]( + r, + func(r []byte) ([]byte, int64, error) { + return buf.DecodeI64(r) + }, + func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }, + ) + }) + }) + if err != nil { + return nil, err + } + r = r2 + v.Value = value + foundValue = true + case buf.EndMarker: + break + } + } + + if !foundValue { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "value" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@types_non_zero.stef.snap b/crates/stef-go/tests/snapshots/render__render@types_non_zero.stef.snap new file mode 100644 index 0000000..218389f --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@types_non_zero.stef.snap @@ -0,0 +1,393 @@ +--- +source: crates/stef-go/tests/render.rs +description: "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 +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct { + F01 stef.NonZeroU8 + F02 stef.NonZeroU16 + F03 stef.NonZeroU32 + F04 stef.NonZeroU64 + F05 stef.NonZeroU128 + F06 stef.NonZeroI8 + F07 stef.NonZeroI16 + F08 stef.NonZeroI32 + F09 stef.NonZeroI64 + F10 stef.NonZeroI128 + F11 stef.NonZeroString + F12 stef.NonZeroBytes + F13 stef.NonZeroVec[string] + F14 stef.NonZeroHashMap[string, []byte] + F15 stef.NonZeroHashSet[string] +} + +func NewSample( + f01 stef.NonZeroU8, + f02 stef.NonZeroU16, + f03 stef.NonZeroU32, + f04 stef.NonZeroU64, + f05 stef.NonZeroU128, + f06 stef.NonZeroI8, + f07 stef.NonZeroI16, + f08 stef.NonZeroI32, + f09 stef.NonZeroI64, + f10 stef.NonZeroI128, + f11 stef.NonZeroString, + f12 stef.NonZeroBytes, + f13 stef.NonZeroVec[string], + f14 stef.NonZeroHashMap[string, []byte], + f15 stef.NonZeroHashSet[string], +) Sample { + return Sample{ + F01: f01, + F02: f02, + F03: f03, + F04: f04, + F05: f05, + F06: f06, + F07: f07, + F08: f08, + F09: f09, + F10: f10, + F11: f11, + F12: f12, + F13: f13, + F14: f14, + F15: f15, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU8(w, v.F01.Get()) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return buf.EncodeU16(w, v.F02.Get()) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeU32(w, v.F03.Get()) + }) + w = buf.EncodeField(w, 4, func (w []byte) []byte { + return buf.EncodeU64(w, v.F04.Get()) + }) + w = buf.EncodeField(w, 5, func (w []byte) []byte { + return buf.EncodeU128(w, v.F05.Get()) + }) + w = buf.EncodeField(w, 6, func (w []byte) []byte { + return buf.EncodeI8(w, v.F06.Get()) + }) + w = buf.EncodeField(w, 7, func (w []byte) []byte { + return buf.EncodeI16(w, v.F07.Get()) + }) + w = buf.EncodeField(w, 8, func (w []byte) []byte { + return buf.EncodeI32(w, v.F08.Get()) + }) + w = buf.EncodeField(w, 9, func (w []byte) []byte { + return buf.EncodeI64(w, v.F09.Get()) + }) + w = buf.EncodeField(w, 10, func (w []byte) []byte { + return buf.EncodeI128(w, v.F10.Get()) + }) + w = buf.EncodeField(w, 11, func (w []byte) []byte { + return buf.EncodeString(w, v.F11.Get()) + }) + w = buf.EncodeField(w, 12, func (w []byte) []byte { + return buf.EncodeBytes(w, v.F12.Get()) + }) + w = buf.EncodeField(w, 13, func (w []byte) []byte { + return buf.EncodeVec[string](w, v.F13.Get(), func(w []byte, v string) []byte { + return buf.EncodeString(w, v) + }) + }) + w = buf.EncodeField(w, 14, func (w []byte) []byte { + return buf.EncodeHashMap[string, []byte]( + w, v.F14.Get(), + func(w []byte, k string) []byte { + return buf.EncodeString(w, k) + }, + func(w []byte, v []byte) []byte { + return buf.EncodeBytes(w, v) + }, + ) + }) + w = buf.EncodeField(w, 15, func (w []byte) []byte { + return buf.EncodeHashSet[string](w, v.F15.Get(), func(w []byte, v string) []byte { + return buf.EncodeString(w, v) + }) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundF01 := false + foundF02 := false + foundF03 := false + foundF04 := false + foundF05 := false + foundF06 := false + foundF07 := false + foundF08 := false + foundF09 := false + foundF10 := false + foundF11 := false + foundF12 := false + foundF13 := false + foundF14 := false + foundF15 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeNonZeroU8(r) + if err != nil { + return nil, err + } + r = r2 + v.F01 = value + foundF01 = true + case 2: + r2, value, err := buf.DecodeNonZeroU16(r) + if err != nil { + return nil, err + } + r = r2 + v.F02 = value + foundF02 = true + case 3: + r2, value, err := buf.DecodeNonZeroU32(r) + if err != nil { + return nil, err + } + r = r2 + v.F03 = value + foundF03 = true + case 4: + r2, value, err := buf.DecodeNonZeroU64(r) + if err != nil { + return nil, err + } + r = r2 + v.F04 = value + foundF04 = true + case 5: + r2, value, err := buf.DecodeNonZeroU128(r) + if err != nil { + return nil, err + } + r = r2 + v.F05 = value + foundF05 = true + case 6: + r2, value, err := buf.DecodeNonZeroI8(r) + if err != nil { + return nil, err + } + r = r2 + v.F06 = value + foundF06 = true + case 7: + r2, value, err := buf.DecodeNonZeroI16(r) + if err != nil { + return nil, err + } + r = r2 + v.F07 = value + foundF07 = true + case 8: + r2, value, err := buf.DecodeNonZeroI32(r) + if err != nil { + return nil, err + } + r = r2 + v.F08 = value + foundF08 = true + case 9: + r2, value, err := buf.DecodeNonZeroI64(r) + if err != nil { + return nil, err + } + r = r2 + v.F09 = value + foundF09 = true + case 10: + r2, value, err := buf.DecodeNonZeroI128(r) + if err != nil { + return nil, err + } + r = r2 + v.F10 = value + foundF10 = true + case 11: + r2, value, err := buf.DecodeNonZeroString(r) + if err != nil { + return nil, err + } + r = r2 + v.F11 = value + foundF11 = true + case 12: + r2, value, err := buf.DecodeNonZeroBytes(r) + if err != nil { + return nil, err + } + r = r2 + v.F12 = value + foundF12 = true + case 13: + r2, value, err := buf.DecodeNonZeroVec[string](r, func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F13 = value + foundF13 = true + case 14: + r2, value, err := buf.DecodeNonZeroHashMap[string, []byte]( + r, + func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }, + func(r []byte) ([]byte, []byte, error) { + return buf.DecodeBytes(r) + }, + ) + if err != nil { + return nil, err + } + r = r2 + v.F14 = value + foundF14 = true + case 15: + r2, value, err := buf.DecodeNonZeroHashSet[string](r, func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.F15 = value + foundF15 = true + case buf.EndMarker: + break + } + } + + if !foundF01 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "f01" + } + } + if !foundF02 { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "f02" + } + } + if !foundF03 { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "f03" + } + } + if !foundF04 { + return nil, buf.MissingFieldError{ + ID: 4 + Field: "f04" + } + } + if !foundF05 { + return nil, buf.MissingFieldError{ + ID: 5 + Field: "f05" + } + } + if !foundF06 { + return nil, buf.MissingFieldError{ + ID: 6 + Field: "f06" + } + } + if !foundF07 { + return nil, buf.MissingFieldError{ + ID: 7 + Field: "f07" + } + } + if !foundF08 { + return nil, buf.MissingFieldError{ + ID: 8 + Field: "f08" + } + } + if !foundF09 { + return nil, buf.MissingFieldError{ + ID: 9 + Field: "f09" + } + } + if !foundF10 { + return nil, buf.MissingFieldError{ + ID: 10 + Field: "f10" + } + } + if !foundF11 { + return nil, buf.MissingFieldError{ + ID: 11 + Field: "f11" + } + } + if !foundF12 { + return nil, buf.MissingFieldError{ + ID: 12 + Field: "f12" + } + } + if !foundF13 { + return nil, buf.MissingFieldError{ + ID: 13 + Field: "f13" + } + } + if !foundF14 { + return nil, buf.MissingFieldError{ + ID: 14 + Field: "f14" + } + } + if !foundF15 { + return nil, buf.MissingFieldError{ + ID: 15 + Field: "f15" + } + } + + return r, nil +} + + diff --git a/crates/stef-go/tests/snapshots/render__render@types_ref.stef.snap b/crates/stef-go/tests/snapshots/render__render@types_ref.stef.snap new file mode 100644 index 0000000..1b11c7e --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@types_ref.stef.snap @@ -0,0 +1,232 @@ +--- +source: crates/stef-go/tests/render.rs +description: "struct Sample {\n basic: Test123 @1,\n with_generics: KeyValue @2,\n}\n\nenum Test123 {\n Value @1,\n}\n\nstruct KeyValue {\n key: K @1,\n value: V @2,\n}" +input_file: crates/stef-parser/tests/inputs/types_ref.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +type Sample struct { + Basic Test123 + WithGenerics KeyValue[uint32, bool] +} + +func NewSample( + basic Test123, + withGenerics KeyValue[uint32, bool], +) Sample { + return Sample{ + Basic: basic, + WithGenerics: withGenerics, + } +} + +var _ buf.Encode = (*Sample)(nil) + +func (v *Sample) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return v.Basic.Encode(w) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.WithGenerics.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Sample)(nil) + +func (v *Sample) Decode(r []byte) ([]byte, error) { + foundBasic := false + foundWithGenerics := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := func(r []byte) ([]byte, Test123, error) { + var value Test123 + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Basic = value + foundBasic = true + case 2: + r2, value, err := func(r []byte) ([]byte, KeyValue[uint32, bool], error) { + var value KeyValue[uint32, bool] + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.WithGenerics = value + foundWithGenerics = true + case buf.EndMarker: + break + } + } + + if !foundBasic { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "basic" + } + } + if !foundWithGenerics { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "with_generics" + } + } + + return r, nil +} + +type Test123Variant interface { + sealed() +} + +type Test123 Test123Variant + +type Test123_Value struct{} + +func (v Test123_Value) sealed() {} + +func NewTest123_Value() Test123_Value { + return Test123_Value{} +} + +var _ buf.Encode = (*Test123_Value)(nil) + +func (v *Test123_Value) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Test123_Value)(nil) + +func (v *Test123_Value) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +type KeyValue[K any, V any] struct { + Key K + Value V +} + +func NewKeyValue[K any, V any]( + key K, + value V, +) KeyValue[K, V] { + return KeyValue[K, V]{ + Key: key, + Value: value, + } +} + +var _ buf.Encode = (*KeyValue[K, V])(nil) + +func (v *KeyValue[K, V]) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return v.Key.Encode(w) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.Value.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*KeyValue[K, V])(nil) + +func (v *KeyValue[K, V]) Decode(r []byte) ([]byte, error) { + foundKey := false + foundValue := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := func(r []byte) ([]byte, K, error) { + var value K + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Key = value + foundKey = true + case 2: + r2, value, err := func(r []byte) ([]byte, V, error) { + var value V + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Value = value + foundValue = true + case buf.EndMarker: + break + } + } + + if !foundKey { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "key" + } + } + if !foundValue { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "value" + } + } + + return r, nil +} + + diff --git a/crates/stef-parser/src/lib.rs b/crates/stef-parser/src/lib.rs index ac28aaf..0c96acf 100644 --- a/crates/stef-parser/src/lib.rs +++ b/crates/stef-parser/src/lib.rs @@ -943,6 +943,12 @@ impl<'a> From<(&'a str, Range)> for Name<'a> { } } +impl AsRef for Name<'_> { + fn as_ref(&self) -> &str { + self.value + } +} + /// Declaration of a constant value. #[derive(Debug, PartialEq)] pub struct Const<'a> { diff --git a/crates/stef-parser/tests/inputs/module_basic.stef b/crates/stef-parser/tests/inputs/module_basic.stef index 3db5a89..5c1c582 100644 --- a/crates/stef-parser/tests/inputs/module_basic.stef +++ b/crates/stef-parser/tests/inputs/module_basic.stef @@ -8,5 +8,6 @@ mod a { struct Sample { value: u32 @1, + inner: b::Sample @2, } } diff --git a/crates/stef-parser/tests/snapshots/parser__parse@module_basic.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@module_basic.stef.snap index bcd758b..be5dc71 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@module_basic.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@module_basic.stef.snap @@ -1,13 +1,13 @@ --- source: crates/stef-parser/tests/parser.rs -description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n }\n}" +description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n inner: b::Sample @2,\n }\n}" input_file: crates/stef-parser/tests/inputs/module_basic.stef --- Schema { path: Some( "module_basic.stef", ), - source: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n }\n}\n", + source: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n inner: b::Sample @2,\n }\n}\n", definitions: [ Module( Module { @@ -136,6 +136,56 @@ Schema { end: 141, }, }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "inner", + span: Span { + start: 151, + end: 156, + }, + }, + ty: Type { + value: External( + ExternalType { + path: [ + Name { + value: "b", + span: Span { + start: 158, + end: 159, + }, + }, + ], + name: Name { + value: "Sample", + span: Span { + start: 161, + end: 167, + }, + }, + generics: [], + }, + ), + span: Span { + start: 158, + end: 167, + }, + }, + id: Id { + value: 2, + span: Span { + start: 168, + end: 170, + }, + }, + span: Span { + start: 151, + end: 170, + }, + }, ], ), }, diff --git a/crates/stef-parser/tests/snapshots/parser__print@module_basic.stef.snap b/crates/stef-parser/tests/snapshots/parser__print@module_basic.stef.snap index 02af4d4..8f9b86f 100644 --- a/crates/stef-parser/tests/snapshots/parser__print@module_basic.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__print@module_basic.stef.snap @@ -1,6 +1,6 @@ --- source: crates/stef-parser/tests/parser.rs -description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n }\n}" +description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n inner: b::Sample @2,\n }\n}" input_file: crates/stef-parser/tests/inputs/module_basic.stef --- mod a { @@ -13,6 +13,7 @@ mod a { struct Sample { value: u32 @1, + inner: b::Sample @2, } } diff --git a/crates/stef-playground/build.rs b/crates/stef-playground/build.rs index e2c6d97..ce76c7a 100644 --- a/crates/stef-playground/build.rs +++ b/crates/stef-playground/build.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + fn main() -> stef_build::Result<()> { let compiler = stef_build::Compiler::default(); compiler.compile(&["src/sample.stef"])?;