// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Rust no-allocation backend //! //! The motivation for this backend is to be a more "idiomatic" backend than //! the existing backend. Specifically, it should //! 1. Use lifetimes, not reference counting //! 2. Avoid expensive memory copies unless needed //! 3. Use the intermediate Schema rather than doing all the logic from scratch //! //! One notable consequence is that we avoid .specialize(), as it has "magic" behavior //! not defined in the spec. Instead we mimic the C++ approach of calling tryParse() and //! getting a Result<> back. mod computed_values; mod enums; mod packet_parser; mod packet_serializer; pub mod test; mod utils; use std::collections::HashMap; use proc_macro2::TokenStream; use quote::quote; use crate::ast; use self::{ enums::generate_enum, packet_parser::generate_packet, packet_serializer::generate_packet_serializer, }; use super::intermediate::Schema; pub fn generate(file: &ast::File, schema: &Schema) -> Result { match file.endianness.value { ast::EndiannessValue::LittleEndian => {} _ => unimplemented!("Only little_endian endianness supported"), }; let mut out = String::new(); out.push_str(include_str!("preamble.rs")); let mut children = HashMap::<&str, Vec<&str>>::new(); for decl in &file.declarations { match &decl.desc { ast::DeclDesc::Packet { id, parent_id: Some(parent_id), .. } | ast::DeclDesc::Struct { id, parent_id: Some(parent_id), .. } => { children.entry(parent_id.as_str()).or_default().push(id.as_str()); } _ => {} } } let declarations = file .declarations .iter() .map(|decl| generate_decl(decl, schema, &children)) .collect::>()?; let syntax_tree = syn::parse2(declarations).expect("Could not parse code"); out.push_str(&prettyplease::unparse(&syntax_tree)); Ok(out) } fn generate_decl( decl: &ast::Decl, schema: &Schema, children: &HashMap<&str, Vec<&str>>, ) -> Result { match &decl.desc { ast::DeclDesc::Enum { id, tags, width, .. } => Ok(generate_enum(id, tags, *width)), ast::DeclDesc::Packet { id, fields, parent_id, .. } | ast::DeclDesc::Struct { id, fields, parent_id, .. } => { let parser = generate_packet( id, fields, parent_id.as_deref(), schema, &schema.packets_and_structs[id.as_str()], )?; let serializer = generate_packet_serializer( id, parent_id.as_deref(), fields, schema, &schema.packets_and_structs[id.as_str()], children, ); Ok(quote! { #parser #serializer }) } _ => unimplemented!("Unsupported decl type"), } }