nimbus_cli/output/
features.rs1use std::path::Path;
6
7use anyhow::Result;
8use nimbus_fml::intermediate_representation::FeatureManifest;
9use serde_json::Value;
10
11use crate::{
12 sources::{ExperimentSource, ManifestSource},
13 value_utils::{self, CliUtils},
14};
15
16impl ManifestSource {
17 pub(crate) fn print_defaults<P>(
18 &self,
19 feature_id: Option<&String>,
20 output: Option<P>,
21 ) -> Result<bool>
22 where
23 P: AsRef<Path>,
24 {
25 let manifest: FeatureManifest = self.try_into()?;
26 let json = self.get_defaults_json(&manifest, feature_id)?;
27 value_utils::write_to_file_or_print(output, &json)?;
28 Ok(true)
29 }
30
31 fn get_defaults_json(
32 &self,
33 fm: &FeatureManifest,
34 feature_id: Option<&String>,
35 ) -> Result<Value> {
36 Ok(match feature_id {
37 Some(id) => {
38 let (_, feature) = fm.find_feature(id).ok_or_else(|| {
39 anyhow::Error::msg(format!("Feature '{id}' does not exist in this manifest"))
40 })?;
41 feature.default_json()
42 }
43 _ => fm.default_json(),
44 })
45 }
46}
47
48impl ExperimentSource {
49 #[allow(clippy::too_many_arguments)]
50 pub(crate) fn print_features<P>(
51 &self,
52 branch: &String,
53 manifest_source: &ManifestSource,
54 feature_id: Option<&String>,
55 validate: bool,
56 multi: bool,
57 output: Option<P>,
58 ) -> Result<bool>
59 where
60 P: AsRef<Path>,
61 {
62 let json = self.get_features_json(manifest_source, feature_id, branch, validate, multi)?;
63 value_utils::write_to_file_or_print(output, &json)?;
64 Ok(true)
65 }
66
67 fn get_features_json(
68 &self,
69 manifest_source: &ManifestSource,
70 feature_id: Option<&String>,
71 branch: &String,
72 validate: bool,
73 multi: bool,
74 ) -> Result<Value> {
75 let value = self.try_into()?;
76
77 let branches = value_utils::try_find_branches_from_experiment(&value)?;
79 let b = branches
80 .iter()
81 .find(|b| b.get_str("slug").unwrap() == branch)
82 .ok_or_else(|| anyhow::format_err!("Branch '{branch}' does not exist"))?;
83
84 let feature_values = value_utils::try_find_features_from_branch(b)?;
86
87 let mut result = serde_json::value::Map::new();
89 for f in feature_values {
90 let id = f.get_str("featureId")?;
91 let value = f
92 .get("value")
93 .ok_or_else(|| anyhow::format_err!("Branch {branch} feature {id} has no value"))?;
94 match feature_id {
95 None => {
96 result.insert(id.to_string(), value.clone());
98 }
99 Some(feature_id) if feature_id == id => {
100 result.insert(id.to_string(), value.clone());
102 }
103 _ => continue,
105 }
106 }
107
108 if validate {
113 let fm: FeatureManifest = manifest_source.try_into()?;
114 let mut new = serde_json::value::Map::new();
115 for (id, value) in result {
116 let def = fm.validate_feature_config(&id, value)?;
117 new.insert(id.to_owned(), def.default_json());
118 }
119 result = new;
120 }
121
122 Ok(if !multi && result.len() == 1 {
123 match (result.values().find(|_| true), feature_id) {
126 (Some(v), _) => v.to_owned(),
127 (_, Some(id)) => anyhow::bail!(
128 "The '{id}' feature is not involved in '{branch}' branch of '{self}'"
129 ),
130 (_, _) => {
131 anyhow::bail!("No features available in '{branch}' branch of '{self}'")
132 }
133 }
134 } else {
135 Value::Object(result)
137 })
138 }
139}