use crate::NodeLanguage;
use proto_core::{
async_trait, is_offline, is_semantic_version, load_versions_manifest, parse_version,
remove_v_prefix, Manifest, ProtoError, Resolvable, Tool, VersionManifest, VersionManifestEntry,
};
use serde::Deserialize;
use std::collections::BTreeMap;
use tracing::debug;
#[derive(Deserialize)]
#[serde(untagged)]
enum NodeLTS {
Name(String),
State(bool),
}
#[derive(Deserialize)]
struct NodeDistVersion {
lts: NodeLTS,
version: String, }
#[async_trait]
impl Resolvable<'_> for NodeLanguage {
fn get_resolved_version(&self) -> &str {
match self.version.as_ref() {
Some(version) => version,
None => "latest",
}
}
async fn load_version_manifest(&self) -> Result<VersionManifest, ProtoError> {
let mut aliases = BTreeMap::new();
let mut versions = BTreeMap::new();
let response: Vec<NodeDistVersion> =
load_versions_manifest("https://nodejs.org/dist/index.json").await?;
for (index, item) in response.iter().enumerate() {
if index == 0 {
aliases.insert("latest".into(), item.version.clone());
}
let mut entry = VersionManifestEntry {
alias: None,
version: remove_v_prefix(&item.version),
};
if let NodeLTS::Name(alias) = &item.lts {
let alias = alias.to_lowercase();
if !aliases.contains_key("stable") {
aliases.insert("stable".into(), item.version.clone());
}
if !aliases.contains_key(&alias) {
aliases.insert(alias.clone(), item.version.clone());
}
entry.alias = Some(alias);
}
versions.insert(entry.version.clone(), entry);
}
let mut manifest = VersionManifest { aliases, versions };
manifest.inherit_aliases(&Manifest::load(self.get_manifest_path())?.aliases);
Ok(manifest)
}
async fn resolve_version(&mut self, initial_version: &str) -> Result<String, ProtoError> {
if let Some(version) = &self.version {
return Ok(version.to_owned());
}
let initial_version = remove_v_prefix(initial_version).to_lowercase();
if is_semantic_version(&initial_version) && is_offline() {
self.set_version(&initial_version);
return Ok(initial_version);
}
debug!("Resolving a semantic version for \"{}\"", initial_version);
let manifest = self.load_version_manifest().await?;
let candidate;
if initial_version == "node" || initial_version == "latest" {
candidate = manifest.get_version_from_alias("latest")?;
} else if initial_version == "stable"
|| initial_version == "lts-*"
|| initial_version == "lts/*"
{
candidate = manifest.get_version_from_alias("stable")?;
} else if initial_version.starts_with("lts-") || initial_version.starts_with("lts/") {
candidate = manifest.get_version_from_alias(&initial_version[4..])?;
} else {
candidate = manifest.find_version(&initial_version)?;
}
let version = parse_version(candidate)?.to_string();
debug!("Resolved to {}", version);
self.set_version(&version);
Ok(version)
}
fn set_version(&mut self, version: &str) {
self.version = Some(version.to_owned());
}
}