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.
82 lines
2.6 KiB
Rust
82 lines
2.6 KiB
Rust
use crate::{Cli, Val};
|
|
use std::io::{self, BufRead};
|
|
use std::path::Path;
|
|
|
|
/// Try to load file by memory mapping and fall back to regular loading if it fails.
|
|
pub fn load_file(path: impl AsRef<Path>) -> io::Result<Box<dyn core::ops::Deref<Target = [u8]>>> {
|
|
let file = std::fs::File::open(path.as_ref())?;
|
|
match unsafe { memmap2::Mmap::map(&file) } {
|
|
Ok(mmap) => Ok(Box::new(mmap)),
|
|
Err(_) => Ok(Box::new(std::fs::read(path)?)),
|
|
}
|
|
}
|
|
|
|
pub fn invalid_data(e: impl std::error::Error + Send + Sync + 'static) -> std::io::Error {
|
|
io::Error::new(io::ErrorKind::InvalidData, e)
|
|
}
|
|
|
|
fn json_slice(slice: &[u8]) -> impl Iterator<Item = io::Result<Val>> + '_ {
|
|
let mut lexer = hifijson::SliceLexer::new(slice);
|
|
core::iter::from_fn(move || {
|
|
use hifijson::token::Lex;
|
|
Some(Val::parse(lexer.ws_token()?, &mut lexer).map_err(invalid_data))
|
|
})
|
|
}
|
|
|
|
fn json_read<'a>(read: impl BufRead + 'a) -> impl Iterator<Item = io::Result<Val>> + 'a {
|
|
let mut lexer = hifijson::IterLexer::new(read.bytes());
|
|
core::iter::from_fn(move || {
|
|
use hifijson::token::Lex;
|
|
let v = Val::parse(lexer.ws_token()?, &mut lexer);
|
|
Some(v.map_err(|e| core::mem::take(&mut lexer.error).unwrap_or_else(|| invalid_data(e))))
|
|
})
|
|
}
|
|
|
|
pub fn json_array(path: impl AsRef<Path>) -> io::Result<Val> {
|
|
json_slice(&load_file(path.as_ref())?).collect()
|
|
}
|
|
|
|
pub fn buffered<'a, R>(cli: &Cli, read: R) -> Box<dyn Iterator<Item = io::Result<Val>> + 'a>
|
|
where
|
|
R: BufRead + 'a,
|
|
{
|
|
if cli.raw_input {
|
|
Box::new(raw_input(cli.slurp, read).map(|r| r.map(Val::from)))
|
|
} else {
|
|
Box::new(collect_if(cli.slurp, json_read(read)))
|
|
}
|
|
}
|
|
|
|
pub fn slice<'a>(cli: &Cli, slice: &'a [u8]) -> Box<dyn Iterator<Item = io::Result<Val>> + 'a> {
|
|
if cli.raw_input {
|
|
let read = io::BufReader::new(slice);
|
|
Box::new(raw_input(cli.slurp, read).map(|r| r.map(Val::from)))
|
|
} else {
|
|
Box::new(collect_if(cli.slurp, json_slice(slice)))
|
|
}
|
|
}
|
|
|
|
fn raw_input<'a, R>(slurp: bool, mut read: R) -> impl Iterator<Item = io::Result<String>> + 'a
|
|
where
|
|
R: BufRead + 'a,
|
|
{
|
|
if slurp {
|
|
let mut buf = String::new();
|
|
let s = read.read_to_string(&mut buf).map(|_| buf);
|
|
Box::new(std::iter::once(s))
|
|
} else {
|
|
Box::new(read.lines()) as Box<dyn Iterator<Item = _>>
|
|
}
|
|
}
|
|
|
|
fn collect_if<'a, T: FromIterator<T> + 'a, E: 'a>(
|
|
slurp: bool,
|
|
iter: impl Iterator<Item = Result<T, E>> + 'a,
|
|
) -> Box<dyn Iterator<Item = Result<T, E>> + 'a> {
|
|
if slurp {
|
|
Box::new(core::iter::once(iter.collect()))
|
|
} else {
|
|
Box::new(iter)
|
|
}
|
|
}
|