Skip to content

Commit

Permalink
Add commands to load and save IR
Browse files Browse the repository at this point in the history
  • Loading branch information
jhugman committed Oct 22, 2021
1 parent 771ba27 commit 0351342
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 30 deletions.
2 changes: 1 addition & 1 deletion components/support/nimbus-fml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license = "MPL-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = "2.33"
clap = {version = "2.33.0", features = ["yaml"]}
anyhow = "1.0.44"
serde_json = "1"
serde = { version = "1.0", features = ["derive"] }
Expand Down
50 changes: 50 additions & 0 deletions components/support/nimbus-fml/fixtures/simple_homescreen.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"enum_defs": [
{
"name": "SectionId",
"doc": "The sections of the homescreen",
"variants": [
{
"name": "top-sites",
"doc": "The original frecency sorted sites"
},
{
"name": "jump-back-in",
"doc": "Jump back in section"
},
{
"name": "recently-saved",
"doc": "Tabs that have been bookmarked recently"
}
]
}
],
"obj_defs": [],
"hints": {},
"feature_defs": [
{
"name": "homescreen",
"doc": "Represents the homescreen feature",
"props": [
{
"name": "sections-enabled",
"doc": "A map of booleans",
"typ": {
"EnumMap": [
{
"Enum": "SectionId"
},
"String"
]
},
"default": {
"jump-back-in": false,
"recently-saved": false,
"top-sites": true
}
}
],
"default": null
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"enum_defs": [],
"obj_defs": [],
"hints": {},
"feature_defs": [
{
"name": "nimbus-validation",
"doc": "A simple validation feature",
"props": [
{
"name": "enabled",
"doc": "An example boolean property",
"typ": "Boolean",
"default": true
},
{
"name": "row-count",
"doc": "An example integer property",
"typ": "Boolean",
"default": 2
},
{
"name": "deeplink",
"doc": "An example string property",
"typ": "String",
"default": "deeplink://settings"
}
],
"default": null
}
]
}
37 changes: 37 additions & 0 deletions components/support/nimbus-fml/src/cli.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: nimbus-fml
version: "1.0"
author: [email protected]
about: Tool for working with Nimbus Feature Manifests
args:
- config:
short: c
long: config
value_name: FILE
help: Sets a custom config file
takes_value: true
- verbose:
short: v
multiple: true
help: Sets the level of verbosity
subcommands:
- struct:
about: Generate the app code for configuring features
args:
- language:
short: l
long: language
value_name: LANGUAGE
possible_values: [ kotlin, swift, ir ]
- INPUT:
help: Sets the input file to use
required: true
index: 1
- ir:
help: The input file is intermediate representation. Useful for debugging FML.
long: ir
- output:
help: The output file
short: o
long: output
value_name: FILE
required: true
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,33 @@ pub(crate) fn get_simple_homescreen_feature() -> FeatureManifest {
)],
}
}

#[cfg(test)]
mod dump_to_file {
use std::path::PathBuf;

use crate::error::Result;

use super::*;

fn write(fm: &FeatureManifest, nm: &str) -> Result<()> {
let root= std::env::var("CARGO_MANIFEST_DIR")
.expect("Missing $CARGO_MANIFEST_DIR, cannot write fixtures files");
let fixtures_dir = "fixtures";
let path: PathBuf = [&root, fixtures_dir, nm].iter().collect();

let contents = serde_json::to_string_pretty(fm)?;

std::fs::write(path, contents)?;

Ok(())
}

#[test]
fn write_to_fixtures_dir() -> Result<()> {
write(&get_simple_homescreen_feature(), "simple_homescreen.json")?;
write(&get_simple_nimbus_validation_feature(), "simple_nimbus_validation.json")?;

Ok(())
}
}
17 changes: 10 additions & 7 deletions components/support/nimbus-fml/src/intermediate_representation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use serde_json::Value;
///
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub(crate) enum TypeRef {
pub enum TypeRef {
// Current primitives.
String,
Int,
Expand Down Expand Up @@ -45,7 +45,7 @@ pub(crate) enum TypeRef {
pub(crate) type StringId = String;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct FeatureManifest {
pub struct FeatureManifest {
pub enum_defs: Vec<EnumDef>,
pub obj_defs: Vec<ObjectDef>,
// `hints` are useful for things that will be constructed from strings
Expand All @@ -55,13 +55,14 @@ pub(crate) struct FeatureManifest {
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct FeatureDef {
pub struct FeatureDef {
name: String,
doc: String,
props: Vec<PropDef>,
default: Option<Literal>,
}
impl FeatureDef {
#[allow(dead_code)]
pub fn new(name: &str, doc: &str, props: Vec<PropDef>, default: Option<Literal>) -> Self {
Self {
name: name.into(),
Expand All @@ -73,14 +74,14 @@ impl FeatureDef {
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct EnumDef {
pub struct EnumDef {
pub name: String,
pub doc: String,
pub variants: Vec<VariantDef>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct FromStringDef {
pub struct FromStringDef {
pub name: String,
pub doc: String,
pub variants: Vec<VariantDef>,
Expand All @@ -92,6 +93,7 @@ pub struct VariantDef {
doc: String,
}
impl VariantDef {
#[allow(dead_code)]
pub fn new(name: &str, doc: &str) -> Self {
Self {
name: name.into(),
Expand All @@ -101,12 +103,13 @@ impl VariantDef {
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct ObjectDef {
pub struct ObjectDef {
name: String,
doc: String,
props: Vec<PropDef>,
}
impl ObjectDef {
#[allow(dead_code)]
pub fn new(name: &str, doc: &str, props: Vec<PropDef>) -> Self {
Self {
name: name.into(),
Expand All @@ -117,7 +120,7 @@ impl ObjectDef {
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct PropDef {
pub struct PropDef {
pub name: String,
pub doc: String,
pub typ: TypeRef,
Expand Down
69 changes: 47 additions & 22 deletions components/support/nimbus-fml/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,53 @@
* 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 clap::{App, Arg};
#[macro_use]
extern crate clap;
use clap::{App, ArgMatches};
use nimbus_fml::error::{FMLError, Result};
use nimbus_fml::intermediate_representation::FeatureManifest;
use nimbus_fml::parser::Parser;
use std::fs::File;
fn main() -> anyhow::Result<()> {
let matches = App::new("Nimbus Feature Manifest")
.version("0.1.0")
.author("Nimbus SDK Engineering")
.about("A tool to generate code using an experiment feature manifest")
.arg(
Arg::with_name("manifest")
.short("m")
.long("manifest")
.value_name("FILE")
.help("Sets the manifest file to use")
.required(true)
.takes_value(true),
)
.get_matches();
let manifest_file_path = matches
.value_of("manifest")
.expect("Manifest path is required, but not found");
let file = File::open(manifest_file_path)?;
let _parser = Parser::new(file);
use std::{fs::File, path::PathBuf};

fn main() -> Result<()> {
let yaml = load_yaml!("cli.yaml");
let matches = App::from_yaml(yaml).get_matches();
let cwd = std::env::current_dir()?;
if let Some(cmd) = matches.subcommand_matches("struct") {
let manifest_file_path = file_path("INPUT", &cmd, &cwd)?;

let ir = if !cmd.is_present("ir") {
let file = File::open(manifest_file_path)?;
let _parser: Parser = Parser::new(file);
unimplemented!("No parser is available")
} else {
let string = slurp_file(&manifest_file_path)?;
serde_json::from_str::<FeatureManifest>(&string)?
};

let output_path = file_path("output", cmd, &cwd)?;
match cmd.value_of("language") {
Some("ir") => {
let contents = serde_json::to_string_pretty(&ir)?;
std::fs::write(output_path, contents)?;
}
_ => unimplemented!("Language not implemented yet"),
};
}
Ok(())
}

fn file_path(name: &str, args: &ArgMatches, cwd: &PathBuf) -> Result<PathBuf> {
let mut abs = cwd.clone();
match args.value_of(name) {
Some(suffix) => {
abs.push(suffix);
Ok(abs)
}
_ => Err(FMLError::InvalidPath(name.into())),
}
}

fn slurp_file(file_name: &PathBuf) -> Result<String> {
Ok(std::fs::read_to_string(file_name)?)
}

0 comments on commit 0351342

Please sign in to comment.