use std::collections::HashSet;
use semver::Version;
use serde::{Deserialize, Serialize};
use serde_with::{VecSkipError, serde_as};
use url::Url;
#[serde_as]
#[derive(Deserialize, Serialize, Debug, Clone, Default, PartialEq, Eq)]
pub struct Config {
pub imager: Imager,
#[serde_as(as = "VecSkipError<_>")]
pub os_list: Vec<OsListItem>,
}
#[serde_as]
#[derive(Deserialize, Serialize, Debug, Clone, Default, PartialEq, Eq)]
pub struct Imager {
pub latest_version: Option<Version>,
#[serde_as(as = "VecSkipError<_>")]
pub devices: Vec<Device>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct Device {
pub name: String,
pub tags: HashSet<String>,
pub icon: Option<Url>,
pub description: String,
pub flasher: Flasher,
pub documentation: Option<Url>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum OsListItem {
Image(OsImage),
SubList(OsSubList),
RemoteSubList(OsRemoteSubList),
}
#[serde_as]
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct OsSubList {
pub name: String,
pub description: String,
pub icon: Url,
#[serde(default)]
pub flasher: Flasher,
#[serde_as(as = "VecSkipError<_>")]
pub subitems: Vec<OsListItem>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct OsRemoteSubList {
pub name: String,
pub description: String,
pub icon: Url,
#[serde(default)]
pub flasher: Flasher,
pub devices: HashSet<String>,
pub subitems_url: Url,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct OsImage {
pub name: String,
pub description: String,
pub icon: Url,
pub url: Url,
#[serde(with = "const_hex")]
pub image_download_sha256: [u8; 32],
pub release_date: chrono::NaiveDate,
pub devices: HashSet<String>,
#[serde(default)]
pub tags: HashSet<String>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[non_exhaustive]
pub enum Flasher {
#[default]
SdCard,
BeagleConnectFreedom,
Msp430Usb,
Pb2Mspm0,
}
impl Extend<Self> for Config {
fn extend<T: IntoIterator<Item = Self>>(&mut self, iter: T) {
for config in iter.into_iter() {
if let Some(v) = config.imager.latest_version {
self.imager.latest_version = Some(v);
}
for c_dev in config.imager.devices {
if let Some(my_dev) = self
.imager
.devices
.iter_mut()
.find(|x| x.name == c_dev.name)
{
my_dev.tags.extend(c_dev.tags.clone());
my_dev.flasher = c_dev.flasher;
if let Some(doc) = &c_dev.documentation {
my_dev.documentation = Some(doc.clone());
}
if let Some(icon) = &c_dev.icon {
my_dev.icon = Some(icon.clone());
}
} else {
self.imager.devices.push(c_dev);
}
}
self.os_list.reserve(config.os_list.len());
for item in config.os_list {
if !self.os_list.contains(&item) {
self.os_list.push(item);
}
}
}
}
}
impl OsListItem {
pub fn icon(&self) -> url::Url {
match self {
OsListItem::Image(img) => img.icon.clone(),
OsListItem::SubList(img) => img.icon.clone(),
OsListItem::RemoteSubList(img) => img.icon.clone(),
}
}
pub fn name(&self) -> &str {
match self {
OsListItem::Image(img) => &img.name,
OsListItem::SubList(img) => &img.name,
OsListItem::RemoteSubList(img) => &img.name,
}
}
pub fn has_board_image(&self, tags: &HashSet<String>) -> bool {
match self {
OsListItem::Image(item) => !tags.is_disjoint(&item.devices),
OsListItem::SubList(item) => item.subitems.iter().any(|x| x.has_board_image(tags)),
OsListItem::RemoteSubList(item) => !tags.is_disjoint(&item.devices),
}
}
}
impl OsRemoteSubList {
pub fn resolve(self, subitems: Vec<OsListItem>) -> OsSubList {
OsSubList {
name: self.name,
description: self.description,
icon: self.icon,
flasher: self.flasher,
subitems,
}
}
}