blob: cb70b28676e351573330843879cf4e13a53346b0 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Wrapper types for the Options table.
use {
fidl_fuchsia_update_installer, fuchsia_inspect as inspect,
proptest_derive::Arbitrary,
serde::{Deserialize, Serialize},
std::convert::TryFrom,
thiserror::Error,
};
/// Who or what initiated the update installation.
#[derive(Clone, Debug, Copy, PartialEq, Arbitrary, Serialize, Deserialize)]
pub enum Initiator {
/// The install was initiated by an interactive user, or the user is
/// otherwise blocked and waiting for the result of this update.
User,
/// The install was initiated by a service, in the background.
Service,
}
impl Initiator {
fn name(&self) -> &'static str {
match self {
Initiator::User => "User",
Initiator::Service => "Service",
}
}
}
/// Configuration options for an update attempt.
#[derive(Clone, Debug, PartialEq, Arbitrary, Serialize, Deserialize)]
pub struct Options {
/// What initiated this update attempt.
pub initiator: Initiator,
/// If an update is already in progress, it's acceptable to instead attach a
/// Monitor to that in-progress update instead of failing this request to
/// install the update. Setting this option to true may convert situations
/// that would have resulted in the ALREADY_IN_PROGRESS to be treated as
/// non-error cases. A controller, if provided, will be ignored if the
/// running update attempt already has a controller.
pub allow_attach_to_existing_attempt: bool,
/// Determines if the installer should update the recovery partition if an
/// update is available. Defaults to true.
pub should_write_recovery: bool,
}
impl Options {
/// Serializes Options to a Fuchsia Inspect node.
pub fn write_to_inspect(&self, node: &inspect::Node) {
let Options { initiator, allow_attach_to_existing_attempt, should_write_recovery } = self;
node.record_string("initiator", initiator.name().to_string());
node.record_bool("allow_attach_to_existing_attempt", *allow_attach_to_existing_attempt);
node.record_bool("should_write_recovery", *should_write_recovery);
}
}
/// Errors for parsing fidl_update_installer_ext Options struct.
#[derive(Error, Debug, PartialEq)]
pub enum OptionsParseError {
/// Initiator is None.
#[error("missing initiator")]
MissingInitiator,
}
impl TryFrom<fidl_fuchsia_update_installer::Options> for Options {
type Error = OptionsParseError;
fn try_from(data: fidl_fuchsia_update_installer::Options) -> Result<Self, OptionsParseError> {
let initiator =
data.initiator.map(|o| o.into()).ok_or(OptionsParseError::MissingInitiator)?;
Ok(Self {
initiator,
allow_attach_to_existing_attempt: data
.allow_attach_to_existing_attempt
.unwrap_or(false),
should_write_recovery: data.should_write_recovery.unwrap_or(true),
})
}
}
impl From<&Options> for fidl_fuchsia_update_installer::Options {
fn from(options: &Options) -> Self {
Self {
initiator: Some(options.initiator.into()),
allow_attach_to_existing_attempt: Some(options.allow_attach_to_existing_attempt.into()),
should_write_recovery: Some(options.should_write_recovery.into()),
..Self::EMPTY
}
}
}
impl From<Options> for fidl_fuchsia_update_installer::Options {
fn from(data: Options) -> Self {
(&data).into()
}
}
impl From<fidl_fuchsia_update_installer::Initiator> for Initiator {
fn from(fidl_initiator: fidl_fuchsia_update_installer::Initiator) -> Self {
match fidl_initiator {
fidl_fuchsia_update_installer::Initiator::User => Initiator::User,
fidl_fuchsia_update_installer::Initiator::Service => Initiator::Service,
}
}
}
impl From<Initiator> for fidl_fuchsia_update_installer::Initiator {
fn from(initiator: Initiator) -> Self {
match initiator {
Initiator::User => fidl_fuchsia_update_installer::Initiator::User,
Initiator::Service => fidl_fuchsia_update_installer::Initiator::Service,
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
proptest::prelude::*,
std::convert::{TryFrom, TryInto},
};
proptest! {
#[test]
/// Verifies that converting any instance of Options to fidl_fuchsia_update_installer::Options
/// and back to Options produces exactly the same options that we started with.
fn options_roundtrips_through_fidl(options: Options) {
let as_fidl: fidl_fuchsia_update_installer::Options = options.clone().into();
prop_assert_eq!(as_fidl.try_into(), Ok(options));
}
#[test]
/// Verifies that a fidl_fuchsia_update_installer::Options without an Initiator raises an error.
fn fidl_options_sans_initiator_error(options: Options) {
let mut as_fidl: fidl_fuchsia_update_installer::Options = options.clone().into();
as_fidl.initiator = None;
prop_assert_eq!(Options::try_from(as_fidl), Err(OptionsParseError::MissingInitiator));
}
}
}