From e1126c7f646dfe1991875427d73aa8fdeeb5944f Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 21 Oct 2021 20:17:53 +0100 Subject: [PATCH] Added first cut design of Intermediate Representation module --- components/support/nimbus-fml/Cargo.toml | 5 +- components/support/nimbus-fml/src/error.rs | 22 +++ components/support/nimbus-fml/src/fixtures.rs | 5 + .../support/nimbus-fml/src/fixtures/ir.rs | 71 +++++++++ components/support/nimbus-fml/src/ir.rs | 138 ++++++++++++++++++ components/support/nimbus-fml/src/lib.rs | 6 + 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 components/support/nimbus-fml/src/error.rs create mode 100644 components/support/nimbus-fml/src/fixtures.rs create mode 100644 components/support/nimbus-fml/src/fixtures/ir.rs create mode 100644 components/support/nimbus-fml/src/ir.rs diff --git a/components/support/nimbus-fml/Cargo.toml b/components/support/nimbus-fml/Cargo.toml index ab4eaa9a98..1bd50c4c87 100644 --- a/components/support/nimbus-fml/Cargo.toml +++ b/components/support/nimbus-fml/Cargo.toml @@ -8,4 +8,7 @@ license = "MPL-2.0" [dependencies] clap = "2.33" -anyhow = "1" \ No newline at end of file +anyhow = "1.0.44" +serde_json = "1" +serde = { version = "1.0", features = ["derive"] } +thiserror = "1.0.29" diff --git a/components/support/nimbus-fml/src/error.rs b/components/support/nimbus-fml/src/error.rs new file mode 100644 index 0000000000..85c18072f2 --- /dev/null +++ b/components/support/nimbus-fml/src/error.rs @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * */ + +//! Not complete yet +//! This is where the error definitions can go +//! TODO: Implement proper error handling, this would include defining the error enum, +//! impl std::error::Error using `thiserror` and ensuring all errors are handled appropriately +#[derive(Debug, thiserror::Error)] +pub enum FMLError { + #[error("IO error: {0}")] + IOError(#[from] std::io::Error), + #[error("JSON Error: {0}")] + JSONError(#[from] serde_json::Error), + #[error("Invalid path: {0}")] + InvalidPath(String), + #[error("Internal error: {0}")] + InternalError(&'static str), +} + +pub type Result = std::result::Result; diff --git a/components/support/nimbus-fml/src/fixtures.rs b/components/support/nimbus-fml/src/fixtures.rs new file mode 100644 index 0000000000..da54a8807b --- /dev/null +++ b/components/support/nimbus-fml/src/fixtures.rs @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +pub(crate) mod ir; diff --git a/components/support/nimbus-fml/src/fixtures/ir.rs b/components/support/nimbus-fml/src/fixtures/ir.rs new file mode 100644 index 0000000000..6c3d7afad5 --- /dev/null +++ b/components/support/nimbus-fml/src/fixtures/ir.rs @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::ir::{EnumDef, FeatureDef, FeatureManifest, PropDef, TypeRef, VariantDef}; +use serde_json::json; + +pub(crate) fn get_simple_nimbus_validation_feature() -> FeatureManifest { + FeatureManifest { + enum_defs: vec![], + obj_defs: vec![], + feature_defs: vec![FeatureDef::new( + "nimbus-validation", + "A simple validation feature", + vec![ + PropDef { + name: "enabled".into(), + doc: "An example boolean property".into(), + typ: TypeRef::Boolean, + default: json!(true), + }, + PropDef { + name: "row-count".into(), + doc: "An example integer property".into(), + typ: TypeRef::Boolean, + default: json!(2), + }, + PropDef { + name: "deeplink".into(), + doc: "An example string property".into(), + typ: TypeRef::String, + default: json!("deeplink://settings"), + }, + ], + None, + )], + } +} + +pub(crate) fn get_simple_homescreen_feature() -> FeatureManifest { + FeatureManifest { + enum_defs: vec![EnumDef { + name: "SectionId".into(), + doc: "The sections of the homescreen".into(), + variants: vec![ + VariantDef::new("top-sites", "The original frecency sorted sites"), + VariantDef::new("jump-back-in", "Jump back in section"), + VariantDef::new("recently-saved", "Tabs that have been bookmarked recently"), + ], + }], + obj_defs: vec![], + feature_defs: vec![FeatureDef::new( + "homescreen", + "Represents the homescreen feature", + vec![PropDef { + name: "sections-enabled".into(), + doc: "A map of booleans".into(), + typ: TypeRef::EnumMap( + Box::new(TypeRef::Enum("SectionId".into())), + Box::new(TypeRef::String), + ), + default: json!({ + "top-sites": true, + "jump-back-in": false, + "recently-saved": false, + }), + }], + None, + )], + } +} diff --git a/components/support/nimbus-fml/src/ir.rs b/components/support/nimbus-fml/src/ir.rs new file mode 100644 index 0000000000..82f4707ef4 --- /dev/null +++ b/components/support/nimbus-fml/src/ir.rs @@ -0,0 +1,138 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// The `TypeRef` enum defines a reference to a type. +/// +/// Other types will be defined in terms of these enum values. +/// +/// They represent the types available via the current `Variables` API— +/// some primitives and structural types— and can be represented by +/// Kotlin, Swift and JSON Schema. +/// +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub(crate) enum TypeRef { + // Current primitives. + String, + Int, + Boolean, + + // Strings can be coerced into a few types. + // The types here will require the app's bundle or context to look up the final value. + // They will likely have + BundleText(String), + BundleImage(String), + + Enum(String), + // JSON objects can represent a data class. + Object(String), + + // JSON objects can also represent a `Map` or a `Map` with + // keys that can be derived from a string. + StringMap(Box), + // We can coerce the String keys into Enums, so this repesents that. + EnumMap(Box, Box), + + List(Box), + Option(Box), +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub(crate) struct FeatureManifest { + pub enum_defs: Vec, + pub obj_defs: Vec, + pub feature_defs: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub(crate) struct FeatureDef { + name: String, + doc: String, + props: Vec, + default: Option, +} +impl FeatureDef { + pub fn new(name: &str, doc: &str, props: Vec, default: Option) -> Self { + Self { + name: name.into(), + doc: doc.into(), + props, + default, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub(crate) struct EnumDef { + pub name: String, + pub doc: String, + pub variants: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub(crate) struct FromStringDef { + pub name: String, + pub doc: String, + pub variants: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct VariantDef { + name: String, + doc: String, +} +impl VariantDef { + pub fn new(name: &str, doc: &str) -> Self { + Self { + name: name.into(), + doc: doc.into(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub(crate) struct ObjectDef { + name: String, + doc: String, + props: Vec, +} +impl ObjectDef { + pub fn new(name: &str, doc: &str, props: Vec) -> Self { + Self { + name: name.into(), + doc: doc.into(), + props, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub(crate) struct PropDef { + pub name: String, + pub doc: String, + pub typ: TypeRef, + pub default: Literal, +} + +type Literal = Value; + +#[cfg(test)] +mod unit_tests { + use super::*; + use crate::error::Result; + use crate::fixtures::ir; + + #[test] + fn can_ir_represent_smoke_test() -> Result<()> { + let m1 = ir::get_simple_homescreen_feature(); + let string = serde_json::to_string(&m1)?; + let m2: FeatureManifest = serde_json::from_str(&string)?; + + assert_eq!(m1, m2); + + Ok(()) + } +} diff --git a/components/support/nimbus-fml/src/lib.rs b/components/support/nimbus-fml/src/lib.rs index e9d454721a..84fe99dbe2 100644 --- a/components/support/nimbus-fml/src/lib.rs +++ b/components/support/nimbus-fml/src/lib.rs @@ -2,4 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +pub mod error; +pub mod ir; pub mod parser; + +#[cfg(test)] +#[allow(dead_code)] +pub mod fixtures;