1#![warn(missing_docs)]
6
7pub mod field;
12
13use crate::field::{config_value_from_json_value, FieldError};
14use cm_rust::{ConfigDecl, ConfigValueSpec, ConfigValuesData};
15use serde_json::Value as JsonValue;
16use std::collections::BTreeMap;
17
18pub fn populate_value_file(
22 config_decl: &ConfigDecl,
23 mut json_values: BTreeMap<String, JsonValue>,
24) -> Result<ConfigValuesData, FileError> {
25 let values = config_decl
26 .fields
27 .iter()
28 .map(|field| {
29 let json_value = json_values
30 .remove(&field.key)
31 .ok_or_else(|| FileError::MissingValue { key: field.key.clone() })?;
32 let value = config_value_from_json_value(&json_value, &field.type_)
33 .map_err(|reason| FileError::InvalidField { key: field.key.clone(), reason })?;
34 Ok(ConfigValueSpec { value })
35 })
36 .collect::<Result<Vec<ConfigValueSpec>, _>>()?;
37
38 if !json_values.is_empty() {
41 return Err(FileError::ExtraValues { keys: json_values.into_keys().collect() });
42 }
43
44 Ok(ConfigValuesData { values, checksum: config_decl.checksum.clone() })
45}
46
47#[derive(Debug, thiserror::Error, PartialEq)]
49#[allow(missing_docs)]
50pub enum FileError {
51 #[error("Invalid config field `{key}`")]
52 InvalidField {
53 key: String,
54 #[source]
55 reason: FieldError,
56 },
57
58 #[error("`{key}` field in manifest does not have a value defined.")]
59 MissingValue { key: String },
60
61 #[error("Fields `{keys:?}` in value definition do not exist in manifest.")]
62 ExtraValues { keys: Vec<String> },
63}
64
65#[cfg(test)]
66mod tests {
67 use super::field::JsonTy;
68 use super::*;
69 use cm_rust::{ConfigChecksum, ConfigSingleValue, ConfigValue, ConfigVectorValue};
70 use fidl_fuchsia_component_config_ext::{config_decl, values_data};
71 use serde_json::json;
72
73 fn test_checksum() -> ConfigChecksum {
74 ConfigChecksum::Sha256([
76 0xb5, 0xf9, 0x33, 0xe8, 0x94, 0x56, 0x3a, 0xf9, 0x61, 0x39, 0xe5, 0x05, 0x79, 0x4b,
77 0x88, 0xa5, 0x3e, 0xd4, 0xd1, 0x5c, 0x32, 0xe2, 0xb4, 0x49, 0x9e, 0x42, 0xeb, 0xa3,
78 0x32, 0xb1, 0xf5, 0xbb,
79 ])
80 }
81
82 #[test]
83 fn basic_success() {
84 let decl = config_decl! {
85 ck@ test_checksum(),
86 my_flag: { bool },
87 my_uint8: { uint8 },
88 my_uint16: { uint16 },
89 my_uint32: { uint32 },
90 my_uint64: { uint64 },
91 my_int8: { int8 },
92 my_int16: { int16 },
93 my_int32: { int32 },
94 my_int64: { int64 },
95 my_string: { string, max_size: 100 },
96 my_vector_of_flag: { vector, element: bool, max_count: 100 },
97 my_vector_of_uint8: { vector, element: uint8, max_count: 100 },
98 my_vector_of_uint16: { vector, element: uint16, max_count: 100 },
99 my_vector_of_uint32: { vector, element: uint32, max_count: 100 },
100 my_vector_of_uint64: { vector, element: uint64, max_count: 100 },
101 my_vector_of_int8: { vector, element: int8, max_count: 100 },
102 my_vector_of_int16: { vector, element: int16, max_count: 100 },
103 my_vector_of_int32: { vector, element: int32, max_count: 100 },
104 my_vector_of_int64: { vector, element: int64, max_count: 100 },
105 my_vector_of_string: {
106 vector,
107 element: { string, max_size: 100 },
108 max_count: 100
109 },
110 };
111
112 let values: BTreeMap<String, serde_json::Value> = serde_json::from_value(json!({
113 "my_flag": false,
114 "my_uint8": 255u8,
115 "my_uint16": 65535u16,
116 "my_uint32": 4000000000u32,
117 "my_uint64": 8000000000u64,
118 "my_int8": -127i8,
119 "my_int16": -32766i16,
120 "my_int32": -2000000000i32,
121 "my_int64": -4000000000i64,
122 "my_string": "hello, world!",
123 "my_vector_of_flag": [ true, false ],
124 "my_vector_of_uint8": [ 1, 2, 3 ],
125 "my_vector_of_uint16": [ 2, 3, 4 ],
126 "my_vector_of_uint32": [ 3, 4, 5 ],
127 "my_vector_of_uint64": [ 4, 5, 6 ],
128 "my_vector_of_int8": [ -1, -2, 3 ],
129 "my_vector_of_int16": [ -2, -3, 4 ],
130 "my_vector_of_int32": [ -3, -4, 5 ],
131 "my_vector_of_int64": [ -4, -5, 6 ],
132 "my_vector_of_string": [ "hello, world!", "hello, again!" ],
133 }))
134 .unwrap();
135
136 let expected = values_data![
137 ck@ test_checksum(),
138 ConfigValue::Single(ConfigSingleValue::Bool(false)),
139 ConfigValue::Single(ConfigSingleValue::Uint8(255u8)),
140 ConfigValue::Single(ConfigSingleValue::Uint16(65535u16)),
141 ConfigValue::Single(ConfigSingleValue::Uint32(4000000000u32)),
142 ConfigValue::Single(ConfigSingleValue::Uint64(8000000000u64)),
143 ConfigValue::Single(ConfigSingleValue::Int8(-127i8)),
144 ConfigValue::Single(ConfigSingleValue::Int16(-32766i16)),
145 ConfigValue::Single(ConfigSingleValue::Int32(-2000000000i32)),
146 ConfigValue::Single(ConfigSingleValue::Int64(-4000000000i64)),
147 ConfigValue::Single(ConfigSingleValue::String("hello, world!".into())),
148 ConfigValue::Vector(ConfigVectorValue::BoolVector(vec![true, false])),
149 ConfigValue::Vector(ConfigVectorValue::Uint8Vector(vec![1, 2, 3])),
150 ConfigValue::Vector(ConfigVectorValue::Uint16Vector(vec![2, 3, 4])),
151 ConfigValue::Vector(ConfigVectorValue::Uint32Vector(vec![3, 4, 5])),
152 ConfigValue::Vector(ConfigVectorValue::Uint64Vector(vec![4, 5, 6])),
153 ConfigValue::Vector(ConfigVectorValue::Int8Vector(vec![-1, -2, 3])),
154 ConfigValue::Vector(ConfigVectorValue::Int16Vector(vec![-2, -3, 4])),
155 ConfigValue::Vector(ConfigVectorValue::Int32Vector(vec![-3, -4, 5])),
156 ConfigValue::Vector(ConfigVectorValue::Int64Vector(vec![-4, -5, 6])),
157 ConfigValue::Vector(ConfigVectorValue::StringVector(
158 vec!["hello, world!".into(), "hello, again!".into()])
159 ),
160 ];
161
162 let observed = populate_value_file(&decl, values).unwrap();
163 assert_eq!(observed, expected);
164 }
165
166 #[test]
167 fn invalid_field_is_correctly_identified() {
168 let decl = config_decl! {
169 ck@ test_checksum(),
170 my_flag: { bool },
171 my_uint8: { uint8 },
172 };
173
174 let values: BTreeMap<String, serde_json::Value> = serde_json::from_value(json!({
175 "my_flag": false,
176 "my_uint8": true,
177 }))
178 .unwrap();
179
180 assert_eq!(
181 populate_value_file(&decl, values).unwrap_err(),
182 FileError::InvalidField {
183 key: "my_uint8".to_string(),
184 reason: FieldError::JsonTypeMismatch {
185 expected: JsonTy::Number,
186 received: JsonTy::Bool
187 }
188 }
189 );
190 }
191
192 #[test]
193 fn all_keys_must_be_defined() {
194 let decl = config_decl! {
195 ck@ test_checksum(),
196 my_flag: { bool },
197 my_uint8: { uint8 },
198 };
199
200 let values: BTreeMap<String, serde_json::Value> = serde_json::from_value(json!({
201 "my_flag": false,
202 }))
203 .unwrap();
204
205 assert_eq!(
206 populate_value_file(&decl, values).unwrap_err(),
207 FileError::MissingValue { key: "my_uint8".to_string() }
208 );
209 }
210
211 #[test]
212 fn no_extra_keys_can_be_defined() {
213 let decl = config_decl! {
214 ck@ test_checksum(),
215 my_flag: { bool },
216 };
217
218 let values: BTreeMap<String, serde_json::Value> = serde_json::from_value(json!({
219 "my_flag": false,
220 "my_uint8": 1,
221 "my_uint16": 2,
222 }))
223 .unwrap();
224
225 assert_eq!(
226 populate_value_file(&decl, values).unwrap_err(),
227 FileError::ExtraValues { keys: vec!["my_uint16".into(), "my_uint8".into()] }
228 );
229 }
230}