diff --git a/src/backend/mod.rs b/src/backend/mod.rs index a1300b2db0..185f01d3b2 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -366,6 +366,7 @@ pub fn install_time_option_keys_for_type(backend_type: &BackendType) -> Vec npm::install_time_option_keys(), BackendType::Pipx => pipx::install_time_option_keys(), BackendType::Aqua => aqua::install_time_option_keys(), + BackendType::Spm => spm::install_time_option_keys(), _ => vec![], } } diff --git a/src/backend/spm.rs b/src/backend/spm.rs index 8670f0c3ac..b56df551ff 100644 --- a/src/backend/spm.rs +++ b/src/backend/spm.rs @@ -1,6 +1,8 @@ use crate::backend::Backend; use crate::backend::VersionInfo; use crate::backend::backend_type::BackendType; +use crate::backend::options::{BackendOptions, is_falsey, is_truthy}; +use crate::backend::platform_target::PlatformTarget; use crate::cli::args::BackendArg; use crate::cmd::CmdLineRunner; use crate::config::{Config, Settings}; @@ -15,6 +17,7 @@ use eyre::{WrapErr, bail}; use serde::Deserialize; use serde::Deserializer; use serde::de::{MapAccess, Visitor}; +use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::{ fmt::{self, Debug}, @@ -32,6 +35,101 @@ pub struct SPMBackend { ba: Arc, } +#[derive(Debug, Clone, Copy)] +struct SpmOptions<'a> { + values: BackendOptions<'a>, +} + +impl<'a> SpmOptions<'a> { + fn new(raw: &'a ToolVersionOptions) -> Self { + Self { + values: BackendOptions::new(raw), + } + } + + fn option_string(&self, key: &str, target: Option<&PlatformTarget>) -> Option { + match target { + Some(target) => self.values.platform_string_for_target(key, target), + None => self.values.platform_string(key), + } + } + + fn provider(&self, target: Option<&PlatformTarget>) -> String { + self.option_string("provider", target) + .unwrap_or_else(|| GitProviderKind::GitHub.as_ref().to_string()) + } + + fn api_url(&self, target: Option<&PlatformTarget>) -> Option { + self.option_string("api_url", target) + } + + fn artifactbundle_asset(&self, target: Option<&PlatformTarget>) -> Option { + self.option_string("artifactbundle_asset", target) + } + + fn artifactbundle_mode( + &self, + target: Option<&PlatformTarget>, + ) -> eyre::Result { + let Some(value) = self.option_string("artifactbundle", target) else { + return Ok(ArtifactBundleMode::Auto); + }; + if is_truthy(&value) { + Ok(ArtifactBundleMode::Required) + } else if is_falsey(&value) { + Ok(ArtifactBundleMode::SourceOnly) + } else { + bail!("artifactbundle must be true, false, 1, or 0, got {value}"); + } + } + + fn requires_artifactbundle(&self, mode: ArtifactBundleMode) -> bool { + mode.requires_artifactbundle() || self.artifactbundle_asset(None).is_some() + } + + fn lockfile_options(&self, target: &PlatformTarget) -> BTreeMap { + let mut opts = BTreeMap::new(); + let provider = self.provider(Some(target)); + if provider != GitProviderKind::GitHub.as_ref() { + opts.insert("provider".to_string(), provider); + } + if let Some(api_url) = self.api_url(Some(target)) { + opts.insert("api_url".to_string(), api_url); + } + match self.artifactbundle_mode(Some(target)) { + Ok(ArtifactBundleMode::Required) => { + opts.insert("artifactbundle".to_string(), "true".to_string()); + } + Ok(ArtifactBundleMode::SourceOnly) => { + opts.insert("artifactbundle".to_string(), "false".to_string()); + } + Ok(ArtifactBundleMode::Auto) | Err(_) => {} + } + if let Some(asset) = self.artifactbundle_asset(Some(target)) { + opts.insert("artifactbundle_asset".to_string(), asset); + } + opts + } + + fn filter_bins(&self) -> Option> { + let value = self.values.raw().opts.get("filter_bins")?; + let bins: Vec = match value { + toml::Value::Array(arr) => arr + .iter() + .filter_map(|v| v.as_str().map(|s| s.trim().to_string())) + .filter(|s| !s.is_empty()) + .collect(), + toml::Value::String(s) => s + .split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(), + _ => return None, + }; + if bins.is_empty() { None } else { Some(bins) } + } +} + #[async_trait] impl Backend for SPMBackend { fn get_type(&self) -> BackendType { @@ -54,8 +152,18 @@ impl Backend for SPMBackend { &["provider", "api_url", "artifactbundle_asset"] } + fn resolve_lockfile_options( + &self, + request: &crate::toolset::ToolRequest, + target: &PlatformTarget, + ) -> BTreeMap { + let raw_opts = request.options(); + SpmOptions::new(&raw_opts).lockfile_options(target) + } + async fn _list_remote_versions(&self, config: &Arc) -> eyre::Result> { - let opts = config.get_tool_opts_with_overrides(&self.ba).await?; + let raw_opts = config.get_tool_opts_with_overrides(&self.ba).await?; + let opts = SpmOptions::new(&raw_opts); let provider = GitProvider::from_ba_with_opts(&self.ba, &opts); let repo = SwiftPackageRepo::new(&self.tool_name(), &provider)?; let versions = match provider.kind { @@ -105,7 +213,8 @@ impl Backend for SPMBackend { ) .await; let mut tv = tv; - let opts = tv.request.options(); + let raw_opts = tv.request.options(); + let opts = SpmOptions::new(&raw_opts); let provider = GitProvider::from_ba_with_opts(&self.ba, &opts); let repo = SwiftPackageRepo::new(&self.tool_name(), &provider)?; let revision = if tv.version == "latest" { @@ -116,14 +225,14 @@ impl Backend for SPMBackend { tv.version.clone() }; - let artifactbundle_mode = resolve_artifactbundle_mode(&opts)?; + let artifactbundle_mode = opts.artifactbundle_mode(None)?; if artifactbundle_mode == ArtifactBundleMode::SourceOnly && Settings::get().spm.artifactbundle_only { bail!("artifactbundle = false conflicts with spm.artifactbundle_only"); } if artifactbundle_mode != ArtifactBundleMode::SourceOnly { - let artifactbundle_required = requires_artifactbundle(artifactbundle_mode, &opts); + let artifactbundle_required = opts.requires_artifactbundle(artifactbundle_mode); match self .try_install_artifactbundle(ctx, &mut tv, &provider, &repo, &revision, &opts) .await @@ -158,6 +267,16 @@ impl Backend for SPMBackend { } } +pub fn install_time_option_keys() -> Vec { + vec![ + "provider".into(), + "api_url".into(), + "artifactbundle".into(), + "artifactbundle_asset".into(), + "filter_bins".into(), + ] +} + impl SPMBackend { pub fn from_arg(ba: BackendArg) -> Self { Self { ba: Arc::new(ba) } @@ -226,7 +345,8 @@ impl SPMBackend { tv: &ToolVersion, executables: Vec, ) -> eyre::Result> { - let opts = tv.request.options(); + let raw_opts = tv.request.options(); + let opts = SpmOptions::new(&raw_opts); filter_executables(&opts, executables) } @@ -323,7 +443,7 @@ impl SPMBackend { provider: &GitProvider, repo: &SwiftPackageRepo, revision: &str, - opts: &ToolVersionOptions, + opts: &SpmOptions<'_>, ) -> eyre::Result { let Some(asset) = resolve_artifactbundle_asset(provider, repo, revision, opts).await? else { @@ -397,37 +517,15 @@ fn with_install_env(mut command: Expression, tv: &ToolVersion) -> Expression { command } -/// Parses the `filter_bins` tool option if set. -/// -/// Accepts either a comma-separated string (`filter_bins = "foo,bar"`) or a -/// TOML array (`filter_bins = ["foo", "bar"]`). Empty entries are ignored. -fn parse_filter_bins(opts: &crate::toolset::ToolVersionOptions) -> Option> { - let value = opts.opts.get("filter_bins")?; - let bins: Vec = match value { - toml::Value::Array(arr) => arr - .iter() - .filter_map(|v| v.as_str().map(|s| s.trim().to_string())) - .filter(|s| !s.is_empty()) - .collect(), - toml::Value::String(s) => s - .split(',') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect(), - _ => return None, - }; - if bins.is_empty() { None } else { Some(bins) } -} - /// Restricts `executables` to those listed in `filter_bins`, preserving the /// original declaration order from `Package.swift` rather than the order in /// `filter_bins`. Returns an error if any name in `filter_bins` does not match /// an available executable product. fn filter_executables( - opts: &crate::toolset::ToolVersionOptions, + opts: &SpmOptions<'_>, executables: Vec, ) -> eyre::Result> { - let Some(filter) = parse_filter_bins(opts) else { + let Some(filter) = opts.filter_bins() else { return Ok(executables); }; let missing: Vec<&str> = filter @@ -474,14 +572,13 @@ pub enum GitProviderKind { impl GitProvider { #[cfg(test)] fn from_ba(ba: &BackendArg) -> Self { - let opts = ba.opts(); + let raw_opts = ba.opts(); + let opts = SpmOptions::new(&raw_opts); Self::from_ba_with_opts(ba, &opts) } - fn from_ba_with_opts(ba: &BackendArg, opts: &ToolVersionOptions) -> Self { - let provider = opts - .get("provider") - .unwrap_or(GitProviderKind::GitHub.as_ref()); + fn from_ba_with_opts(ba: &BackendArg, opts: &SpmOptions<'_>) -> Self { + let provider = opts.provider(None); let kind = if ba.tool_name.contains("gitlab.com") { GitProviderKind::GitLab } else { @@ -491,7 +588,7 @@ impl GitProvider { } }; - let api_url = match opts.get("api_url") { + let api_url = match opts.api_url(None) { Some(api_url) => api_url.trim_end_matches('/').to_string(), None => { Self::derive_api_url_from_tool_name(&ba.tool_name, &kind).unwrap_or_else(|| { @@ -596,19 +693,6 @@ impl ArtifactBundleMode { } } -fn resolve_artifactbundle_mode(opts: &ToolVersionOptions) -> eyre::Result { - match opts.get_string("artifactbundle").as_deref() { - None => Ok(ArtifactBundleMode::Auto), - Some("true") => Ok(ArtifactBundleMode::Required), - Some("false") => Ok(ArtifactBundleMode::SourceOnly), - Some(value) => bail!("artifactbundle must be true or false, got {value}"), - } -} - -fn requires_artifactbundle(mode: ArtifactBundleMode, opts: &ToolVersionOptions) -> bool { - mode.requires_artifactbundle() || opts.get("artifactbundle_asset").is_some() -} - #[derive(Clone, Debug, Eq, PartialEq)] struct ArtifactBundleReleaseAsset { name: String, @@ -621,7 +705,7 @@ async fn resolve_artifactbundle_asset( provider: &GitProvider, repo: &SwiftPackageRepo, revision: &str, - opts: &ToolVersionOptions, + opts: &SpmOptions<'_>, ) -> eyre::Result> { let assets = match provider.kind { GitProviderKind::GitLab => { @@ -668,11 +752,11 @@ async fn resolve_artifactbundle_asset( fn select_artifactbundle_asset( assets: Vec, - opts: &ToolVersionOptions, + opts: &SpmOptions<'_>, ) -> eyre::Result> { - let artifactbundle_asset = opts.get("artifactbundle_asset"); + let artifactbundle_asset = opts.artifactbundle_asset(None); if let Some(name) = artifactbundle_asset { - if !is_artifactbundle_zip(name) { + if !is_artifactbundle_zip(&name) { bail!("artifactbundle_asset must end with .artifactbundle.zip, got {name}"); } return assets @@ -820,7 +904,7 @@ fn is_artifactbundle_dir(path: &Path) -> bool { } fn filter_artifactbundle_binaries( - opts: &ToolVersionOptions, + opts: &SpmOptions<'_>, binaries: Vec, ) -> eyre::Result> { let names = binaries.iter().map(|b| b.name.clone()).collect::>(); @@ -1043,6 +1127,115 @@ mod tests { } } + #[test] + fn test_lockfile_options_include_artifact_inputs_not_filter_bins() { + let mut opts = ToolVersionOptions::default(); + opts.opts.insert( + "provider".to_string(), + toml::Value::String("gitlab".to_string()), + ); + opts.opts.insert( + "api_url".to_string(), + toml::Value::String("https://gitlab.example.com/api/v4".to_string()), + ); + opts.opts + .insert("artifactbundle".to_string(), toml::Value::Boolean(true)); + opts.opts.insert( + "artifactbundle_asset".to_string(), + toml::Value::String("tool.artifactbundle.zip".to_string()), + ); + opts.opts.insert( + "filter_bins".to_string(), + toml::Value::String("tool".to_string()), + ); + + assert_eq!( + SpmOptions::new(&opts).lockfile_options(&PlatformTarget::from_current()), + BTreeMap::from([ + ( + "api_url".to_string(), + "https://gitlab.example.com/api/v4".to_string() + ), + ("artifactbundle".to_string(), "true".to_string()), + ( + "artifactbundle_asset".to_string(), + "tool.artifactbundle.zip".to_string() + ), + ("provider".to_string(), "gitlab".to_string()), + ]) + ); + } + + #[test] + fn test_lockfile_options_use_target_platform_artifact_inputs() { + let mut opts = ToolVersionOptions::default(); + let mut platforms = toml::Table::new(); + let mut linux = toml::Table::new(); + let mut windows = toml::Table::new(); + linux.insert( + "artifactbundle_asset".to_string(), + toml::Value::String("linux.artifactbundle.zip".to_string()), + ); + windows.insert( + "artifactbundle_asset".to_string(), + toml::Value::String("windows.artifactbundle.zip".to_string()), + ); + platforms.insert("linux-x64".to_string(), toml::Value::Table(linux)); + platforms.insert("windows-x64".to_string(), toml::Value::Table(windows)); + opts.opts + .insert("platforms".to_string(), toml::Value::Table(platforms)); + + let linux = PlatformTarget::new(crate::platform::Platform::parse("linux-x64").unwrap()); + let windows = PlatformTarget::new(crate::platform::Platform::parse("windows-x64").unwrap()); + + assert_eq!( + SpmOptions::new(&opts).lockfile_options(&linux), + BTreeMap::from([( + "artifactbundle_asset".to_string(), + "linux.artifactbundle.zip".to_string() + )]) + ); + assert_eq!( + SpmOptions::new(&opts).lockfile_options(&windows), + BTreeMap::from([( + "artifactbundle_asset".to_string(), + "windows.artifactbundle.zip".to_string() + )]) + ); + + let mut current_host_only_opts = ToolVersionOptions::default(); + let mut platforms = toml::Table::new(); + let mut linux = toml::Table::new(); + linux.insert( + "artifactbundle_asset".to_string(), + toml::Value::String("linux.artifactbundle.zip".to_string()), + ); + platforms.insert("linux-x64".to_string(), toml::Value::Table(linux)); + current_host_only_opts + .opts + .insert("platforms".to_string(), toml::Value::Table(platforms)); + + assert!( + SpmOptions::new(¤t_host_only_opts) + .lockfile_options(&windows) + .is_empty() + ); + } + + #[test] + fn test_install_time_options_include_layout_and_artifact_inputs() { + assert_eq!( + install_time_option_keys(), + vec![ + "provider".to_string(), + "api_url".to_string(), + "artifactbundle".to_string(), + "artifactbundle_asset".to_string(), + "filter_bins".to_string(), + ] + ); + } + fn release_asset(name: &str) -> ArtifactBundleReleaseAsset { ArtifactBundleReleaseAsset { name: name.to_string(), @@ -1052,111 +1245,158 @@ mod tests { } } + fn filter_bins(raw: &ToolVersionOptions) -> Option> { + SpmOptions::new(raw).filter_bins() + } + + fn filter_executables_with_raw( + raw: &ToolVersionOptions, + executables: Vec, + ) -> eyre::Result> { + let opts = SpmOptions::new(raw); + filter_executables(&opts, executables) + } + #[test] fn test_resolve_artifactbundle_mode() { + let default_opts = ToolVersionOptions::default(); assert_eq!( - resolve_artifactbundle_mode(&ToolVersionOptions::default()).unwrap(), + SpmOptions::new(&default_opts) + .artifactbundle_mode(None) + .unwrap(), ArtifactBundleMode::Auto ); + let required_opts = opts_with("artifactbundle", toml::Value::Boolean(true)); + assert_eq!( + SpmOptions::new(&required_opts) + .artifactbundle_mode(None) + .unwrap(), + ArtifactBundleMode::Required + ); + let source_only_opts = opts_with("artifactbundle", toml::Value::Boolean(false)); + assert_eq!( + SpmOptions::new(&source_only_opts) + .artifactbundle_mode(None) + .unwrap(), + ArtifactBundleMode::SourceOnly + ); + let required_opts = opts_with("artifactbundle", toml::Value::String("TRUE".to_string())); assert_eq!( - resolve_artifactbundle_mode(&opts_with("artifactbundle", toml::Value::Boolean(true))) + SpmOptions::new(&required_opts) + .artifactbundle_mode(None) .unwrap(), ArtifactBundleMode::Required ); + let required_opts = opts_with("artifactbundle", toml::Value::String("1".to_string())); assert_eq!( - resolve_artifactbundle_mode(&opts_with("artifactbundle", toml::Value::Boolean(false))) + SpmOptions::new(&required_opts) + .artifactbundle_mode(None) + .unwrap(), + ArtifactBundleMode::Required + ); + let source_only_opts = + opts_with("artifactbundle", toml::Value::String("FALSE".to_string())); + assert_eq!( + SpmOptions::new(&source_only_opts) + .artifactbundle_mode(None) .unwrap(), ArtifactBundleMode::SourceOnly ); + let source_only_opts = opts_with("artifactbundle", toml::Value::String("0".to_string())); + assert_eq!( + SpmOptions::new(&source_only_opts) + .artifactbundle_mode(None) + .unwrap(), + ArtifactBundleMode::SourceOnly + ); + let invalid_opts = opts_with("artifactbundle", toml::Value::String("00".to_string())); assert!( - resolve_artifactbundle_mode(&opts_with( - "artifactbundle", - toml::Value::String("sometimes".to_string()) - )) - .is_err() + SpmOptions::new(&invalid_opts) + .artifactbundle_mode(None) + .is_err() + ); + let invalid_opts = opts_with( + "artifactbundle", + toml::Value::String("sometimes".to_string()), + ); + assert!( + SpmOptions::new(&invalid_opts) + .artifactbundle_mode(None) + .is_err() ); } #[test] fn test_requires_artifactbundle() { - assert!(!requires_artifactbundle( - ArtifactBundleMode::Auto, - &ToolVersionOptions::default() - )); - assert!(requires_artifactbundle( - ArtifactBundleMode::Required, - &ToolVersionOptions::default() - )); - assert!(requires_artifactbundle( - ArtifactBundleMode::Auto, - &opts_with( - "artifactbundle_asset", - toml::Value::String("tool.artifactbundle.zip".to_string()), - ) - )); + let default_opts = ToolVersionOptions::default(); + let opts = SpmOptions::new(&default_opts); + assert!(!opts.requires_artifactbundle(ArtifactBundleMode::Auto)); + assert!(opts.requires_artifactbundle(ArtifactBundleMode::Required)); + + let asset_opts = opts_with( + "artifactbundle_asset", + toml::Value::String("tool.artifactbundle.zip".to_string()), + ); + let opts = SpmOptions::new(&asset_opts); + assert!(opts.requires_artifactbundle(ArtifactBundleMode::Auto)); } #[test] fn test_select_artifactbundle_asset() { - let selected = select_artifactbundle_asset( - vec![release_asset("tool.artifactbundle.zip")], - &ToolVersionOptions::default(), - ) - .unwrap() - .unwrap(); + let default_opts = ToolVersionOptions::default(); + let opts = SpmOptions::new(&default_opts); + let selected = + select_artifactbundle_asset(vec![release_asset("tool.artifactbundle.zip")], &opts) + .unwrap() + .unwrap(); assert_eq!(selected.name, "tool.artifactbundle.zip"); + let selected_opts = opts_with( + "artifactbundle_asset", + toml::Value::String("tool.artifactbundle.zip".to_string()), + ); + let opts = SpmOptions::new(&selected_opts); let selected = select_artifactbundle_asset( vec![ release_asset("tool.artifactbundle.zip"), release_asset("tool.tar.gz"), ], - &opts_with( - "artifactbundle_asset", - toml::Value::String("tool.artifactbundle.zip".to_string()), - ), + &opts, ) .unwrap() .unwrap(); assert_eq!(selected.name, "tool.artifactbundle.zip"); - assert!( - select_artifactbundle_asset( - vec![release_asset("tool.tar.gz")], - &opts_with( - "artifactbundle_asset", - toml::Value::String("tool.tar.gz".to_string()), - ), - ) - .is_err() + let non_bundle_opts = opts_with( + "artifactbundle_asset", + toml::Value::String("tool.tar.gz".to_string()), + ); + let opts = SpmOptions::new(&non_bundle_opts); + assert!(select_artifactbundle_asset(vec![release_asset("tool.tar.gz")], &opts).is_err()); + let missing_opts = opts_with( + "artifactbundle_asset", + toml::Value::String("missing.artifactbundle.zip".to_string()), ); + let opts = SpmOptions::new(&missing_opts); assert!( - select_artifactbundle_asset( - vec![release_asset("tool.artifactbundle.zip")], - &opts_with( - "artifactbundle_asset", - toml::Value::String("missing.artifactbundle.zip".to_string()), - ), - ) - .is_err() + select_artifactbundle_asset(vec![release_asset("tool.artifactbundle.zip")], &opts,) + .is_err() ); + let opts = SpmOptions::new(&default_opts); assert!( select_artifactbundle_asset( vec![ release_asset("a.artifactbundle.zip"), release_asset("b.artifactbundle.zip"), ], - &ToolVersionOptions::default(), + &opts, ) .is_err() ); assert!( - select_artifactbundle_asset( - vec![release_asset("tool.tar.gz")], - &ToolVersionOptions::default(), - ) - .unwrap() - .is_none() + select_artifactbundle_asset(vec![release_asset("tool.tar.gz")], &opts,) + .unwrap() + .is_none() ); } @@ -1339,6 +1579,7 @@ mod tests { }, ]; let opts = opts_with_filter_bins(toml::Value::String("b".to_string())); + let opts = SpmOptions::new(&opts); let filtered = filter_artifactbundle_binaries(&opts, binaries).unwrap(); assert_eq!(filtered.len(), 1); assert_eq!(filtered[0].name, "b"); @@ -1346,24 +1587,24 @@ mod tests { #[test] fn test_parse_filter_bins() { - assert_eq!(parse_filter_bins(&ToolVersionOptions::default()), None); + assert_eq!(filter_bins(&ToolVersionOptions::default()), None); assert_eq!( - parse_filter_bins(&opts_with_filter_bins(toml::Value::String( + filter_bins(&opts_with_filter_bins(toml::Value::String( "swiftly".to_string() ))), Some(vec!["swiftly".to_string()]) ); assert_eq!( - parse_filter_bins(&opts_with_filter_bins(toml::Value::String( + filter_bins(&opts_with_filter_bins(toml::Value::String( " foo , bar , ".to_string() ))), Some(vec!["foo".to_string(), "bar".to_string()]) ); assert_eq!( - parse_filter_bins(&opts_with_filter_bins(toml::Value::Array(vec![ + filter_bins(&opts_with_filter_bins(toml::Value::Array(vec![ toml::Value::String("foo".to_string()), toml::Value::String(" bar".to_string()), toml::Value::String("".to_string()), @@ -1372,7 +1613,7 @@ mod tests { ); assert_eq!( - parse_filter_bins(&opts_with_filter_bins(toml::Value::String( + filter_bins(&opts_with_filter_bins(toml::Value::String( " , ".to_string() ))), None, @@ -1384,7 +1625,8 @@ mod tests { fn test_filter_executables_passthrough_when_unset() { let executables = vec!["a".to_string(), "b".to_string()]; let result = - filter_executables(&ToolVersionOptions::default(), executables.clone()).unwrap(); + filter_executables_with_raw(&ToolVersionOptions::default(), executables.clone()) + .unwrap(); assert_eq!(result, executables); } @@ -1399,7 +1641,7 @@ mod tests { toml::Value::String("helper".to_string()), toml::Value::String("swiftly".to_string()), ])); - let result = filter_executables(&opts, executables).unwrap(); + let result = filter_executables_with_raw(&opts, executables).unwrap(); assert_eq!(result, vec!["swiftly".to_string(), "helper".to_string()]); } @@ -1407,7 +1649,7 @@ mod tests { fn test_filter_executables_errors_on_missing_name() { let executables = vec!["swiftly".to_string(), "test-swiftly".to_string()]; let opts = opts_with_filter_bins(toml::Value::String("does-not-exist".to_string())); - let err = filter_executables(&opts, executables).unwrap_err(); + let err = filter_executables_with_raw(&opts, executables).unwrap_err(); let msg = err.to_string(); assert!(msg.contains("does-not-exist"), "got: {msg}"); assert!(msg.contains("swiftly"), "got: {msg}");