config_encoder/
lib.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![warn(missing_docs)]
6
7//! Library for resolving and encoding the runtime configuration values of a component.
8
9use cm_rust::{
10    ConfigChecksum, ConfigDecl, ConfigField as ConfigFieldDecl, ConfigMutability,
11    ConfigNestedValueType, ConfigOverride, ConfigSingleValue, ConfigValue, ConfigValueType,
12    ConfigValuesData, ConfigVectorValue, NativeIntoFidl,
13};
14use dynfidl::{BasicField, Field, Structure, VectorField};
15use fidl_fuchsia_component_decl as fdecl;
16use thiserror::Error;
17
18/// The resolved configuration for a component.
19#[derive(Clone, Debug, PartialEq)]
20pub struct ConfigFields {
21    /// A list of all resolved fields, in the order of the compiled manifest.
22    pub fields: Vec<ConfigField>,
23    /// A checksum from the compiled manifest.
24    pub checksum: ConfigChecksum,
25}
26
27impl ConfigFields {
28    /// Resolve a component's configuration values according to its declared schema. Should not fail if
29    /// `decl` and `specs` are well-formed.
30    pub fn resolve(
31        decl: &ConfigDecl,
32        base_values: ConfigValuesData,
33        parent_overrides: Option<&[ConfigOverride]>,
34    ) -> Result<Self, ResolutionError> {
35        // base values must have been packaged for the exact same layout as the decl contains
36        if decl.checksum != base_values.checksum {
37            return Err(ResolutionError::ChecksumFailure {
38                expected: decl.checksum.clone(),
39                received: base_values.checksum,
40            });
41        }
42
43        // ensure we have the correct number of base values for resolving by offset
44        if decl.fields.len() != base_values.values.len() {
45            return Err(ResolutionError::WrongNumberOfValues {
46                expected: decl.fields.len(),
47                received: base_values.values.len(),
48            });
49        }
50
51        // reject overrides which don't match any fields in the schema
52        if let Some(overrides) = parent_overrides {
53            for key in overrides.iter().map(|o| &o.key) {
54                if !decl.fields.iter().any(|f| &f.key == key) {
55                    return Err(ResolutionError::FieldDoesNotExist { key: key.to_owned() });
56                }
57            }
58        }
59
60        let mut resolved_fields = vec![];
61        for (decl_field, spec_field) in
62            decl.fields.iter().zip(IntoIterator::into_iter(base_values.values))
63        {
64            // see if the parent component provided a value
65            let mut from_parent = None;
66            if let Some(parent_overrides) = parent_overrides {
67                for o in parent_overrides {
68                    // parent overrides are resolved according to string key, not index
69                    if o.key == decl_field.key {
70                        if !decl_field.mutability.contains(ConfigMutability::PARENT) {
71                            return Err(ResolutionError::FieldIsNotMutable {
72                                key: decl_field.key.clone(),
73                            });
74                        }
75                        from_parent = Some(o.value.clone());
76                    }
77                }
78            }
79
80            // prefer parent-provided values over packaged values
81            let value = if let Some(v) = from_parent { v } else { spec_field.value };
82            let resolved_field = ConfigField::resolve(value, &decl_field).map_err(|source| {
83                ResolutionError::InvalidValue { key: decl_field.key.clone(), source }
84            })?;
85            resolved_fields.push(resolved_field);
86        }
87
88        Ok(Self { fields: resolved_fields, checksum: decl.checksum.clone() })
89    }
90
91    /// Encode the resolved fields as a FIDL struct with every field non-nullable.
92    ///
93    /// The first two bytes of the encoded buffer are a little-endian unsigned integer which denotes
94    /// the `checksum_length`. Bytes `2..2+checksum_length` are used to store the declaration
95    /// checksum. All remaining bytes are used to store the FIDL header and struct.
96    pub fn encode_as_fidl_struct(self) -> Vec<u8> {
97        let Self { fields, checksum: ConfigChecksum::Sha256(checksum) } = self;
98        let mut structure = Structure::default();
99        for ConfigField { value, .. } in fields {
100            structure = match value {
101                ConfigValue::Single(ConfigSingleValue::Bool(b)) => {
102                    structure.field(Field::Basic(BasicField::Bool(b)))
103                }
104                ConfigValue::Single(ConfigSingleValue::Uint8(n)) => {
105                    structure.field(Field::Basic(BasicField::UInt8(n)))
106                }
107                ConfigValue::Single(ConfigSingleValue::Uint16(n)) => {
108                    structure.field(Field::Basic(BasicField::UInt16(n)))
109                }
110                ConfigValue::Single(ConfigSingleValue::Uint32(n)) => {
111                    structure.field(Field::Basic(BasicField::UInt32(n)))
112                }
113                ConfigValue::Single(ConfigSingleValue::Uint64(n)) => {
114                    structure.field(Field::Basic(BasicField::UInt64(n)))
115                }
116                ConfigValue::Single(ConfigSingleValue::Int8(n)) => {
117                    structure.field(Field::Basic(BasicField::Int8(n)))
118                }
119                ConfigValue::Single(ConfigSingleValue::Int16(n)) => {
120                    structure.field(Field::Basic(BasicField::Int16(n)))
121                }
122                ConfigValue::Single(ConfigSingleValue::Int32(n)) => {
123                    structure.field(Field::Basic(BasicField::Int32(n)))
124                }
125                ConfigValue::Single(ConfigSingleValue::Int64(n)) => {
126                    structure.field(Field::Basic(BasicField::Int64(n)))
127                }
128                ConfigValue::Single(ConfigSingleValue::String(s)) => {
129                    // TODO(https://fxbug.dev/42169377) improve string representation too
130                    structure.field(Field::Vector(VectorField::UInt8Vector(s.into_bytes())))
131                }
132                ConfigValue::Vector(ConfigVectorValue::BoolVector(b)) => {
133                    structure.field(Field::Vector(VectorField::BoolVector(b.into())))
134                }
135                ConfigValue::Vector(ConfigVectorValue::Uint8Vector(n)) => {
136                    structure.field(Field::Vector(VectorField::UInt8Vector(n.into())))
137                }
138                ConfigValue::Vector(ConfigVectorValue::Uint16Vector(n)) => {
139                    structure.field(Field::Vector(VectorField::UInt16Vector(n.into())))
140                }
141                ConfigValue::Vector(ConfigVectorValue::Uint32Vector(n)) => {
142                    structure.field(Field::Vector(VectorField::UInt32Vector(n.into())))
143                }
144                ConfigValue::Vector(ConfigVectorValue::Uint64Vector(n)) => {
145                    structure.field(Field::Vector(VectorField::UInt64Vector(n.into())))
146                }
147                ConfigValue::Vector(ConfigVectorValue::Int8Vector(n)) => {
148                    structure.field(Field::Vector(VectorField::Int8Vector(n.into())))
149                }
150                ConfigValue::Vector(ConfigVectorValue::Int16Vector(n)) => {
151                    structure.field(Field::Vector(VectorField::Int16Vector(n.into())))
152                }
153                ConfigValue::Vector(ConfigVectorValue::Int32Vector(n)) => {
154                    structure.field(Field::Vector(VectorField::Int32Vector(n.into())))
155                }
156                ConfigValue::Vector(ConfigVectorValue::Int64Vector(n)) => {
157                    structure.field(Field::Vector(VectorField::Int64Vector(n.into())))
158                }
159                ConfigValue::Vector(ConfigVectorValue::StringVector(s)) => {
160                    structure.field(Field::Vector(
161                        // TODO(https://fxbug.dev/42169377) improve string representation too
162                        VectorField::UInt8VectorVector(
163                            IntoIterator::into_iter(s).map(|s| s.into_bytes()).collect(),
164                        ),
165                    ))
166                }
167            };
168        }
169
170        let mut buf = Vec::new();
171        buf.extend((checksum.len() as u16).to_le_bytes());
172        buf.extend(checksum);
173        buf.extend(structure.encode_persistent());
174        buf
175    }
176}
177
178impl Into<fdecl::ResolvedConfig> for ConfigFields {
179    fn into(self) -> fdecl::ResolvedConfig {
180        let checksum = self.checksum.native_into_fidl();
181        let fields = self.fields.into_iter().map(|f| f.into()).collect();
182        fdecl::ResolvedConfig { checksum, fields }
183    }
184}
185
186/// A single resolved configuration field.
187#[derive(Clone, Debug, PartialEq)]
188pub struct ConfigField {
189    /// The configuration field's key.
190    pub key: String,
191
192    /// The configuration field's value.
193    pub value: ConfigValue,
194
195    /// Ways this component's packaged values could have been overridden.
196    pub mutability: ConfigMutability,
197}
198
199impl ConfigField {
200    /// Reconciles a config field schema from the manifest with a value from the value file.
201    /// If the types and constraints don't match, an error is returned.
202    pub fn resolve(value: ConfigValue, decl_field: &ConfigFieldDecl) -> Result<Self, ValueError> {
203        let key = decl_field.key.clone();
204
205        match (&value, &decl_field.type_) {
206            (ConfigValue::Single(ConfigSingleValue::Bool(_)), ConfigValueType::Bool)
207            | (ConfigValue::Single(ConfigSingleValue::Uint8(_)), ConfigValueType::Uint8)
208            | (ConfigValue::Single(ConfigSingleValue::Uint16(_)), ConfigValueType::Uint16)
209            | (ConfigValue::Single(ConfigSingleValue::Uint32(_)), ConfigValueType::Uint32)
210            | (ConfigValue::Single(ConfigSingleValue::Uint64(_)), ConfigValueType::Uint64)
211            | (ConfigValue::Single(ConfigSingleValue::Int8(_)), ConfigValueType::Int8)
212            | (ConfigValue::Single(ConfigSingleValue::Int16(_)), ConfigValueType::Int16)
213            | (ConfigValue::Single(ConfigSingleValue::Int32(_)), ConfigValueType::Int32)
214            | (ConfigValue::Single(ConfigSingleValue::Int64(_)), ConfigValueType::Int64) => (),
215            (
216                ConfigValue::Single(ConfigSingleValue::String(text)),
217                ConfigValueType::String { max_size },
218            ) => {
219                let max_size = *max_size as usize;
220                if text.len() > max_size {
221                    return Err(ValueError::StringTooLong { max: max_size, actual: text.len() });
222                }
223            }
224            (ConfigValue::Vector(list), ConfigValueType::Vector { nested_type, max_count }) => {
225                let max_count = *max_count as usize;
226                let actual_count = match (list, nested_type) {
227                    (ConfigVectorValue::BoolVector(l), ConfigNestedValueType::Bool) => l.len(),
228                    (ConfigVectorValue::Uint8Vector(l), ConfigNestedValueType::Uint8) => l.len(),
229                    (ConfigVectorValue::Uint16Vector(l), ConfigNestedValueType::Uint16) => l.len(),
230                    (ConfigVectorValue::Uint32Vector(l), ConfigNestedValueType::Uint32) => l.len(),
231                    (ConfigVectorValue::Uint64Vector(l), ConfigNestedValueType::Uint64) => l.len(),
232                    (ConfigVectorValue::Int8Vector(l), ConfigNestedValueType::Int8) => l.len(),
233                    (ConfigVectorValue::Int16Vector(l), ConfigNestedValueType::Int16) => l.len(),
234                    (ConfigVectorValue::Int32Vector(l), ConfigNestedValueType::Int32) => l.len(),
235                    (ConfigVectorValue::Int64Vector(l), ConfigNestedValueType::Int64) => l.len(),
236                    (
237                        ConfigVectorValue::StringVector(l),
238                        ConfigNestedValueType::String { max_size },
239                    ) => {
240                        let max_size = *max_size as usize;
241                        for (i, s) in l.iter().enumerate() {
242                            if s.len() > max_size {
243                                return Err(ValueError::VectorElementInvalid {
244                                    offset: i,
245                                    source: Box::new(ValueError::StringTooLong {
246                                        max: max_size,
247                                        actual: s.len(),
248                                    }),
249                                });
250                            }
251                        }
252                        l.len()
253                    }
254                    (other_list, other_ty) => {
255                        return Err(ValueError::TypeMismatch {
256                            expected: format!("{:?}", other_ty),
257                            received: format!("{:?}", other_list),
258                        })
259                    }
260                };
261
262                if actual_count > max_count {
263                    return Err(ValueError::VectorTooLong { max: max_count, actual: actual_count });
264                }
265            }
266            (other_val, other_ty) => {
267                return Err(ValueError::TypeMismatch {
268                    expected: format!("{:?}", other_ty),
269                    received: format!("{:?}", other_val),
270                });
271            }
272        }
273
274        Ok(ConfigField { key, value, mutability: decl_field.mutability })
275    }
276}
277
278impl Into<fdecl::ResolvedConfigField> for ConfigField {
279    fn into(self) -> fdecl::ResolvedConfigField {
280        fdecl::ResolvedConfigField { key: self.key, value: self.value.native_into_fidl() }
281    }
282}
283
284#[derive(Clone, Debug, Error, PartialEq)]
285#[allow(missing_docs)]
286pub enum ResolutionError {
287    #[error("Checksums in declaration and value file do not match. Expected {expected:04x?}, received {received:04x?}")]
288    ChecksumFailure { expected: ConfigChecksum, received: ConfigChecksum },
289
290    #[error("Value file has a different number of values ({received}) than declaration has fields ({expected}).")]
291    WrongNumberOfValues { expected: usize, received: usize },
292
293    #[error("Provided a value for `{key}` which is not in the component's configuration schema.")]
294    FieldDoesNotExist { key: String },
295
296    #[error("Provided an override which is not mutable in the component's configuration schema.")]
297    FieldIsNotMutable { key: String },
298
299    #[error("Received invalid value for `{key}`:\n\t{source}")]
300    InvalidValue {
301        key: String,
302        #[source]
303        source: ValueError,
304    },
305}
306
307#[derive(Clone, Debug, Error, PartialEq)]
308#[allow(missing_docs)]
309pub enum ValueError {
310    #[error("Value of type `{received}` does not match declaration of type {expected}.")]
311    TypeMismatch { expected: String, received: String },
312
313    #[error("Received string of length {actual} for a field with a max of {max}.")]
314    StringTooLong { max: usize, actual: usize },
315
316    #[error("Received vector of length {actual} for a field with a max of {max}.")]
317    VectorTooLong { max: usize, actual: usize },
318
319    #[error("Vector element at {offset} index is invalid.")]
320    VectorElementInvalid {
321        offset: usize,
322        #[source]
323        source: Box<Self>,
324    },
325}
326
327#[cfg(test)]
328mod tests {
329    use super::*;
330    use fidl_fuchsia_component_config_ext::{config_decl, values_data};
331    use fidl_test_config_encoder::BasicSuccessSchema;
332
333    use ConfigSingleValue::*;
334    use ConfigValue::*;
335    use ConfigVectorValue::*;
336
337    #[test]
338    fn basic_success() {
339        let decl = config_decl! {
340            ck@ ConfigChecksum::Sha256([0; 32]),
341            my_flag: { bool },
342            my_uint8: { uint8 },
343            my_uint16: { uint16 },
344            my_uint32: { uint32 },
345            my_uint64: { uint64 },
346            my_int8: { int8 },
347            my_int16: { int16 },
348            my_int32: { int32 },
349            my_int64: { int64 },
350            my_string: { string, max_size: 100 },
351            my_vector_of_flag: { vector, element: bool, max_count: 100 },
352            my_vector_of_uint8: { vector, element: uint8, max_count: 100 },
353            my_vector_of_uint16: { vector, element: uint16, max_count: 100 },
354            my_vector_of_uint32: { vector, element: uint32, max_count: 100 },
355            my_vector_of_uint64: { vector, element: uint64, max_count: 100 },
356            my_vector_of_int8: { vector, element: int8, max_count: 100 },
357            my_vector_of_int16: { vector, element: int16, max_count: 100 },
358            my_vector_of_int32: { vector, element: int32, max_count: 100 },
359            my_vector_of_int64: { vector, element: int64, max_count: 100 },
360            my_vector_of_string: {
361                vector,
362                element: { string, max_size: 100 },
363                max_count: 100
364            },
365        };
366
367        let specs = values_data![
368            ck@ decl.checksum.clone(),
369            Single(Bool(false)),
370            Single(Uint8(255u8)),
371            Single(Uint16(65535u16)),
372            Single(Uint32(4000000000u32)),
373            Single(Uint64(8000000000u64)),
374            Single(Int8(-127i8)),
375            Single(Int16(-32766i16)),
376            Single(Int32(-2000000000i32)),
377            Single(Int64(-4000000000i64)),
378            Single(String("hello, world!".into())),
379            Vector(BoolVector(Box::from([true, false]))),
380            Vector(Uint8Vector(Box::from([1, 2, 3]))),
381            Vector(Uint16Vector(Box::from([2, 3, 4]))),
382            Vector(Uint32Vector(Box::from([3, 4, 5]))),
383            Vector(Uint64Vector(Box::from([4, 5, 6]))),
384            Vector(Int8Vector(Box::from([-1, -2, 3]))),
385            Vector(Int16Vector(Box::from([-2, -3, 4]))),
386            Vector(Int32Vector(Box::from([-3, -4, 5]))),
387            Vector(Int64Vector(Box::from([-4, -5, 6]))),
388            Vector(StringVector(Box::from(["valid".into(), "valid".into()]))),
389        ];
390
391        let resolved = ConfigFields::resolve(&decl, specs, None).unwrap();
392        assert_eq!(
393            resolved,
394            ConfigFields {
395                fields: vec![
396                    ConfigField {
397                        key: "my_flag".to_string(),
398                        value: Single(Bool(false)),
399                        mutability: Default::default(),
400                    },
401                    ConfigField {
402                        key: "my_uint8".to_string(),
403                        value: Single(Uint8(255)),
404                        mutability: Default::default(),
405                    },
406                    ConfigField {
407                        key: "my_uint16".to_string(),
408                        value: Single(Uint16(65535)),
409                        mutability: Default::default(),
410                    },
411                    ConfigField {
412                        key: "my_uint32".to_string(),
413                        value: Single(Uint32(4000000000)),
414                        mutability: Default::default(),
415                    },
416                    ConfigField {
417                        key: "my_uint64".to_string(),
418                        value: Single(Uint64(8000000000)),
419                        mutability: Default::default(),
420                    },
421                    ConfigField {
422                        key: "my_int8".to_string(),
423                        value: Single(Int8(-127)),
424                        mutability: Default::default(),
425                    },
426                    ConfigField {
427                        key: "my_int16".to_string(),
428                        value: Single(Int16(-32766)),
429                        mutability: Default::default(),
430                    },
431                    ConfigField {
432                        key: "my_int32".to_string(),
433                        value: Single(Int32(-2000000000)),
434                        mutability: Default::default(),
435                    },
436                    ConfigField {
437                        key: "my_int64".to_string(),
438                        value: Single(Int64(-4000000000)),
439                        mutability: Default::default(),
440                    },
441                    ConfigField {
442                        key: "my_string".to_string(),
443                        value: Single(String("hello, world!".into())),
444                        mutability: Default::default(),
445                    },
446                    ConfigField {
447                        key: "my_vector_of_flag".to_string(),
448                        value: Vector(BoolVector(Box::from([true, false]))),
449                        mutability: Default::default(),
450                    },
451                    ConfigField {
452                        key: "my_vector_of_uint8".to_string(),
453                        value: Vector(Uint8Vector(Box::from([1, 2, 3]))),
454                        mutability: Default::default(),
455                    },
456                    ConfigField {
457                        key: "my_vector_of_uint16".to_string(),
458                        value: Vector(Uint16Vector(Box::from([2, 3, 4]))),
459                        mutability: Default::default(),
460                    },
461                    ConfigField {
462                        key: "my_vector_of_uint32".to_string(),
463                        value: Vector(Uint32Vector(Box::from([3, 4, 5]))),
464                        mutability: Default::default(),
465                    },
466                    ConfigField {
467                        key: "my_vector_of_uint64".to_string(),
468                        value: Vector(Uint64Vector(Box::from([4, 5, 6]))),
469                        mutability: Default::default(),
470                    },
471                    ConfigField {
472                        key: "my_vector_of_int8".to_string(),
473                        value: Vector(Int8Vector(Box::from([-1, -2, 3]))),
474                        mutability: Default::default(),
475                    },
476                    ConfigField {
477                        key: "my_vector_of_int16".to_string(),
478                        value: Vector(Int16Vector(Box::from([-2, -3, 4]))),
479                        mutability: Default::default(),
480                    },
481                    ConfigField {
482                        key: "my_vector_of_int32".to_string(),
483                        value: Vector(Int32Vector(Box::from([-3, -4, 5]))),
484                        mutability: Default::default(),
485                    },
486                    ConfigField {
487                        key: "my_vector_of_int64".to_string(),
488                        value: Vector(Int64Vector(Box::from([-4, -5, 6]))),
489                        mutability: Default::default(),
490                    },
491                    ConfigField {
492                        key: "my_vector_of_string".to_string(),
493                        value: Vector(StringVector(Box::from(["valid".into(), "valid".into()]))),
494                        mutability: Default::default(),
495                    },
496                ],
497                checksum: decl.checksum.clone(),
498            }
499        );
500
501        let encoded = resolved.encode_as_fidl_struct();
502
503        let checksum_len = u16::from_le_bytes(encoded[..2].try_into().unwrap());
504        let struct_start = 2 + checksum_len as usize;
505        assert_eq!(&encoded[2..struct_start], [0; 32]);
506
507        let decoded: BasicSuccessSchema = fidl::unpersist(&encoded[struct_start..]).unwrap();
508        assert_eq!(
509            decoded,
510            BasicSuccessSchema {
511                my_flag: false,
512                my_uint8: 255,
513                my_uint16: 65535,
514                my_uint32: 4000000000,
515                my_uint64: 8000000000,
516                my_int8: -127,
517                my_int16: -32766,
518                my_int32: -2000000000,
519                my_int64: -4000000000,
520                my_string: "hello, world!".into(),
521                my_vector_of_flag: vec![true, false],
522                my_vector_of_uint8: vec![1, 2, 3],
523                my_vector_of_uint16: vec![2, 3, 4],
524                my_vector_of_uint32: vec![3, 4, 5],
525                my_vector_of_uint64: vec![4, 5, 6],
526                my_vector_of_int8: vec![-1, -2, 3],
527                my_vector_of_int16: vec![-2, -3, 4],
528                my_vector_of_int32: vec![-3, -4, 5],
529                my_vector_of_int64: vec![-4, -5, 6],
530                my_vector_of_string: vec!["valid".into(), "valid".into()],
531            }
532        );
533    }
534
535    #[test]
536    fn checksums_must_match() {
537        let expected = ConfigChecksum::Sha256([0; 32]);
538        let received = ConfigChecksum::Sha256([0xFF; 32]);
539        let decl = config_decl! {
540            ck@ expected.clone(),
541            foo: { bool },
542        };
543        let specs = values_data! [
544            ck@ received.clone(),
545            ConfigValue::Single(ConfigSingleValue::Bool(true)),
546        ];
547        assert_eq!(
548            ConfigFields::resolve(&decl, specs, None).unwrap_err(),
549            ResolutionError::ChecksumFailure { expected, received }
550        );
551    }
552
553    #[test]
554    fn too_many_values_fails() {
555        let decl = config_decl! {
556            ck@ ConfigChecksum::Sha256([0; 32]),
557            foo: { bool },
558        };
559        let specs = values_data! [
560            ck@ decl.checksum.clone(),
561            ConfigValue::Single(ConfigSingleValue::Bool(true)),
562            ConfigValue::Single(ConfigSingleValue::Bool(false)),
563        ];
564        assert_eq!(
565            ConfigFields::resolve(&decl, specs, None).unwrap_err(),
566            ResolutionError::WrongNumberOfValues { expected: 1, received: 2 }
567        );
568    }
569
570    #[test]
571    fn not_enough_values_fails() {
572        let decl = config_decl! {
573            ck@ ConfigChecksum::Sha256([0; 32]),
574            foo: { bool },
575        };
576        let specs = values_data! {
577            ck@ decl.checksum.clone(),
578        };
579        assert_eq!(
580            ConfigFields::resolve(&decl, specs, None).unwrap_err(),
581            ResolutionError::WrongNumberOfValues { expected: 1, received: 0 }
582        );
583    }
584
585    #[test]
586    fn string_length_is_validated() {
587        let decl = config_decl! {
588            ck@ ConfigChecksum::Sha256([0; 32]),
589            foo: { string, max_size: 10 },
590        };
591        let specs = values_data! [
592            ck@ decl.checksum.clone(),
593            ConfigValue::Single(ConfigSingleValue::String("hello, world!".into())),
594        ];
595        assert_eq!(
596            ConfigFields::resolve(&decl, specs, None).unwrap_err(),
597            ResolutionError::InvalidValue {
598                key: "foo".to_string(),
599                source: ValueError::StringTooLong { max: 10, actual: 13 },
600            }
601        );
602    }
603
604    #[test]
605    fn vector_length_is_validated() {
606        let decl = config_decl! {
607            ck@ ConfigChecksum::Sha256([0; 32]),
608            foo: { vector, element: uint8, max_count: 2 },
609        };
610        let specs = values_data! [
611            ck@ decl.checksum.clone(),
612            ConfigValue::Vector(ConfigVectorValue::Uint8Vector(Box::from([1, 2, 3]))),
613        ];
614        assert_eq!(
615            ConfigFields::resolve(&decl, specs, None).unwrap_err(),
616            ResolutionError::InvalidValue {
617                key: "foo".to_string(),
618                source: ValueError::VectorTooLong { max: 2, actual: 3 },
619            }
620        );
621    }
622
623    #[test]
624    fn vector_elements_validated() {
625        let decl = config_decl! {
626            ck@ ConfigChecksum::Sha256([0; 32]),
627            foo: { vector, element: { string, max_size: 5 }, max_count: 2 },
628        };
629        let specs = values_data! [
630            ck@ decl.checksum.clone(),
631            ConfigValue::Vector(ConfigVectorValue::StringVector(Box::from([
632                "valid".into(),
633                "invalid".into(),
634            ]))),
635        ];
636        assert_eq!(
637            ConfigFields::resolve(&decl, specs, None).unwrap_err(),
638            ResolutionError::InvalidValue {
639                key: "foo".to_string(),
640                source: ValueError::VectorElementInvalid {
641                    offset: 1,
642                    source: Box::new(ValueError::StringTooLong { max: 5, actual: 7 })
643                },
644            }
645        );
646    }
647
648    #[test]
649    fn parent_overrides_take_precedence() {
650        let overridden_key = "my_flag".to_string();
651        let defaulted_key = "my_other_flag".to_string();
652        let decl = cm_rust::ConfigDecl {
653            fields: Box::from([
654                cm_rust::ConfigField {
655                    key: overridden_key.clone(),
656                    type_: cm_rust::ConfigValueType::Bool,
657                    mutability: ConfigMutability::PARENT,
658                },
659                cm_rust::ConfigField {
660                    key: defaulted_key.clone(),
661                    type_: cm_rust::ConfigValueType::Bool,
662                    mutability: ConfigMutability::empty(),
663                },
664            ]),
665            checksum: ConfigChecksum::Sha256([0; 32]),
666            value_source: cm_rust::ConfigValueSource::PackagePath("fake.cvf".to_string()),
667        };
668
669        let packaged = values_data![
670            ck@ decl.checksum.clone(),
671            Single(Bool(false)),
672            Single(Bool(false)),
673        ];
674
675        let expected_value = Single(Bool(true));
676        let overrides =
677            vec![ConfigOverride { key: overridden_key.clone(), value: expected_value.clone() }];
678
679        assert_eq!(
680            ConfigFields::resolve(&decl, packaged, Some(&overrides)).unwrap(),
681            ConfigFields {
682                fields: vec![
683                    ConfigField {
684                        key: overridden_key.clone(),
685                        value: expected_value,
686                        mutability: ConfigMutability::PARENT,
687                    },
688                    ConfigField {
689                        key: defaulted_key.clone(),
690                        value: Single(Bool(false)),
691                        mutability: ConfigMutability::empty(),
692                    },
693                ],
694                checksum: decl.checksum.clone(),
695            }
696        );
697    }
698
699    #[test]
700    fn overrides_must_match_declared_fields() {
701        let decl = cm_rust::ConfigDecl {
702            fields: Box::from([cm_rust::ConfigField {
703                key: "my_flag".to_string(),
704                type_: cm_rust::ConfigValueType::Bool,
705                mutability: ConfigMutability::PARENT,
706            }]),
707            checksum: ConfigChecksum::Sha256([0; 32]),
708            value_source: cm_rust::ConfigValueSource::PackagePath("fake.cvf".to_string()),
709        };
710
711        let packaged = values_data![
712            ck@ decl.checksum.clone(),
713            Single(Bool(false)),
714        ];
715
716        let overridden_key = "not_my_flag".to_string();
717        let expected_value = Single(Bool(true));
718        let overrides =
719            vec![ConfigOverride { key: overridden_key.clone(), value: expected_value.clone() }];
720
721        assert_eq!(
722            ConfigFields::resolve(&decl, packaged, Some(&overrides)).unwrap_err(),
723            ResolutionError::FieldDoesNotExist { key: overridden_key.clone() },
724        );
725    }
726
727    #[test]
728    fn overrides_must_be_for_mutable_by_parent_fields() {
729        let overridden_key = "my_flag".to_string();
730        let defaulted_key = "my_other_flag".to_string();
731        let decl = cm_rust::ConfigDecl {
732            fields: Box::from([
733                cm_rust::ConfigField {
734                    key: overridden_key.clone(),
735                    type_: cm_rust::ConfigValueType::Bool,
736                    mutability: ConfigMutability::empty(),
737                },
738                // this field having parent mutability should not allow the other field to mutate
739                cm_rust::ConfigField {
740                    key: defaulted_key.clone(),
741                    type_: cm_rust::ConfigValueType::Bool,
742                    mutability: ConfigMutability::PARENT,
743                },
744            ]),
745            checksum: ConfigChecksum::Sha256([0; 32]),
746            value_source: cm_rust::ConfigValueSource::PackagePath("fake.cvf".to_string()),
747        };
748
749        let packaged = values_data![
750            ck@ decl.checksum.clone(),
751            Single(Bool(false)),
752            Single(Bool(false)),
753        ];
754
755        let overrides =
756            vec![ConfigOverride { key: overridden_key.clone(), value: Single(Bool(true)) }];
757
758        assert_eq!(
759            ConfigFields::resolve(&decl, packaged, Some(&overrides)).unwrap_err(),
760            ResolutionError::FieldIsNotMutable { key: overridden_key.clone() },
761        );
762    }
763
764    #[test]
765    fn overrides_must_match_declared_type_exactly() {
766        let overridden_key = "my_flag".to_string();
767        let decl = cm_rust::ConfigDecl {
768            fields: Box::from([cm_rust::ConfigField {
769                key: overridden_key.clone(),
770                type_: cm_rust::ConfigValueType::Bool,
771                mutability: ConfigMutability::PARENT,
772            }]),
773            checksum: ConfigChecksum::Sha256([0; 32]),
774            value_source: cm_rust::ConfigValueSource::PackagePath("fake.cvf".to_string()),
775        };
776
777        let packaged = values_data![
778            ck@ decl.checksum.clone(),
779            Single(Bool(false)),
780        ];
781
782        let overrides =
783            vec![ConfigOverride { key: overridden_key.clone(), value: Single(Uint8(1)) }];
784
785        assert_eq!(
786            ConfigFields::resolve(&decl, packaged, Some(&overrides)).unwrap_err(),
787            ResolutionError::InvalidValue {
788                key: overridden_key.clone(),
789                source: ValueError::TypeMismatch {
790                    expected: "Bool".to_string(),
791                    received: "Single(Uint8(1))".to_string()
792                },
793            },
794        );
795    }
796
797    macro_rules! type_mismatch_test {
798        ($test_name:ident: { $($ty_toks:tt)* }, $valid_spec:pat) => {
799            #[test]
800            fn $test_name() {
801                let decl = ConfigFieldDecl {
802                    key: "test_key".to_string(),
803                    type_: fidl_fuchsia_component_config_ext::config_ty!($($ty_toks)*),
804                    mutability: Default::default(),
805                };
806                for value in [
807                    // one value of each type
808                    Single(Bool(true)),
809                    Single(Uint8(1)),
810                    Single(Uint16(1)),
811                    Single(Uint32(1)),
812                    Single(Uint64(1)),
813                    Single(Int8(1)),
814                    Single(Int16(1)),
815                    Single(Int32(1)),
816                    Single(Int64(1)),
817                    Single(String("".to_string())),
818                    Vector(BoolVector(Box::from([]))),
819                    Vector(Uint8Vector(Box::from([]))),
820                    Vector(Uint16Vector(Box::from([]))),
821                    Vector(Uint32Vector(Box::from([]))),
822                    Vector(Uint64Vector(Box::from([]))),
823                    Vector(Int8Vector(Box::from([]))),
824                    Vector(Int16Vector(Box::from([]))),
825                    Vector(Int32Vector(Box::from([]))),
826                    Vector(Int64Vector(Box::from([]))),
827                    Vector(StringVector(Box::from([]))),
828                ] {
829                    let should_succeed = matches!(value, $valid_spec);
830                    match ConfigField::resolve(value, &decl) {
831                        Ok(..) if should_succeed => (),
832                        Err(ValueError::TypeMismatch { .. }) if !should_succeed => (),
833                        other => panic!(
834                            "test case {:?} received unexpected resolved value {:#?}",
835                            decl, other
836                        ),
837                    }
838                }
839            }
840        };
841    }
842
843    type_mismatch_test!(bool_type_mismatches: { bool }, Single(Bool(..)));
844    type_mismatch_test!(uint8_type_mismatches:  { uint8 },  Single(Uint8(..)));
845    type_mismatch_test!(uint16_type_mismatches: { uint16 }, Single(Uint16(..)));
846    type_mismatch_test!(uint32_type_mismatches: { uint32 }, Single(Uint32(..)));
847    type_mismatch_test!(uint64_type_mismatches: { uint64 }, Single(Uint64(..)));
848    type_mismatch_test!(int8_type_mismatches:  { int8 },  Single(Int8(..)));
849    type_mismatch_test!(int16_type_mismatches: { int16 }, Single(Int16(..)));
850    type_mismatch_test!(int32_type_mismatches: { int32 }, Single(Int32(..)));
851    type_mismatch_test!(int64_type_mismatches: { int64 }, Single(Int64(..)));
852    type_mismatch_test!(string_type_mismatches: { string, max_size: 10 }, Single(String(..)));
853
854    type_mismatch_test!(
855        bool_vector_type_mismatches: { vector, element: bool, max_count: 1 }, Vector(BoolVector(..))
856    );
857    type_mismatch_test!(
858        uint8_vector_type_mismatches:
859        { vector, element: uint8, max_count: 1 },
860        Vector(Uint8Vector(..))
861    );
862    type_mismatch_test!(
863        uint16_vector_type_mismatches:
864        { vector, element: uint16, max_count: 1 },
865        Vector(Uint16Vector(..))
866    );
867    type_mismatch_test!(
868        uint32_vector_type_mismatches:
869        { vector, element: uint32, max_count: 1 },
870        Vector(Uint32Vector(..))
871    );
872    type_mismatch_test!(
873        uint64_vector_type_mismatches:
874        { vector, element: uint64, max_count: 1 },
875        Vector(Uint64Vector(..))
876    );
877    type_mismatch_test!(
878        int8_vector_type_mismatches:
879        { vector, element: int8, max_count: 1 },
880        Vector(Int8Vector(..))
881    );
882    type_mismatch_test!(
883        int16_vector_type_mismatches:
884        { vector, element: int16, max_count: 1 },
885        Vector(Int16Vector(..))
886    );
887    type_mismatch_test!(
888        int32_vector_type_mismatches:
889        { vector, element: int32, max_count: 1 },
890        Vector(Int32Vector(..))
891    );
892    type_mismatch_test!(
893        int64_vector_type_mismatches:
894        { vector, element: int64, max_count: 1 },
895        Vector(Int64Vector(..))
896    );
897    type_mismatch_test!(
898        string_vector_type_mismatches:
899        { vector, element: { string, max_size: 10 }, max_count: 1 },
900        Vector(StringVector(..))
901    );
902}