use anyhow::{Context, Result, bail};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use url::Url;
use super::directory::{
Directory, Id, MOMENTUM_DIRECTORY, OFFICIAL_DIRECTORY, UNLEASHED_DIRECTORY, Version,
};
#[derive(Debug)]
pub enum Firmware {
Official(Id),
Momentum(Id),
Unleashed(Id),
Custom(String),
}
impl Default for Firmware {
fn default() -> Self {
Self::Official(Id::Release)
}
}
impl Serialize for Firmware {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = match self {
Firmware::Official(channel) => &format!("official@{channel}"),
Firmware::Momentum(channel) => &format!("momentum@{channel}"),
Firmware::Unleashed(channel) => &format!("unleashed@{channel}"),
Firmware::Custom(url) => url,
};
serializer.serialize_str(s)
}
}
impl<'de> Deserialize<'de> for Firmware {
fn deserialize<D>(deserializer: D) -> Result<Firmware, D::Error>
where
D: Deserializer<'de>,
{
let firmware = String::deserialize(deserializer)?;
if let Some((source, channel)) = firmware.split_once('@') {
let channel = match channel {
"release" => Id::Release,
"release-candidate" => Id::ReleaseCanidate,
"development" => Id::Development,
_ => todo!(),
};
match source {
"official" => Ok(Firmware::Official(channel)),
"momentum" => Ok(Firmware::Momentum(channel)),
"unleashed" => Ok(Firmware::Unleashed(channel)),
_ => todo!(),
}
} else {
let url = Url::parse(&firmware)
.map_err(|err| de::Error::custom(format!("{}: {:?}", err, firmware)))?;
Ok(Firmware::Custom(url.to_string()))
}
}
}
impl Firmware {
pub async fn fetch_manifest(&self) -> Result<Version> {
match self {
Firmware::Custom(_) => bail!("fetch_manifest called on a custom firmware variant"),
Firmware::Official(ch) | Firmware::Unleashed(ch) | Firmware::Momentum(ch) => {
let base =
Url::parse(self.get_directory()).context("parsing base directory URL")?;
let dir = Directory::fetch(base).await?;
let ver = dir
.channel_latest_version(ch)
.cloned()
.with_context(|| format!("no latest version for channel `{}`", ch))?;
Ok(ver)
}
}
}
fn get_directory(&self) -> &'static str {
match self {
Firmware::Official(_) => OFFICIAL_DIRECTORY,
Firmware::Unleashed(_) => UNLEASHED_DIRECTORY,
Firmware::Momentum(_) => MOMENTUM_DIRECTORY,
Firmware::Custom(_) => unreachable!("matched in fetch_manifest, error taken care of."),
}
}
}