rg (ripgrep 15.2), grep (ours, on ripgrep's regex crates), find and xargs (ours), sed (uutils 0.3 wrapper), diff and cmp (uutils diffutils 0.6 with -r), fd 10.5, jaq 2.3, env and timeout. userland/spawn is a small Command-like client of the process interface. search-test.nu: 42 cases match native tools.
88 lines
2.3 KiB
Rust
88 lines
2.3 KiB
Rust
use crate::filesystem::absolute_path;
|
|
use std::fmt::{self, Formatter, Write};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
pub(crate) struct PathUrl(PathBuf);
|
|
|
|
impl PathUrl {
|
|
pub(crate) fn new(path: &Path) -> Option<Self> {
|
|
Some(Self(absolute_path(path).ok()?))
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for PathUrl {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
write!(f, "file://{}", host())?;
|
|
let bytes = self.0.as_os_str().as_encoded_bytes();
|
|
for &byte in bytes.iter() {
|
|
encode(f, byte)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn encode(f: &mut Formatter, byte: u8) -> fmt::Result {
|
|
// NOTE:
|
|
// Most terminals can handle non-ascii unicode characters in a file url fine. But on some OSes (notably
|
|
// windows), the encoded bytes of the path may not be valid UTF-8. Since we don't know if a
|
|
// byte >= 128 is part of a valid UTF-8 encoding or not, we just percent encode any non-ascii
|
|
// byte.
|
|
// Percent encoding these bytes is probably safer anyway.
|
|
match byte {
|
|
b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'/' | b':' | b'-' | b'.' | b'_' | b'~' => {
|
|
f.write_char(byte.into())
|
|
}
|
|
#[cfg(windows)]
|
|
b'\\' => f.write_char('/'),
|
|
_ => {
|
|
write!(f, "%{byte:02X}")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
fn host() -> &'static str {
|
|
use std::sync::OnceLock;
|
|
|
|
static HOSTNAME: OnceLock<String> = OnceLock::new();
|
|
|
|
HOSTNAME
|
|
.get_or_init(|| {
|
|
nix::unistd::gethostname()
|
|
.ok()
|
|
.and_then(|h| h.into_string().ok())
|
|
.unwrap_or_default()
|
|
})
|
|
.as_ref()
|
|
}
|
|
|
|
#[cfg(not(unix))]
|
|
const fn host() -> &'static str {
|
|
"/"
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
// This allows us to test the encoding without having to worry about the host, or absolute path
|
|
struct Encoded(&'static str);
|
|
|
|
impl fmt::Display for Encoded {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
for byte in self.0.bytes() {
|
|
encode(f, byte)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_unicode_encoding() {
|
|
assert_eq!(
|
|
Encoded("$*\x1bßé/∫😃\x07").to_string(),
|
|
"%24%2A%1B%C3%9F%C3%A9/%E2%88%AB%F0%9F%98%83%07",
|
|
);
|
|
}
|
|
}
|