blob: 51b286034d8f8f1d78fbb9495017a74a52e78d86 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::ast::{Message, QmiType, Service, ServiceSet, Structure, SubParam, TLV};
use anyhow::{format_err, Error};
use std::io;
pub struct Codegen<'a, W: io::Write> {
w: &'a mut W,
depth: usize,
}
fn type_fmt(ty: QmiType, optional: bool) -> String {
let ty = ty.to_rust_str();
if optional {
format!("Option<{}>", ty)
} else {
ty
}
}
macro_rules! indent {
($depth:expr) => {
$depth.depth += 4;
};
}
macro_rules! dedent {
($depth:expr) => {
$depth.depth -= 4;
};
}
macro_rules! write_indent {
($dst:expr, $($arg:tt)*) => (
$dst.w.write_fmt(format_args!("{space:>indent$}", indent = $dst.depth, space=""))?;
$dst.w.write_fmt(format_args!($($arg)*))?
)
}
macro_rules! writeln_indent {
($dst:expr, $($arg:tt)*) => (
write_indent!($dst, $($arg)*);
$dst.w.write_fmt(format_args!("\n"))?
)
}
impl<'a, W: io::Write> Codegen<'a, W> {
pub fn new(w: &'a mut W) -> Codegen<'a, W> {
Codegen { w, depth: 0 }
}
fn codegen_svc_tlv_encode(&mut self, field: &TLV, _structure: &Structure) -> Result<(), Error> {
if field.optional {
writeln_indent!(self, "if let Some(ref {}) = self.{} {{", field.param, field.param);
indent!(self);
} else {
if field.has_sub_params() {
// TODO check the subparam size is equal to the whole type
for param in field.sub_params.iter() {
writeln_indent!(self, "let {} = &self.{};", param.param, param.param);
}
} else {
writeln_indent!(self, "let {} = &self.{};", field.param, field.param);
}
}
// service msg type
writeln_indent!(self, "buf.put_u8({});", field.id);
match field.ty {
QmiType::Str => {
// service msg length (variable)
writeln_indent!(self, "buf.put_u16_le({}.as_bytes().len() as u16);", field.param);
// service msg value
writeln_indent!(self, "buf.put_slice({}.as_bytes());", field.param);
}
sized_type => {
// service msg length (fixed size)
writeln_indent!(self, "buf.put_u16_le({});", sized_type.to_size().unwrap());
// service msg value
if field.has_sub_params() {
for param in field.sub_params.iter() {
match param.ty {
QmiType::Str => {
return Err(format_err!(
"No way to handle an unsized type in a subparam"
))
}
QmiType::Uint8 | QmiType::Int8 => {
writeln_indent!(
self,
"buf.put_{}(*{});",
param.ty.to_rust_str(),
param.param
);
}
_ => {
writeln_indent!(
self,
"buf.put_{}_le(*{});",
param.ty.to_rust_str(),
param.param
);
}
}
}
} else {
match sized_type {
QmiType::Uint8 | QmiType::Int8 => {
writeln_indent!(
self,
"buf.put_{}(*{});",
sized_type.to_rust_str(),
field.param
);
}
QmiType::Str => unreachable!("handled above"),
_ => {
writeln_indent!(
self,
"buf.put_{}_le(*{});",
sized_type.to_rust_str(),
field.param
);
}
}
}
}
}
if field.optional {
dedent!(self);
writeln_indent!(self, "}}");
}
Ok(())
}
// This function should not decrement the total size
fn codegen_svc_decode_subfield(&mut self, field: &SubParam) -> Result<(), Error> {
match field.ty {
QmiType::Str => return Err(format_err!("Cannot have unsized types in subparams")),
QmiType::Uint8 | QmiType::Int8 => {
writeln_indent!(self, "{} = buf.get_{}();", field.param, field.ty.to_rust_str());
}
_ => {
writeln_indent!(self, "{} = buf.get_{}_le();", field.param, field.ty.to_rust_str());
}
}
Ok(())
}
fn codegen_svc_decode_field(&mut self, field: &TLV) -> Result<(), Error> {
match field.ty {
QmiType::Str => {
writeln_indent!(self, "let mut dst = vec![0; tlv_len as usize];");
writeln_indent!(self, "buf.copy_to_slice(&mut dst[..]);");
writeln_indent!(self, "total_len -= tlv_len;");
writeln_indent!(self, "let str = String::from_utf8(dst);");
if field.optional {
writeln_indent!(self, "{} = str;", field.param);
} else {
writeln_indent!(self, "{} = str.unwrap();", field.param);
}
}
sized_type => {
if field.has_sub_params() {
// TODO check the subparam size is equal to the whole type
for param in field.sub_params.iter() {
self.codegen_svc_decode_subfield(param)?;
}
} else {
match sized_type {
QmiType::Uint8 | QmiType::Int8 => {
if field.optional {
writeln_indent!(
self,
"{} = Some(buf.get_{}());",
field.param,
field.ty.to_rust_str()
);
} else {
writeln_indent!(
self,
"{} = buf.get_{}();",
field.param,
field.ty.to_rust_str()
);
}
}
_ => {
if field.optional {
writeln_indent!(
self,
"{} = Some(buf.get_{}_le());",
field.param,
field.ty.to_rust_str()
);
} else {
writeln_indent!(
self,
"{} = buf.get_{}_le();",
field.param,
field.ty.to_rust_str()
);
}
}
}
}
writeln_indent!(self, "total_len -= {};", sized_type.to_size().unwrap());
}
}
Ok(())
}
pub fn codegen_svc_decode(
&mut self,
_svc_id: u16,
msg: &Message,
_structure: &Structure,
) -> Result<(), Error> {
// define the request struct
writeln_indent!(self, "impl Decodable for {}Resp {{", msg.name);
indent!(self);
// transaction_id_len fn
writeln_indent!(
self,
"fn from_bytes<T: Buf>(mut buf: T) -> Result<{}Resp, QmiError> {{",
msg.name
);
indent!(self);
// parse the result tlv
writeln_indent!(self, "assert_eq!({}, buf.get_u16_le());", msg.ty);
writeln_indent!(self, "let mut total_len = buf.get_u16_le();");
writeln_indent!(self, "let _ = buf.get_u8();");
writeln_indent!(self, "total_len -= 1;");
writeln_indent!(self, "let res_len = buf.get_u16_le();");
writeln_indent!(self, "total_len -= res_len + 2;");
writeln_indent!(self, "if 0x00 != buf.get_u16_le() {{");
// This is an error case, generate a qmi error
indent!(self);
writeln_indent!(self, "let error_code = buf.get_u16_le();");
writeln_indent!(self, "return Err(QmiError::from_code(error_code))");
dedent!(self);
writeln_indent!(self, "}} else {{");
indent!(self);
writeln_indent!(
self,
"assert_eq!(0x00, buf.get_u16_le()); // this must be zero if no error from above check"
);
dedent!(self);
writeln_indent!(self, "}}");
for field in msg.get_response_fields() {
if field.has_sub_params() {
for param in field.sub_params.iter() {
writeln_indent!(self, "let mut {} = Default::default();", param.param);
}
} else {
writeln_indent!(self, "let mut {} = Default::default();", field.param);
}
}
writeln_indent!(self, "while total_len > 0 {{");
indent!(self);
writeln_indent!(self, "let msg_id = buf.get_u8();");
writeln_indent!(self, "total_len -= 1;");
writeln_indent!(self, "let tlv_len = buf.get_u16_le();");
writeln_indent!(self, "total_len -= 2;");
writeln_indent!(self, "match msg_id {{");
indent!(self);
for field in msg.get_response_fields() {
writeln_indent!(self, "{} => {{", field.id);
indent!(self);
self.codegen_svc_decode_field(field)?;
dedent!(self);
writeln_indent!(self, "}}");
}
writeln_indent!(self, "0 => {{ eprintln!(\"Found a type of 0, modem gave a bad TLV, trying to recover\"); break; }}");
writeln_indent!(self, "e_code => {{");
indent!(self);
writeln_indent!(self, "eprintln!(\"Unknown id for this message type: {{}}, removing {{}} of len\", e_code, tlv_len);");
writeln_indent!(self, "total_len -= tlv_len;");
dedent!(self);
writeln_indent!(self, "}}");
dedent!(self);
writeln_indent!(self, "}}");
dedent!(self);
writeln_indent!(self, "}}");
writeln_indent!(self, "Ok({}Resp {{", msg.name);
for field in msg.get_response_fields() {
indent!(self);
if field.has_sub_params() {
for param in field.sub_params.iter() {
writeln_indent!(self, "{},", param.param);
}
} else {
writeln_indent!(self, "{},", field.param);
}
dedent!(self);
}
writeln_indent!(self, "}})");
dedent!(self);
writeln_indent!(self, "}}");
dedent!(self);
writeln_indent!(self, "}}");
Ok(())
}
pub fn codegen_svc_encode(
&mut self,
svc_id: u16,
msg: &Message,
structure: &Structure,
) -> Result<(), Error> {
// define the request struct
writeln_indent!(self, "impl Encodable for {}Req {{", msg.name);
indent!(self);
// corresponding decoded type
writeln_indent!(self, "type DecodeResult = {}Resp;", msg.name);
// transaction_id_len fn
writeln_indent!(self, "fn transaction_id_len(&self) -> u8 {{");
indent!(self);
writeln_indent!(self, "{}", structure.transaction_len);
dedent!(self);
writeln_indent!(self, "}}");
// svc_id fn
writeln_indent!(self, "fn svc_id(&self) -> u8 {{");
indent!(self);
writeln_indent!(self, "{}", svc_id);
dedent!(self);
writeln_indent!(self, "}}");
// to_bytes fn
writeln_indent!(self, "fn to_bytes(&self) -> (Bytes, u16) {{");
indent!(self);
writeln_indent!(self, "let mut buf = BytesMut::with_capacity(MSG_MAX);");
// Service headers
// Service Type id
writeln_indent!(self, "// svc id");
writeln_indent!(self, "buf.put_u16_le({});", msg.ty);
// calculate the length of the service message (including variable tlvs)
writeln_indent!(self, "// svc length calculation");
writeln_indent!(self, "let mut {}_len = 0u16;", msg.name);
for field in msg.get_request_fields() {
let field_name = match field.ty.to_size() {
Some(_) => format!("_{}", field.param),
None => field.param.clone(),
};
if field.optional {
writeln_indent!(self, "if let Some(ref {}) = self.{} {{", field_name, field.param);
indent!(self);
writeln_indent!(self, "{}_len += 1; // tlv type length;", msg.name);
writeln_indent!(self, "{}_len += 2; // tlv length length;", msg.name);
} else {
writeln_indent!(self, "{}_len += 1; // tlv type length;", msg.name);
writeln_indent!(self, "{}_len += 2; // tlv length length;", msg.name);
writeln_indent!(self, "let {} = &self.{};", field_name, field.param);
}
if let Some(size) = field.ty.to_size() {
writeln_indent!(self, "{}_len += {};", msg.name, size);
} else {
writeln_indent!(
self,
"{}_len += {}.as_bytes().len() as u16;",
msg.name,
field_name
);
}
if field.optional {
dedent!(self);
writeln_indent!(self, "}}");
}
}
writeln_indent!(self, "buf.put_u16_le({}_len);", msg.name);
// Service SDU
writeln_indent!(self, "// tlvs");
for field in msg.get_request_fields() {
self.codegen_svc_tlv_encode(field, structure)?;
}
writeln_indent!(
self,
"return (buf.freeze(), {}_len + 2 /*msg id len*/ + 2 /*msg len len*/);",
msg.name
);
// close fn
dedent!(self);
writeln_indent!(self, "}}");
// close impl
dedent!(self);
writeln_indent!(self, "}}");
Ok(())
}
fn codegen_svc_msg(&mut self, msg: &Message) -> Result<(), Error> {
// define the request struct
writeln_indent!(self, "pub struct {}Req {{", msg.name);
indent!(self);
for field in msg.get_request_fields() {
if field.has_sub_params() {
for param in field.sub_params.iter() {
writeln_indent!(
self,
"pub {param}: {ty},",
param = param.param,
ty = type_fmt(param.ty, field.optional)
);
}
} else {
writeln_indent!(
self,
"pub {param}: {ty},",
param = field.param,
ty = type_fmt(field.ty, field.optional)
);
}
}
dedent!(self);
writeln_indent!(self, "}}");
// define the request struct impl
writeln_indent!(self, "impl {}Req {{", msg.name);
indent!(self);
write_indent!(self, "pub fn new(");
for field in msg.get_request_fields() {
if field.has_sub_params() {
for param in field.sub_params.iter() {
write!(self.w, "{}: {},", param.param, type_fmt(param.ty, field.optional))?;
}
} else {
write!(self.w, "{}: {},", field.param, type_fmt(field.ty, field.optional))?;
}
}
writeln!(self.w, ") -> Self {{")?;
indent!(self);
writeln_indent!(self, "{}Req {{", msg.name);
indent!(self);
for field in msg.get_request_fields() {
if field.has_sub_params() {
for param in field.sub_params.iter() {
writeln_indent!(self, "{},", param.param);
}
} else {
writeln_indent!(self, "{},", field.param);
}
}
dedent!(self);
writeln_indent!(self, "}}");
// close impl
dedent!(self);
writeln_indent!(self, "}}");
// close struct
dedent!(self);
writeln_indent!(self, "}}");
// define the response struct
writeln_indent!(self, "#[derive(Debug)]");
writeln_indent!(self, "pub struct {}Resp {{", msg.name);
indent!(self);
for field in msg.get_response_fields() {
if field.has_sub_params() {
for param in field.sub_params.iter() {
writeln_indent!(
self,
"pub {param}: {ty},",
param = param.param,
ty = type_fmt(param.ty, false)
);
}
} else {
writeln_indent!(
self,
"pub {param}: {ty},",
param = field.param,
ty = type_fmt(field.ty, field.optional)
);
}
}
dedent!(self);
writeln_indent!(self, "}}");
Ok(())
}
fn codegen_service(&mut self, svc: &Service, structure: &Structure) -> Result<(), Error> {
writeln!(self.w, "pub mod {} {{", svc.name)?;
indent!(self);
writeln_indent!(self, "use crate::{{Decodable, Encodable}};");
writeln_indent!(self, "use crate::QmiError;");
writeln_indent!(self, "use bytes::{{Bytes, Buf, BufMut, BytesMut}};");
writeln_indent!(self, "const MSG_MAX: usize = 512;");
for msg in svc.get_messages() {
self.codegen_svc_msg(msg)?;
self.codegen_svc_encode(svc.ty, msg, &structure)?;
self.codegen_svc_decode(svc.ty, msg, &structure)?;
}
dedent!(self);
writeln!(self.w, "}}")?;
Ok(())
}
pub fn codegen(&mut self, svc_set: ServiceSet) -> Result<(), Error> {
writeln!(
self.w,
"// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(unused_mut, non_snake_case)]
use thiserror::Error;
use bytes::{{Bytes, Buf}};
use std::fmt::Debug;
use std::result;
pub type QmiResult<T> = result::Result<T, QmiError>;
pub trait Encodable {{
type DecodeResult;
fn to_bytes(&self) -> (Bytes, u16);
fn transaction_id_len(&self) -> u8;
fn svc_id(&self) -> u8;
}}
pub trait Decodable {{
fn from_bytes<T: Buf + Debug>(b: T) -> QmiResult<Self> where Self: Sized;
}}
"
)?;
// codegen error types
writeln_indent!(self, "#[derive(Debug, Error)]");
writeln_indent!(self, "pub enum QmiError {{");
indent!(self);
for (error_type, error_set) in &svc_set.results {
writeln_indent!(self, "// {} error types", error_type);
for (name, code) in error_set {
writeln_indent!(self, "#[error( \"Qmi Result Error Code: {:#X}\")]", code);
writeln_indent!(self, "{},", name);
}
}
dedent!(self);
writeln_indent!(self, "}}");
writeln_indent!(self, "impl QmiError {{");
indent!(self);
writeln_indent!(self, "pub fn from_code(code: u16) -> Self {{");
indent!(self);
writeln_indent!(self, "match code {{");
indent!(self);
for (error_type, error_set) in &svc_set.results {
writeln_indent!(self, "// {} error type", error_type);
for (name, code) in error_set {
writeln_indent!(self, "{} => QmiError::{},", code, name);
}
}
writeln_indent!(self, "_c => panic!(\"Unknown Error Code: {{}}\", _c),");
dedent!(self);
writeln_indent!(self, "}}");
dedent!(self);
writeln_indent!(self, "}}");
dedent!(self);
writeln_indent!(self, "}}");
// codegen service modules
for service in svc_set.get_services() {
self.codegen_service(service, svc_set.get_structure(&service.message_structure)?)?;
}
Ok(())
}
}