diff --git a/src/config/schema.rs b/src/config/schema.rs index 67d8858..137bacf 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -33,6 +33,10 @@ pub struct ConfigAsset { /// Name of the specific asset to download pub asset_name: AssetName, + + /// Release tag to download + /// Defaults to the latest release + pub tag: Option, } impl Config { diff --git a/src/config/toml.rs b/src/config/toml.rs index 3aae027..1136ebd 100644 --- a/src/config/toml.rs +++ b/src/config/toml.rs @@ -62,12 +62,14 @@ fn decode_config_asset(table: &Map) -> ConfigAsset { let repo = str_by_key(table, "repo"); let exe_name = str_by_key(table, "exe_name"); let asset_name = decode_asset_name(table); + let tag = str_by_key(table, "tag"); ConfigAsset { owner, repo, exe_name, asset_name, + tag, } } @@ -161,6 +163,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }, )]), }; @@ -193,6 +196,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }, ), ( @@ -206,6 +210,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }, ), ]), @@ -239,6 +244,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }, )]), }; @@ -275,6 +281,7 @@ mod tests { macos: Some("C3-PO".to_owned()), windows: Some("IG-88".to_owned()), }, + tag: None, }, )]), }; diff --git a/src/model/tool.rs b/src/model/tool.rs index 7416843..240bd79 100644 --- a/src/model/tool.rs +++ b/src/model/tool.rs @@ -26,6 +26,25 @@ impl ToolError { } } +/// Determines whether to download the latest version of a tool or a +/// specific version of it. +#[derive(Debug, PartialEq, Eq)] +pub enum ToolInfoTag { + /// Download latest + Latest, + /// Download a specific version + Specific(String), +} + +impl ToolInfoTag { + pub fn as_specific_tag(&self) -> Option<&str> { + match self { + Self::Latest => None, + Self::Specific(version) => Some(&version), + } + } +} + /// All info about installing a tool from GitHub releases #[derive(Debug, PartialEq, Eq)] pub struct ToolInfo { @@ -40,4 +59,7 @@ pub struct ToolInfo { /// Asset name depending on the OS pub asset_name: AssetName, + + /// Version tag + pub tag: ToolInfoTag, } diff --git a/src/sync/configure.rs b/src/sync/configure.rs index 060137d..40301cf 100644 --- a/src/sync/configure.rs +++ b/src/sync/configure.rs @@ -1,6 +1,6 @@ use crate::config::schema::ConfigAsset; use crate::model::asset_name::AssetName; -use crate::model::tool::{Tool, ToolError, ToolInfo}; +use crate::model::tool::{Tool, ToolError, ToolInfo, ToolInfoTag}; use crate::sync::db::lookup_tool; pub fn configure_tool(tool_name: &str, config_asset: &ConfigAsset) -> Tool { @@ -32,6 +32,11 @@ fn full_configure(config_asset: &ConfigAsset) -> Option { let owner = config_asset.owner.clone()?; let repo = config_asset.repo.clone()?; let exe_name = config_asset.exe_name.clone()?; + let tag = config_asset + .tag + .clone() + .map(|version| ToolInfoTag::Specific(version)) + .unwrap_or(ToolInfoTag::Latest); Some(ToolInfo { owner, @@ -42,6 +47,7 @@ fn full_configure(config_asset: &ConfigAsset) -> Option { macos: config_asset.asset_name.macos.clone(), windows: config_asset.asset_name.windows.clone(), }, + tag, }) } @@ -78,6 +84,11 @@ impl ToolInfo { .clone() .or_else(|| self.asset_name.windows.clone()), }, + tag: config_asset + .tag + .clone() + .map(|version| ToolInfoTag::Specific(version)) + .unwrap_or(ToolInfoTag::Latest), } } } @@ -99,6 +110,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }; assert_eq!( @@ -120,6 +132,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }; assert_eq!( @@ -141,6 +154,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }; assert_eq!( @@ -164,6 +178,7 @@ mod tests { macos: None, windows: None, }, + tag: Some(String::from("1.2.3")), }; assert_eq!( @@ -185,6 +200,7 @@ mod tests { macos: Some(String::from("my-macos")), windows: Some(String::from("yours-windows")), }, + tag: Some(String::from("1.2.3")), }; assert_eq!( @@ -197,7 +213,8 @@ mod tests { linux: Some("my-linux".to_string()), macos: Some("my-macos".to_string()), windows: Some("yours-windows".to_string()), - } + }, + tag: ToolInfoTag::Specific("1.2.3".to_string()), }) ); } @@ -215,6 +232,7 @@ mod tests { macos: None, windows: None, }, + tag: None, }; assert_eq!( @@ -227,7 +245,8 @@ mod tests { linux: Some("unknown-linux-musl".to_string()), macos: Some("apple-darwin".to_string()), windows: Some("x86_64-pc-windows-msvc".to_string()), - } + }, + tag: ToolInfoTag::Latest, }) ); } @@ -245,6 +264,7 @@ mod tests { macos: Some(String::from("my-macos")), windows: Some(String::from("yours-windows")), }, + tag: Some(String::from("3.2.1")), }; assert_eq!( @@ -257,7 +277,8 @@ mod tests { linux: Some("my-linux".to_string()), macos: Some("my-macos".to_string()), windows: Some("yours-windows".to_string()), - } + }, + tag: ToolInfoTag::Specific("3.2.1".to_string()), }) ); } diff --git a/src/sync/db.rs b/src/sync/db.rs index d106655..b805674 100644 --- a/src/sync/db.rs +++ b/src/sync/db.rs @@ -1,5 +1,5 @@ use crate::model::asset_name::AssetName; -use crate::model::tool::ToolInfo; +use crate::model::tool::{ToolInfo, ToolInfoTag}; /// Get info about known tools from a hardcoded database pub fn lookup_tool(tool_name: &str) -> Option { @@ -13,6 +13,7 @@ pub fn lookup_tool(tool_name: &str) -> Option { macos: Some("x86_64-apple-darwin".to_string()), windows: Some("x86_64-pc-windows-msvc".to_string()), }, + tag: ToolInfoTag::Latest, }), "difftastic" => Some(ToolInfo { owner: "Wilfred".to_string(), @@ -23,6 +24,7 @@ pub fn lookup_tool(tool_name: &str) -> Option { macos: Some("x86_64-apple-darwin".to_string()), windows: Some("x86_64-pc-windows-msvc".to_string()), }, + tag: ToolInfoTag::Latest, }), "exa" => Some(ToolInfo { owner: "ogham".to_string(), @@ -33,6 +35,7 @@ pub fn lookup_tool(tool_name: &str) -> Option { macos: Some("macos-x86_64".to_string()), windows: None, }, + tag: ToolInfoTag::Latest, }), "fd" => Some(ToolInfo { owner: "sharkdp".to_string(), @@ -43,6 +46,7 @@ pub fn lookup_tool(tool_name: &str) -> Option { macos: Some("x86_64-apple-darwin".to_string()), windows: Some("x86_64-pc-windows-msvc".to_string()), }, + tag: ToolInfoTag::Latest, }), "ripgrep" => Some(ToolInfo { owner: "BurntSushi".to_string(), @@ -53,6 +57,7 @@ pub fn lookup_tool(tool_name: &str) -> Option { macos: Some("apple-darwin".to_string()), windows: Some("x86_64-pc-windows-msvc".to_string()), }, + tag: ToolInfoTag::Latest, }), "tool-sync" => Some(ToolInfo { owner: "chshersh".to_string(), @@ -63,6 +68,7 @@ pub fn lookup_tool(tool_name: &str) -> Option { macos: Some("x86_64-apple-darwin".to_string()), windows: Some("x86_64-pc-windows-msvc".to_string()), }, + tag: ToolInfoTag::Latest, }), // "tokei" => Some(ToolInfo { // owner: "XAMPPRocky".to_string(), @@ -73,6 +79,7 @@ pub fn lookup_tool(tool_name: &str) -> Option { // macos: Some("apple-darwin".to_string()), // windows: Some("x86_64-pc-windows-msvc".to_string()), // } + // tag: ToolInfoTag::Latest, // }), _ => None, } diff --git a/src/sync/download.rs b/src/sync/download.rs index cf90381..8e08f7d 100644 --- a/src/sync/download.rs +++ b/src/sync/download.rs @@ -12,6 +12,7 @@ pub struct Downloader<'a> { pub owner: &'a str, pub repo: &'a str, pub asset_name: &'a str, + pub specific_tag: &'a Option<&'a str>, pub pb_msg: &'a ProgressBar, pub sync_progress: &'a SyncProgress, } @@ -24,11 +25,20 @@ pub struct DownloadInfo { impl<'a> Downloader<'a> { fn release_url(&self) -> String { - format!( - "https://api.github.com/repos/{owner}/{repo}/releases/latest", - owner = self.owner, - repo = self.repo - ) + if let Some(tag) = self.specific_tag { + format!( + "https://api.github.com/repos/{owner}/{repo}/releases/tags/{tag}", + owner = self.owner, + repo = self.repo, + tag = tag, + ) + } else { + format!( + "https://api.github.com/repos/{owner}/{repo}/releases/latest", + owner = self.owner, + repo = self.repo, + ) + } } fn asset_url(&self, asset_id: u32) -> String { @@ -118,3 +128,42 @@ pub fn add_auth_header(req: ureq::Request) -> ureq::Request { Ok(token) => req.set("Authorization", &format!("token {}", token)), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn release_url_with_no_specific_tag_is_correct() { + let downloader = Downloader { + owner: "OWNER", + repo: "REPO", + asset_name: "ASSET_NAME", + specific_tag: &None, + pb_msg: &ProgressBar::hidden(), + sync_progress: &SyncProgress::new(vec!["tool".to_string()]), + }; + + assert_eq!( + downloader.release_url(), + "https://api.github.com/repos/OWNER/REPO/releases/latest" + ); + } + + #[test] + fn release_url_with_specific_tag_is_correct() { + let downloader = Downloader { + owner: "OWNER", + repo: "REPO", + asset_name: "ASSET_NAME", + specific_tag: &Some("SPECIFIC_TAG"), + pb_msg: &ProgressBar::hidden(), + sync_progress: &SyncProgress::new(vec!["tool".to_string()]), + }; + + assert_eq!( + downloader.release_url(), + "https://api.github.com/repos/OWNER/REPO/releases/tags/SPECIFIC_TAG" + ); + } +} diff --git a/src/sync/install.rs b/src/sync/install.rs index b72ace3..48ba25e 100644 --- a/src/sync/install.rs +++ b/src/sync/install.rs @@ -71,6 +71,7 @@ impl Installer { let downloader = Downloader { owner: &tool_info.owner, repo: &tool_info.repo, + specific_tag: &tool_info.tag.as_specific_tag(), sync_progress: &self.sync_progress, pb_msg, asset_name,