Remove gpui's dependency on async-process (#59358)

This removes gpui's dependency on the `util` crate, which depends on
async process. It now depends on `gpui_util` only, and uses
`std::process::Command` for the two cases where it was previously using
an async `Command`. This lifts the requirement of non-Zed consumers of
gpui to depend on our forked async-process (see #59156).

Release Notes:

- N/A
This commit is contained in:
Cole Miller 2026-06-16 09:47:55 -04:00 committed by GitHub
parent 33a54ce423
commit a873cf402c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 213 additions and 189 deletions

10
Cargo.lock generated
View file

@ -7828,6 +7828,7 @@ dependencies = [
"filedescriptor",
"futures 0.3.32",
"gpui",
"gpui_util",
"gpui_wgpu",
"http_client",
"image",
@ -7846,7 +7847,6 @@ dependencies = [
"strum 0.27.2",
"swash",
"url",
"util",
"uuid",
"wayland-backend",
"wayland-client",
@ -7885,6 +7885,7 @@ dependencies = [
"foreign-types 0.5.0",
"futures 0.3.32",
"gpui",
"gpui_util",
"image",
"itertools 0.14.0",
"libc",
@ -7902,7 +7903,6 @@ dependencies = [
"semver",
"smallvec",
"strum 0.27.2",
"util",
"uuid",
"zed-font-kit",
]
@ -7945,8 +7945,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
"gpui_util",
"tokio",
"util",
]
[[package]]
@ -7955,6 +7955,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"log",
"which 6.0.3",
]
[[package]]
@ -8017,9 +8018,11 @@ dependencies = [
"accesskit_windows",
"anyhow",
"collections",
"dunce",
"etagere",
"futures 0.3.32",
"gpui",
"gpui_util",
"image",
"itertools 0.14.0",
"log",
@ -8027,7 +8030,6 @@ dependencies = [
"rand 0.9.4",
"raw-window-handle",
"smallvec",
"util",
"uuid",
"windows 0.61.3",
"windows-core 0.61.2",

View file

@ -59,6 +59,7 @@ collections.workspace = true
image.workspace = true
futures.workspace = true
gpui.workspace = true
gpui_util.workspace = true
gpui_wgpu = { workspace = true, optional = true, features = ["font-kit"] }
http_client.workspace = true
itertools.workspace = true
@ -72,7 +73,6 @@ smallvec.workspace = true
smol.workspace = true
strum.workspace = true
url.workspace = true
util.workspace = true
uuid.workspace = true
# Always used

View file

@ -3,7 +3,7 @@ use calloop::{
channel::{self, Sender},
timer::TimeoutAction,
};
use util::ResultExt;
use gpui_util::ResultExt;
use std::{mem::MaybeUninit, thread, time::Duration};

View file

@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::rc::Rc;
use calloop::{EventLoop, LoopHandle};
use util::ResultExt;
use gpui_util::ResultExt;
use crate::linux::{LinuxClient, LinuxCommon, LinuxKeyboardLayout};
use gpui::{

View file

@ -16,8 +16,7 @@ use std::{
use anyhow::{Context as _, anyhow};
use calloop::LoopSignal;
use futures::channel::oneshot;
use util::ResultExt as _;
use util::command::{new_command, new_std_command};
use gpui_util::{ResultExt as _, new_std_command};
#[cfg(any(feature = "wayland", feature = "x11"))]
use xkbcommon::xkb::{self, Keycode, Keysym, State};
@ -461,15 +460,15 @@ impl<P: LinuxClient + 'static> Platform for LinuxPlatform<P> {
let path = path.to_owned();
self.background_executor()
.spawn(async move {
let _ = new_command("xdg-open")
#[allow(
clippy::disallowed_methods,
reason = "running on a background thread, so blocking is fine"
)]
new_std_command("xdg-open")
.arg(path)
.spawn()
.context("invoking xdg-open")
.log_err()?
.status()
.await
.log_err()?;
Some(())
.context("invoking xdg-open")
.log_err();
})
.detach();
}

View file

@ -15,9 +15,9 @@ use calloop::{
use calloop_wayland_source::WaylandSource;
use collections::HashMap;
use filedescriptor::Pipe;
use gpui_util::ResultExt as _;
use http_client::Url;
use smallvec::SmallVec;
use util::ResultExt as _;
use wayland_backend::client::ObjectId;
use wayland_backend::protocol::WEnum;
use wayland_client::event_created_child;

View file

@ -1,7 +1,7 @@
use crate::linux::Globals;
use crate::linux::{DEFAULT_CURSOR_ICON_NAME, log_cursor_icon_warning};
use anyhow::{Context as _, anyhow};
use util::ResultExt;
use gpui_util::ResultExt;
use wayland_client::Connection;
use wayland_client::protocol::wl_surface::WlSurface;

View file

@ -7,6 +7,7 @@ use calloop::{
use collections::HashMap;
use core::str;
use gpui::{Capslock, profiler};
use gpui_util::ResultExt as _;
use http_client::Url;
use log::Level;
use smallvec::SmallVec;
@ -18,7 +19,6 @@ use std::{
rc::{Rc, Weak},
time::{Duration, Instant},
};
use util::ResultExt as _;
use x11rb::{
connection::{Connection, RequestConnection},

View file

@ -844,7 +844,7 @@ fn serve_requests(context: Arc<Inner>) -> Result<(), Box<dyn std::error::Error>>
log::trace!("Started serve requests thread.");
let _guard = util::defer(|| {
let _guard = gpui_util::defer(|| {
context.serve_stopped.store(true, Ordering::Relaxed);
});

View file

@ -12,8 +12,8 @@ use gpui::{
use gpui_wgpu::{CompositorGpuHint, WgpuRenderer, WgpuSurfaceConfig};
use collections::FxHashSet;
use gpui_util::{ResultExt, maybe};
use raw_window_handle as rwh;
use util::{ResultExt, maybe};
use x11rb::{
connection::Connection,
cookie::{Cookie, VoidCookie},

View file

@ -42,6 +42,7 @@ etagere = "0.2"
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "94b0f28166665e8fd2f53ff6d268a14955c82269", package = "zed-font-kit", version = "0.14.1-zed", optional = true }
foreign-types = "0.5"
futures.workspace = true
gpui_util.workspace = true
image.workspace = true
itertools.workspace = true
libc.workspace = true
@ -59,7 +60,6 @@ raw-window-handle = "0.6"
semver.workspace = true
smallvec.workspace = true
strum.workspace = true
util.workspace = true
uuid.workspace = true
[target.'cfg(target_os = "macos")'.build-dependencies]

View file

@ -1,5 +1,6 @@
use dispatch2::{DispatchQueue, DispatchQueueGlobalPriority, DispatchTime, GlobalQueueIdentifier};
use gpui::{PlatformDispatcher, Priority, RunnableMeta, RunnableVariant};
use gpui_util::ResultExt;
use mach2::{
kern_return::KERN_SUCCESS,
mach_time::mach_timebase_info_data_t,
@ -10,7 +11,6 @@ use mach2::{
thread_precedence_policy_data_t, thread_time_constraint_policy_data_t,
},
};
use util::ResultExt;
use async_task::Runnable;
use objc::{

View file

@ -3,8 +3,8 @@ use core_graphics::display::CGDirectDisplayID;
use dispatch2::{
_dispatch_source_type_data_add, DispatchObject, DispatchQueue, DispatchRetained, DispatchSource,
};
use gpui_util::ResultExt;
use std::ffi::c_void;
use util::ResultExt;
pub struct DisplayLink {
display_link: Option<sys::DisplayLink>,

View file

@ -33,6 +33,7 @@ use gpui::{
PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem,
PlatformWindow, Result, SystemMenuType, Task, ThermalState, WindowAppearance, WindowParams,
};
use gpui_util::{ResultExt, new_std_command};
use itertools::Itertools;
use objc::{
class,
@ -57,10 +58,6 @@ use std::{
atomic::{AtomicBool, Ordering},
},
};
use util::{
ResultExt,
command::{new_command, new_std_command},
};
#[allow(non_upper_case_globals)]
const NSUTF8StringEncoding: NSUInteger = 4;
@ -880,15 +877,16 @@ impl Platform for MacPlatform {
.lock()
.background_executor
.spawn(async move {
if let Some(mut child) = new_command("open")
#[allow(
clippy::disallowed_methods,
reason = "running on a background thread, so blocking is fine"
)]
new_std_command("open")
.arg("--")
.arg(path)
.spawn()
.status()
.context("invoking open command")
.log_err()
{
child.status().await.log_err();
}
.log_err();
})
.detach();
}

View file

@ -41,6 +41,7 @@ use core_foundation_sys::number::{CFBooleanGetValue, CFBooleanRef};
use core_graphics::display::{CGDirectDisplayID, CGRect};
use ctor::ctor;
use futures::channel::oneshot;
use gpui_util::ResultExt;
use objc::{
class,
declare::ClassDecl,
@ -71,7 +72,6 @@ use std::{
},
time::Duration,
};
use util::ResultExt;
const WINDOW_STATE_IVAR: &str = "windowState";

View file

@ -14,6 +14,6 @@ doctest = false
[dependencies]
anyhow.workspace = true
util.workspace = true
gpui.workspace = true
gpui_util.workspace = true
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }

View file

@ -1,7 +1,7 @@
use std::future::Future;
use gpui::{App, AppContext, Global, ReadGlobal, Task};
use util::defer;
use gpui_util::defer;
pub use tokio::task::JoinError;

View file

@ -8,5 +8,8 @@ edition.workspace = true
log.workspace = true
anyhow.workspace = true
[target.'cfg(target_os = "windows")'.dependencies]
which.workspace = true
[lints]
workspace = true

View file

@ -3,6 +3,7 @@
use std::{
env,
ffi::OsStr,
ops::AddAssign,
panic::Location,
pin::Pin,
@ -13,6 +14,137 @@ use std::{
pub mod arc_cow;
#[cfg(target_os = "windows")]
const CREATE_NO_WINDOW: u32 = 0x0800_0000_u32;
#[cfg(target_os = "windows")]
pub fn new_std_command(program: impl AsRef<OsStr>) -> std::process::Command {
use std::os::windows::process::CommandExt;
let mut command = std::process::Command::new(program);
command.creation_flags(CREATE_NO_WINDOW);
command
}
#[cfg(not(target_os = "windows"))]
pub fn new_std_command(program: impl AsRef<OsStr>) -> std::process::Command {
std::process::Command::new(program)
}
#[cfg(target_os = "windows")]
pub fn get_windows_system_shell() -> String {
use std::path::PathBuf;
fn find_pwsh_in_programfiles(find_alternate: bool, find_preview: bool) -> Option<PathBuf> {
#[cfg(target_pointer_width = "64")]
let env_var = if find_alternate {
"ProgramFiles(x86)"
} else {
"ProgramFiles"
};
#[cfg(target_pointer_width = "32")]
let env_var = if find_alternate {
"ProgramW6432"
} else {
"ProgramFiles"
};
let install_base_dir = PathBuf::from(std::env::var_os(env_var)?).join("PowerShell");
install_base_dir
.read_dir()
.ok()?
.filter_map(Result::ok)
.filter(|entry| matches!(entry.file_type(), Ok(ft) if ft.is_dir()))
.filter_map(|entry| {
let dir_name = entry.file_name();
let dir_name = dir_name.to_string_lossy();
let version = if find_preview {
let dash_index = dir_name.find('-')?;
if &dir_name[dash_index + 1..] != "preview" {
return None;
};
dir_name[..dash_index].parse::<u32>().ok()?
} else {
dir_name.parse::<u32>().ok()?
};
let exe_path = entry.path().join("pwsh.exe");
if exe_path.exists() {
Some((version, exe_path))
} else {
None
}
})
.max_by_key(|(version, _)| *version)
.map(|(_, path)| path)
}
fn find_pwsh_in_msix(find_preview: bool) -> Option<PathBuf> {
let msix_app_dir =
PathBuf::from(std::env::var_os("LOCALAPPDATA")?).join("Microsoft\\WindowsApps");
if !msix_app_dir.exists() {
return None;
}
let prefix = if find_preview {
"Microsoft.PowerShellPreview_"
} else {
"Microsoft.PowerShell_"
};
msix_app_dir
.read_dir()
.ok()?
.filter_map(|entry| {
let entry = entry.ok()?;
if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) {
return None;
}
if !entry.file_name().to_string_lossy().starts_with(prefix) {
return None;
}
let exe_path = entry.path().join("pwsh.exe");
exe_path.exists().then_some(exe_path)
})
.next()
}
fn find_pwsh_in_scoop() -> Option<PathBuf> {
let pwsh_exe =
PathBuf::from(std::env::var_os("USERPROFILE")?).join("scoop\\shims\\pwsh.exe");
pwsh_exe.exists().then_some(pwsh_exe)
}
static SYSTEM_SHELL: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
let locations = [
|| find_pwsh_in_programfiles(false, false),
|| find_pwsh_in_programfiles(true, false),
|| find_pwsh_in_msix(false),
|| find_pwsh_in_programfiles(false, true),
|| find_pwsh_in_msix(true),
|| find_pwsh_in_programfiles(true, true),
|| find_pwsh_in_scoop(),
|| which::which_global("pwsh.exe").ok(),
|| which::which_global("powershell.exe").ok(),
];
locations
.into_iter()
.find_map(|f| f())
.map(|p| p.to_string_lossy().trim().to_owned())
.inspect(|shell| log::info!("Found powershell in: {}", shell))
.unwrap_or_else(|| {
log::warn!("Powershell not found, falling back to `cmd`");
"cmd.exe".to_string()
})
});
(*SYSTEM_SHELL).clone()
}
pub fn post_inc<T: From<u8> + AddAssign<T> + Copy>(value: &mut T) -> T {
let prev = *value;
*value += T::from(1);

View file

@ -24,8 +24,10 @@ accesskit.workspace = true
accesskit_windows.workspace = true
anyhow.workspace = true
collections.workspace = true
dunce.workspace = true
etagere = "0.2"
futures.workspace = true
gpui_util.workspace = true
image.workspace = true
itertools.workspace = true
log.workspace = true
@ -33,7 +35,6 @@ parking_lot.workspace = true
rand.workspace = true
raw-window-handle = "0.6"
smallvec.workspace = true
util.workspace = true
uuid.workspace = true
windows.workspace = true
windows-core.workspace = true

View file

@ -1,9 +1,9 @@
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use ::util::ResultExt;
use anyhow::Result;
use gpui::*;
use gpui_util::ResultExt;
use windows::Win32::{
Foundation::*,
Graphics::{DirectManipulation::*, Gdi::*},

View file

@ -4,9 +4,9 @@ use std::{
mem::ManuallyDrop,
};
use ::util::{ResultExt, maybe};
use anyhow::{Context, Result};
use collections::HashMap;
use gpui_util::{ResultExt, maybe};
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use windows::{
Win32::{

View file

@ -1,6 +1,6 @@
use anyhow::{Context, Result};
use gpui_util::ResultExt;
use itertools::Itertools;
use util::ResultExt;
use windows::Win32::{
Foundation::HMODULE,
Graphics::{

View file

@ -3,8 +3,8 @@ use std::{
sync::{Arc, OnceLock},
};
use ::util::ResultExt;
use anyhow::{Context, Result};
use gpui_util::ResultExt;
use windows::{
Win32::{
Foundation::HWND,

View file

@ -5,7 +5,7 @@ use std::{
};
use anyhow::Context;
use util::ResultExt;
use gpui_util::ResultExt;
use windows::{
System::Threading::{
ThreadPool, ThreadPoolTimer, TimerElapsedHandler, WorkItemHandler, WorkItemPriority,
@ -152,7 +152,7 @@ impl PlatformDispatcher for WindowsDispatcher {
unsafe {
timeBeginPeriod(1);
}
util::defer(Box::new(|| unsafe {
gpui_util::defer(Box::new(|| unsafe {
timeEndPeriod(1);
}))
}

View file

@ -1,7 +1,7 @@
use gpui_util::ResultExt;
use itertools::Itertools;
use smallvec::SmallVec;
use std::rc::Rc;
use util::ResultExt;
use uuid::Uuid;
use windows::{
Win32::{

View file

@ -1,7 +1,7 @@
use std::{rc::Rc, sync::atomic::Ordering};
use ::util::ResultExt;
use anyhow::Context as _;
use gpui_util::ResultExt;
use windows::{
Win32::{
Foundation::*,

View file

@ -9,9 +9,9 @@ use std::{
},
};
use ::util::{ResultExt, paths::SanitizedPath};
use anyhow::{Context as _, Result, anyhow};
use futures::channel::oneshot::{self, Receiver};
use gpui_util::{ResultExt, get_windows_system_shell, new_std_command};
use itertools::Itertools;
use parking_lot::RwLock;
use smallvec::SmallVec;
@ -467,11 +467,10 @@ impl Platform for WindowsPlatform {
clippy::disallowed_methods,
reason = "We are restarting ourselves, using std command thus is fine"
)]
let restart_process =
::util::command::new_std_command(::util::shell::get_windows_system_shell())
.arg("-command")
.arg(script)
.spawn();
let restart_process = new_std_command(get_windows_system_shell())
.arg("-command")
.arg(script)
.spawn();
match restart_process {
Ok(_) => unsafe { PostQuitMessage(0) },
@ -1186,8 +1185,8 @@ fn file_save_dialog(
.context("failed to canonicalize directory")
.log_err()
{
let full_path = SanitizedPath::new(&full_path);
let full_path_string = full_path.to_string();
let full_path = dunce::simplified(&full_path);
let full_path_string = full_path.display().to_string();
let path_item: IShellItem =
unsafe { SHCreateItemFromParsingName(&HSTRING::from(full_path_string), None)? };
unsafe {

View file

@ -3,7 +3,7 @@ use std::{
ffi::{c_uint, c_void},
};
use ::util::ResultExt;
use gpui_util::ResultExt;
use windows::Win32::UI::WindowsAndMessaging::{
SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_ACTION,
SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, SystemParametersInfoW,

View file

@ -1,7 +1,7 @@
use std::sync::OnceLock;
use ::util::ResultExt;
use anyhow::Context;
use gpui_util::ResultExt;
use windows::{
UI::{
Color,

View file

@ -4,7 +4,7 @@ use std::{
};
use anyhow::{Context, Result};
use util::ResultExt;
use gpui_util::ResultExt;
use windows::Win32::{
Foundation::HWND,
Graphics::Dwm::{DWM_TIMING_INFO, DwmFlush, DwmGetCompositionTimingInfo},

View file

@ -10,9 +10,9 @@ use std::{
time::{Duration, Instant},
};
use ::util::ResultExt;
use anyhow::{Context as _, Result};
use futures::channel::oneshot::{self, Receiver};
use gpui_util::ResultExt;
use raw_window_handle as rwh;
use smallvec::SmallVec;
use windows::{

View file

@ -11,6 +11,13 @@ workspace = true
[features]
test-support = []
github-download = [
"dep:async-fs",
"dep:async-tar",
"dep:sha2",
"dep:tempfile",
"dep:util",
]
[lib]
path = "src/http_client.rs"
@ -32,8 +39,8 @@ serde_urlencoded.workspace = true
url.workspace = true
[target.'cfg(not(target_family = "wasm"))'.dependencies]
util.workspace = true
async-fs.workspace = true
async-tar.workspace = true
sha2.workspace = true
tempfile.workspace = true
util = { workspace = true, optional = true }
async-fs = { workspace = true, optional = true }
async-tar = { workspace = true, optional = true }
sha2 = { workspace = true, optional = true }
tempfile = { workspace = true, optional = true }

View file

@ -1,7 +1,7 @@
mod async_body;
#[cfg(not(target_family = "wasm"))]
pub mod github;
#[cfg(not(target_family = "wasm"))]
#[cfg(all(not(target_family = "wasm"), feature = "github-download"))]
pub mod github_download;
pub use anyhow::{Result, anyhow};

View file

@ -30,7 +30,7 @@ futures.workspace = true
globset.workspace = true
gpui.workspace = true
grammars.workspace = true
http_client.workspace = true
http_client = { workspace = true, features = ["github-download"] }
itertools.workspace = true
json_schema_store.workspace = true
language.workspace = true

View file

@ -58,7 +58,7 @@ git.workspace = true
git_hosting_providers.workspace = true
globset.workspace = true
gpui.workspace = true
http_client.workspace = true
http_client = { workspace = true, features = ["github-download"] }
image.workspace = true
itertools.workspace = true
indexmap.workspace = true

View file

@ -11,24 +11,12 @@ pub use darwin::{Child, Command, Stdio};
#[cfg(target_os = "windows")]
const CREATE_NO_WINDOW: u32 = 0x0800_0000_u32;
pub use gpui_util::new_std_command;
pub fn new_command(program: impl AsRef<OsStr>) -> Command {
Command::new(program)
}
#[cfg(target_os = "windows")]
pub fn new_std_command(program: impl AsRef<OsStr>) -> std::process::Command {
use std::os::windows::process::CommandExt;
let mut command = std::process::Command::new(program);
command.creation_flags(CREATE_NO_WINDOW);
command
}
#[cfg(not(target_os = "windows"))]
pub fn new_std_command(program: impl AsRef<OsStr>) -> std::process::Command {
std::process::Command::new(program)
}
#[cfg(not(target_os = "macos"))]
pub type Child = smol::process::Child;

View file

@ -121,116 +121,11 @@ pub fn get_windows_bash() -> Option<String> {
}
pub fn get_windows_system_shell() -> String {
use std::path::PathBuf;
#[cfg(windows)]
return gpui_util::get_windows_system_shell();
fn find_pwsh_in_programfiles(find_alternate: bool, find_preview: bool) -> Option<PathBuf> {
#[cfg(target_pointer_width = "64")]
let env_var = if find_alternate {
"ProgramFiles(x86)"
} else {
"ProgramFiles"
};
#[cfg(target_pointer_width = "32")]
let env_var = if find_alternate {
"ProgramW6432"
} else {
"ProgramFiles"
};
let install_base_dir = PathBuf::from(std::env::var_os(env_var)?).join("PowerShell");
install_base_dir
.read_dir()
.ok()?
.filter_map(Result::ok)
.filter(|entry| matches!(entry.file_type(), Ok(ft) if ft.is_dir()))
.filter_map(|entry| {
let dir_name = entry.file_name();
let dir_name = dir_name.to_string_lossy();
let version = if find_preview {
let dash_index = dir_name.find('-')?;
if &dir_name[dash_index + 1..] != "preview" {
return None;
};
dir_name[..dash_index].parse::<u32>().ok()?
} else {
dir_name.parse::<u32>().ok()?
};
let exe_path = entry.path().join("pwsh.exe");
if exe_path.exists() {
Some((version, exe_path))
} else {
None
}
})
.max_by_key(|(version, _)| *version)
.map(|(_, path)| path)
}
fn find_pwsh_in_msix(find_preview: bool) -> Option<PathBuf> {
let msix_app_dir =
PathBuf::from(std::env::var_os("LOCALAPPDATA")?).join("Microsoft\\WindowsApps");
if !msix_app_dir.exists() {
return None;
}
let prefix = if find_preview {
"Microsoft.PowerShellPreview_"
} else {
"Microsoft.PowerShell_"
};
msix_app_dir
.read_dir()
.ok()?
.filter_map(|entry| {
let entry = entry.ok()?;
if !matches!(entry.file_type(), Ok(ft) if ft.is_dir()) {
return None;
}
if !entry.file_name().to_string_lossy().starts_with(prefix) {
return None;
}
let exe_path = entry.path().join("pwsh.exe");
exe_path.exists().then_some(exe_path)
})
.next()
}
fn find_pwsh_in_scoop() -> Option<PathBuf> {
let pwsh_exe =
PathBuf::from(std::env::var_os("USERPROFILE")?).join("scoop\\shims\\pwsh.exe");
pwsh_exe.exists().then_some(pwsh_exe)
}
static SYSTEM_SHELL: LazyLock<String> = LazyLock::new(|| {
let locations = [
|| find_pwsh_in_programfiles(false, false),
|| find_pwsh_in_programfiles(true, false),
|| find_pwsh_in_msix(false),
|| find_pwsh_in_programfiles(false, true),
|| find_pwsh_in_msix(true),
|| find_pwsh_in_programfiles(true, true),
|| find_pwsh_in_scoop(),
|| which::which_global("pwsh.exe").ok(),
|| which::which_global("powershell.exe").ok(),
];
locations
.into_iter()
.find_map(|f| f())
.map(|p| p.to_string_lossy().trim().to_owned())
.inspect(|shell| log::info!("Found powershell in: {}", shell))
.unwrap_or_else(|| {
log::warn!("Powershell not found, falling back to `cmd`");
"cmd.exe".to_string()
})
});
(*SYSTEM_SHELL).clone()
#[cfg(not(windows))]
return "cmd.exe".to_string();
}
impl fmt::Display for ShellKind {