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}