Launch correct fzf binary on Windows

This commit is contained in:
Ajeet D'Souza 2021-12-24 14:59:42 +05:30
parent 339a789261
commit fd088b43db
7 changed files with 72 additions and 21 deletions

View File

@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Parse error on Cygwin/MSYS due to CRLF line endings. - Parse error on Cygwin/MSYS due to CRLF line endings.
- Fzf: handle spaces correctly in preview window. - Fzf: handle spaces correctly in preview window.
- Bash: avoid initializing completions on older versions. - Bash: avoid initializing completions on older versions.
- Fzf: avoid launching binary from current directory on Windows.
## [0.7.9] - 2021-11-02 ## [0.7.9] - 2021-11-02

21
Cargo.lock generated
View File

@ -679,6 +679,26 @@ version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.3" version = "1.1.3"
@ -792,4 +812,5 @@ dependencies = [
"rstest", "rstest",
"serde", "serde",
"tempfile", "tempfile",
"thiserror",
] ]

View File

@ -27,6 +27,7 @@ glob = "0.3.0"
ordered-float = "2.0.0" ordered-float = "2.0.0"
serde = { version = "1.0.116", features = ["derive"] } serde = { version = "1.0.116", features = ["derive"] }
tempfile = "3.1.0" tempfile = "3.1.0"
thiserror = "1.0.30"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
rand = { version = "0.8.4", features = [ rand = { version = "0.8.4", features = [

View File

@ -120,6 +120,12 @@ impl<'file> Database<'file> {
} }
} }
#[cfg(unix)]
fn persist<P: AsRef<Path>>(file: NamedTempFile, path: P) -> Result<(), PersistError> {
file.persist(path)?;
Ok(())
}
#[cfg(windows)] #[cfg(windows)]
fn persist<P: AsRef<Path>>(mut file: NamedTempFile, path: P) -> Result<(), PersistError> { fn persist<P: AsRef<Path>>(mut file: NamedTempFile, path: P) -> Result<(), PersistError> {
use std::thread; use std::thread;
@ -152,12 +158,6 @@ fn persist<P: AsRef<Path>>(mut file: NamedTempFile, path: P) -> Result<(), Persi
Ok(()) Ok(())
} }
#[cfg(unix)]
fn persist<P: AsRef<Path>>(file: NamedTempFile, path: P) -> Result<(), PersistError> {
file.persist(path)?;
Ok(())
}
pub struct DatabaseFile { pub struct DatabaseFile {
buffer: Vec<u8>, buffer: Vec<u8>,
data_dir: PathBuf, data_dir: PathBuf,

View File

@ -1,20 +1,19 @@
use std::fmt::{self, Display, Formatter};
use std::io; use std::io;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use thiserror::Error;
#[derive(Debug, Error)]
#[error("could not find fzf, is it installed?")]
pub struct FzfNotFound;
/// Custom error type for early exit. /// Custom error type for early exit.
#[derive(Debug)] #[derive(Debug, Error)]
#[error("")]
pub struct SilentExit { pub struct SilentExit {
pub code: i32, pub code: i32,
} }
impl Display for SilentExit {
fn fmt(&self, _: &mut Formatter) -> fmt::Result {
Ok(())
}
}
pub trait BrokenPipeHandler { pub trait BrokenPipeHandler {
fn pipe_exit(self, device: &str) -> Result<()>; fn pipe_exit(self, device: &str) -> Result<()>;
} }

View File

@ -1,11 +1,11 @@
use std::io::{self, Read}; use std::io::{self, Read};
use std::mem; use std::mem;
use std::process::{Child, ChildStdin, Command, Stdio}; use std::process::{Child, ChildStdin, Stdio};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use crate::config; use crate::error::{FzfNotFound, SilentExit};
use crate::error::SilentExit; use crate::{config, util};
pub struct Fzf { pub struct Fzf {
child: Child, child: Child,
@ -13,7 +13,8 @@ pub struct Fzf {
impl Fzf { impl Fzf {
pub fn new(multiple: bool) -> Result<Self> { pub fn new(multiple: bool) -> Result<Self> {
let mut command = Command::new("fzf"); let bin = if cfg!(windows) { "fzf.exe" } else { "fzf" };
let mut command = util::get_command(bin).map_err(|_| FzfNotFound)?;
if multiple { if multiple {
command.arg("-m"); command.arg("-m");
} }
@ -37,9 +38,7 @@ impl Fzf {
let child = match command.spawn() { let child = match command.spawn() {
Ok(child) => child, Ok(child) => child,
Err(e) if e.kind() == io::ErrorKind::NotFound => { Err(e) if e.kind() == io::ErrorKind::NotFound => bail!(FzfNotFound),
bail!("could not find fzf, is it installed?")
}
Err(e) => Err(e).context("could not launch fzf")?, Err(e) => Err(e).context("could not launch fzf")?,
}; };

View File

@ -1,5 +1,6 @@
use std::env; use std::env;
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
use std::process::Command;
use std::time::SystemTime; use std::time::SystemTime;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
@ -21,6 +22,35 @@ pub fn current_time() -> Result<Epoch> {
Ok(current_time) Ok(current_time)
} }
/// Constructs a new [`Command`] for launching the program with the name
/// `program`.
///
/// On Windows, CreateProcess implicitly searches the current working directory
/// for the executable, which is a potential security issue. Instead, we resolve
/// the path to the executable and then pass it to CreateProcess.
///
/// On other platforms, this is a no-op.
///
pub fn get_command<P: AsRef<Path>>(program: P) -> Result<Command> {
let program = program.as_ref();
if !cfg!(windows) {
return Ok(Command::new(program));
}
let paths = env::var_os("PATH").context("PATH environment variable not set")?;
for path in env::split_paths(&paths) {
if path.as_os_str().is_empty() {
continue;
}
let path = path.join(program);
if path.metadata().map_or(false, |m| !m.is_dir()) {
return Ok(Command::new(path));
}
}
bail!("executable not found in PATH: {}", program.display());
}
pub fn path_to_str<P: AsRef<Path>>(path: &P) -> Result<&str> { pub fn path_to_str<P: AsRef<Path>>(path: &P) -> Result<&str> {
let path = path.as_ref(); let path = path.as_ref();
path.to_str().with_context(|| format!("invalid unicode in path: {}", path.display())) path.to_str().with_context(|| format!("invalid unicode in path: {}", path.display()))