| // Copyright 2021 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. |
| |
| //! Library for generating structured configuration accessors. Each generated |
| //! language-specific library depends on the output of [`create_fidl_source`]. |
| |
| pub mod cpp; |
| pub mod fidl; |
| pub mod rust; |
| |
| use syn::Error as SynError; |
| use thiserror::Error; |
| |
| // TODO(https://fxbug.dev/42173254): This list should be kept in sync with fidlgen_rust. |
| const RESERVED_SUFFIXES: [&str; 7] = |
| ["Impl", "Marker", "Proxy", "ProxyProtocol", "ControlHandle", "Responder", "Server"]; |
| |
| // TODO(https://fxbug.dev/42173254): This list should be kept in sync with fidlgen_rust. |
| const RESERVED_WORDS: [&str; 73] = [ |
| "as", |
| "box", |
| "break", |
| "const", |
| "continue", |
| "crate", |
| "else", |
| "enum", |
| "extern", |
| "false", |
| "fn", |
| "for", |
| "if", |
| "impl", |
| "in", |
| "let", |
| "loop", |
| "match", |
| "mod", |
| "move", |
| "mut", |
| "pub", |
| "ref", |
| "return", |
| "self", |
| "Self", |
| "static", |
| "struct", |
| "super", |
| "trait", |
| "true", |
| "type", |
| "unsafe", |
| "use", |
| "where", |
| "while", |
| // Keywords reserved for future use (future-proofing...) |
| "abstract", |
| "alignof", |
| "await", |
| "become", |
| "do", |
| "final", |
| "macro", |
| "offsetof", |
| "override", |
| "priv", |
| "proc", |
| "pure", |
| "sizeof", |
| "typeof", |
| "unsized", |
| "virtual", |
| "yield", |
| // Weak keywords (special meaning in specific contexts) |
| // These are ok in all contexts of fidl names. |
| //"default", |
| //"union", |
| |
| // Things that are not keywords, but for which collisions would be very unpleasant |
| "Result", |
| "Ok", |
| "Err", |
| "Vec", |
| "Option", |
| "Some", |
| "None", |
| "Box", |
| "Future", |
| "Stream", |
| "Never", |
| "Send", |
| "fidl", |
| "futures", |
| "zx", |
| "async", |
| "on_open", |
| "OnOpen", |
| // TODO(https://fxbug.dev/42145610): Remove "WaitForEvent". |
| "wait_for_event", |
| "WaitForEvent", |
| ]; |
| |
| /// Error from generating a source file |
| #[derive(Debug, Error)] |
| pub enum SourceGenError { |
| #[error("The given string `{input}` is not a valid Rust identifier")] |
| InvalidIdentifier { input: String, source: SynError }, |
| } |
| |
| // TODO(https://fxbug.dev/42173254): This logic should be kept in sync with fidlgen_rust. |
| fn normalize_field_key(key: &str) -> String { |
| let mut identifier = String::new(); |
| let mut saw_lowercase_or_digit = false; |
| for c in key.chars() { |
| if c.is_ascii_uppercase() { |
| if saw_lowercase_or_digit { |
| // A lowercase letter or digit preceded this uppercase letter. |
| // Break this into two words. |
| identifier.push('_'); |
| saw_lowercase_or_digit = false; |
| } |
| identifier.push(c.to_ascii_lowercase()); |
| } else if c == '_' { |
| identifier.push('_'); |
| saw_lowercase_or_digit = false; |
| } else { |
| identifier.push(c); |
| saw_lowercase_or_digit = true; |
| } |
| } |
| |
| if RESERVED_WORDS.contains(&key) || RESERVED_SUFFIXES.iter().any(|s| key.starts_with(s)) { |
| identifier.push('_') |
| } |
| |
| identifier |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use cm_rust::ConfigChecksum; |
| use fidl_fuchsia_component_config_ext::config_decl; |
| use quote::quote; |
| |
| fn test_checksum() -> ConfigChecksum { |
| // sha256("Back to the Fuchsia") |
| ConfigChecksum::Sha256([ |
| 0xb5, 0xf9, 0x33, 0xe8, 0x94, 0x56, 0x3a, 0xf9, 0x61, 0x39, 0xe5, 0x05, 0x79, 0x4b, |
| 0x88, 0xa5, 0x3e, 0xd4, 0xd1, 0x5c, 0x32, 0xe2, 0xb4, 0x49, 0x9e, 0x42, 0xeb, 0xa3, |
| 0x32, 0xb1, 0xf5, 0xbb, |
| ]) |
| } |
| |
| #[test] |
| fn bad_field_names() { |
| let decl = config_decl! { |
| ck@ test_checksum(), |
| snake_case_string: { bool }, |
| lowerCamelCaseString: { bool }, |
| UpperCamelCaseString: { bool }, |
| CONST_CASE: { bool }, |
| stringThatHas02Digits: { bool }, |
| mixedLowerCamel_snakeCaseString: { bool }, |
| MixedUpperCamel_SnakeCaseString: { bool }, |
| multiple__underscores: { bool }, |
| unsafe: { bool }, |
| ServerMode: { bool }, |
| }; |
| |
| let observed_fidl_src = fidl::create_fidl_source(&decl, "cf.sc.internal".to_string()); |
| let expected_fidl_src = " |
| library cf.sc.internal; |
| |
| type Config = struct { |
| snake_case_string bool; |
| lower_camel_case_string bool; |
| upper_camel_case_string bool; |
| const_case bool; |
| string_that_has02_digits bool; |
| mixed_lower_camel_snake_case_string bool; |
| mixed_upper_camel_snake_case_string bool; |
| multiple__underscores bool; |
| unsafe_ bool; |
| server_mode_ bool; |
| }; |
| "; |
| assert_eq!(observed_fidl_src, expected_fidl_src); |
| |
| let actual_rust_src = |
| rust::create_rust_wrapper(&decl, "cf.sc.internal".to_string()).unwrap(); |
| |
| let expected_rust_src = quote! { |
| use fidl_cf_sc_internal::Config as FidlConfig; |
| use fidl::unpersist; |
| use fuchsia_inspect::{Node}; |
| use fuchsia_runtime::{take_startup_handle, HandleInfo, HandleType}; |
| use fuchsia_zircon as zx; |
| |
| #[derive(Debug)] |
| pub struct Config { |
| pub snake_case_string: bool, |
| pub lower_camel_case_string: bool, |
| pub upper_camel_case_string: bool, |
| pub const_case: bool, |
| pub string_that_has02_digits: bool, |
| pub mixed_lower_camel_snake_case_string: bool, |
| pub mixed_upper_camel_snake_case_string: bool, |
| pub multiple__underscores: bool, |
| pub unsafe_: bool, |
| pub server_mode_: bool |
| } |
| |
| impl Config { |
| pub fn take_from_startup_handle() -> Self { |
| let config_vmo: zx::Vmo = take_startup_handle(HandleInfo::new(HandleType::ComponentConfigVmo, 0)) |
| .expect("Config VMO handle must be provided and cannot already have been taken.") |
| .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![ |
| 0xb5, 0xf9, 0x33, 0xe8, 0x94, 0x56, 0x3a, 0xf9, 0x61, 0x39, 0xe5, 0x05, 0x79, |
| 0x4b, 0x88, 0xa5, 0x3e, 0xd4, 0xd1, 0x5c, 0x32, 0xe2, 0xb4, 0x49, 0x9e, 0x42, |
| 0xeb, 0xa3, 0x32, 0xb1, 0xf5, 0xbb |
| ]; |
| |
| assert_eq!(observed_checksum, expected_checksum, "checksum from config VMO does not match expected checksum"); |
| |
| let fidl_config: FidlConfig = unpersist(&config_bytes[fidl_start..]).expect("must be able to parse bytes as config FIDL"); |
| |
| Self { |
| snake_case_string: fidl_config.snake_case_string, |
| lower_camel_case_string: fidl_config.lower_camel_case_string, |
| upper_camel_case_string: fidl_config.upper_camel_case_string, |
| const_case: fidl_config.const_case, |
| string_that_has02_digits: fidl_config.string_that_has02_digits, |
| mixed_lower_camel_snake_case_string: fidl_config.mixed_lower_camel_snake_case_string, |
| mixed_upper_camel_snake_case_string: fidl_config.mixed_upper_camel_snake_case_string, |
| multiple__underscores: fidl_config.multiple__underscores, |
| unsafe_: fidl_config.unsafe_, |
| server_mode_: fidl_config.server_mode_ |
| } |
| } |
| pub fn record_inspect(&self, inspector_node : & Node) { |
| inspector_node.record_bool("snake_case_string", self.snake_case_string); |
| inspector_node.record_bool("lowerCamelCaseString", self.lower_camel_case_string); |
| inspector_node.record_bool("UpperCamelCaseString", self.upper_camel_case_string); |
| inspector_node.record_bool("CONST_CASE", self.const_case); |
| inspector_node.record_bool("stringThatHas02Digits", self.string_that_has02_digits); |
| inspector_node.record_bool("mixedLowerCamel_snakeCaseString", self.mixed_lower_camel_snake_case_string); |
| inspector_node.record_bool("MixedUpperCamel_SnakeCaseString", self.mixed_upper_camel_snake_case_string); |
| inspector_node.record_bool("multiple__underscores", self.multiple__underscores); |
| inspector_node.record_bool("unsafe", self.unsafe_); |
| inspector_node.record_bool("ServerMode", self.server_mode_); |
| } |
| } |
| }.to_string(); |
| |
| assert_eq!(actual_rust_src, expected_rust_src); |
| } |
| } |