From a98f052997d7463e54f9ff568438dc60c741033c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20B=C3=B8ving?= Date: Sat, 11 Feb 2023 11:50:47 +0100 Subject: [PATCH] Add identifier for releases with multiple compatible assets This change is relevant when you have multiple binaries as part of a single release. In this case, the target is not enough to determine which asset to choose. Thus this change allows an identifier to be passed along from the builders, to select the correct one in such cases. This adds argument `identifier: Option<&str>` to `Release::asset_for`, which allows the caller to specify a string that must be part of the asset name in addition to the target. Additionally it adds a method to `ReleaseUpdate` which allows the identifier to be specified in the `UpdateBuilder` structs. One possible change, could be to make the `identifier` a closure, or like the `Pattern` like the struct search functions in `std`, instead of relying on only substrings. --- src/backends/github.rs | 16 ++++++++++++++++ src/update.rs | 27 ++++++++++++++++++++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/backends/github.rs b/src/backends/github.rs index 8bc9971..2fd940a 100644 --- a/src/backends/github.rs +++ b/src/backends/github.rs @@ -230,6 +230,7 @@ pub struct UpdateBuilder { repo_owner: Option, repo_name: Option, target: Option, + identifier: Option, bin_name: Option, bin_install_path: Option, bin_path_in_archive: Option, @@ -295,6 +296,14 @@ impl UpdateBuilder { self } + /// Set the identifiable token for the asset in case of multiple compatible assets + /// + /// If unspecified, the first asset matching the target will be chosen + pub fn identifier(&mut self, identifier: &str) -> &mut Self { + self.identifier = Some(identifier.to_owned()); + self + } + /// Set the exe's name. Also sets `bin_path_in_archive` if it hasn't already been set. /// /// This method will append the platform specific executable file suffix @@ -415,6 +424,7 @@ impl UpdateBuilder { .as_ref() .map(|t| t.to_owned()) .unwrap_or_else(|| get_target().to_owned()), + identifier: self.identifier.clone(), bin_name: if let Some(ref name) = self.bin_name { name.to_owned() } else { @@ -449,6 +459,7 @@ pub struct Update { repo_owner: String, repo_name: String, target: String, + identifier: Option, current_version: String, target_version: Option, bin_name: String, @@ -535,6 +546,10 @@ impl ReleaseUpdate for Update { self.target_version.clone() } + fn identifier(&self) -> Option { + self.identifier.clone() + } + fn bin_name(&self) -> String { self.bin_name.clone() } @@ -578,6 +593,7 @@ impl Default for UpdateBuilder { repo_owner: None, repo_name: None, target: None, + identifier: None, bin_name: None, bin_install_path: None, bin_path_in_archive: None, diff --git a/src/update.rs b/src/update.rs index b720722..52475e7 100644 --- a/src/update.rs +++ b/src/update.rs @@ -58,13 +58,19 @@ impl Release { } /// Return the first `ReleaseAsset` for the current release who's name - /// contains the specified `target` - pub fn asset_for(&self, target: &str) -> Option { + /// contains the specified `target` and possibly `identifier` + pub fn asset_for(&self, target: &str, identifier: Option<&str>) -> Option { self.assets .iter() - .filter(|asset| asset.name.contains(target)) + .find(|asset| { + asset.name.contains(target) + && if let Some(i) = identifier { + asset.name.contains(i) + } else { + true + } + }) .cloned() - .next() } } @@ -85,6 +91,11 @@ pub trait ReleaseUpdate { /// Target version optionally specified for the update fn target_version(&self) -> Option; + /// Optional identifier of determining the asset among multiple matches + fn identifier(&self) -> Option { + None + } + /// Name of the binary being updated fn bin_name(&self) -> String; @@ -190,9 +201,11 @@ pub trait ReleaseUpdate { } }; - let target_asset = release.asset_for(&target).ok_or_else(|| { - format_err!(Error::Release, "No asset found for target: `{}`", target) - })?; + let target_asset = release + .asset_for(&target, self.identifier().as_deref()) + .ok_or_else(|| { + format_err!(Error::Release, "No asset found for target: `{}`", target) + })?; let prompt_confirmation = !self.no_confirm(); if self.show_output() || prompt_confirmation {