From 9ac15e5a3140dda2f3e7ee450c8ada56f35eb503 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Wed, 26 Aug 2026 12:03:14 +0200 Subject: [PATCH] Add CLI completions for Nushell --- Cargo.lock | 135 ++++++++---------- build.sh | 2 +- .../mullvad-vpn/tasks/distribution.cjs | 5 + dist-assets/pkg-scripts/postinstall | 4 + dist-assets/uninstall_macos.sh | 3 + mullvad-cli/Cargo.toml | 1 + mullvad-cli/src/main.rs | 56 +++++++- mullvad-daemon/Cargo.toml | 2 + test/test-runner/src/app.rs | 2 + 9 files changed, 126 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05d08d3738..075188a73c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,21 +67,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse 0.2.7", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstream" version = "1.0.0" @@ -89,7 +74,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", - "anstyle-parse 1.0.0", + "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -103,15 +88,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - [[package]] name = "anstyle-parse" version = "1.0.0" @@ -127,7 +103,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -138,7 +114,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -777,9 +753,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.60" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -787,11 +763,11 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ - "anstream 0.6.21", + "anstream", "anstyle", "clap_lex", "strsim", @@ -807,15 +783,25 @@ dependencies = [ ] [[package]] -name = "clap_derive" -version = "4.5.55" +name = "clap_complete_nushell" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "ffb66bc82eb9c92b1727310ae2c5868df22ae7cf46185bc5c544a4fa71955e49" +dependencies = [ + "clap", + "clap_complete", +] + +[[package]] +name = "clap_derive" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 3.0.3", ] [[package]] @@ -836,7 +822,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1321,7 +1307,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1533,7 +1519,7 @@ version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ - "anstream 1.0.0", + "anstream", "anstyle", "env_filter", "log", @@ -1569,7 +1555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1916,14 +1902,14 @@ dependencies = [ "rand_chacha", "rand_core 0.6.4", "ring", - "socket2 0.6.5", + "socket2", "thiserror 1.0.69", "tokio", "tokio-util 0.7.18", "tracing", "tun 0.8.14", "typed-builder 0.21.2", - "windows-sys 0.60.2", + "windows-sys 0.61.2", "x25519-dalek", "zerocopy", ] @@ -2321,7 +2307,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2", "tokio", "tower-service", "tracing", @@ -2618,7 +2604,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.6.5", + "socket2", "widestring", "windows-registry", "windows-result", @@ -3221,6 +3207,7 @@ dependencies = [ "chrono", "clap", "clap_complete", + "clap_complete_nushell", "futures", "itertools 0.14.0", "mullvad-management-interface", @@ -3409,7 +3396,7 @@ dependencies = [ "log", "nix 0.31.3", "pnet_packet 0.35.0", - "socket2 0.6.5", + "socket2", "talpid-windows", "tokio", "windows-sys 0.61.2", @@ -3464,7 +3451,7 @@ dependencies = [ "rand 0.9.4", "rustls", "rustls-pki-types", - "socket2 0.6.5", + "socket2", "talpid-tunnel", "thiserror 2.0.19", "tokio", @@ -3891,7 +3878,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4744,9 +4731,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -4851,7 +4838,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2", "thiserror 2.0.19", "tokio", "tracing", @@ -4888,16 +4875,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -5196,7 +5183,7 @@ dependencies = [ "errno 0.3.14", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5332,7 +5319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5512,7 +5499,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "shadowsocks-crypto", - "socket2 0.6.5", + "socket2", "spin", "thiserror 2.0.19", "tokio", @@ -5563,7 +5550,7 @@ version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ - "errno 0.2.8", + "errno 0.3.14", "libc", ] @@ -5642,16 +5629,6 @@ dependencies = [ "managed", ] -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.5" @@ -5659,7 +5636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5736,7 +5713,7 @@ dependencies = [ "parking_lot", "pnet_packet 0.34.0", "rand 0.9.4", - "socket2 0.6.5", + "socket2", "thiserror 1.0.69", "tokio", "tracing", @@ -5868,7 +5845,7 @@ dependencies = [ "rand 0.9.4", "serde", "serde_json", - "socket2 0.6.5", + "socket2", "talpid-cgroup", "talpid-dbus", "talpid-dns", @@ -5960,7 +5937,7 @@ dependencies = [ "libc", "log", "nix 0.31.3", - "socket2 0.6.5", + "socket2", "talpid-types", "thiserror 2.0.19", ] @@ -6075,7 +6052,7 @@ name = "talpid-windows" version = "0.0.0" dependencies = [ "futures", - "socket2 0.6.5", + "socket2", "talpid-types", "thiserror 2.0.19", "tokio", @@ -6106,7 +6083,7 @@ dependencies = [ "proptest", "rand 0.9.4", "rtnetlink", - "socket2 0.6.5", + "socket2", "surge-ping", "talpid-dbus", "talpid-net", @@ -6135,7 +6112,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6340,7 +6317,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.5", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -6402,7 +6379,7 @@ dependencies = [ "log", "once_cell", "pin-project", - "socket2 0.6.5", + "socket2", "tokio", "windows-sys 0.60.2", ] @@ -6546,7 +6523,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.5", + "socket2", "sync_wrapper", "tokio", "tokio-stream", @@ -6879,7 +6856,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset 0.9.1", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7336,7 +7313,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/build.sh b/build.sh index d4bbf20226..5cac4b441c 100755 --- a/build.sh +++ b/build.sh @@ -372,7 +372,7 @@ log_header "Preparing for packaging Mullvad VPN $PRODUCT_VERSION" if [[ "$(uname -s)" == "Darwin" || "$(uname -s)" == "Linux" ]]; then mkdir -p "build/shell-completions" - for sh in bash zsh fish; do + for sh in bash zsh fish nushell; do log_info "Generating shell completion script for $sh..." cargo run --bin mullvad "${CARGO_ARGS[@]}" -- shell-completions "$sh" \ "build/shell-completions/" diff --git a/desktop/packages/mullvad-vpn/tasks/distribution.cjs b/desktop/packages/mullvad-vpn/tasks/distribution.cjs index 0deff89c8e..1cc811191e 100644 --- a/desktop/packages/mullvad-vpn/tasks/distribution.cjs +++ b/desktop/packages/mullvad-vpn/tasks/distribution.cjs @@ -104,6 +104,7 @@ function newConfig() { { from: distAssets('uninstall_macos.sh'), to: './uninstall.sh' }, { from: buildAssets('shell-completions/_mullvad'), to: '.' }, { from: buildAssets('shell-completions/mullvad.fish'), to: '.' }, + { from: buildAssets('shell-completions/mullvad.nu'), to: '.' }, ], }, @@ -214,6 +215,8 @@ function newConfig() { buildAssets('shell-completions/_mullvad') + '=/usr/local/share/zsh/site-functions/_mullvad', buildAssets('shell-completions/mullvad.fish') + '=/usr/share/fish/vendor_completions.d/mullvad.fish', + buildAssets('shell-completions/mullvad.nu') + + '=/usr/share/nushell/vendor/autoload/mullvad.nu', ], afterInstall: distAssets('linux/after-install.sh'), afterRemove: distAssets('linux/after-remove.sh'), @@ -257,6 +260,8 @@ function newConfig() { buildAssets('shell-completions/_mullvad') + '=/usr/share/zsh/site-functions/_mullvad', buildAssets('shell-completions/mullvad.fish') + '=/usr/share/fish/vendor_completions.d/mullvad.fish', + buildAssets('shell-completions/mullvad.nu') + + '=/usr/share/nushell/vendor/autoload/mullvad.nu', ], afterInstall: distAssets('linux/after-install.sh'), afterRemove: distAssets('linux/after-remove.sh'), diff --git a/dist-assets/pkg-scripts/postinstall b/dist-assets/pkg-scripts/postinstall index 1671646d54..05cf50a0a4 100755 --- a/dist-assets/pkg-scripts/postinstall +++ b/dist-assets/pkg-scripts/postinstall @@ -79,6 +79,7 @@ EOM ) ZSH_COMPLETIONS_DIR="/usr/local/share/zsh/site-functions/" +NUSHELL_COMPLETIONS_DIR="/usr/local/share/nushell/vendor/autoload/" if [[ -d "/opt/homebrew/share/fish/vendor_completions.d/" ]]; then FISH_COMPLETIONS_DIR="/opt/homebrew/share/fish/vendor_completions.d/" @@ -106,6 +107,9 @@ ln -sf "$INSTALL_DIR/Mullvad VPN.app/Contents/Resources/mullvad-problem-report" mkdir -p "$ZSH_COMPLETIONS_DIR" ln -sf "$INSTALL_DIR/Mullvad VPN.app/Contents/Resources/_mullvad" "$ZSH_COMPLETIONS_DIR/_mullvad" +mkdir -p "$NUSHELL_COMPLETIONS_DIR" +ln -sf "$INSTALL_DIR/Mullvad VPN.app/Contents/Resources/mullvad.nu" "$NUSHELL_COMPLETIONS_DIR/mullvad.nu" + if [[ -d "$FISH_COMPLETIONS_DIR" ]]; then ln -sf "$INSTALL_DIR/Mullvad VPN.app/Contents/Resources/mullvad.fish" "$FISH_COMPLETIONS_DIR/mullvad.fish" fi diff --git a/dist-assets/uninstall_macos.sh b/dist-assets/uninstall_macos.sh index 9c7687614d..66ba578fc8 100755 --- a/dist-assets/uninstall_macos.sh +++ b/dist-assets/uninstall_macos.sh @@ -48,6 +48,9 @@ echo "Removing fish shell completion symlink ..." sudo rm -f "/opt/homebrew/share/fish/vendor_completions.d/mullvad.fish" sudo rm -f "/usr/local/share/fish/vendor_completions.d/mullvad.fish" +echo "Removing nushell completion symlink ..." +sudo rm -f "/usr/local/share/nushell/vendor/autoload/mullvad.nu" + echo "Removing CLI symlinks from /usr/local/bin/ ..." sudo rm -f /usr/local/bin/mullvad /usr/local/bin/mullvad-problem-report diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml index ea4c374002..1172767fb8 100644 --- a/mullvad-cli/Cargo.toml +++ b/mullvad-cli/Cargo.toml @@ -35,6 +35,7 @@ tokio = { workspace = true, features = ["fs", "macros", "rt-multi-thread"] } [target.'cfg(all(unix, not(target_os = "android")))'.dependencies] clap_complete = { version = "4.4.8" } +clap_complete_nushell = "4.6.2" nix = { workspace = true, features = ["signal"] } [target."cfg(windows)".build-dependencies] diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs index b28316ea77..e1a4de487a 100644 --- a/mullvad-cli/src/main.rs +++ b/mullvad-cli/src/main.rs @@ -1,5 +1,7 @@ use anyhow::Result; use clap::Parser; +#[cfg(all(unix, not(target_os = "android")))] +use clap::ValueEnum; mod cmds; mod format; @@ -9,6 +11,53 @@ use crate::cmds::reset::SettingsKey; pub const BIN_NAME: &str = env!("CARGO_BIN_NAME"); +/// Shell with auto-generated completion script available. +/// +/// NOTE: this enum as a copy of [`clap_complete::Shell`], +/// with Nushell added. +#[cfg(all(unix, not(target_os = "android")))] +#[derive(Clone, ValueEnum, Debug)] +enum CompletionShell { + /// Bourne Again `SHell` (bash) + Bash, + /// Elvish shell + Elvish, + /// Friendly Interactive `SHell` (fish) + Fish, + /// `PowerShell` + #[value(name = "powershell")] + PowerShell, + /// Z `SHell` (zsh) + Zsh, + /// Nushell + Nushell, +} + +#[cfg(all(unix, not(target_os = "android")))] +impl CompletionShell { + fn generate_to(self, dir: &std::path::Path) -> std::io::Result { + use clap::CommandFactory; + use clap_complete::Shell; + + let mut command = Cli::command(); + match self { + Self::Bash => clap_complete::generate_to(Shell::Bash, &mut command, BIN_NAME, dir), + Self::Elvish => clap_complete::generate_to(Shell::Elvish, &mut command, BIN_NAME, dir), + Self::Fish => clap_complete::generate_to(Shell::Fish, &mut command, BIN_NAME, dir), + Self::Nushell => clap_complete::generate_to( + clap_complete_nushell::Nushell, + &mut command, + BIN_NAME, + dir, + ), + Self::PowerShell => { + clap_complete::generate_to(Shell::PowerShell, &mut command, BIN_NAME, dir) + } + Self::Zsh => clap_complete::generate_to(Shell::Zsh, &mut command, BIN_NAME, dir), + } + } +} + #[derive(Debug, Parser)] #[command(version = mullvad_version::VERSION, about, long_about = None)] #[command(propagate_version = true)] @@ -116,7 +165,7 @@ enum Cli { #[command(hide = true)] ShellCompletions { /// The shell to generate the script for - shell: clap_complete::Shell, + shell: CompletionShell, /// Output directory where the shell completions are written #[arg(default_value = "./")] @@ -200,11 +249,10 @@ async fn main() -> Result<()> { #[cfg(all(unix, not(target_os = "android")))] Cli::ShellCompletions { shell, dir } => { use anyhow::Context; - use clap::CommandFactory; - // FIXME: The shell completions include hidden commands (including "shell-completions") println!("Generating shell completions to {}", dir.display()); - clap_complete::generate_to(shell, &mut Cli::command(), BIN_NAME, dir) + shell + .generate_to(&dir) .context("Failed to generate shell completions")?; Ok(()) } diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index 6dba3621ba..f698a7912d 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -37,6 +37,7 @@ assets = [ { source = "../build/shell-completions/mullvad.bash", dest = "usr/share/bash-completion/completions/mullvad", mode = "644" }, { source = "../build/shell-completions/_mullvad", dest = "usr/local/share/zsh/site-functions/_mullvad", mode = "644" }, { source = "../build/shell-completions/mullvad.fish", dest = "usr/share/fish/vendor_completions.d/mullvad.fish", mode = "644" }, + { source = "../build/shell-completions/mullvad.nu", dest = "usr/share/nushell/vendor/autoload/mullvad.nu", mode = "644" }, { source = "../CHANGELOG.md", dest = "opt/Mullvad VPN/resources/", mode = "644" }, ] @@ -62,6 +63,7 @@ assets = [ { source = "../build/shell-completions/mullvad.bash", dest = "/usr/share/bash-completion/completions/mullvad", mode = "644" }, { source = "../build/shell-completions/_mullvad", dest = "/usr/share/zsh/site-functions/_mullvad", mode = "644" }, { source = "../build/shell-completions/mullvad.fish", dest = "/usr/share/fish/vendor_completions.d/mullvad.fish", mode = "644" }, + { source = "../build/shell-completions/mullvad.nu", dest = "/usr/share/nushell/vendor/autoload/mullvad.nu", mode = "644" }, { source = "../CHANGELOG.md", dest = "/opt/Mullvad VPN/resources/", mode = "644" }, ] diff --git a/test/test-runner/src/app.rs b/test/test-runner/src/app.rs index 009638c315..19590088f1 100644 --- a/test/test-runner/src/app.rs +++ b/test/test-runner/src/app.rs @@ -77,6 +77,7 @@ pub fn find_traces() -> Result, Error> { Path::new(r"/usr/share/bash-completion/completions/mullvad"), Path::new(r"/usr/local/share/zsh/site-functions/_mullvad"), Path::new(r"/usr/share/fish/vendor_completions.d/mullvad.fish"), + Path::new(r"/usr/share/nushell/vendor/autoload/mullvad.nu"), ]; Ok(existing_paths(&traces)) @@ -106,6 +107,7 @@ pub fn find_traces() -> Result, Error> { Path::new(r"/usr/local/share/zsh/site-functions/_mullvad"), Path::new(r"/opt/homebrew/share/fish/vendor_completions.d/mullvad.fish"), Path::new(r"/usr/local/share/fish/vendor_completions.d/mullvad.fish"), + Path::new(r"/usr/local/share/nushell/vendor/autoload/mullvad.nu"), ]; Ok(existing_paths(&traces))