Enforce strict strategies parsing rule: Reject duplicate strategy in --strategies (#545)

`cargo-binstall` should reject duplicate strategy in `--strategies` as it is illegal input and it cannot be interpreted reasonably.

* Reject duplicate strategy in `--strategies`
* Optimize `compute_resolvers`: Use `Vec::retain`
   instead of `Iterator::filter` plus collecting into `Vec`.
* Optimize `compute_resolvers`: Reject `strategies.len() > Strategy::COUNT`
* Improve err msg for cases where user disabled all strategies
* Simplify `BinstallError::InvalidStrategies`: Takes `&'static str`
   instead of `&'static &'static str` since other variants are larger than 8B.

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2022-11-21 12:02:58 +11:00 committed by GitHub
parent bdb4b2070d
commit 83b2de3ea2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 25 deletions

View file

@ -298,6 +298,7 @@ impl Default for RateLimit {
/// Strategy for installing the package /// Strategy for installing the package
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ValueEnum, EnumCount)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ValueEnum, EnumCount)]
#[repr(u8)]
pub enum Strategy { pub enum Strategy {
/// Attempt to download official pre-built artifacts using /// Attempt to download official pre-built artifacts using
/// information provided in `Cargo.toml`. /// information provided in `Cargo.toml`.

View file

@ -185,22 +185,27 @@ pub async fn install_crates(args: Args, jobserver_client: LazyJobserverClient) -
/// Return (resolvers, cargo_install_fallback) /// Return (resolvers, cargo_install_fallback)
fn compute_resolvers( fn compute_resolvers(
input_strategies: Vec<Strategy>, mut strategies: Vec<Strategy>,
mut disable_strategies: Vec<Strategy>, mut disable_strategies: Vec<Strategy>,
) -> Result<(Vec<Resolver>, bool), BinstallError> { ) -> Result<(Vec<Resolver>, bool), BinstallError> {
// Compute strategies let dup_strategy_err =
let mut strategies = vec![]; BinstallError::InvalidStrategies("--strategies should not contain duplicate strategy");
// Remove duplicate strategies if strategies.len() > Strategy::COUNT {
for strategy in input_strategies { // If len of strategies is larger than number of variants of Strategy,
if strategies.len() == Strategy::COUNT { // then there must be duplicates by pigeon hole principle.
// All variants of Strategy is present in strategies, return Err(dup_strategy_err);
// there is no need to continue since all the remaining }
// args.strategies must be present in stratetgies.
break; // Whether specific variant of Strategy is present
} let mut is_variant_present = [false; Strategy::COUNT];
if !strategies.contains(&strategy) {
strategies.push(strategy); for strategy in &strategies {
let index = *strategy as u8 as usize;
if is_variant_present[index] {
return Err(dup_strategy_err);
} else {
is_variant_present[index] = true;
} }
} }
@ -213,22 +218,22 @@ fn compute_resolvers(
]; ];
} }
let mut strategies: Vec<Strategy> = if !disable_strategies.is_empty() { // Filter out all disabled strategies
if !disable_strategies.is_empty() {
// Since order doesn't matter, we can sort it and remove all duplicates // Since order doesn't matter, we can sort it and remove all duplicates
// to speedup checking. // to speedup checking.
disable_strategies.sort_unstable(); disable_strategies.sort_unstable();
disable_strategies.dedup(); disable_strategies.dedup();
strategies // disable_strategies.len() <= Strategy::COUNT, of which is faster
.into_iter() // to just use [T]::contains rather than [T]::binary_search
.filter(|strategy| !disable_strategies.contains(strategy)) strategies.retain(|strategy| !disable_strategies.contains(strategy));
.collect()
} else {
strategies
};
if strategies.is_empty() { if strategies.is_empty() {
return Err(BinstallError::InvalidStrategies(&"No strategy is provided")); return Err(BinstallError::InvalidStrategies(
"You have disabled all strategies",
));
}
} }
let cargo_install_fallback = *strategies.last().unwrap() == Strategy::Compile; let cargo_install_fallback = *strategies.last().unwrap() == Strategy::Compile;
@ -243,7 +248,7 @@ fn compute_resolvers(
Strategy::CrateMetaData => Ok(GhCrateMeta::new as Resolver), Strategy::CrateMetaData => Ok(GhCrateMeta::new as Resolver),
Strategy::QuickInstall => Ok(QuickInstall::new as Resolver), Strategy::QuickInstall => Ok(QuickInstall::new as Resolver),
Strategy::Compile => Err(BinstallError::InvalidStrategies( Strategy::Compile => Err(BinstallError::InvalidStrategies(
&"Compile strategy must be the last one", "Compile strategy must be the last one",
)), )),
}) })
.collect::<Result<Vec<_>, BinstallError>>()?; .collect::<Result<Vec<_>, BinstallError>>()?;

View file

@ -291,7 +291,7 @@ pub enum BinstallError {
/// - Exit: 93 /// - Exit: 93
#[error("Invalid strategies configured: {0}")] #[error("Invalid strategies configured: {0}")]
#[diagnostic(severity(error), code(binstall::strategies))] #[diagnostic(severity(error), code(binstall::strategies))]
InvalidStrategies(&'static &'static str), InvalidStrategies(&'static str),
/// Fallback to `cargo-install` is disabled. /// Fallback to `cargo-install` is disabled.
/// ///