Files
sausage/userland/jaq/src/read.rs
T
surma b9e3e3677d userland: search and exec tools, spawn library (T14)
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.
2026-11-06 17:21:51 +00:00

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)
}
}