apple_flat_package/
distribution.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Distribution XML file format.
6//!
7//! See https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html
8//! for Apple's documentation of this file format.
9
10use {
11    crate::PkgResult,
12    serde::{Deserialize, Serialize},
13    std::io::Read,
14};
15
16/// Represents a distribution XML file.
17#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
18#[serde(rename = "installer-gui-script", rename_all = "kebab-case")]
19pub struct Distribution {
20    #[serde(rename = "minSpecVersion")]
21    pub min_spec_version: u8,
22
23    // maxSpecVersion and verifiedSpecVersion are reserved attributes but not yet defined.
24    pub background: Option<Background>,
25    pub choice: Vec<Choice>,
26    pub choices_outline: ChoicesOutline,
27    pub conclusion: Option<Conclusion>,
28    pub domains: Option<Domains>,
29    pub installation_check: Option<InstallationCheck>,
30    pub license: Option<License>,
31    #[serde(default)]
32    pub locator: Vec<Locator>,
33    pub options: Option<Options>,
34    #[serde(default)]
35    pub pkg_ref: Vec<PkgRef>,
36    pub product: Option<Product>,
37    pub readme: Option<Readme>,
38    pub script: Option<Script>,
39    pub title: Option<Title>,
40    pub volume_check: Option<VolumeCheck>,
41    pub welcome: Option<Welcome>,
42}
43
44impl Distribution {
45    /// Parse Distribution XML from a reader.
46    pub fn from_reader(reader: impl Read) -> PkgResult<Self> {
47        let mut de =
48            serde_xml_rs::Deserializer::new_from_reader(reader).non_contiguous_seq_elements(true);
49
50        Ok(Self::deserialize(&mut de)?)
51    }
52
53    /// Parse Distribution XML from a string.
54    pub fn from_xml(s: &str) -> PkgResult<Self> {
55        let mut de = serde_xml_rs::Deserializer::new_from_reader(s.as_bytes())
56            .non_contiguous_seq_elements(true);
57
58        Ok(Self::deserialize(&mut de)?)
59    }
60}
61
62#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
63pub struct AllowedOsVersions {
64    #[serde(rename = "os-version")]
65    os_versions: Vec<OsVersion>,
66}
67
68#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
69pub struct App {
70    pub id: String,
71}
72
73#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
74#[serde(rename_all = "kebab-case")]
75pub struct Background {
76    // TODO convert to enum.
77    pub alignment: Option<String>,
78    pub file: String,
79    pub mime_type: Option<String>,
80    // TODO convert to enum
81    pub scaling: Option<String>,
82    pub uti: Option<String>,
83}
84
85#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
86pub struct Bundle {
87    #[serde(rename = "CFBundleShortVersionString")]
88    pub cf_bundle_short_version_string: Option<String>,
89    #[serde(rename = "CFBundleVersion")]
90    pub cf_bundle_version: Option<String>,
91    pub id: String,
92    pub path: String,
93    pub search: Option<bool>,
94    // BuildVersion, SourceVersion reserved attributes.
95}
96
97#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
98pub struct BundleVersion {
99    #[serde(default)]
100    pub bundle: Vec<Bundle>,
101}
102
103#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
104pub struct Choice {
105    // The naming format on this element is all over the place.
106    #[serde(rename = "customLocation")]
107    pub custom_location: Option<String>,
108    #[serde(rename = "customLocationAllowAlternateVolumes")]
109    pub custom_location_allow_alternative_volumes: Option<bool>,
110    pub description: Option<String>,
111    #[serde(rename = "description-mime-type")]
112    pub description_mime_type: Option<String>,
113    pub enabled: Option<bool>,
114    pub id: String,
115    pub selected: Option<bool>,
116    pub start_enabled: Option<bool>,
117    pub start_selected: Option<bool>,
118    pub start_visible: Option<bool>,
119    // Supposed to be required. But there are elements with only `id` attribute in wild.
120    pub title: Option<String>,
121    pub visible: Option<bool>,
122    // bundle, customLocationIsSelfContained, tooltip, and versStr are reserved attributes.
123    #[serde(default, rename = "pkg-ref")]
124    pub pkg_ref: Vec<PkgRef>,
125}
126
127#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
128pub struct ChoicesOutline {
129    // ui is a reserved attribute.
130    pub line: Vec<Line>,
131}
132
133#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
134pub struct Conclusion {
135    pub file: String,
136    #[serde(rename = "mime-type")]
137    pub mime_type: Option<String>,
138    pub uti: Option<String>,
139    // language is a reserved attribute.
140}
141
142#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
143pub struct Domains {
144    pub enable_anywhere: bool,
145    #[serde(rename = "enable_currentUserHome")]
146    pub enable_current_user_home: bool,
147    #[serde(rename = "enable_localSystem")]
148    pub enable_local_system: bool,
149}
150
151#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
152pub struct InstallationCheck {
153    pub script: Option<bool>,
154    pub ram: Option<Ram>,
155    #[serde(rename = "required-graphics")]
156    pub required_graphics: Option<RequiredGraphics>,
157}
158
159#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
160#[serde(rename_all = "kebab-case")]
161pub struct License {
162    pub file: String,
163    pub mime_type: Option<String>,
164    pub uti: Option<String>,
165    // auto, language, and sla are reserved but not defined.
166}
167
168#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
169pub struct Line {
170    pub choice: String,
171    #[serde(default, rename = "line")]
172    pub lines: Vec<Line>,
173}
174
175#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
176pub struct Locator {
177    #[serde(rename = "search")]
178    pub searches: Vec<Search>,
179}
180
181#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
182pub struct MustClose {
183    pub app: Vec<App>,
184}
185
186#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
187pub struct Options {
188    #[serde(rename = "allow-external-scripts")]
189    pub allow_external_scripts: Option<bool>,
190    pub customize: Option<String>,
191    #[serde(rename = "hostArchitectures")]
192    pub host_architecutres: Option<String>,
193    pub mpkg: Option<String>,
194    #[serde(rename = "require-scripts")]
195    pub require_scripts: Option<bool>,
196    #[serde(rename = "rootVolumeOnly")]
197    pub root_volume_only: Option<bool>,
198    // type, visibleOnlyForPredicate are reserved attributes.
199}
200
201/// Defines a range of supported OS versions.
202#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
203pub struct OsVersion {
204    pub before: Option<String>,
205    pub min: String,
206}
207
208#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
209pub struct PkgRef {
210    pub active: Option<bool>,
211    pub auth: Option<String>,
212    pub id: String,
213    #[serde(rename = "installKBytes")]
214    pub install_kbytes: Option<u64>,
215    // TODO make enum
216    #[serde(rename = "onConclusion")]
217    pub on_conclusion: Option<String>,
218    #[serde(rename = "onConclusionScript")]
219    pub on_conclusion_script: Option<String>,
220    pub version: Option<String>,
221    // archiveKBytes, packageIdentifier reserved attributes.
222    #[serde(rename = "must-close")]
223    pub must_close: Option<MustClose>,
224    #[serde(rename = "bundle-version")]
225    pub bundle_version: Option<BundleVersion>,
226    #[serde(default)]
227    pub relocate: Vec<Relocate>,
228}
229
230#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
231pub struct Product {
232    pub id: String,
233    pub version: Option<String>,
234}
235
236#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
237pub struct Ram {
238    #[serde(rename = "min-gb")]
239    pub min_gb: String,
240}
241
242#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
243pub struct Readme {
244    pub file: String,
245    pub mime_type: Option<String>,
246    pub uti: Option<String>,
247    // language is reserved.
248}
249
250#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
251pub struct Relocate {
252    #[serde(rename = "search-id")]
253    pub search_id: String,
254    pub bundle: Bundle,
255}
256
257#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
258pub struct RequiredBundles {
259    pub all: Option<bool>,
260    pub description: Option<String>,
261    #[serde(rename = "bundle")]
262    pub bundles: Vec<Bundle>,
263}
264
265#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
266pub struct RequiredClDevice {
267    #[serde(rename = "$value")]
268    pub predicate: String,
269}
270
271#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
272pub struct RequiredGlRenderer {
273    #[serde(rename = "$value")]
274    pub predicate: String,
275}
276
277#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
278#[serde(rename_all = "kebab-case")]
279pub struct RequiredGraphics {
280    pub description: Option<String>,
281    pub single_device: Option<bool>,
282    pub required_cl_device: Option<RequiredClDevice>,
283    pub required_gl_renderer: Option<RequiredGlRenderer>,
284}
285
286#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
287pub struct Script {
288    // language is a reserved attribute.
289    #[serde(rename = "$value")]
290    pub script: String,
291}
292
293#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
294pub enum SearchValue {
295    #[serde(rename = "bundle")]
296    Bundle(Bundle),
297    #[serde(rename = "script")]
298    Script(Script),
299}
300
301#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
302#[serde(rename_all = "kebab-case")]
303pub struct Search {
304    pub id: String,
305    pub script: Option<String>,
306    pub search_id: Option<String>,
307    pub search_path: Option<String>,
308    #[serde(rename = "type")]
309    pub search_type: String,
310    pub value: SearchValue,
311}
312
313#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
314pub struct Title {
315    #[serde(rename = "$value")]
316    pub title: String,
317}
318
319#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
320#[serde(rename = "kebab-case")]
321pub struct VolumeCheck {
322    pub script: bool,
323    pub allowed_os_versions: Option<AllowedOsVersions>,
324    pub required_bundles: Option<RequiredBundles>,
325}
326
327#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
328#[serde(rename_all = "kebab-case")]
329pub struct Welcome {
330    pub file: String,
331    pub mime_type: Option<String>,
332    pub uti: Option<String>,
333    // language reserved attribute.
334}