blob: 72bb07e448b4c2985f62b0de4c9cefa1e776642c [file] [log] [blame]
// Copyright 2022 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::{normalize_field_key, SourceGenError};
use cm_rust::{ConfigChecksum, ConfigDecl, ConfigNestedValueType, ConfigValueType};
use proc_macro2::{Ident, Literal, TokenStream};
use quote::quote;
use std::str::FromStr;
use syn::parse_str;
/// Create a Rust wrapper file containing all the fields of a config declaration
pub fn create_rust_wrapper(
config_decl: &ConfigDecl,
fidl_library_name: String,
) -> Result<String, SourceGenError> {
let fidl_library_name =
format!("fidl_{}", fidl_library_name.replace('.', "_").to_ascii_lowercase());
let fidl_library_name = parse_str::<Ident>(&fidl_library_name)
.map_err(|source| SourceGenError::InvalidIdentifier { input: fidl_library_name, source })?;
let ConfigChecksum::Sha256(expected_checksum) = &config_decl.checksum;
let expected_checksum =
expected_checksum.into_iter().map(|b| Literal::from_str(&format!("{:#04x}", b)).unwrap());
let mut field_declarations = vec![];
let mut field_conversions = vec![];
let mut record_inspect_ops = vec![];
let mut inspect_uses = vec![quote!(Node)];
let mut needs_array_property = false;
let mut needs_arithmetic_array_property = false;
for field in &config_decl.fields {
let RustTokens { decl, conversion, record_inspect } =
get_rust_tokens(&field.key, &field.type_)?;
if let ConfigValueType::Vector {
nested_type: ConfigNestedValueType::String { .. }, ..
} = &field.type_
{
needs_array_property = true;
} else if let ConfigValueType::Vector { .. } = &field.type_ {
needs_arithmetic_array_property = true;
}
record_inspect_ops.push(record_inspect);
field_declarations.push(decl);
field_conversions.push(conversion)
}
if needs_arithmetic_array_property {
inspect_uses.push(quote!(ArithmeticArrayProperty))
}
if needs_array_property {
inspect_uses.push(quote!(ArrayProperty))
}
let stream = quote! {
use #fidl_library_name::Config as FidlConfig;
use fidl::encoding::decode_persistent;
use fuchsia_inspect::{#(#inspect_uses),*};
use fuchsia_runtime::{take_startup_handle, HandleInfo, HandleType};
use fuchsia_zircon as zx;
pub struct Config {
#(#field_declarations),*
}
impl Config {
pub fn take_from_startup_handle() -> Self {
let config_vmo: zx::Vmo = take_startup_handle(HandleInfo::new(HandleType::ConfigVmo, 0))
.expect("must have been provided with a config vmo")
.into();
let config_size = config_vmo.get_content_size().expect("must be able to read config vmo content size");
assert_ne!(config_size, 0, "config vmo must be non-empty");
let mut config_bytes = Vec::new();
config_bytes.resize(config_size as usize, 0);
config_vmo.read(&mut config_bytes, 0).expect("must be able to read config vmo");
let checksum_length = u16::from_le_bytes([config_bytes[0], config_bytes[1]]) as usize;
let fidl_start = 2 + checksum_length;
let observed_checksum = &config_bytes[2..fidl_start];
let expected_checksum = vec![#(#expected_checksum),*];
assert_eq!(observed_checksum, expected_checksum, "checksum from config VMO does not match expected checksum");
let fidl_config: FidlConfig = decode_persistent(&config_bytes[fidl_start..]).expect("must be able to parse bytes as config FIDL");
Self {
#(#field_conversions),*
}
}
pub fn record_inspect(&self, inspector_node: &Node) {
#(#record_inspect_ops)*
}
}
};
Ok(stream.to_string())
}
struct RustTokens {
decl: TokenStream,
conversion: TokenStream,
record_inspect: TokenStream,
}
fn get_rust_tokens(key: &str, value_type: &ConfigValueType) -> Result<RustTokens, SourceGenError> {
let identifier = normalize_field_key(key);
let field = parse_str::<Ident>(&identifier)
.map_err(|source| SourceGenError::InvalidIdentifier { input: key.to_string(), source })?;
let (record_inspect, decl) = match value_type {
ConfigValueType::Bool => (
quote! {
inspector_node.record_bool(#key, self.#field);
},
quote! {
pub #field: bool
},
),
ConfigValueType::Uint8 => (
quote! {
inspector_node.record_uint(#key, self.#field as u64);
},
quote! {
pub #field: u8
},
),
ConfigValueType::Uint16 => (
quote! {
inspector_node.record_uint(#key, self.#field as u64);
},
quote! {
pub #field: u16
},
),
ConfigValueType::Uint32 => (
quote! {
inspector_node.record_uint(#key, self.#field as u64);
},
quote! {
pub #field: u32
},
),
ConfigValueType::Uint64 => (
quote! {
inspector_node.record_uint(#key, self.#field);
},
quote! {
pub #field: u64
},
),
ConfigValueType::Int8 => (
quote! {
inspector_node.record_int(#key, self.#field as i64);
},
quote! {
pub #field: i8
},
),
ConfigValueType::Int16 => (
quote! {
inspector_node.record_int(#key, self.#field as i64);
},
quote! {
pub #field: i16
},
),
ConfigValueType::Int32 => (
quote! {
inspector_node.record_int(#key, self.#field as i64);
},
quote! {
pub #field: i32
},
),
ConfigValueType::Int64 => (
quote! {
inspector_node.record_int(#key, self.#field);
},
quote! {
pub #field: i64
},
),
ConfigValueType::String { .. } => (
quote! {
inspector_node.record_string(#key, &self.#field);
},
quote! {
pub #field: String
},
),
ConfigValueType::Vector { nested_type, .. } => match nested_type {
ConfigNestedValueType::Bool => (
quote! {
let arr = inspector_node.create_uint_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i] as u64);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<bool>
},
),
ConfigNestedValueType::Uint8 => (
quote! {
let arr = inspector_node.create_uint_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i] as u64);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<u8>
},
),
ConfigNestedValueType::Uint16 => (
quote! {
let arr = inspector_node.create_uint_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i] as u64);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<u16>
},
),
ConfigNestedValueType::Uint32 => (
quote! {
let arr = inspector_node.create_uint_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i] as u64);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<u32>
},
),
ConfigNestedValueType::Uint64 => (
quote! {
let arr = inspector_node.create_uint_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i]);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<u64>
},
),
ConfigNestedValueType::Int8 => (
quote! {
let arr = inspector_node.create_int_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i] as i64);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<i8>
},
),
ConfigNestedValueType::Int16 => (
quote! {
let arr = inspector_node.create_int_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i] as i64);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<i16>
},
),
ConfigNestedValueType::Int32 => (
quote! {
let arr = inspector_node.create_int_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i] as i64);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<i32>
},
),
ConfigNestedValueType::Int64 => (
quote! {
let arr = inspector_node.create_int_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.add(i, self.#field[i]);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<i64>
},
),
ConfigNestedValueType::String { .. } => (
quote! {
let arr = inspector_node.create_string_array(#key, self.#field.len());
for i in 0..self.#field.len() {
arr.set(i, &self.#field[i]);
}
inspector_node.record(arr);
},
quote! {
pub #field: Vec<String>
},
),
},
};
let conversion = quote! {
#field: fidl_config.#field
};
Ok(RustTokens { decl, conversion, record_inspect })
}