diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f9d9be5..37a5cd7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -23,7 +23,6 @@ version = "0.1.0" dependencies = [ "mammoth", "yunq", - "yunq-derive", "yunqc", ] @@ -60,7 +59,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.72", + "syn", ] [[package]] @@ -96,17 +95,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.72" @@ -156,7 +144,6 @@ dependencies = [ "mammoth", "yellowstone-yunq", "yunq", - "yunq-derive", "yunqc", ] @@ -167,7 +154,6 @@ dependencies = [ "mammoth", "yellowstone-yunq", "yunq", - "yunq-derive", "yunqc", ] @@ -189,7 +175,6 @@ version = "0.1.0" dependencies = [ "mammoth", "yunq", - "yunq-derive", "yunqc", ] @@ -198,16 +183,6 @@ name = "yunq" version = "0.1.0" dependencies = [ "mammoth", - "yunq-derive", -] - -[[package]] -name = "yunq-derive" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", ] [[package]] @@ -216,7 +191,6 @@ version = "0.1.0" dependencies = [ "mammoth", "yunq", - "yunq-derive", "yunqc", ] @@ -228,5 +202,5 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.72", + "syn", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b6db314..c357206 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = [ "lib/denali", - "lib/mammoth", "lib/victoriafalls", "lib/voyageurs", "lib/yellowstone", "lib/yunq", "lib/yunq-derive", "lib/yunq-test", "sys/teton", "sys/yellowstone", "usr/testbed", + "lib/mammoth", "lib/victoriafalls", "lib/voyageurs", "lib/yellowstone", "lib/yunq", "lib/yunq-test", "sys/teton", "sys/yellowstone", "usr/testbed", ] resolver = "2" diff --git a/rust/lib/denali/Cargo.toml b/rust/lib/denali/Cargo.toml index e05782f..2993024 100644 --- a/rust/lib/denali/Cargo.toml +++ b/rust/lib/denali/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] mammoth = { path = "../mammoth" } yunq = {path = "../yunq"} -yunq-derive = {path = "../yunq-derive"} [build-dependencies] yunqc = {path = "../../../yunq/rust"} diff --git a/rust/lib/victoriafalls/Cargo.toml b/rust/lib/victoriafalls/Cargo.toml index aeb0bf1..6aeae9e 100644 --- a/rust/lib/victoriafalls/Cargo.toml +++ b/rust/lib/victoriafalls/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" mammoth = { path = "../mammoth" } yellowstone-yunq = { path = "../yellowstone" } yunq = {path = "../yunq"} -yunq-derive = {path = "../yunq-derive"} [build-dependencies] yunqc = {path = "../../../yunq/rust"} diff --git a/rust/lib/voyageurs/Cargo.toml b/rust/lib/voyageurs/Cargo.toml index b396220..e011e0c 100644 --- a/rust/lib/voyageurs/Cargo.toml +++ b/rust/lib/voyageurs/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" mammoth = { path = "../mammoth" } yellowstone-yunq = { path = "../yellowstone" } yunq = {path = "../yunq"} -yunq-derive = {path = "../yunq-derive"} [build-dependencies] yunqc = {path = "../../../yunq/rust"} diff --git a/rust/lib/yellowstone/Cargo.toml b/rust/lib/yellowstone/Cargo.toml index c9a6299..74c80c4 100644 --- a/rust/lib/yellowstone/Cargo.toml +++ b/rust/lib/yellowstone/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] mammoth = { path = "../mammoth" } yunq = {path = "../yunq"} -yunq-derive = {path = "../yunq-derive"} [build-dependencies] yunqc = {path = "../../../yunq/rust"} diff --git a/rust/lib/yunq-derive/Cargo.toml b/rust/lib/yunq-derive/Cargo.toml deleted file mode 100644 index c17a506..0000000 --- a/rust/lib/yunq-derive/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "yunq-derive" -version = "0.1.0" -edition = "2021" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0", features = ["full", "extra-traits", "printing"] } diff --git a/rust/lib/yunq-derive/src/lib.rs b/rust/lib/yunq-derive/src/lib.rs deleted file mode 100644 index bf99f3b..0000000 --- a/rust/lib/yunq-derive/src/lib.rs +++ /dev/null @@ -1,188 +0,0 @@ -use proc_macro::{self, TokenStream}; -use quote::{quote, ToTokens}; -use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, Ident, Path, Type, TypePath}; - -fn serialize_field(name: &Ident, ind: usize, path: &Path) -> proc_macro2::TokenStream { - if path.is_ident("String") { - quote! { - { - let str_offset = next_extension; - let str_length = self.#name.len() as u32; - - buf.write_str_at(offset + next_extension as usize, &self.#name)?; - next_extension += str_length; - - buf.write_at(yunq::message::field_offset(offset, #ind), str_offset)?; - buf.write_at(yunq::message::field_offset(offset, #ind) + 4, str_length)?; - } - - } - } else if path.is_ident("z_cap_t") { - quote! { - { - let cap_ind = caps.len(); - caps.push(self.#name); - - buf.write_at(yunq::message::field_offset(offset, #ind), cap_ind as u64)?; - } - } - } else if path.is_ident("u64") { - quote! { - { - buf.write_at(yunq::message::field_offset(offset, #ind), self.#name)?; - } - } - } else if path.is_ident("i64") { - quote! { - { - buf.write_at(yunq::message::field_offset(offset, #ind), self.#name)?; - } - } - } else { - quote! { - { - let msg_offset = next_extension; - let msg_length = self.#name.serialize(buf, offset + next_extension as usize, caps)? as u32; - next_extension += msg_length; - - buf.write_at(yunq::message::field_offset(offset, #ind), msg_offset)?; - buf.write_at(yunq::message::field_offset(offset, #ind) + 4, msg_length)?; - } - } - } -} - -fn parse_field(name: &Ident, ind: usize, path: &Path) -> proc_macro2::TokenStream { - if path.is_ident("String") { - quote! { - let #name = { - let str_offset = buf.at::(yunq::message::field_offset(offset, #ind))?; - let str_length = buf.at::(yunq::message::field_offset(offset, #ind) + 4)?; - - buf.str_at(offset + str_offset as usize, str_length as usize)?.to_string() - }; - } - } else if path.is_ident("z_cap_t") { - quote! { - let #name = { - let cap_ind = buf.at::(yunq::message::field_offset(offset, #ind))?; - caps[cap_ind as usize] - }; - } - } else if path.is_ident("u64") { - quote! { - let #name = buf.at::(yunq::message::field_offset(offset, #ind))?; - } - } else if path.is_ident("i64") { - quote! { - let #name = buf.at::(yunq::message::field_offset(offset, #ind))?; - } - } else { - quote! { - let #name = { - let msg_offset = buf.at::(yunq::message::field_offset(offset, #ind))? as usize; - - #path::parse(buf, offset + msg_offset, caps)? - }; - } - } -} - -fn field_names(name: &Ident, _ind: usize, _path: &Path) -> proc_macro2::TokenStream { - quote! { #name } -} - -fn apply_to_struct_fields(input: &DeriveInput, func: fn(&Ident, usize, &Path) -> T) -> Vec { - match &input.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => fields - .named - .iter() - .enumerate() - .map(|(ind, field)| match &field.ty { - Type::Path(TypePath { path, .. }) => { - func(&field.ident.as_ref().unwrap(), ind, path) - } - _ => { - panic!("Unrecognized type: {:?}", field) - } - }) - .collect(), - _ => { - panic!("Unrecognized input (Expected Struct): {:?}", input.data) - } - } -} - -#[proc_macro_derive(YunqMessage)] -pub fn derive_client(input_tokens: TokenStream) -> TokenStream { - let input: DeriveInput = parse_macro_input!(input_tokens); - let ident = input.ident.clone(); - - let prelude = quote! { - impl YunqMessage for #ident - }; - - let num_fields = apply_to_struct_fields(&input, |_, _, _| ()).len(); - - let serializers = apply_to_struct_fields(&input, serialize_field); - let serialize = quote! { - fn serialize( - &self, - buf: &mut yunq::ByteBuffer, - offset: usize, - caps: &mut alloc::vec::Vec, - ) -> Result { - let num_fields = #num_fields; - let core_size: u32 = (yunq::message::MESSAGE_HEADER_SIZE + 8 * num_fields) as u32; - let mut next_extension = core_size; - - #(#serializers)* - - buf.write_at(offset + 0, yunq::message::MESSAGE_IDENT)?; - buf.write_at(offset + 4, core_size)?; - buf.write_at(offset + 8, next_extension)?; - buf.write_at(offset + 12, 0 as u32)?; - Ok(next_extension as usize) - } - }; - - let field_names = apply_to_struct_fields(&input, field_names); - let parsers = apply_to_struct_fields(&input, parse_field); - let parse = quote! { - fn parse( - buf: &yunq::ByteBuffer, - offset: usize, - caps: &alloc::vec::Vec, - ) -> Result - where - Self: Sized, - { - if buf.at::(offset + 0)? != yunq::message::MESSAGE_IDENT { - return Err(ZError::INVALID_ARGUMENT); - } - // TODO: Parse core size. - // TODO: Parse extension size. - // TODO: Check CRC32 - // TODO: Parse options. - - #(#parsers)* - - Ok(Self { - #(#field_names),* - }) - - } - }; - - let output = quote! { - #prelude { - #serialize - - #parse - } - }; - output.into() -} diff --git a/rust/lib/yunq-test/Cargo.toml b/rust/lib/yunq-test/Cargo.toml index 8d550ce..4a4591d 100644 --- a/rust/lib/yunq-test/Cargo.toml +++ b/rust/lib/yunq-test/Cargo.toml @@ -6,9 +6,7 @@ edition = "2021" [dependencies] mammoth = { path = "../mammoth", default-features = false} yunq = {path = "../yunq", default-features = false} -yunq-derive = {path = "../yunq-derive"} [build-dependencies] yunqc = {path = "../../../yunq/rust"} -[dev-dependencies] diff --git a/rust/lib/yunq-test/src/lib.rs b/rust/lib/yunq-test/src/lib.rs index dd2b5be..58825a6 100644 --- a/rust/lib/yunq-test/src/lib.rs +++ b/rust/lib/yunq-test/src/lib.rs @@ -7,6 +7,7 @@ mod tests { use super::*; extern crate std; + use std::println; use std::vec; #[test] @@ -59,4 +60,21 @@ mod tests { Ok(()) } + + #[test] + fn repeated_serialization() -> Result<(), ZError> { + let rep = Repeated { unsigned_ints: vec![0, 1, 3],}; + + let mut buf = ByteBuffer::<1024>::new(); + let mut caps = Vec::new(); + rep.serialize(&mut buf, 0, &mut caps)?; + + let parsed = Repeated::parse(&buf, 0, &caps)?; + + println!("{:?}", parsed.unsigned_ints); + + assert!(parsed == rep); + + Ok(()) + } } diff --git a/rust/lib/yunq/Cargo.toml b/rust/lib/yunq/Cargo.toml index 2a75596..d5c3db0 100644 --- a/rust/lib/yunq/Cargo.toml +++ b/rust/lib/yunq/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] mammoth = {path = "../mammoth", default-features = false} -yunq-derive = {path = "../yunq-derive"} [features] default = ["hosted"] diff --git a/rust/lib/yunq/src/message.rs b/rust/lib/yunq/src/message.rs index 44ca368..eb5dfc6 100644 --- a/rust/lib/yunq/src/message.rs +++ b/rust/lib/yunq/src/message.rs @@ -12,6 +12,57 @@ pub fn field_offset(offset: usize, field_index: usize) -> usize { offset + MESSAGE_HEADER_SIZE + (8 * field_index) } +pub fn parse_repeated( + buf: &ByteBuffer, + offset: usize, + len: usize, +) -> Result, ZError> { + let mut repeated = Vec::new(); + for i in 0..len { + repeated.push(buf.at::(offset + (i * size_of::()))?); + } + Ok(repeated) +} + +pub fn parse_repeated_message( + buf: &ByteBuffer, + mut offset: usize, + len: usize, + caps: &Vec, +) -> Result, ZError> { + let mut repeated = Vec::new(); + for _ in 0..len { + // FIXME: This is a bad way to get the length. + let msg_len = buf.at::(offset + 8)? as usize; + repeated.push(T::parse(buf, offset, caps)?); + offset += msg_len; + } + Ok(repeated) +} + +pub fn serialize_repeated( + buf: &mut ByteBuffer, + offset: usize, + data: &Vec, +) -> Result { + for i in 0..data.len() { + buf.write_at(offset + (i * size_of::()), data[i])?; + } + Ok(offset + (data.len() * size_of::())) +} + +pub fn serialize_repeated_message( + buf: &mut ByteBuffer, + mut offset: usize, + data: &Vec, + caps: &mut Vec, +) -> Result { + for item in data { + offset = item.serialize(buf, offset, caps)?; + } + Ok(offset) +} + pub fn serialize_error(buf: &mut ByteBuffer, err: ZError) { buf.write_at(0, SENTINEL) .expect("Failed to serialize SENTINEL"); diff --git a/yunq/rust/src/codegen.rs b/yunq/rust/src/codegen.rs index 928ec58..00d2a64 100644 --- a/yunq/rust/src/codegen.rs +++ b/yunq/rust/src/codegen.rs @@ -1,4 +1,5 @@ -use crate::parser::{Decl, Interface, Message, Method}; +use crate::parser::{Decl, Interface, Message, Method, Type}; +use crate::parser::{Field, FieldType}; use convert_case::{Case, Casing}; use proc_macro2::Ident; use proc_macro2::Span; @@ -9,19 +10,237 @@ fn ident(s: &str) -> Ident { Ident::new(s, Span::call_site()) } +fn to_path(field_type: &FieldType) -> TokenStream { + let rust_type = ident(&field_type.inner_type.rust_type()); + if field_type.repeated { + quote! { + Vec::<#rust_type> + } + } else { + quote! { + #rust_type + } + } +} + +fn serialize_field(field: &Field) -> proc_macro2::TokenStream { + let ind = field.number as usize; + let name = ident(&field.name); + if field.field_type.repeated { + match &field.field_type.inner_type { + Type::String => unimplemented!(), + Type::Capability => unimplemented!(), + Type::U64 => quote! { + { + let rep_offset = next_extension; + let rep_len = self.#name.len() as u32; + next_extension = yunq::message::serialize_repeated(buf, next_extension as usize, &self.#name)? as u32; + + buf.write_at(yunq::message::field_offset(offset, #ind), rep_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, rep_len)?; + } + }, + Type::I64 => unimplemented!(), + Type::Bytes => unimplemented!(), + Type::Message(_) => quote! { + { + let rep_offset = next_extension; + let rep_len = self.#name.len() as u32; + next_extension = yunq::message::serialize_repeated_message(buf, next_extension as usize, &self.#name, caps)? as u32; + + buf.write_at(yunq::message::field_offset(offset, #ind), rep_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, rep_len)?; + } + }, + } + } else { + match &field.field_type.inner_type { + Type::String => quote! { + { + let str_offset = next_extension; + let str_length = self.#name.len() as u32; + + buf.write_str_at(offset + next_extension as usize, &self.#name)?; + next_extension += str_length; + + buf.write_at(yunq::message::field_offset(offset, #ind), str_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, str_length)?; + } + }, + Type::Capability => quote! { + { + let cap_ind = caps.len(); + caps.push(self.#name); + + buf.write_at(yunq::message::field_offset(offset, #ind), cap_ind as u64)?; + } + }, + Type::U64 => quote! { + { + buf.write_at(yunq::message::field_offset(offset, #ind), self.#name)?; + } + }, + Type::I64 => quote! { + { + buf.write_at(yunq::message::field_offset(offset, #ind), self.#name)?; + } + }, + Type::Message(_) => quote! { + { + let msg_offset = next_extension; + let msg_length = self.#name.serialize(buf, offset + next_extension as usize, caps)? as u32; + next_extension += msg_length; + + buf.write_at(yunq::message::field_offset(offset, #ind), msg_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, msg_length)?; + } + }, + Type::Bytes => unimplemented!(), + } + } +} + +fn parse_field(field: &Field) -> TokenStream { + let ind = field.number as usize; + let name = ident(&field.name); + if field.field_type.repeated { + match &field.field_type.inner_type { + Type::String => unimplemented!(), + Type::Capability => unimplemented!(), + Type::U64 => quote! { + let #name = { + let rep_offset = buf.at::(yunq::message::field_offset(offset, #ind))?; + let rep_len = buf.at::(yunq::message::field_offset(offset, #ind) + 4)?; + + yunq::message::parse_repeated(buf, rep_offset as usize, rep_len as usize)? + }; + }, + Type::I64 => unimplemented!(), + Type::Bytes => unimplemented!(), + Type::Message(_s) => quote! { + let #name = { + let rep_offset = buf.at::(yunq::message::field_offset(offset, #ind))?; + let rep_len = buf.at::(yunq::message::field_offset(offset, #ind) + 4)?; + + yunq::message::parse_repeated_message(buf, rep_offset as usize, rep_len as usize, &caps)? + }; + }, + } + } else { + match &field.field_type.inner_type { + Type::String => quote! { + let #name = { + let str_offset = buf.at::(yunq::message::field_offset(offset, #ind))?; + let str_length = buf.at::(yunq::message::field_offset(offset, #ind) + 4)?; + + buf.str_at(offset + str_offset as usize, str_length as usize)?.to_string() + }; + }, + Type::Capability => quote! { + let #name = { + let cap_ind = buf.at::(yunq::message::field_offset(offset, #ind))?; + caps[cap_ind as usize] + }; + }, + Type::U64 => quote! { + let #name = buf.at::(yunq::message::field_offset(offset, #ind))?; + }, + Type::I64 => quote! { + let #name = buf.at::(yunq::message::field_offset(offset, #ind))?; + }, + Type::Bytes => { + unimplemented!(); + } + Type::Message(s) => { + let m_type = ident(&s); + quote! { + let #name = { + let msg_offset = buf.at::(yunq::message::field_offset(offset, #ind))? as usize; + + #m_type::parse(buf, offset + msg_offset, caps)? + }; + } + } + } + } +} + +fn generate_serialize(message: &Message) -> TokenStream { + let num_fields = message.fields.len(); + let serializers = message.fields.iter().map(serialize_field); + quote! { + #[allow(unused_variables)] // caps may be unused. + fn serialize( + &self, + buf: &mut yunq::ByteBuffer, + offset: usize, + caps: &mut alloc::vec::Vec, + ) -> Result { + let num_fields = #num_fields; + let core_size: u32 = (yunq::message::MESSAGE_HEADER_SIZE + 8 * num_fields) as u32; + #[allow(unused_mut)] + let mut next_extension = core_size; + + #(#serializers)* + + buf.write_at(offset + 0, yunq::message::MESSAGE_IDENT)?; + buf.write_at(offset + 4, core_size)?; + buf.write_at(offset + 8, next_extension)?; + buf.write_at(offset + 12, 0 as u32)?; + Ok(next_extension as usize) + } + } +} + +fn generate_parse(message: &Message) -> TokenStream { + let field_names = message.fields.iter().map(|field| ident(&field.name)); + let parsers = message.fields.iter().map(parse_field); + quote! { + #[allow(unused_variables)] // caps may be unused. + fn parse( + buf: &yunq::ByteBuffer, + offset: usize, + caps: &alloc::vec::Vec, + ) -> Result + where + Self: Sized, + { + if buf.at::(offset + 0)? != yunq::message::MESSAGE_IDENT { + return Err(ZError::INVALID_ARGUMENT); + } + // TODO: Parse core size. + // TODO: Parse extension size. + // TODO: Check CRC32 + // TODO: Parse options. + + #(#parsers)* + + Ok(Self { + #(#field_names),* + }) + + } + } +} + fn generate_message(message: &Message) -> TokenStream { let name = ident(&message.name); let field_names = message.fields.iter().map(|f| ident(&f.name)); - let field_types = message - .fields - .iter() - .map(|f| Ident::new(f.field_type.rust_type(), Span::call_site())); + let field_types = message.fields.iter().map(|f| to_path(&f.field_type)); + + let serialize = generate_serialize(message); + let parse = generate_parse(message); quote! { - #[derive(YunqMessage, PartialEq, Eq)] + #[derive(PartialEq, Eq)] pub struct #name { #(pub #field_names: #field_types),* } + impl YunqMessage for #name { + #serialize + + #parse + } } } @@ -199,20 +418,29 @@ pub fn generate_code(ast: &Vec) -> String { extern crate alloc; - use alloc::boxed::Box; use alloc::string::String; use alloc::string::ToString; use alloc::vec::Vec; - use core::ffi::c_void; - use mammoth::syscall; - use mammoth::thread; - use mammoth::cap::Capability; use mammoth::zion::z_cap_t; use mammoth::zion::ZError; use yunq::ByteBuffer; use yunq::YunqMessage; + + + // Used only by modules with an interface. + #[allow(unused_imports)] + use alloc::boxed::Box; + #[allow(unused_imports)] + use core::ffi::c_void; + #[allow(unused_imports)] + use mammoth::cap::Capability; + #[allow(unused_imports)] + use mammoth::syscall; + #[allow(unused_imports)] + use mammoth::thread; + #[allow(unused_imports)] use yunq::server::YunqServer; - use yunq_derive::YunqMessage; + }; let message_decls = ast @@ -240,5 +468,10 @@ pub fn generate_code(ast: &Vec) -> String { } .to_string(); - prettyplease::unparse(&syn::parse_file(&output).unwrap()) + let tokens = syn::parse_file(&output).unwrap_or_else(|e| { + println!("{}", output); + panic!("{}", e); + }); + + prettyplease::unparse(&tokens) } diff --git a/yunq/rust/src/parser.rs b/yunq/rust/src/parser.rs index f116fb9..07841f8 100644 --- a/yunq/rust/src/parser.rs +++ b/yunq/rust/src/parser.rs @@ -17,24 +17,33 @@ pub enum Type { } impl Type { - pub fn rust_type(&self) -> &str { + pub fn rust_type(&self) -> String { match self { - Type::U64 => "u64", - Type::I64 => "i64", - Type::String => "String", - Type::Bytes => "Vec", - Type::Capability => "z_cap_t", - Type::Message(s) => s, + Type::U64 => "u64".to_string(), + Type::I64 => "i64".to_string(), + Type::String => "String".to_string(), + Type::Bytes => "Vec".to_string(), + Type::Capability => "z_cap_t".to_string(), + Type::Message(s) => s.clone(), } } } -impl Display for Type { +#[derive(Clone)] +pub struct FieldType { + pub repeated: bool, + pub inner_type: Type, +} + +impl Display for FieldType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.repeated { + write!(f, "repeated ")?; + } write!( f, "{}", - match self { + match &self.inner_type { Type::U64 => "u64", Type::I64 => "i64", Type::String => "string", @@ -63,10 +72,9 @@ impl TryFrom<&String> for Type { #[derive(Clone)] pub struct Field { - pub field_type: Type, + pub field_type: FieldType, pub name: String, pub number: u64, - pub repeated: bool, } #[derive(Clone)] @@ -118,12 +126,11 @@ impl Debug for Decl { Decl::Message(m) => { writeln!(f, "Message {}", m.name)?; for field in &m.fields { - let typestr = if field.repeated { - format!("repeated {}", field.field_type) - } else { - field.field_type.to_string() - }; - writeln!(f, "\t{}: {} ({})", field.number, field.name, typestr)?; + writeln!( + f, + "\t{}: {} ({})", + field.number, field.name, field.field_type + )?; } } Decl::Interface(i) => { @@ -220,10 +227,12 @@ impl<'a> Parser<'a> { self.consume_token_type(TokenType::Semicolon)?; Ok(Field { - field_type: parsed_type, + field_type: FieldType { + inner_type: parsed_type, + repeated, + }, name: name_identifier, number, - repeated, }) } @@ -357,7 +366,7 @@ impl<'a> Parser<'a> { } field_names.insert(field.name.clone()); - if let Type::Message(name) = &field.field_type { + if let Type::Message(name) = &field.field_type.inner_type { if !self.type_map.contains_key(name) { return Err(format!( "Unknown type '{}' on field '{}' in message '{}'",