fidl_fuchsia_update_installer_ext/
options.rs1use cobalt_client::traits::AsEventCode;
8use fuchsia_inspect::{self as inspect, ArrayProperty as _};
9use proptest::prelude::*;
10use proptest_derive::Arbitrary;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14#[derive(Clone, Debug, Copy, PartialEq, Arbitrary, Serialize, Deserialize)]
16pub enum Initiator {
17 User,
20
21 Service,
23}
24
25impl Initiator {
26 fn name(&self) -> &'static str {
27 match self {
28 Initiator::User => "User",
29 Initiator::Service => "Service",
30 }
31 }
32}
33
34impl AsEventCode for Initiator {
35 fn as_event_code(&self) -> u32 {
36 use cobalt_sw_delivery_registry::SoftwareDeliveryMetricDimensionInitiator::*;
37 match self {
38 Initiator::Service => AutomaticUpdateCheck,
39 Initiator::User => UserInitiatedCheck,
40 }
41 .as_event_code()
42 }
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary, Serialize, Deserialize)]
47pub struct Range {
48 pub offset: u64,
50 pub size: u64,
52}
53
54#[derive(Clone, Debug, PartialEq, Arbitrary, Serialize, Deserialize)]
56pub struct Options {
57 pub initiator: Initiator,
59
60 pub allow_attach_to_existing_attempt: bool,
67
68 pub should_write_recovery: bool,
71
72 pub manifest_range: Option<Range>,
74
75 #[serde(skip)]
77 #[proptest(strategy = "prop::collection::vec(any_header(), 0..2)")]
78 pub manifest_headers: Vec<fidl_fuchsia_net_http::Header>,
79
80 #[serde(default = "default_should_commit")]
85 pub should_commit: bool,
86}
87
88fn default_should_commit() -> bool {
89 true
90}
91
92fn any_header() -> impl Strategy<Value = fidl_fuchsia_net_http::Header> {
93 (prop::collection::vec(any::<u8>(), 0..10), prop::collection::vec(any::<u8>(), 0..10))
94 .prop_map(|(name, value)| fidl_fuchsia_net_http::Header { name, value })
95}
96
97impl Options {
98 pub fn write_to_inspect(&self, node: &inspect::Node) {
100 let Options {
101 initiator,
102 allow_attach_to_existing_attempt,
103 should_write_recovery,
104 manifest_range,
105 manifest_headers,
106 should_commit,
107 } = self;
108 node.record_string("initiator", initiator.name());
109 node.record_bool("allow_attach_to_existing_attempt", *allow_attach_to_existing_attempt);
110 node.record_bool("should_write_recovery", *should_write_recovery);
111 node.record_bool("should_commit", *should_commit);
112 if let Some(range) = manifest_range {
113 node.record_child("manifest_range", |range_node| {
114 range_node.record_uint("offset", range.offset);
115 range_node.record_uint("size", range.size);
116 });
117 }
118 if !manifest_headers.is_empty() {
120 let header_names =
121 node.create_string_array("manifest_header_names", manifest_headers.len());
122 for (i, header) in manifest_headers.iter().enumerate() {
123 header_names.set(i, String::from_utf8_lossy(&header.name));
124 }
125 node.record(header_names);
126 }
127 }
128}
129
130#[derive(Error, Debug, PartialEq)]
132pub enum OptionsParseError {
133 #[error("missing initiator")]
135 MissingInitiator,
136}
137
138impl From<fidl_fuchsia_update_installer::Range> for Range {
139 fn from(data: fidl_fuchsia_update_installer::Range) -> Self {
140 Self { offset: data.offset, size: data.size }
141 }
142}
143
144impl From<&Range> for fidl_fuchsia_update_installer::Range {
145 fn from(range: &Range) -> Self {
146 Self { offset: range.offset, size: range.size }
147 }
148}
149
150impl TryFrom<fidl_fuchsia_update_installer::Options> for Options {
151 type Error = OptionsParseError;
152
153 fn try_from(data: fidl_fuchsia_update_installer::Options) -> Result<Self, OptionsParseError> {
154 let initiator =
155 data.initiator.map(|o| o.into()).ok_or(OptionsParseError::MissingInitiator)?;
156
157 let manifest_range = data.manifest_range.map(Range::from);
158 let manifest_headers = data.manifest_headers.unwrap_or_default();
159
160 Ok(Self {
161 initiator,
162 allow_attach_to_existing_attempt: data
163 .allow_attach_to_existing_attempt
164 .unwrap_or(false),
165 should_write_recovery: data.should_write_recovery.unwrap_or(true),
166 manifest_range,
167 manifest_headers,
168 should_commit: data.should_commit.unwrap_or(true),
169 })
170 }
171}
172
173impl From<&Options> for fidl_fuchsia_update_installer::Options {
174 fn from(options: &Options) -> Self {
175 Self {
176 initiator: Some(options.initiator.into()),
177 allow_attach_to_existing_attempt: Some(options.allow_attach_to_existing_attempt),
178 should_write_recovery: Some(options.should_write_recovery),
179 manifest_range: options.manifest_range.as_ref().map(|r| r.into()),
180 manifest_headers: if options.manifest_headers.is_empty() {
181 None
182 } else {
183 Some(options.manifest_headers.clone())
184 },
185 should_commit: Some(options.should_commit),
186 ..Default::default()
187 }
188 }
189}
190
191impl From<Options> for fidl_fuchsia_update_installer::Options {
192 fn from(data: Options) -> Self {
193 (&data).into()
194 }
195}
196
197impl From<fidl_fuchsia_update_installer::Initiator> for Initiator {
198 fn from(fidl_initiator: fidl_fuchsia_update_installer::Initiator) -> Self {
199 match fidl_initiator {
200 fidl_fuchsia_update_installer::Initiator::User => Initiator::User,
201 fidl_fuchsia_update_installer::Initiator::Service => Initiator::Service,
202 }
203 }
204}
205
206impl From<Initiator> for fidl_fuchsia_update_installer::Initiator {
207 fn from(initiator: Initiator) -> Self {
208 match initiator {
209 Initiator::User => fidl_fuchsia_update_installer::Initiator::User,
210 Initiator::Service => fidl_fuchsia_update_installer::Initiator::Service,
211 }
212 }
213}
214
215#[cfg(test)]
216mod tests {
217
218 use super::*;
219
220 proptest! {
221 #[test]
222 fn options_roundtrips_through_fidl(options: Options) {
225 let as_fidl: fidl_fuchsia_update_installer::Options = options.clone().into();
226 prop_assert_eq!(as_fidl.try_into(), Ok(options));
227 }
228
229 #[test]
230 fn fidl_options_sans_initiator_error(options: Options) {
232 let mut as_fidl: fidl_fuchsia_update_installer::Options = options.into();
233 as_fidl.initiator = None;
234 prop_assert_eq!(Options::try_from(as_fidl), Err(OptionsParseError::MissingInitiator));
235 }
236 }
237}