diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/go_version_manager.iml b/.idea/go_version_manager.iml new file mode 100644 index 0000000..c254557 --- /dev/null +++ b/.idea/go_version_manager.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146ab09 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..28a804d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a28843a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index adeae9d..ce1d6ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -411,10 +411,12 @@ dependencies = [ "duct", "human-panic", "indicatif", + "lazy_static", "leg", "manic", "quit", "reqwest", + "serde", "soup", "structopt", "thiserror", @@ -1309,6 +1311,7 @@ dependencies = [ "pin-project-lite 0.2.0", "rustls", "serde", + "serde_json", "serde_urlencoded", "tokio", "tokio-rustls", @@ -1375,6 +1378,9 @@ name = "serde" version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" diff --git a/Cargo.toml b/Cargo.toml index d130a2a..504d392 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,11 +28,13 @@ dialoguer = "0.7.1" quit = "1.1.2" duct = "*" leg = "0.4.1" +lazy_static = "*" +serde = { version = "*", features = ["derive"] } colored = "^1.8.0" [dependencies.reqwest] version = "0.10.10" default-features = false -features = ["rustls-tls"] +features = ["rustls-tls", "json"] [badges] diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..692b3e6 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,24 @@ +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +pub const FILE_EXT: &str = "linux-amd64.tar.gz"; +#[cfg(all(target_os = "linux", target_arch = "x86"))] +pub const FILE_EXT: &str = "linux-386.tar.gz"; +#[cfg(all(target_os = "linux", target_arch = "aarch64"))] +pub const FILE_EXT: &str = "linux-arm64.tar.gz"; +#[cfg(all(target_os = "linux", target_arch = "arm"))] +pub const FILE_EXT: &str = "linux-armv6l.tar.gz"; +#[cfg(all(target_os = "linux", target_arch = "powerpc64"))] +pub const FILE_EXT: &str = "linux-ppc64le.tar.gz"; +#[cfg(all(target_os = "linux", target_arch = "s390x"))] +pub const FILE_EXT: &str = "linux-s390x.tar.gz"; +#[cfg(all(target_os = "windows", target_arch = "x86_64"))] +pub const FILE_EXT: &str = "windows-amd64.msi"; +#[cfg(all(target_os = "windows", target_arch = "x86"))] +pub const FILE_EXT: &str = "windows-386.msi"; +#[cfg(all(target_os = "macos", target_arch = "x86_64"))] +pub const FILE_EXT: &str = "darwin-amd64.pkg"; +#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] +pub const FILE_EXT: &str = "freebsd-amd64.tar.gz"; +#[cfg(all(target_os = "freebsd", target_arch = "x86"))] +pub const FILE_EXT: &str = "freebsd-386.tar.gz"; + +pub const DL_URL: &str = "https://golang.org/dl"; diff --git a/src/github.rs b/src/github.rs new file mode 100644 index 0000000..f5cc333 --- /dev/null +++ b/src/github.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Tag { + pub name: String, + zipball_url: String, + tarball_url: String, + commit: Commit, + node_id: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct Commit { + sha: String, + url: String, +} diff --git a/src/goversion.rs b/src/goversion.rs index a74bc97..3ed020a 100644 --- a/src/goversion.rs +++ b/src/goversion.rs @@ -1,20 +1,14 @@ - +use crate::consts::{DL_URL, FILE_EXT}; +use crate::error::Error; +use crate::github::Tag; +use duct::cmd; use indicatif::ProgressBar; +use manic::progress::downloader; +use reqwest::header::USER_AGENT; use soup::prelude::*; use soup::Soup; use std::path::PathBuf; use versions::Versioning; -use crate::error::Error; -use manic::progress::downloader; -use duct::cmd; -#[cfg(target_os = "linux")] -static FILE_EXT: &str = "linux-amd64.tar.gz"; -#[cfg(target_os = "windows")] -static FILE_EXT: &str = "windows-amd64.msi"; -#[cfg(target_os = "macos")] -static FILE_EXT: &str = "darwin-amd64.pkg"; - -static DL_URL: &str = "https://golang.org/dl"; /// Golang version represented as a struct pub struct GoVersion { @@ -29,16 +23,38 @@ pub struct GoVersion { impl GoVersion { /// Gets golang versions from git tags fn get_git_versions() -> Result, Error> { - let output: Vec = cmd!("git", "ls-remote", "--tags", "https://github.com/golang/go").read()? - .trim() - .lines() - .filter(|x| x.contains("go")) - .filter_map(|x| x.split('\t').nth(1)) - .filter_map(|x| x.split('/').nth(2)) - .map(|x| x.replace("go", "")) - .collect(); + let output: Vec = + cmd!("git", "ls-remote", "--tags", "https://github.com/golang/go") + .read()? + .trim() + .lines() + .filter(|x| x.contains("go")) + .filter_map(|x| x.split('\t').nth(1)) + .filter_map(|x| x.split('/').nth(2)) + .map(|x| x.replace("go", "")) + .collect(); Ok(output) } + pub async fn get_gh_version() -> Result, Error> { + let client = reqwest::Client::new(); + let resp: Vec = client + .get("https://api.github.com/repos/golang/go/tags?page=2&per_page=100") + .header(USER_AGENT, "Get_Tag") + .send() + .await? + .json() + .await?; + let mut filtered: Vec = resp + .iter() + .filter(|x| x.name.contains("go")) + .map(|x| x.name.clone().replace("go", "")) + .filter_map(|x| Versioning::new(x.as_ref())) + .filter(|x| x.is_ideal()) + .collect::>(); + filtered.sort_unstable(); + filtered.reverse(); + Ok(filtered) + } /// Parses the versions into Versioning structs pub fn get_versions() -> Result, Error> { let unparsed = Self::get_git_versions()?; @@ -52,8 +68,12 @@ impl GoVersion { Ok(parsed) } /// Gets the latest versions by sorting the parsed versions - fn get_latest() -> Result { - let mut versions = GoVersion::get_versions()?; + async fn get_latest(git: bool) -> Result { + let mut versions = if git { + Self::get_versions()? + } else { + Self::get_gh_version().await? + }; versions.sort_by(|a, b| b.cmp(&a)); let latest = versions.first().ok_or(Error::NoVersion)?.to_owned(); Ok(latest) @@ -64,8 +84,25 @@ impl GoVersion { let soup = Soup::new(&resp.text().await?); let govers = format!("go{}", vers); let gofile = format!("{}.{}", govers, FILE_EXT); - let latest = soup.tag("div").attr("id", govers).find().ok_or(Error::NoSha)?; - let mut children = latest.tag("tr").class("highlight").find_all(); + println!("{}", gofile); + let latest = soup + .tag("div") + .attr("id", govers) + .find() + .ok_or(Error::NoSha)?; + println!("Found latest"); + let mut children = latest.tag("tr").find_all().filter(|f| { + let cls = f.get("class"); + if cls.is_some() { + if cls.unwrap() == "first" { + false + } else { + true + } + } else { + true + } + }); let found = children .find(|child| { child @@ -76,12 +113,13 @@ impl GoVersion { .contains(&gofile) }) .ok_or(Error::NoSha)?; + println!("Found filename"); let sha = found.tag("tt").find().ok_or(Error::NoSha)?.text(); Ok(sha) } /// Constructs the url for the version fn construct_url(vers: impl std::fmt::Display) -> String { - format!("{}/go{}.{}", DL_URL, vers, FILE_EXT) + return format!("{}/go{}.{}", DL_URL, vers, FILE_EXT); } /// Downloads the required version async pub async fn download(&self, output: PathBuf, workers: u8) -> Result { @@ -90,15 +128,23 @@ impl GoVersion { .progress_chars("#>-"); let path_str = output.to_str().ok_or(Error::PathBufErr)?; let pb = ProgressBar::new(100); - pb.set_style(style); + pb.set_style(style); let client = reqwest::Client::new(); let filename = manic::downloader::get_filename(&self.dl_url)?; - downloader::download_verify_and_save(&client, &self.dl_url, workers, &self.sha256, path_str, pb).await?; + downloader::download_verify_and_save( + &client, + &self.dl_url, + workers, + &self.sha256, + path_str, + pb, + ) + .await?; Ok(output.join(filename)) } /// Constructs the latest GoVersion - pub async fn latest() -> Result { - let vers = GoVersion::get_latest()?; + pub async fn latest(git: bool) -> Result { + let vers = GoVersion::get_latest(git).await?; let url = GoVersion::construct_url(&vers); let sha = GoVersion::get_sha(&vers).await?; Ok(GoVersion { @@ -120,10 +166,10 @@ impl GoVersion { pub fn check_git() -> bool { match cmd!("git", "version").stdout_null().run() { - Ok(_) => return true, + Ok(_) => true, Err(e) => match e.kind() { - std::io::ErrorKind::NotFound => return false, - _ => return true, - } + std::io::ErrorKind::NotFound => false, + _ => true, + }, } } diff --git a/src/main.rs b/src/main.rs index 44e0d0f..600399c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,8 @@ struct Opt { version: Option, #[structopt(short, long)] interactive: bool, + #[structopt(short, long)] + git: bool, } /// Reads output path from command line arguments @@ -31,6 +33,8 @@ async fn main() -> Result<(), Error> { let opt = Opt::from_args(); let term = Term::stdout(); let git_present = check_git(); + println!("ARCH: {}", std::env::consts::ARCH); + println!("File ext: {}", crate::consts::FILE_EXT); let golang = { if let Some(vers) = opt.version { goversion::GoVersion::version(vers).await? @@ -38,15 +42,19 @@ async fn main() -> Result<(), Error> { let vers = ask_for_version(&term)?; goversion::GoVersion::version(vers).await? } else { - if git_present { - goversion::GoVersion::latest().await? - } else { - leg::error( + if opt.git { + if git_present { + goversion::GoVersion::latest(true).await? + } else { + leg::error( "You requested the latest version and git is not installed, please install git", None, None, ); - quit::with_code(127); + quit::with_code(127); + } + } else { + goversion::GoVersion::latest(false).await? } } }; @@ -69,7 +77,6 @@ async fn main() -> Result<(), Error> { None, None, ); - Ok(()) } @@ -96,5 +103,7 @@ fn ask_for_version(term: &Term) -> Result { } } +mod consts; mod error; +mod github; mod goversion;