fidl_fuchsia_update_installer_ext/
options.rs

1// Copyright 2020 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//! Wrapper types for the Options table.
6
7use fuchsia_inspect as inspect;
8use proptest_derive::Arbitrary;
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12/// Who or what initiated the update installation.
13#[derive(Clone, Debug, Copy, PartialEq, Arbitrary, Serialize, Deserialize)]
14pub enum Initiator {
15    /// The install was initiated by an interactive user, or the user is
16    /// otherwise blocked and waiting for the result of this update.
17    User,
18
19    /// The install was initiated by a service, in the background.
20    Service,
21}
22
23impl Initiator {
24    fn name(&self) -> &'static str {
25        match self {
26            Initiator::User => "User",
27            Initiator::Service => "Service",
28        }
29    }
30}
31
32/// Configuration options for an update attempt.
33#[derive(Clone, Debug, PartialEq, Arbitrary, Serialize, Deserialize)]
34pub struct Options {
35    /// What initiated this update attempt.
36    pub initiator: Initiator,
37
38    /// If an update is already in progress, it's acceptable to instead attach a
39    /// Monitor to that in-progress update instead of failing this request to
40    /// install the update.  Setting this option to true may convert situations
41    /// that would have resulted in the ALREADY_IN_PROGRESS to be treated as
42    /// non-error cases. A controller, if provided, will be ignored if the
43    /// running update attempt already has a controller.
44    pub allow_attach_to_existing_attempt: bool,
45
46    /// Determines if the installer should update the recovery partition if an
47    /// update is available.  Defaults to true.
48    pub should_write_recovery: bool,
49}
50
51impl Options {
52    /// Serializes Options to a Fuchsia Inspect node.
53    pub fn write_to_inspect(&self, node: &inspect::Node) {
54        let Options { initiator, allow_attach_to_existing_attempt, should_write_recovery } = self;
55        node.record_string("initiator", initiator.name());
56        node.record_bool("allow_attach_to_existing_attempt", *allow_attach_to_existing_attempt);
57        node.record_bool("should_write_recovery", *should_write_recovery);
58    }
59}
60
61/// Errors for parsing fidl_update_installer_ext Options struct.
62#[derive(Error, Debug, PartialEq)]
63pub enum OptionsParseError {
64    /// Initiator is None.
65    #[error("missing initiator")]
66    MissingInitiator,
67}
68
69impl TryFrom<fidl_fuchsia_update_installer::Options> for Options {
70    type Error = OptionsParseError;
71
72    fn try_from(data: fidl_fuchsia_update_installer::Options) -> Result<Self, OptionsParseError> {
73        let initiator =
74            data.initiator.map(|o| o.into()).ok_or(OptionsParseError::MissingInitiator)?;
75
76        Ok(Self {
77            initiator,
78            allow_attach_to_existing_attempt: data
79                .allow_attach_to_existing_attempt
80                .unwrap_or(false),
81            should_write_recovery: data.should_write_recovery.unwrap_or(true),
82        })
83    }
84}
85
86impl From<&Options> for fidl_fuchsia_update_installer::Options {
87    fn from(options: &Options) -> Self {
88        Self {
89            initiator: Some(options.initiator.into()),
90            allow_attach_to_existing_attempt: Some(options.allow_attach_to_existing_attempt),
91            should_write_recovery: Some(options.should_write_recovery),
92            ..Default::default()
93        }
94    }
95}
96
97impl From<Options> for fidl_fuchsia_update_installer::Options {
98    fn from(data: Options) -> Self {
99        (&data).into()
100    }
101}
102
103impl From<fidl_fuchsia_update_installer::Initiator> for Initiator {
104    fn from(fidl_initiator: fidl_fuchsia_update_installer::Initiator) -> Self {
105        match fidl_initiator {
106            fidl_fuchsia_update_installer::Initiator::User => Initiator::User,
107            fidl_fuchsia_update_installer::Initiator::Service => Initiator::Service,
108        }
109    }
110}
111
112impl From<Initiator> for fidl_fuchsia_update_installer::Initiator {
113    fn from(initiator: Initiator) -> Self {
114        match initiator {
115            Initiator::User => fidl_fuchsia_update_installer::Initiator::User,
116            Initiator::Service => fidl_fuchsia_update_installer::Initiator::Service,
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123
124    use super::*;
125    use proptest::prelude::*;
126
127    proptest! {
128        #[test]
129        /// Verifies that converting any instance of Options to fidl_fuchsia_update_installer::Options
130        /// and back to Options produces exactly the same options that we started with.
131        fn options_roundtrips_through_fidl(options: Options) {
132            let as_fidl: fidl_fuchsia_update_installer::Options = options.clone().into();
133            prop_assert_eq!(as_fidl.try_into(), Ok(options));
134        }
135
136        #[test]
137        /// Verifies that a fidl_fuchsia_update_installer::Options without an Initiator raises an error.
138        fn fidl_options_sans_initiator_error(options: Options) {
139            let mut as_fidl: fidl_fuchsia_update_installer::Options = options.into();
140            as_fidl.initiator = None;
141            prop_assert_eq!(Options::try_from(as_fidl), Err(OptionsParseError::MissingInitiator));
142        }
143    }
144}