bb_config/config.rs
1//! Abstractions to parse and generate distros.json file.
2
3use std::collections::HashSet;
4
5use semver::Version;
6use serde::{Deserialize, Serialize};
7use serde_with::{VecSkipError, serde_as};
8use url::Url;
9
10/// [BeagleBoard.org] distros.json abstraction.
11///
12/// # Merging Behaviour
13///
14/// Configs can be merged together using [Extend::extend]. This allows for having some portion of
15/// config locally while downloading parts from network.
16///
17/// If [Imager::latest_version] is present, it will overwrite the current field.
18///
19/// For [Imager::devices], any new board will be appended to the end of the board list. Existing
20/// boards are not added again. Duplicate boards are checked by [Device::name] field. The [Device]
21/// fields are overwritten.
22///
23/// For [Config::os_list], all non-duplicate [OsListItem] are appended to the end of the list.
24///
25/// [BeagleBoard.org]: https://www.beagleboard.org/
26#[serde_as]
27#[derive(Deserialize, Serialize, Debug, Clone, Default, PartialEq, Eq)]
28pub struct Config {
29 pub imager: Imager,
30 #[serde_as(as = "VecSkipError<_>")]
31 /// List of OS images for the boards
32 pub os_list: Vec<OsListItem>,
33}
34
35/// Contains information regarding BeagleBoard Images version and a list of [BeagleBoard.org]
36/// boards along with information regarding each board.
37///
38/// [BeagleBoard.org]: https://www.beagleboard.org/
39#[serde_as]
40#[derive(Deserialize, Serialize, Debug, Clone, Default, PartialEq, Eq)]
41pub struct Imager {
42 /// Latest BeagleBoard Imaging Utility version
43 pub latest_version: Option<Version>,
44 #[serde_as(as = "VecSkipError<_>")]
45 /// List of BeagleBoard.org boards
46 pub devices: Vec<Device>,
47}
48
49/// Structure describing [BeagleBoard.org] board
50///
51/// [BeagleBoard.org]: https://www.beagleboard.org/
52#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
53pub struct Device {
54 /// Board Name
55 pub name: String,
56 /// Board tags are used to match OS images with boards
57 pub tags: HashSet<String>,
58 /// Board image URL
59 pub icon: Option<Url>,
60 /// Board description
61 pub description: String,
62 /// The default [`Flasher`] for the board. This will be used when flasher type is not present
63 /// in the OS image.
64 pub flasher: Flasher,
65 /// Link to board documentation
66 pub documentation: Option<Url>,
67}
68
69/// Os List can contain multiple types of items depending on the situation.
70#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
71#[serde(untagged)]
72pub enum OsListItem {
73 /// Single Os Image
74 Image(OsImage),
75 /// SubList which itself can contain a list of [`OsListItem`].
76 ///
77 /// This is used to define Testing and other images which do not need to be present at the top
78 /// level.
79 SubList(OsSubList),
80 /// SubList stored in a remote location.
81 ///
82 /// This is used to define images managed/hosted outside of the normal [BeagleBoard.org] image
83 /// infrastructure, such as from CI, etc.
84 ///
85 /// [BeagleBoard.org]: https://www.beagleboard.org/
86 RemoteSubList(OsRemoteSubList),
87}
88
89/// [`OsListItem`] which itself can contain a list of [`OsListItem`].
90#[serde_as]
91#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
92pub struct OsSubList {
93 /// Sublist name
94 pub name: String,
95 /// Sublist description
96 pub description: String,
97 /// Sublist icon URL
98 pub icon: Url,
99 /// Flasher type for all top level Os Images in the sublist
100 #[serde(default)]
101 pub flasher: Flasher,
102 /// List of items
103 #[serde_as(as = "VecSkipError<_>")]
104 pub subitems: Vec<OsListItem>,
105}
106
107/// Sublists stored in a remote location
108#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
109pub struct OsRemoteSubList {
110 /// Remote Sublist name
111 pub name: String,
112 /// Remote Sublist description
113 pub description: String,
114 /// Remote Sublist icon URL
115 pub icon: Url,
116 /// Flasher type for all top level Os Images in the sublist
117 #[serde(default)]
118 pub flasher: Flasher,
119 /// Union of devices the OsImages in the SubList can be used with
120 pub devices: HashSet<String>,
121 /// Url to the Remote list
122 pub subitems_url: Url,
123}
124
125/// A singular Os Image for board(s)
126#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
127pub struct OsImage {
128 /// Os Image name
129 pub name: String,
130 /// Os Image description
131 pub description: String,
132 /// Os Image icon
133 pub icon: Url,
134 /// Os Image download URL
135 pub url: Url,
136 /// Os Image sha256 (before extraction)
137 #[serde(with = "const_hex")]
138 pub image_download_sha256: [u8; 32],
139 /// Os Image release date
140 pub release_date: chrono::NaiveDate,
141 /// Devices the Os Image can be used with
142 pub devices: HashSet<String>,
143 /// Os Image tags
144 #[serde(default)]
145 pub tags: HashSet<String>,
146}
147
148/// Types of flashers Os Image(s) support
149#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
150#[non_exhaustive]
151pub enum Flasher {
152 #[default]
153 /// Image needs to be written to SD Card
154 SdCard,
155 /// BeagleConnect Freedom CC1352P7 Firmware
156 BeagleConnectFreedom,
157 /// BeagleConnect Freedom Msp430 Firmware
158 Msp430Usb,
159 /// PocketBeagle2 Mspm0 firmware
160 Pb2Mspm0,
161}
162
163impl Extend<Self> for Config {
164 fn extend<T: IntoIterator<Item = Self>>(&mut self, iter: T) {
165 for config in iter.into_iter() {
166 // Overwrite version if present
167 if let Some(v) = config.imager.latest_version {
168 self.imager.latest_version = Some(v);
169 }
170
171 for c_dev in config.imager.devices {
172 // If the board already exists, overwrite fields.
173 // Else, add new board
174 if let Some(my_dev) = self
175 .imager
176 .devices
177 .iter_mut()
178 .find(|x| x.name == c_dev.name)
179 {
180 my_dev.tags.extend(c_dev.tags.clone());
181 my_dev.flasher = c_dev.flasher;
182
183 if let Some(doc) = &c_dev.documentation {
184 my_dev.documentation = Some(doc.clone());
185 }
186
187 if let Some(icon) = &c_dev.icon {
188 my_dev.icon = Some(icon.clone());
189 }
190 } else {
191 self.imager.devices.push(c_dev);
192 }
193 }
194
195 // Only add non_duplicate os_list items
196 self.os_list.reserve(config.os_list.len());
197 for item in config.os_list {
198 if !self.os_list.contains(&item) {
199 self.os_list.push(item);
200 }
201 }
202 }
203 }
204}
205
206impl OsListItem {
207 pub fn icon(&self) -> url::Url {
208 match self {
209 OsListItem::Image(img) => img.icon.clone(),
210 OsListItem::SubList(img) => img.icon.clone(),
211 OsListItem::RemoteSubList(img) => img.icon.clone(),
212 }
213 }
214
215 pub fn name(&self) -> &str {
216 match self {
217 OsListItem::Image(img) => &img.name,
218 OsListItem::SubList(img) => &img.name,
219 OsListItem::RemoteSubList(img) => &img.name,
220 }
221 }
222
223 /// Check if the [OsListItem] (or any of it's children) has an image for a board
224 pub fn has_board_image(&self, tags: &HashSet<String>) -> bool {
225 match self {
226 OsListItem::Image(item) => !tags.is_disjoint(&item.devices),
227 OsListItem::SubList(item) => item.subitems.iter().any(|x| x.has_board_image(tags)),
228 OsListItem::RemoteSubList(item) => !tags.is_disjoint(&item.devices),
229 }
230 }
231}
232
233impl OsRemoteSubList {
234 /// Construct [OsSubList] once subitems have been downloaded.
235 pub fn resolve(self, subitems: Vec<OsListItem>) -> OsSubList {
236 OsSubList {
237 name: self.name,
238 description: self.description,
239 icon: self.icon,
240 flasher: self.flasher,
241 subitems,
242 }
243 }
244}