From 85938a4a7532d034b7eccbea1643a95a84434954 Mon Sep 17 00:00:00 2001 From: Dominik Nakamura Date: Wed, 13 Sep 2023 17:42:42 +0900 Subject: [PATCH] test: enable more snapshot tests and fix errors --- CHANGELOG.md | 10 ++ crates/stef-build/src/decode.rs | 26 +++- crates/stef-build/src/definition.rs | 17 ++- crates/stef-build/src/encode.rs | 26 +++- crates/stef-build/tests/parser.rs | 4 +- .../parser__compile@alias-basic.stef.snap | 8 + .../parser__compile@attribute-multi.stef.snap | 21 +++ ...parser__compile@attribute-single.stef.snap | 21 +++ .../parser__compile@attribute-unit.stef.snap | 21 +++ ...arser__compile@attributes-min-ws.stef.snap | 21 +++ .../parser__compile@attributes.stef.snap | 21 +++ .../parser__compile@const-basic.stef.snap | 12 ++ .../parser__compile@const-string.stef.snap | 10 ++ .../parser__compile@enum-basic.stef.snap | 110 ++++++++++++++ .../parser__compile@enum-generics.stef.snap | 109 ++++++++++++++ .../parser__compile@enum-many-ws.stef.snap | 105 +++++++++++++ .../parser__compile@enum-min-ws.stef.snap | 126 ++++++++++++++++ .../parser__compile@import-basic.stef.snap | 8 + .../parser__compile@module-basic.stef.snap | 70 +++++++++ .../parser__compile@schema-basic.stef.snap | 142 ++++++++++++++++++ .../parser__compile@struct-basic.stef.snap | 44 ++++++ .../parser__compile@struct-generics.stef.snap | 51 +++++++ .../parser__compile@struct-many-ws.stef.snap | 51 +++++++ .../parser__compile@struct-min-ws.stef.snap | 57 +++++++ 24 files changed, 1075 insertions(+), 16 deletions(-) create mode 100644 crates/stef-build/tests/snapshots/parser__compile@alias-basic.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@attribute-multi.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@attribute-single.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@attribute-unit.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@attributes-min-ws.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@attributes.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@const-basic.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@const-string.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@enum-basic.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@enum-generics.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@enum-many-ws.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@enum-min-ws.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@import-basic.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@module-basic.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@schema-basic.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@struct-basic.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@struct-generics.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@struct-many-ws.stef.snap create mode 100644 crates/stef-build/tests/snapshots/parser__compile@struct-min-ws.stef.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 728b66b..640a9ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ All notable changes to this project will be documented in this file. - Implement encoding in the Rust codegen ([2787b51](https://github.com/dnaka91/wazzup/commit/2787b51c04803311bf5ca3160b37e7db31d5a8ea)) - Simplify encoding logic ([e8205bc](https://github.com/dnaka91/wazzup/commit/e8205bcb6749fce6dd1f56ede38128076820bffd)) +- Implement basic decoding logic ([f67c572](https://github.com/dnaka91/wazzup/commit/f67c57220ac2c57961bf54f7f47bca467d3fb20b)) + +### ๐Ÿ› Bug Fixes + +- Don't double wrap optional types in decode ([a6d3d4b](https://github.com/dnaka91/wazzup/commit/a6d3d4bde28d28acb0afba123949ed7e5cbfeb98)) ### ๐Ÿ“š Documentation @@ -21,6 +26,11 @@ All notable changes to this project will be documented in this file. - Generate definitions and impls together ([b32bcfd](https://github.com/dnaka91/wazzup/commit/b32bcfd8630bc445421ce32b784de6601659aade)) +### ๐Ÿงช Testing + +- Add snapshot tests to stef-build ([1313fe9](https://github.com/dnaka91/wazzup/commit/1313fe9f99cceee8a883791c99e318768e27f801)) +- Enable more snapshot tests and fix errors ([8ac6f73](https://github.com/dnaka91/wazzup/commit/8ac6f7369a3aa95b0708e9ad02b2e5ff3f496280)) + ### โš™๏ธ Miscellaneous Tasks - Initial commit ([5eb2f2b](https://github.com/dnaka91/wazzup/commit/5eb2f2b9687146363974ea645de22a8441e890a1)) diff --git a/crates/stef-build/src/decode.rs b/crates/stef-build/src/decode.rs index 3bbf56d..88e665d 100644 --- a/crates/stef-build/src/decode.rs +++ b/crates/stef-build/src/decode.rs @@ -1,23 +1,24 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; -use stef_parser::{DataType, Enum, Fields, NamedField, Struct, UnnamedField, Variant}; +use stef_parser::{DataType, Enum, Fields, Generics, NamedField, Struct, UnnamedField, Variant}; pub fn compile_struct( Struct { comment: _, attributes: _, name, - generics: _, + generics, fields, }: &Struct<'_>, ) -> TokenStream { let name = Ident::new(name, Span::call_site()); + let (generics, generics_where) = compile_generics(generics); let field_vars = compile_field_vars(fields); let field_matches = compile_field_matches(fields); let field_assigns = compile_field_assigns(fields); quote! { - impl ::stef::Decode for #name { + impl #generics ::stef::Decode for #name #generics #generics_where { fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { #field_vars @@ -40,15 +41,16 @@ pub fn compile_enum( comment: _, attributes: _, name, - generics: _, + generics, variants, }: &Enum<'_>, ) -> TokenStream { let name = Ident::new(name, Span::call_site()); + let (generics, generics_where) = compile_generics(generics); let variants = variants.iter().map(compile_variant); quote! { - impl ::stef::Decode for #name { + impl #generics ::stef::Decode for #name #generics #generics_where { fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { match ::stef::buf::decode_id(r)? { #(#variants,)* @@ -195,6 +197,20 @@ fn compile_field_assigns(fields: &Fields<'_>) -> TokenStream { } } +fn compile_generics(Generics(types): &Generics<'_>) -> (TokenStream, TokenStream) { + (!types.is_empty()) + .then(|| { + let types = types.iter().map(|ty| Ident::new(ty, Span::call_site())); + let types2 = types.clone(); + + ( + quote! { <#(#types,)*> }, + quote! { where #(#types2: ::stef::buf::Decode,)* }, + ) + }) + .unwrap_or_default() +} + #[allow(clippy::needless_pass_by_value)] fn compile_data_type(ty: &DataType<'_>) -> TokenStream { match ty { diff --git a/crates/stef-build/src/definition.rs b/crates/stef-build/src/definition.rs index 39d237f..baa562d 100644 --- a/crates/stef-build/src/definition.rs +++ b/crates/stef-build/src/definition.rs @@ -186,10 +186,13 @@ fn compile_comment(Comment(lines): &Comment<'_>) -> TokenStream { } fn compile_generics(Generics(types): &Generics<'_>) -> Option { - (!types.is_empty()).then(|| quote! { <#(#types,)*> }) + (!types.is_empty()).then(|| { + let types = types.iter().map(|ty| Ident::new(ty, Span::call_site())); + quote! { <#(#types,)*> } + }) } -fn compile_fields(fields: &Fields<'_>, public: bool) -> TokenStream { +fn compile_fields(fields: &Fields<'_>, for_struct: bool) -> TokenStream { match fields { Fields::Named(named) => { let fields = named.iter().map( @@ -200,7 +203,7 @@ fn compile_fields(fields: &Fields<'_>, public: bool) -> TokenStream { id: _, }| { let comment = compile_comment(comment); - let public = public.then(|| quote! { pub }); + let public = for_struct.then(|| quote! { pub }); let name = Ident::new(name, Span::call_site()); let ty = compile_data_type(ty); quote! { @@ -222,7 +225,13 @@ fn compile_fields(fields: &Fields<'_>, public: bool) -> TokenStream { quote! { (#(#fields,)*) } } - Fields::Unit => quote! {}, + Fields::Unit => { + if for_struct { + quote! { ; } + } else { + quote! {} + } + } } } diff --git a/crates/stef-build/src/encode.rs b/crates/stef-build/src/encode.rs index f2589c4..e1b7e52 100644 --- a/crates/stef-build/src/encode.rs +++ b/crates/stef-build/src/encode.rs @@ -1,21 +1,22 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; -use stef_parser::{DataType, Enum, Fields, NamedField, Struct, UnnamedField, Variant}; +use stef_parser::{DataType, Enum, Fields, Generics, NamedField, Struct, UnnamedField, Variant}; pub fn compile_struct( Struct { comment: _, attributes: _, name, - generics: _, + generics, fields, }: &Struct<'_>, ) -> TokenStream { let name = Ident::new(name, Span::call_site()); + let (generics, generics_where) = compile_generics(generics); let fields = compile_struct_fields(fields); quote! { - impl ::stef::Encode for #name { + impl #generics ::stef::Encode for #name #generics #generics_where { fn encode(&self, w: &mut impl ::stef::BufMut) { #fields } @@ -66,15 +67,16 @@ pub fn compile_enum( comment: _, attributes: _, name, - generics: _, + generics, variants, }: &Enum<'_>, ) -> TokenStream { let name = Ident::new(name, Span::call_site()); + let (generics, generics_where) = compile_generics(generics); let variants = variants.iter().map(compile_variant); quote! { - impl ::stef::Encode for #name { + impl #generics ::stef::Encode for #name #generics #generics_where { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { #(#variants,)* @@ -169,6 +171,20 @@ fn compile_variant_fields(fields: &Fields<'_>) -> TokenStream { } } +fn compile_generics(Generics(types): &Generics<'_>) -> (TokenStream, TokenStream) { + (!types.is_empty()) + .then(|| { + let types = types.iter().map(|ty| Ident::new(ty, Span::call_site())); + let types2 = types.clone(); + + ( + quote! { <#(#types,)*> }, + quote! { where #(#types2: ::stef::buf::Encode,)* }, + ) + }) + .unwrap_or_default() +} + #[allow(clippy::needless_pass_by_value)] fn compile_data_type(ty: &DataType<'_>, name: TokenStream) -> TokenStream { match ty { diff --git a/crates/stef-build/tests/parser.rs b/crates/stef-build/tests/parser.rs index fc9afe0..465eade 100644 --- a/crates/stef-build/tests/parser.rs +++ b/crates/stef-build/tests/parser.rs @@ -5,11 +5,11 @@ use stef_parser::Schema; #[test] fn compile_schema() { - glob!("inputs/types-*.stef", |path| { + glob!("inputs/*.stef", |path| { let input = fs::read_to_string(path).unwrap(); let value = Schema::parse(input.as_str()).unwrap(); let value = stef_build::compile_schema(&value); - let value = prettyplease::unparse(&syn::parse2(value).unwrap()); + let value = prettyplease::unparse(&syn::parse2(value.clone()).unwrap()); assert_snapshot!("compile", format!("{value}"), input.trim()); }); diff --git a/crates/stef-build/tests/snapshots/parser__compile@alias-basic.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@alias-basic.stef.snap new file mode 100644 index 0000000..6c97e57 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@alias-basic.stef.snap @@ -0,0 +1,8 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// Sample type alias.\ntype Sample = u32;" +input_file: crates/stef-parser/tests/inputs/alias-basic.stef +--- +/// Sample type alias. +pub type Sample = u32; + diff --git a/crates/stef-build/tests/snapshots/parser__compile@attribute-multi.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@attribute-multi.stef.snap new file mode 100644 index 0000000..45fb7a6 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@attribute-multi.stef.snap @@ -0,0 +1,21 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "#[validate(min = 1, max = 100)]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute-multi.stef +--- +pub struct Sample; +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) {} +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@attribute-single.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@attribute-single.stef.snap new file mode 100644 index 0000000..1c91dc3 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@attribute-single.stef.snap @@ -0,0 +1,21 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "#[deprecated = \"don't use\"]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute-single.stef +--- +pub struct Sample; +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) {} +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@attribute-unit.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@attribute-unit.stef.snap new file mode 100644 index 0000000..db93be7 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@attribute-unit.stef.snap @@ -0,0 +1,21 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "#[deprecated]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute-unit.stef +--- +pub struct Sample; +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) {} +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@attributes-min-ws.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@attributes-min-ws.stef.snap new file mode 100644 index 0000000..28f80af --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@attributes-min-ws.stef.snap @@ -0,0 +1,21 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "#[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 +--- +pub struct Sample; +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) {} +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@attributes.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@attributes.stef.snap new file mode 100644 index 0000000..0ceb36b --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@attributes.stef.snap @@ -0,0 +1,21 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "#[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 +--- +pub struct Sample; +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) {} +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@const-basic.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@const-basic.stef.snap new file mode 100644 index 0000000..58cf2f0 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@const-basic.stef.snap @@ -0,0 +1,12 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "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 +--- +const BOOL_TRUE: bool = true; +const BOOL_FALSE: bool = false; +const INT: u32 = 100; +const FLOAT: f64 = 5.5; +const STRING: &str = "value"; +const BYTES: &[u8] = b"\x01\x02\x03"; + diff --git a/crates/stef-build/tests/snapshots/parser__compile@const-string.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@const-string.stef.snap new file mode 100644 index 0000000..01a1409 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@const-string.stef.snap @@ -0,0 +1,10 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "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 +--- +const SIMPLE: &str = "value"; +const NEWLINE_ESCAPE: &str = "one two three"; +const ESCAPES: &str = "escape basics \r\n \t \u{8} \u{c} \\ \"hello\" \nunicode โค emoji โค "; +const MULTILINE: &str = "a\n b\n c\n"; + diff --git a/crates/stef-build/tests/snapshots/parser__compile@enum-basic.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@enum-basic.stef.snap new file mode 100644 index 0000000..ca2594a --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@enum-basic.stef.snap @@ -0,0 +1,110 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// 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 enum. +pub enum Sample { + One, + /// Second variant + Two(u32, u64), + Three { + field1: u32, + /// Second field of third variant + field2: bool, + }, +} +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::One => { + ::stef::buf::encode_id(w, 1); + } + Self::Two(n0, n1) => { + ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, n0) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, n1) }); + } + Self::Three { field1, field2 } => { + ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_field( + w, + 1, + |w| { ::stef::buf::encode_u32(w, field1) }, + ); + ::stef::buf::encode_field( + w, + 2, + |w| { ::stef::buf::encode_bool(w, field2) }, + ); + } + } + } +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self::One) + } + 2 => { + let mut n0: Option = None; + let mut n1: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => n0 = Some(::stef::buf::decode_u32(r)?), + 2 => n1 = Some(::stef::buf::decode_u64(r)?), + _ => continue, + } + } + Ok( + Self::Two( + n0 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: None, + }), + n1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: None, + }), + ), + ) + } + 3 => { + let mut field1: Option = None; + let mut field2: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => field1 = Some(::stef::buf::decode_u32(r)?), + 2 => field2 = Some(::stef::buf::decode_bool(r)?), + _ => continue, + } + } + Ok(Self::Three { + field1: field1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("field1"), + }), + field2: field2 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("field2"), + }), + }) + } + id => Err(Error::UnknownVariant(id)), + } + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@enum-generics.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@enum-generics.stef.snap new file mode 100644 index 0000000..c300d7a --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@enum-generics.stef.snap @@ -0,0 +1,109 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// 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 +--- +/// Enum with generics. +pub enum Sample { + One, + Two(A, B), + Three { field1: C, field2: D }, +} +impl ::stef::Encode for Sample +where + A: ::stef::buf::Encode, + B: ::stef::buf::Encode, + C: ::stef::buf::Encode, + D: ::stef::buf::Encode, +{ + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::One => { + ::stef::buf::encode_id(w, 1); + } + Self::Two(n0, n1) => { + ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_field(w, 1, |w| { n0.encode(w) }); + ::stef::buf::encode_field(w, 2, |w| { n1.encode(w) }); + } + Self::Three { field1, field2 } => { + ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_field(w, 1, |w| { field1.encode(w) }); + ::stef::buf::encode_field(w, 2, |w| { field2.encode(w) }); + } + } + } +} +impl ::stef::Decode for Sample +where + A: ::stef::buf::Decode, + B: ::stef::buf::Decode, + C: ::stef::buf::Decode, + D: ::stef::buf::Decode, +{ + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self::One) + } + 2 => { + let mut n0: Option = None; + let mut n1: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => n0 = Some(A::decode(r)?), + 2 => n1 = Some(B::decode(r)?), + _ => continue, + } + } + Ok( + Self::Two( + n0 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: None, + }), + n1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: None, + }), + ), + ) + } + 3 => { + let mut field1: Option = None; + let mut field2: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => field1 = Some(C::decode(r)?), + 2 => field2 = Some(D::decode(r)?), + _ => continue, + } + } + Ok(Self::Three { + field1: field1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("field1"), + }), + field2: field2 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("field2"), + }), + }) + } + id => Err(Error::UnknownVariant(id)), + } + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@enum-many-ws.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@enum-many-ws.stef.snap new file mode 100644 index 0000000..2fecc09 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@enum-many-ws.stef.snap @@ -0,0 +1,105 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// 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 enum. +pub enum Sample { + One, + Two(u32, u64), + Three { field1: u32, field2: bool }, +} +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::One => { + ::stef::buf::encode_id(w, 1); + } + Self::Two(n0, n1) => { + ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, n0) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, n1) }); + } + Self::Three { field1, field2 } => { + ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_field( + w, + 1, + |w| { ::stef::buf::encode_u32(w, field1) }, + ); + ::stef::buf::encode_field( + w, + 2, + |w| { ::stef::buf::encode_bool(w, field2) }, + ); + } + } + } +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self::One) + } + 2 => { + let mut n0: Option = None; + let mut n1: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => n0 = Some(::stef::buf::decode_u32(r)?), + 2 => n1 = Some(::stef::buf::decode_u64(r)?), + _ => continue, + } + } + Ok( + Self::Two( + n0 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: None, + }), + n1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: None, + }), + ), + ) + } + 3 => { + let mut field1: Option = None; + let mut field2: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => field1 = Some(::stef::buf::decode_u32(r)?), + 2 => field2 = Some(::stef::buf::decode_bool(r)?), + _ => continue, + } + } + Ok(Self::Three { + field1: field1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("field1"), + }), + field2: field2 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("field2"), + }), + }) + } + id => Err(Error::UnknownVariant(id)), + } + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@enum-min-ws.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@enum-min-ws.stef.snap new file mode 100644 index 0000000..0f99fb2 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@enum-min-ws.stef.snap @@ -0,0 +1,126 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "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 +--- +pub enum Sample { + One, + Two(u32, u64, T), + Three { field1: u32, field2: bool, field3: T }, +} +impl ::stef::Encode for Sample +where + T: ::stef::buf::Encode, +{ + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::One => { + ::stef::buf::encode_id(w, 1); + } + Self::Two(n0, n1, n2) => { + ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, n0) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, n1) }); + ::stef::buf::encode_field(w, 3, |w| { n2.encode(w) }); + } + Self::Three { field1, field2, field3 } => { + ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_field( + w, + 1, + |w| { ::stef::buf::encode_u32(w, field1) }, + ); + ::stef::buf::encode_field( + w, + 2, + |w| { ::stef::buf::encode_bool(w, field2) }, + ); + ::stef::buf::encode_field(w, 3, |w| { field3.encode(w) }); + } + } + } +} +impl ::stef::Decode for Sample +where + T: ::stef::buf::Decode, +{ + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self::One) + } + 2 => { + let mut n0: Option = None; + let mut n1: Option = None; + let mut n2: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => n0 = Some(::stef::buf::decode_u32(r)?), + 2 => n1 = Some(::stef::buf::decode_u64(r)?), + 3 => n2 = Some(T::decode(r)?), + _ => continue, + } + } + Ok( + Self::Two( + n0 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: None, + }), + n1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: None, + }), + n2 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 3, + name: None, + }), + ), + ) + } + 3 => { + let mut field1: Option = None; + let mut field2: Option = None; + let mut field3: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => field1 = Some(::stef::buf::decode_u32(r)?), + 2 => field2 = Some(::stef::buf::decode_bool(r)?), + 3 => field3 = Some(T::decode(r)?), + _ => continue, + } + } + Ok(Self::Three { + field1: field1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("field1"), + }), + field2: field2 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("field2"), + }), + field3: field3 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 3, + name: Some("field3"), + }), + }) + } + id => Err(Error::UnknownVariant(id)), + } + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@import-basic.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@import-basic.stef.snap new file mode 100644 index 0000000..0937da4 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@import-basic.stef.snap @@ -0,0 +1,8 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "use other::schema::Sample;\nuse second::submodule;" +input_file: crates/stef-parser/tests/inputs/import-basic.stef +--- +use other::schema::Sample; +use second::submodule; + diff --git a/crates/stef-build/tests/snapshots/parser__compile@module-basic.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@module-basic.stef.snap new file mode 100644 index 0000000..cb47d2a --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@module-basic.stef.snap @@ -0,0 +1,70 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "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}" +input_file: crates/stef-parser/tests/inputs/module-basic.stef +--- +pub mod a { + /// Inner module + pub mod b { + pub enum Sample { + One, + } + impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::One => { + ::stef::buf::encode_id(w, 1); + } + } + } + } + impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self::One) + } + id => Err(Error::UnknownVariant(id)), + } + } + } + } + pub struct Sample { + pub value: u32, + } + impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field( + w, + 1, + |w| { ::stef::buf::encode_u32(w, self.value) }, + ); + } + } + impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut value: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => value = Some(::stef::buf::decode_u32(r)?), + _ => continue, + } + } + Ok(Self { + value: value + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("value"), + }), + }) + } + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@schema-basic.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@schema-basic.stef.snap new file mode 100644 index 0000000..6f3c7e9 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@schema-basic.stef.snap @@ -0,0 +1,142 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// Basic struct.\nstruct Sample {\n a: u32 @1,\n b: bool @2,\n}\n\n/// Sample enum.\nenum Sample {\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 +--- +/// Basic struct. +pub struct Sample { + pub a: u32, + pub b: bool, +} +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, self.a) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_bool(w, self.b) }); + } +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut a: Option = None; + let mut b: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => a = Some(::stef::buf::decode_u32(r)?), + 2 => b = Some(::stef::buf::decode_bool(r)?), + _ => continue, + } + } + Ok(Self { + a: a + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("a"), + }), + b: b + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("b"), + }), + }) + } +} +/// Sample enum. +pub enum Sample { + One, + Two(u32, u64), + Three { field1: u32, field2: bool }, +} +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::One => { + ::stef::buf::encode_id(w, 1); + } + Self::Two(n0, n1) => { + ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, n0) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, n1) }); + } + Self::Three { field1, field2 } => { + ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_field( + w, + 1, + |w| { ::stef::buf::encode_u32(w, field1) }, + ); + ::stef::buf::encode_field( + w, + 2, + |w| { ::stef::buf::encode_bool(w, field2) }, + ); + } + } + } +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + _ => continue, + } + } + Ok(Self::One) + } + 2 => { + let mut n0: Option = None; + let mut n1: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => n0 = Some(::stef::buf::decode_u32(r)?), + 2 => n1 = Some(::stef::buf::decode_u64(r)?), + _ => continue, + } + } + Ok( + Self::Two( + n0 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: None, + }), + n1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: None, + }), + ), + ) + } + 3 => { + let mut field1: Option = None; + let mut field2: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => field1 = Some(::stef::buf::decode_u32(r)?), + 2 => field2 = Some(::stef::buf::decode_bool(r)?), + _ => continue, + } + } + Ok(Self::Three { + field1: field1 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("field1"), + }), + field2: field2 + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("field2"), + }), + }) + } + id => Err(Error::UnknownVariant(id)), + } + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@struct-basic.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@struct-basic.stef.snap new file mode 100644 index 0000000..61b3709 --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@struct-basic.stef.snap @@ -0,0 +1,44 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// 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 +--- +/// Basic struct. +pub struct Sample { + pub a: u32, + /// Second field + pub b: bool, +} +impl ::stef::Encode for Sample { + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, self.a) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_bool(w, self.b) }); + } +} +impl ::stef::Decode for Sample { + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut a: Option = None; + let mut b: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => a = Some(::stef::buf::decode_u32(r)?), + 2 => b = Some(::stef::buf::decode_bool(r)?), + _ => continue, + } + } + Ok(Self { + a: a + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("a"), + }), + b: b + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("b"), + }), + }) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@struct-generics.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@struct-generics.stef.snap new file mode 100644 index 0000000..9e6455c --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@struct-generics.stef.snap @@ -0,0 +1,51 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// 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 +--- +/// Generic key-value pair. +pub struct KeyValue { + pub key: K, + pub value: V, +} +impl ::stef::Encode for KeyValue +where + K: ::stef::buf::Encode, + V: ::stef::buf::Encode, +{ + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field(w, 1, |w| { self.key.encode(w) }); + ::stef::buf::encode_field(w, 2, |w| { self.value.encode(w) }); + } +} +impl ::stef::Decode for KeyValue +where + K: ::stef::buf::Decode, + V: ::stef::buf::Decode, +{ + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut key: Option = None; + let mut value: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => key = Some(K::decode(r)?), + 2 => value = Some(V::decode(r)?), + _ => continue, + } + } + Ok(Self { + key: key + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("key"), + }), + value: value + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("value"), + }), + }) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@struct-many-ws.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@struct-many-ws.stef.snap new file mode 100644 index 0000000..e927bfa --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@struct-many-ws.stef.snap @@ -0,0 +1,51 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "/// Some comment\n struct Sample<\n A,\n B\n > {\n\n a: u32 @1,\n b: bool @2,\n\n }" +input_file: crates/stef-parser/tests/inputs/struct-many-ws.stef +--- +/// Some comment +pub struct Sample { + pub a: u32, + pub b: bool, +} +impl ::stef::Encode for Sample +where + A: ::stef::buf::Encode, + B: ::stef::buf::Encode, +{ + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, self.a) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_bool(w, self.b) }); + } +} +impl ::stef::Decode for Sample +where + A: ::stef::buf::Decode, + B: ::stef::buf::Decode, +{ + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut a: Option = None; + let mut b: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => a = Some(::stef::buf::decode_u32(r)?), + 2 => b = Some(::stef::buf::decode_bool(r)?), + _ => continue, + } + } + Ok(Self { + a: a + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("a"), + }), + b: b + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("b"), + }), + }) + } +} + diff --git a/crates/stef-build/tests/snapshots/parser__compile@struct-min-ws.stef.snap b/crates/stef-build/tests/snapshots/parser__compile@struct-min-ws.stef.snap new file mode 100644 index 0000000..e3ba05b --- /dev/null +++ b/crates/stef-build/tests/snapshots/parser__compile@struct-min-ws.stef.snap @@ -0,0 +1,57 @@ +--- +source: crates/stef-build/tests/parser.rs +expression: "struct Sample{a:u32@1,b:bool@2,c:T@3}" +input_file: crates/stef-parser/tests/inputs/struct-min-ws.stef +--- +pub struct Sample { + pub a: u32, + pub b: bool, + pub c: T, +} +impl ::stef::Encode for Sample +where + T: ::stef::buf::Encode, +{ + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, self.a) }); + ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_bool(w, self.b) }); + ::stef::buf::encode_field(w, 3, |w| { self.c.encode(w) }); + } +} +impl ::stef::Decode for Sample +where + T: ::stef::buf::Decode, +{ + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut a: Option = None; + let mut b: Option = None; + let mut c: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => a = Some(::stef::buf::decode_u32(r)?), + 2 => b = Some(::stef::buf::decode_bool(r)?), + 3 => c = Some(T::decode(r)?), + _ => continue, + } + } + Ok(Self { + a: a + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 1, + name: Some("a"), + }), + b: b + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 2, + name: Some("b"), + }), + c: c + .unwrap_or_else(|| ::stef::buf::Error::MissingField { + id: 3, + name: Some("c"), + }), + }) + } +} +