From 139ff5793c28f98308ed75bcfbf338c8745f733e Mon Sep 17 00:00:00 2001 From: James Date: Mon, 29 Jan 2018 23:37:17 +1100 Subject: [PATCH] Select latest release file in install pipeline --- src/assets.rs | 1 - src/config.rs | 13 ++++--- src/installer.rs | 20 ++++++----- src/main.rs | 2 +- src/rest.rs | 6 ++-- src/sources/github/mod.rs | 74 ++++++++++++++++++++++++--------------- src/sources/mod.rs | 8 ++--- src/sources/types.rs | 42 ++++++++++++---------- 8 files changed, 93 insertions(+), 73 deletions(-) diff --git a/src/assets.rs b/src/assets.rs index 7d9611e..6a36ca5 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -1,5 +1,4 @@ /// Serves static files from a asset directory. - extern crate mime_guess; use std::borrow::Cow; diff --git a/src/config.rs b/src/config.rs index e0f855d..737d764 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,10 +13,9 @@ use sources::get_by_name; /// Description of the source of a package. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct PackageSource { - pub name : String, - #[serde(rename="match")] - pub match_regex : String, - pub config : toml::Value + pub name: String, + #[serde(rename = "match")] pub match_regex: String, + pub config: toml::Value, } /// Describes a overview of a individual package. @@ -25,7 +24,7 @@ pub struct PackageDescription { pub name: String, pub description: String, pub default: Option, - pub source: PackageSource + pub source: PackageSource, } /// Describes the application itself. @@ -58,9 +57,9 @@ impl PackageSource { pub fn get_current_releases(&self) -> Result, String> { let package_handler = match get_by_name(&self.name) { Some(v) => v, - _ => return Err(format!("Handler {} not found", self.name)) + _ => return Err(format!("Handler {} not found", self.name)), }; package_handler.get_current_releases(&self.config) } -} \ No newline at end of file +} diff --git a/src/installer.rs b/src/installer.rs index 2c99a16..caa8242 100644 --- a/src/installer.rs +++ b/src/installer.rs @@ -39,7 +39,7 @@ impl InstallerFramework { } /// Sends a request for something to be installed. - pub fn install(&self, items : Vec) { + pub fn install(&self, items: Vec) { // TODO: Error handling println!("Framework: Installing {:?}", items); @@ -60,17 +60,21 @@ impl InstallerFramework { let results = package.source.get_current_releases().unwrap(); - println!("Got releases"); - let filtered_regex = package.source.match_regex.replace("#PLATFORM#", OS); - println!("Filtered regex: {}" , filtered_regex); let regex = Regex::new(&filtered_regex).unwrap(); // Find the latest release in here - let latest_result = results.into_iter() - .filter(|f| f.files.iter().filter(|x| regex.is_match(x)).count() > 0) - .max_by_key(|f| f.version.clone()); - println!("{:?}", latest_result); + let latest_result = results + .into_iter() + .filter(|f| f.files.iter().filter(|x| regex.is_match(&x.name)).count() > 0) + .max_by_key(|f| f.version.clone()).unwrap(); + + // Find the matching file in here + let latest_file = latest_result.files.into_iter() + .filter(|x| regex.is_match(&x.name)) + .next().unwrap(); + + println!("{:?}", latest_file); } } diff --git a/src/main.rs b/src/main.rs index 83b5d8b..415b2eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,8 +16,8 @@ extern crate serde_derive; extern crate serde_json; extern crate toml; -extern crate semver; extern crate regex; +extern crate semver; mod assets; mod rest; diff --git a/src/rest.rs b/src/rest.rs index 8c09578..2ca42a2 100644 --- a/src/rest.rs +++ b/src/rest.rs @@ -2,7 +2,6 @@ /// /// Provides a HTTP/REST server for both frontend<->backend communication, as well /// as talking to external applications. - extern crate nfd; extern crate url; @@ -153,7 +152,8 @@ impl Service for WebService { return Box::new(req.body().concat2().map(move |b| { let results = form_urlencoded::parse(b.as_ref()) - .into_owned().collect::>(); + .into_owned() + .collect::>(); let mut to_install = Vec::new(); @@ -194,7 +194,7 @@ impl Service for WebService { .with_header(ContentLength(file.len() as u64)) .with_header(content_type) .with_body(file) - }, + } None => Response::new().with_status(StatusCode::NotFound), } } diff --git a/src/sources/github/mod.rs b/src/sources/github/mod.rs index 817b7e9..7ce2c6d 100644 --- a/src/sources/github/mod.rs +++ b/src/sources/github/mod.rs @@ -18,13 +18,12 @@ use serde_json; use sources::types::*; -pub struct GithubReleases { -} +pub struct GithubReleases {} /// The configuration for this release. #[derive(Serialize, Deserialize)] struct GithubConfig { - repo : String + repo: String, } impl GithubReleases { @@ -38,87 +37,104 @@ impl ReleaseSource for GithubReleases { // Reparse our Config as strongly typed let config_string = match toml::to_string(config) { Ok(v) => v, - Err(v) => return Err(format!("Failed to convert config: {:?}", v)) + Err(v) => return Err(format!("Failed to convert config: {:?}", v)), }; - let config : GithubConfig = match toml::from_str(&config_string) { + let config: GithubConfig = match toml::from_str(&config_string) { Ok(v) => v, - Err(v) => return Err(format!("Failed to convert config: {:?}", v)) + Err(v) => return Err(format!("Failed to convert config: {:?}", v)), }; let mut core = match Core::new() { Ok(v) => v, - Err(v) => return Err(format!("Failed to init Tokio: {:?}", v)) + Err(v) => return Err(format!("Failed to init Tokio: {:?}", v)), }; // Build the HTTP client up let client = Client::configure() .connector(match HttpsConnector::new(4, &core.handle()) { Ok(v) => v, - Err(v) => return Err(format!("Failed to init https: {:?}", v)) + Err(v) => return Err(format!("Failed to init https: {:?}", v)), }) .build(&core.handle()); let mut results: Vec = Vec::new(); - let target_url : Uri = match format!("https://api.github.com/repos/{}/releases", - config.repo).parse() { - Ok(v) => v, - Err(v) => return Err(format!("Failed to generate target url: {:?}", v)) - }; + let target_url: Uri = + match format!("https://api.github.com/repos/{}/releases", config.repo).parse() { + Ok(v) => v, + Err(v) => return Err(format!("Failed to generate target url: {:?}", v)), + }; let mut req = Request::new(Method::Get, target_url); - req.headers_mut().set(UserAgent::new("installer-rs (j-selby)")); + req.headers_mut() + .set(UserAgent::new("installer-rs (j-selby)")); // Build our future let future = client.request(req).and_then(|res| { res.body().concat2().and_then(move |body| { - let raw_json : Result - = match serde_json::from_slice(&body) { - Ok(v) => Ok(v), - Err(v) => Err(format!("Failed to parse response: {:?}", v)) - }; + let raw_json: Result = + match serde_json::from_slice(&body) { + Ok(v) => Ok(v), + Err(v) => Err(format!("Failed to parse response: {:?}", v)), + }; Ok(raw_json) }) }); // Unwrap the future's results - let result : serde_json::Value = match core.run(future) { + let result: serde_json::Value = match core.run(future) { Ok(v) => v, - Err(v) => return Err(format!("Failed to fetch info: {:?}", v)) + Err(v) => return Err(format!("Failed to fetch info: {:?}", v)), }?; - let result : &Vec = match result.as_array() { + let result: &Vec = match result.as_array() { Some(v) => v, - None => return Err(format!("JSON payload not an array")) + None => return Err(format!("JSON payload not an array")), }; // Parse JSON from server for entry in result.into_iter() { let mut files = Vec::new(); - let id : u64 = match entry["id"].as_u64() { + let id: u64 = match entry["id"].as_u64() { Some(v) => v, - None => return Err(format!("JSON payload missing information about ID")) + None => return Err(format!("JSON payload missing information about ID")), }; let assets = match entry["assets"].as_array() { Some(v) => v, - None => return Err(format!("JSON payload not an array")) + None => return Err(format!("JSON payload not an array")), }; for asset in assets.into_iter() { let string = match asset["name"].as_str() { Some(v) => v, - None => return Err(format!("JSON payload missing information about ID")) + None => { + return Err(format!( + "JSON payload missing information about release name" + )) + } }; - files.push(string.to_owned()); + let url = match asset["browser_download_url"].as_str() { + Some(v) => v, + None => { + return Err(format!( + "JSON payload missing information about release URL" + )) + } + }; + + files.push(File { + name: string.to_owned(), + url: url.to_owned(), + }); } results.push(Release { version: Version::new_number(id), - files + files, }); } diff --git a/src/sources/mod.rs b/src/sources/mod.rs index 1c41433..d3a0533 100644 --- a/src/sources/mod.rs +++ b/src/sources/mod.rs @@ -9,11 +9,9 @@ pub mod github; use self::types::ReleaseSource; /// Returns a ReleaseSource by a name, if possible -pub fn get_by_name(name : &str) -> Option> { +pub fn get_by_name(name: &str) -> Option> { match name { - "github" => { - Some(Box::new(github::GithubReleases::new())) - } - _ => None + "github" => Some(Box::new(github::GithubReleases::new())), + _ => None, } } diff --git a/src/sources/types.rs b/src/sources/types.rs index aac5cbb..b8fcbc6 100644 --- a/src/sources/types.rs +++ b/src/sources/types.rs @@ -12,7 +12,7 @@ pub use toml::value::Value as TomlValue; #[derive(Debug, Eq, PartialEq, Clone)] pub enum Version { Semver(SemverVersion), - Integer(u64) + Integer(u64), } impl Version { @@ -21,18 +21,19 @@ impl Version { fn coarse_into_semver(&self) -> SemverVersion { match self { &Version::Semver(ref version) => version.to_owned(), - &Version::Integer(ref version) => SemverVersion::from((version.to_owned(), - 0 as u64, 0 as u64)) + &Version::Integer(ref version) => { + SemverVersion::from((version.to_owned(), 0 as u64, 0 as u64)) + } } } /// Returns a new Version, backed by semver. - pub fn new_semver(version : SemverVersion) -> Version { + pub fn new_semver(version: SemverVersion) -> Version { Version::Semver(version) } /// Returns a new Version, backed by a integer. - pub fn new_number(version : u64) -> Version { + pub fn new_number(version: u64) -> Version { Version::Integer(version) } } @@ -40,18 +41,14 @@ impl Version { impl PartialOrd for Version { fn partial_cmp(&self, other: &Version) -> Option { match self { - &Version::Semver(ref version) => { - match other { - &Version::Semver(ref other_version) => Some(version.cmp(other_version)), - _ => None - } + &Version::Semver(ref version) => match other { + &Version::Semver(ref other_version) => Some(version.cmp(other_version)), + _ => None, + }, + &Version::Integer(ref num) => match other { + &Version::Integer(ref other_num) => Some(num.cmp(other_num)), + _ => None, }, - &Version::Integer(ref num) => { - match other { - &Version::Integer(ref other_num) => Some(num.cmp(other_num)), - _ => None - } - } } } } @@ -62,16 +59,23 @@ impl Ord for Version { } } +/// A individual file in a release. +#[derive(Debug)] +pub struct File { + pub name: String, + pub url: String, +} + /// A individual release of an application. #[derive(Debug)] pub struct Release { - pub version : Version, - pub files : Vec + pub version: Version, + pub files: Vec, } /// A source of releases. pub trait ReleaseSource { /// Gets a list of the available releases from this source. Should cache internally /// if possible using a mutex. - fn get_current_releases(&self, config : &TomlValue) -> Result, String>; + fn get_current_releases(&self, config: &TomlValue) -> Result, String>; }