fuchsia_component_config/
lib.rs

1// Copyright 2025 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//! Generic traits for configuration.
6
7use fuchsia_inspect::Node;
8use fuchsia_runtime::{take_startup_handle, HandleInfo, HandleType};
9
10pub trait Config: Sized {
11    /// Take the config startup handle and parse its contents.
12    ///
13    /// # Panics
14    ///
15    /// If the config startup handle was already taken or if it is not valid.
16    fn take_from_startup_handle() -> Self {
17        let handle_info = HandleInfo::new(HandleType::ComponentConfigVmo, 0);
18        let config_vmo: zx::Vmo =
19            take_startup_handle(handle_info).expect("Config VMO handle must be present.").into();
20        Self::from_vmo(&config_vmo).expect("Config VMO handle must be valid.")
21    }
22
23    /// Parse `Self` from `vmo`.
24    fn from_vmo(vmo: &zx::Vmo) -> Result<Self, Error> {
25        let config_size = vmo.get_content_size().map_err(Error::GettingContentSize)?;
26        let config_bytes = vmo.read_to_vec(0, config_size).map_err(Error::ReadingConfigBytes)?;
27        Self::from_bytes(&config_bytes)
28    }
29
30    /// Parse `Self` from `bytes`.
31    fn from_bytes(bytes: &[u8]) -> Result<Self, Error>;
32
33    /// Record config into inspect node.
34    fn record_inspect(&self, inspector_node: &Node);
35}
36
37#[derive(Debug)]
38pub enum Error {
39    /// Failed to read the content size of the VMO.
40    GettingContentSize(zx::Status),
41    /// Failed to read the content of the VMO.
42    ReadingConfigBytes(zx::Status),
43    /// The VMO was too small for this config library.
44    TooFewBytes,
45    /// The VMO's config ABI checksum did not match this library's.
46    ChecksumMismatch { expected_checksum: Vec<u8>, observed_checksum: Vec<u8> },
47    /// Failed to parse the non-checksum bytes of the VMO as this library's FIDL type.
48    Unpersist(fidl::Error),
49}
50
51impl std::fmt::Display for Error {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        match self {
54            Self::GettingContentSize(status) => {
55                write!(f, "Failed to get content size: {status}")
56            }
57            Self::ReadingConfigBytes(status) => {
58                write!(f, "Failed to read VMO content: {status}")
59            }
60            Self::TooFewBytes => {
61                write!(f, "VMO content is not large enough for this config library.")
62            }
63            Self::ChecksumMismatch { expected_checksum, observed_checksum } => {
64                write!(
65                    f,
66                    "ABI checksum mismatch, expected {:?}, got {:?}",
67                    expected_checksum, observed_checksum,
68                )
69            }
70            Self::Unpersist(fidl_error) => {
71                write!(f, "Failed to parse contents of config VMO: {fidl_error}")
72            }
73        }
74    }
75}
76
77impl std::error::Error for Error {
78    #[allow(unused_parens, reason = "rustfmt errors without parens here")]
79    fn source(&self) -> Option<(&'_ (dyn std::error::Error + 'static))> {
80        match self {
81            Self::GettingContentSize(ref status) | Self::ReadingConfigBytes(ref status) => {
82                Some(status)
83            }
84            Self::TooFewBytes => None,
85            Self::ChecksumMismatch { .. } => None,
86            Self::Unpersist(ref fidl_error) => Some(fidl_error),
87        }
88    }
89    fn description(&self) -> &str {
90        match self {
91            Self::GettingContentSize(_) => "getting content size",
92            Self::ReadingConfigBytes(_) => "reading VMO contents",
93            Self::TooFewBytes => "VMO contents too small",
94            Self::ChecksumMismatch { .. } => "ABI checksum mismatch",
95            Self::Unpersist(_) => "FIDL parsing error",
96        }
97    }
98}