Skip to content

Commit

Permalink
Add 'available' command to query downloable JDK versions
Browse files Browse the repository at this point in the history
This will include the platform OS and architecture in the API call so
only actually valid versions will be shown.
  • Loading branch information
DenWav committed Sep 26, 2021
1 parent 24618d0 commit ac75ac9
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 4 deletions.
72 changes: 71 additions & 1 deletion src/api/adoptium.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use serde::Deserialize;

use crate::api::def::{JdkFetchApi, JdkFetchError, JdkFetchResult};
use crate::api::http_failure::handle_response_fail;
use std::collections::HashSet;

#[derive(Clone)]
pub struct AdoptiumApi {
Expand Down Expand Up @@ -44,6 +45,26 @@ impl AdoptiumApi {
major + 1,
));
}

fn get_jdk_version_list_url(&self, page: u8) -> JdkFetchResult<String> {
let os_name = get_os_name()?;
let arch_name = get_arch_name()?;

return Ok(format!(
"{}/info/release_names\
?architecture={}\
&OS={}\
&image_type=jdk\
&project=jdk\
&release_type=ga\
&sort_method=DEFAULT\
&sort_order=DESC\
&vendor={}\
&page={}\
&page_size=20",
&self.base_url, arch_name, os_name, &self.vendor, page,
));
}
}

impl JdkFetchApi for AdoptiumApi {
Expand All @@ -57,7 +78,7 @@ impl JdkFetchApi for AdoptiumApi {
let url = self.get_latest_jdk_version_url(major)?;
let response = attohttpc::get(&url).send().map_err(JdkFetchError::HttpIo)?;
if !response.is_success() {
if response.status().as_u16() == 404 {
if response.status() == attohttpc::StatusCode::NOT_FOUND {
return Ok(None);
}
return Err(handle_response_fail(
Expand All @@ -83,6 +104,50 @@ impl JdkFetchApi for AdoptiumApi {
};
Ok(Some(fixed_version))
}

fn get_available_jdk_versions(&self) -> JdkFetchResult<HashSet<String>> {
let mut versions = HashSet::<String>::new();

let mut i = 0;
loop {
let url = self.get_jdk_version_list_url(i)?;
i += 1;

let response = attohttpc::get(&url).send().map_err(JdkFetchError::HttpIo)?;

if response.status() == attohttpc::StatusCode::NOT_FOUND {
break;
}
if !response.is_success() {
return Err(handle_response_fail(
response,
"Failed to get available JDK versions",
));
}

let page: JdkNamesPage = response.json().map_err(JdkFetchError::HttpIo)?;
for name in page.releases {
let mut version_part = name.as_str();
if version_part.starts_with("jdk") {
version_part = &version_part[3..];
}
if version_part.starts_with('-') {
version_part = &version_part[1..];
}

let end_index = version_part.find(|c: char| !('0'..='9').contains(&c)).ok_or(
JdkFetchError::Generic {
message: format!("Failed to find major JDK version from: {}", name),
},
)?;

version_part = &version_part[..end_index];
versions.insert(version_part.to_string());
}
}

Ok(versions)
}
}

#[derive(Debug, Deserialize)]
Expand All @@ -103,6 +168,11 @@ enum Prerelease {
EA,
}

#[derive(Debug, Deserialize)]
struct JdkNamesPage {
releases: Vec<String>,
}

fn get_os_name() -> JdkFetchResult<&'static str> {
let env_os = std::env::consts::OS;
match env_os {
Expand Down
3 changes: 3 additions & 0 deletions src/api/def.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashSet;
use thiserror::Error;

#[derive(Error, Debug)]
Expand All @@ -20,4 +21,6 @@ pub trait JdkFetchApi {
/// Get the latest JDK version, returning `None` if the API doesn't know about this major
/// version.
fn get_latest_jdk_version(&self, major: u8) -> JdkFetchResult<Option<String>>;

fn get_available_jdk_versions(&self) -> JdkFetchResult<HashSet<String>>;
}
21 changes: 18 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ enum Subcommand {
},
#[structopt(about = "List downloaded JDKs")]
List {},
#[structopt(about = "List JDKs available to download")]
Available {},
#[structopt(about = "Print currently active JDK version (full)")]
Current {},
#[structopt(about = "Configure the default JDK")]
Expand All @@ -61,14 +63,14 @@ enum Subcommand {
JavaHome {},
}

fn parse_jdk_or_keyword(s: String) -> Either<u8, String> {
fn parse_jdk_or_keyword(s: &str) -> Either<u8, &str> {
s.parse::<u8>()
.map(Either::Left)
.unwrap_or_else(|_| Either::Right(s))
}

fn load_default(config: &Configuration, jdk: String) -> Result<u8> {
let jdk_or_keyword = parse_jdk_or_keyword(jdk);
let jdk_or_keyword = parse_jdk_or_keyword(jdk.as_str());
match jdk_or_keyword {
Either::Left(jdk_major) => Ok(jdk_major),
Either::Right(unknown) => {
Expand All @@ -86,7 +88,7 @@ fn load_jdk_list(
config: &Configuration,
jdk: String,
) -> Result<Vec<u8>> {
let jdk_or_keyword = parse_jdk_or_keyword(jdk);
let jdk_or_keyword = parse_jdk_or_keyword(jdk.as_str());
match jdk_or_keyword {
Either::Left(jdk_major) => Ok(vec![jdk_major]),
Either::Right(unknown) => {
Expand Down Expand Up @@ -252,6 +254,19 @@ fn main_for_result(args: Jpre) -> Result<()> {
);
}
}
Subcommand::Available {} => {
let versions = jdk_manager.api.get_available_jdk_versions()?;
let mut sorted_versions = versions
.iter()
.filter_map(|version| parse_jdk_or_keyword(version).left())
.collect::<Vec<u8>>();
sorted_versions.sort_unstable();

println!("{}", "Major JDK versions available:".to_string().blue());
for version in sorted_versions {
println!(" {}", version.to_string().green());
}
}
Subcommand::Current {} => {
check_env_bound(&jdk_manager).context("Failed to check environment variables")?;
let jdk_version = jdk_manager
Expand Down

0 comments on commit ac75ac9

Please sign in to comment.