diff --git a/README.md b/README.md index c9903db..1690131 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,9 @@ owner = "XAMPPRocky" # GitHub username repo = "tokei" # GitHub repository exe_name = "tokei" # Executable name inside the asset +# uncomment to download a specific version or tag +# tag = "12.1.1" + # Asset name to download on linux OSes asset_name.linux = "x86_64-unknown-linux-musl" 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..41aa369 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, }, )]), }; @@ -258,6 +264,7 @@ mod tests { asset_name.linux = "R2D2" asset_name.macos = "C3-PO" asset_name.windows = "IG-88" + tag = "4.2.0" "#; let res = parse_string(toml); @@ -275,6 +282,7 @@ mod tests { macos: Some("C3-PO".to_owned()), windows: Some("IG-88".to_owned()), }, + tag: Some("4.2.0".to_owned()), }, )]), }; diff --git a/src/model/tool.rs b/src/model/tool.rs index 7416843..d45d5f8 100644 --- a/src/model/tool.rs +++ b/src/model/tool.rs @@ -26,6 +26,27 @@ 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), +} + +const LATEST_VERSION: &str = "latest"; + +impl ToolInfoTag { + pub fn to_str_version(&self) -> String { + match self { + Self::Latest => LATEST_VERSION.to_owned(), + Self::Specific(version) => format!("tags/{}", version), + } + } +} + /// All info about installing a tool from GitHub releases #[derive(Debug, PartialEq, Eq)] pub struct ToolInfo { @@ -40,4 +61,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..d3b2a6e 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(ToolInfoTag::Specific) + .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..16bdb07 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 version: &'a str, pub pb_msg: &'a ProgressBar, pub sync_progress: &'a SyncProgress, } @@ -25,9 +26,10 @@ pub struct DownloadInfo { impl<'a> Downloader<'a> { fn release_url(&self) -> String { format!( - "https://api.github.com/repos/{owner}/{repo}/releases/latest", + "https://api.github.com/repos/{owner}/{repo}/releases/{version}", owner = self.owner, - repo = self.repo + repo = self.repo, + version = self.version, ) } @@ -118,3 +120,44 @@ pub fn add_auth_header(req: ureq::Request) -> ureq::Request { Ok(token) => req.set("Authorization", &format!("token {}", token)), } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::model::tool::ToolInfoTag; + + #[test] + fn release_url_with_latest_tag_is_correct() { + let downloader = Downloader { + owner: "OWNER", + repo: "REPO", + asset_name: "ASSET_NAME", + version: &ToolInfoTag::Latest.to_str_version(), + 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", + version: &ToolInfoTag::Specific("SPECIFIC_TAG".to_string()).to_str_version(), + 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..a2f8ffd 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, + version: &tool_info.tag.to_str_version(), sync_progress: &self.sync_progress, pb_msg, asset_name,