Skip to main content

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/// A byte range.
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary, Serialize, Deserialize)]
34pub struct Range {
35    /// The start offset in bytes.
36    pub offset: u64,
37    /// The size of the range in bytes.
38    pub size: u64,
39}
40
41/// Configuration options for an update attempt.
42#[derive(Clone, Debug, PartialEq, Arbitrary, Serialize, Deserialize)]
43pub struct Options {
44    /// What initiated this update attempt.
45    pub initiator: Initiator,
46
47    /// If an update is already in progress, it's acceptable to instead attach a
48    /// Monitor to that in-progress update instead of failing this request to
49    /// install the update.  Setting this option to true may convert situations
50    /// that would have resulted in the ALREADY_IN_PROGRESS to be treated as
51    /// non-error cases. A controller, if provided, will be ignored if the
52    /// running update attempt already has a controller.
53    pub allow_attach_to_existing_attempt: bool,
54
55    /// Determines if the installer should update the recovery partition if an
56    /// update is available.  Defaults to true.
57    pub should_write_recovery: bool,
58
59    /// Optional range parameter to be used as the `Range` HTTP header when fetching the manifest.
60    pub manifest_range: Option<Range>,
61}
62
63impl Options {
64    /// Serializes Options to a Fuchsia Inspect node.
65    pub fn write_to_inspect(&self, node: &inspect::Node) {
66        let Options {
67            initiator,
68            allow_attach_to_existing_attempt,
69            should_write_recovery,
70            manifest_range,
71        } = self;
72        node.record_string("initiator", initiator.name());
73        node.record_bool("allow_attach_to_existing_attempt", *allow_attach_to_existing_attempt);
74        node.record_bool("should_write_recovery", *should_write_recovery);
75        if let Some(range) = manifest_range {
76            node.record_child("manifest_range", |range_node| {
77                range_node.record_uint("offset", range.offset);
78                range_node.record_uint("size", range.size);
79            });
80        }
81    }
82}
83
84/// Errors for parsing fidl_update_installer_ext Options struct.
85#[derive(Error, Debug, PartialEq)]
86pub enum OptionsParseError {
87    /// Initiator is None.
88    #[error("missing initiator")]
89    MissingInitiator,
90}
91
92impl From<fidl_fuchsia_update_installer::Range> for Range {
93    fn from(data: fidl_fuchsia_update_installer::Range) -> Self {
94        Self { offset: data.offset, size: data.size }
95    }
96}
97
98impl From<&Range> for fidl_fuchsia_update_installer::Range {
99    fn from(range: &Range) -> Self {
100        Self { offset: range.offset, size: range.size }
101    }
102}
103
104impl TryFrom<fidl_fuchsia_update_installer::Options> for Options {
105    type Error = OptionsParseError;
106
107    fn try_from(data: fidl_fuchsia_update_installer::Options) -> Result<Self, OptionsParseError> {
108        let initiator =
109            data.initiator.map(|o| o.into()).ok_or(OptionsParseError::MissingInitiator)?;
110
111        let manifest_range = data.manifest_range.map(Range::from);
112
113        Ok(Self {
114            initiator,
115            allow_attach_to_existing_attempt: data
116                .allow_attach_to_existing_attempt
117                .unwrap_or(false),
118            should_write_recovery: data.should_write_recovery.unwrap_or(true),
119            manifest_range,
120        })
121    }
122}
123
124impl From<&Options> for fidl_fuchsia_update_installer::Options {
125    fn from(options: &Options) -> Self {
126        Self {
127            initiator: Some(options.initiator.into()),
128            allow_attach_to_existing_attempt: Some(options.allow_attach_to_existing_attempt),
129            should_write_recovery: Some(options.should_write_recovery),
130            manifest_range: options.manifest_range.as_ref().map(|r| r.into()),
131            ..Default::default()
132        }
133    }
134}
135
136impl From<Options> for fidl_fuchsia_update_installer::Options {
137    fn from(data: Options) -> Self {
138        (&data).into()
139    }
140}
141
142impl From<fidl_fuchsia_update_installer::Initiator> for Initiator {
143    fn from(fidl_initiator: fidl_fuchsia_update_installer::Initiator) -> Self {
144        match fidl_initiator {
145            fidl_fuchsia_update_installer::Initiator::User => Initiator::User,
146            fidl_fuchsia_update_installer::Initiator::Service => Initiator::Service,
147        }
148    }
149}
150
151impl From<Initiator> for fidl_fuchsia_update_installer::Initiator {
152    fn from(initiator: Initiator) -> Self {
153        match initiator {
154            Initiator::User => fidl_fuchsia_update_installer::Initiator::User,
155            Initiator::Service => fidl_fuchsia_update_installer::Initiator::Service,
156        }
157    }
158}
159
160#[cfg(test)]
161mod tests {
162
163    use super::*;
164    use proptest::prelude::*;
165
166    proptest! {
167        #[test]
168        /// Verifies that converting any instance of Options to fidl_fuchsia_update_installer::Options
169        /// and back to Options produces exactly the same options that we started with.
170        fn options_roundtrips_through_fidl(options: Options) {
171            let as_fidl: fidl_fuchsia_update_installer::Options = options.clone().into();
172            prop_assert_eq!(as_fidl.try_into(), Ok(options));
173        }
174
175        #[test]
176        /// Verifies that a fidl_fuchsia_update_installer::Options without an Initiator raises an error.
177        fn fidl_options_sans_initiator_error(options: Options) {
178            let mut as_fidl: fidl_fuchsia_update_installer::Options = options.into();
179            as_fidl.initiator = None;
180            prop_assert_eq!(Options::try_from(as_fidl), Err(OptionsParseError::MissingInitiator));
181        }
182    }
183}