Skip to main content

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