2021-12-17 00:17:40 -05:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2021-12-17 06:01:28 -05:00
|
|
|
use std::process::Command;
|
2021-12-17 00:17:40 -05:00
|
|
|
|
|
|
|
use color_eyre::{
|
|
|
|
eyre::{bail, Context, ContextCompat, Result},
|
|
|
|
Help, SectionExt,
|
|
|
|
};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
color_eyre::install()?;
|
|
|
|
|
|
|
|
// Get the available versions
|
|
|
|
let versions = get_all_versions()?;
|
|
|
|
// Uniqueify them
|
|
|
|
let mut slugs: HashMap<String, (Release, String)> = HashMap::new();
|
|
|
|
for version in versions {
|
|
|
|
if let Ok(url) = get_url(&version) {
|
|
|
|
let slug = version.to_slug();
|
|
|
|
let entry = slugs.entry(slug).or_insert((version.clone(), url.clone()));
|
|
|
|
if version > entry.0 {
|
|
|
|
*entry = (version, url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut sources = Sources {
|
|
|
|
versions: HashMap::new(),
|
|
|
|
};
|
|
|
|
for (_slug, (version, url)) in slugs {
|
2021-12-17 06:01:28 -05:00
|
|
|
sources.add_release(&version, &url)?;
|
2021-12-17 00:17:40 -05:00
|
|
|
}
|
|
|
|
let mut output: HashMap<String, Sources> = HashMap::new();
|
|
|
|
output.insert("x86_64-unknown-linux-gnu".to_string(), sources);
|
|
|
|
let json_output =
|
|
|
|
serde_json::to_string_pretty(&output).context("Failed to serialize output")?;
|
|
|
|
println!("{}", json_output);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Serde serialization format
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
|
|
|
|
struct Sources {
|
|
|
|
versions: HashMap<String, Source>,
|
|
|
|
}
|
|
|
|
impl Sources {
|
2021-12-17 06:01:28 -05:00
|
|
|
fn add_release(&mut self, release: &Release, url: &str) -> Result<()> {
|
2021-12-17 00:17:40 -05:00
|
|
|
let version = release.to_java_version();
|
|
|
|
let source = Source {
|
|
|
|
major_version: release.major.to_string(),
|
|
|
|
version,
|
|
|
|
url: url.to_string(),
|
2021-12-17 06:01:28 -05:00
|
|
|
sha256: get_sha256(url)?,
|
2021-12-17 00:17:40 -05:00
|
|
|
};
|
|
|
|
self.versions.insert(release.major.to_string(), source);
|
2021-12-17 06:01:28 -05:00
|
|
|
Ok(())
|
2021-12-17 00:17:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
|
|
struct Source {
|
|
|
|
major_version: String,
|
|
|
|
version: String,
|
|
|
|
url: String,
|
2021-12-17 06:01:28 -05:00
|
|
|
sha256: String,
|
2021-12-17 00:17:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Serde formatting struct
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Clone)]
|
|
|
|
struct Release {
|
|
|
|
build: usize,
|
|
|
|
major: usize,
|
|
|
|
minor: usize,
|
|
|
|
openjdk_version: String,
|
|
|
|
security: usize,
|
|
|
|
semver: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialOrd for Release {
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
|
|
match self.build.partial_cmp(&other.build) {
|
|
|
|
Some(core::cmp::Ordering::Equal) => {}
|
|
|
|
ord => return ord,
|
|
|
|
}
|
|
|
|
match self.major.partial_cmp(&other.major) {
|
|
|
|
Some(core::cmp::Ordering::Equal) => {}
|
|
|
|
ord => return ord,
|
|
|
|
}
|
|
|
|
match self.minor.partial_cmp(&other.minor) {
|
|
|
|
Some(core::cmp::Ordering::Equal) => {}
|
|
|
|
ord => return ord,
|
|
|
|
}
|
|
|
|
match self.openjdk_version.partial_cmp(&other.openjdk_version) {
|
|
|
|
Some(core::cmp::Ordering::Equal) => {}
|
|
|
|
ord => return ord,
|
|
|
|
}
|
|
|
|
match self.security.partial_cmp(&other.security) {
|
|
|
|
Some(core::cmp::Ordering::Equal) => {}
|
|
|
|
ord => return ord,
|
|
|
|
}
|
|
|
|
match self.openjdk_version.partial_cmp(&other.openjdk_version) {
|
|
|
|
Some(core::cmp::Ordering::Equal) => Some(core::cmp::Ordering::Equal),
|
|
|
|
_ => {
|
|
|
|
let x = self.openjdk_version.split('-').collect::<Vec<_>>();
|
|
|
|
let x = x[x.len() - 1];
|
|
|
|
let y = other.openjdk_version.split('-').collect::<Vec<_>>();
|
|
|
|
let y = y[y.len() - 1];
|
|
|
|
x.partial_cmp(y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ord for Release {
|
|
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
|
|
self.partial_cmp(other).unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Release {
|
|
|
|
fn to_slug(&self) -> String {
|
|
|
|
format!("1.{}.{}", self.major, self.minor)
|
|
|
|
}
|
|
|
|
fn to_java_version(&self) -> String {
|
|
|
|
format!(
|
|
|
|
"{}.{}.{}+{}",
|
|
|
|
self.major, self.minor, self.security, self.build
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Serde formatting struct
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
|
|
struct Versions {
|
|
|
|
versions: Vec<Release>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a page of the versions
|
|
|
|
fn get_versions_page(page: usize) -> Result<Vec<Release>> {
|
|
|
|
let page = format!("{}", page);
|
|
|
|
let request = ureq::get("https://api.adoptium.net/v3/info/release_versions")
|
|
|
|
.query("architecture", "x64")
|
|
|
|
.query("image_type", "jdk")
|
|
|
|
.query("os", "linux")
|
|
|
|
.query("page", &page)
|
2021-12-17 06:01:28 -05:00
|
|
|
.query("page_size", "50")
|
2021-12-17 00:17:40 -05:00
|
|
|
.query("project", "jdk");
|
|
|
|
let url = request.url().to_string();
|
|
|
|
let response = request
|
|
|
|
.call()
|
|
|
|
.with_section(|| format!("Page: {} Url: {}", page, url).header("Failed Request:"))
|
|
|
|
.context("Failed getting page of versions list")?;
|
|
|
|
let versions: Versions = response
|
|
|
|
.into_json()
|
|
|
|
.with_section(|| format!("Page: {} Url: {}", page, url).header("Failed Request:"))
|
|
|
|
.context("Failed deserializing page of versions list")?;
|
|
|
|
Ok(versions.versions)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get all the versions available
|
|
|
|
fn get_all_versions() -> Result<HashSet<Release>> {
|
|
|
|
let mut versions = HashSet::new();
|
|
|
|
// Go through the pages until we hit a 404
|
|
|
|
let mut page_number = 0;
|
|
|
|
// Err early if the first page fails, so we can have a more useful context message
|
|
|
|
let mut page =
|
|
|
|
Ok(get_versions_page(0).context("Failed getting first page in get_all_versions")?);
|
|
|
|
while let Ok(current_page) = page {
|
|
|
|
for version in current_page {
|
|
|
|
versions.insert(version);
|
|
|
|
}
|
|
|
|
page_number += 1;
|
|
|
|
page = get_versions_page(page_number).context("Ran out of pages");
|
|
|
|
}
|
|
|
|
Ok(versions)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Serde deserialization type
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
|
|
struct VersionInfo {
|
|
|
|
binaries: Vec<BinaryInfo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Serde deserialization type
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
|
|
struct BinaryInfo {
|
|
|
|
package: PackageInfo,
|
|
|
|
}
|
|
|
|
/// Serde deserialization type
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
|
|
struct PackageInfo {
|
|
|
|
link: String,
|
|
|
|
name: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the url for the latest release of a version
|
|
|
|
fn get_url(version: &Release) -> Result<String> {
|
|
|
|
let request = ureq::get(&format!(
|
|
|
|
"https://api.adoptium.net/v3/assets/version/{}",
|
|
|
|
version.openjdk_version
|
|
|
|
))
|
|
|
|
.query("architecture", "x64")
|
|
|
|
.query("image_type", "jdk")
|
|
|
|
.query("os", "linux")
|
|
|
|
.query("page", "0")
|
2021-12-17 06:01:28 -05:00
|
|
|
.query("page_size", "1")
|
2021-12-17 00:17:40 -05:00
|
|
|
.query("project", "jdk");
|
|
|
|
let url = request.url().to_string();
|
|
|
|
let response = request
|
|
|
|
.call()
|
|
|
|
.with_section(|| format!("Url: {}", url).header("Failed Request"))
|
|
|
|
.context("Failed requesting version information")?;
|
|
|
|
let version_info: Vec<VersionInfo> = response
|
|
|
|
.into_json()
|
|
|
|
.with_section(|| format!("Url: {}", url).header("Failed Request"))
|
|
|
|
.context("Failed to deserialize response for version information")?;
|
|
|
|
let version_info = version_info
|
|
|
|
.into_iter()
|
|
|
|
.next()
|
|
|
|
.context("Nothing providred")?;
|
|
|
|
|
|
|
|
match version_info.binaries.get(0) {
|
|
|
|
Some(binary_info) => Ok(binary_info.package.link.clone()),
|
|
|
|
None => bail!("Binary was missing!"),
|
|
|
|
}
|
|
|
|
}
|
2021-12-17 06:01:28 -05:00
|
|
|
|
|
|
|
/// Gets the nix sha256 for a url
|
|
|
|
fn get_sha256(url: &str) -> Result<String> {
|
|
|
|
let output = Command::new("nix-prefetch-url")
|
|
|
|
.args([url, "--type", "sha256"])
|
|
|
|
.output()
|
|
|
|
.with_section(|| format!("Failed to prefetch url: {}", url).header("Prefetch Failure"))
|
|
|
|
.context("Failed to prefetch")?;
|
|
|
|
let output = String::from_utf8(output.stdout).context("Invalid utf-8 from nix pre fetch")?;
|
|
|
|
// Trim the trailing new line
|
|
|
|
Ok(output.trim().to_string())
|
|
|
|
}
|