mod authority;
pub mod channel;
pub mod component;
use serde::Deserialize;
pub use self::{channel::*, component::*};
use super::ManifestError;
pub const MANIFEST_VERSION: semver::Version = semver::Version::new(1, 0, 1);
#[derive(Deserialize, Debug, Clone)]
pub struct Manifest {
pub(super) date: i64,
pub(super) channels: Vec<Channel>,
}
impl Default for Manifest {
fn default() -> Self {
let date = chrono::Utc::now().timestamp();
Self { date, channels: vec![] }
}
}
impl TryFrom<Manifest> for crate::manifest::v3::Manifest {
type Error = ManifestError;
fn try_from(value: Manifest) -> Result<Self, Self::Error> {
use crate::manifest::v3;
let mut channels = Vec::<v3::Channel>::with_capacity(value.channels.len());
let mut aliases = Vec::new();
for channel in value.channels {
let mut components = Vec::<v3::Component>::with_capacity(channel.components.len());
for component in channel.components {
components.push(component.try_into()?);
}
let migrates_from = channel.tags.iter().find_map(|tag| match tag {
super::v1::channel::Tags::Migration { old_channel } => Some(old_channel.clone()),
super::v1::channel::Tags::Partial => None,
});
if let Some(alias) = channel.alias {
aliases.push((alias, channel.name.clone()));
}
channels.push(v3::Channel {
name: channel.name,
migrates_from,
components,
extra: Default::default(),
});
}
let mut manifest = v3::Manifest {
manifest_version: v3::MANIFEST_VERSION,
date: value.date,
networks: Default::default(),
channels,
extra: Default::default(),
};
for (alias, channel) in aliases {
let network = match alias {
channel::ChannelAlias::Stable => crate::channel::DEFAULT_NETWORK.to_string(),
channel::ChannelAlias::Nightly(None) => {
crate::channel::canonical_network("nightly").to_string()
},
channel::ChannelAlias::Nightly(Some(suffix)) => format!("nightly-{suffix}"),
channel::ChannelAlias::Tag(tag) => {
let network = crate::channel::canonical_network(&tag);
if semver::Version::parse(network).is_ok() {
continue;
}
network.to_string()
},
};
manifest.promote(&network, channel);
}
if manifest.network_version(crate::channel::DEFAULT_NETWORK).is_none()
&& let Some(highest) = manifest
.channels
.iter()
.map(|c| c.name.clone())
.max_by(|a, b| a.cmp_precedence(b))
{
manifest.promote(crate::channel::DEFAULT_NETWORK, highest);
}
Ok(manifest)
}
}
#[cfg(test)]
mod tests {
use crate::manifest::{VersionedManifest, v3};
fn v1(channels: serde_json::Value) -> String {
serde_json::json!({
"manifest_version": "1.0.1",
"date": 1735689600,
"channels": channels
})
.to_string()
}
fn channel(name: &str, alias: Option<&str>) -> serde_json::Value {
let mut value = serde_json::json!({ "name": name, "components": [] });
if let Some(alias) = alias {
value["alias"] = serde_json::Value::String(alias.to_string());
}
value
}
#[test]
fn v1_aliases_become_networks() {
let src = v1(serde_json::json!([
channel("0.14.0", Some("stable")),
channel("0.15.0", Some("nightly")),
channel("0.15.1", Some("custom-dev-build")),
]));
let manifest = VersionedManifest::parse_str(&src).expect("must convert");
assert_eq!(manifest.network_version("mainnet"), Some(&semver::Version::new(0, 14, 0)));
assert_eq!(manifest.network_version("devnet"), Some(&semver::Version::new(0, 15, 0)));
assert_eq!(
manifest.network_version("custom-dev-build"),
Some(&semver::Version::new(0, 15, 1))
);
assert_eq!(manifest.manifest_version(), &v3::MANIFEST_VERSION);
}
#[test]
fn a_suffixed_nightly_keeps_its_full_name() {
let src = v1(serde_json::json!([channel("0.15.0", Some("nightly-experimental"))]));
let manifest = VersionedManifest::parse_str(&src).expect("must convert");
assert_eq!(
manifest.network_version("nightly-experimental"),
Some(&semver::Version::new(0, 15, 0))
);
}
#[test]
fn a_beta_alias_becomes_the_testnet_network() {
let src = v1(serde_json::json!([channel("0.15.0", Some("beta"))]));
let manifest = VersionedManifest::parse_str(&src).expect("must convert");
assert_eq!(manifest.network_version("testnet"), Some(&semver::Version::new(0, 15, 0)));
assert!(manifest.network_version("beta").is_none());
}
#[test]
fn a_version_shaped_alias_does_not_become_a_network() {
let src = v1(serde_json::json!([channel("0.15.0", Some("0.15.0-stable"))]));
let manifest = VersionedManifest::parse_str(&src).expect("must still convert");
assert!(manifest.network_version("0.15.0-stable").is_none());
assert_eq!(manifest.network_version("mainnet"), Some(&semver::Version::new(0, 15, 0)));
}
#[test]
fn without_a_stable_alias_mainnet_becomes_the_highest_channel() {
let src = v1(serde_json::json!([channel("0.14.0", None), channel("0.15.0", None)]));
let manifest = VersionedManifest::parse_str(&src).expect("must convert");
assert_eq!(manifest.network_version("mainnet"), Some(&semver::Version::new(0, 15, 0)));
}
}