Add read filter

This commit is contained in:
Félix Saparelli 2023-04-08 20:20:20 +12:00
parent e82a7dbc14
commit 309b7583bd
No known key found for this signature in database
2 changed files with 62 additions and 3 deletions

View file

@ -802,8 +802,11 @@ pub struct Args {
///
/// - 'path | metadata' returns file metadata or null if the file does not exist.
///
/// - 'path | read' and 'path read(bytes)' return a string containing the file at path, or
/// the first n bytes of it. Obviously care should be taken not to read large files.
/// - 'path | filesize' returns the size of the file at path, or null if it does not exist.
///
/// - 'path | read(bytes)' returns a string containing the first n bytes of the file at path.
/// If the file is smaller than n bytes, the whole file is returned. There is no filter to
/// read the whole file at once to encourage limiting the amount of data read and processed.
///
/// - 'string | hash', and 'path | hashfile' return the hash of the string or file at path.
/// No guarantee is made about the algorithm used: treat it as an opaque value.

View file

@ -1,4 +1,9 @@
use std::{iter::once, sync::Arc};
use std::{
fs::File,
io::{BufReader, Read},
iter::once,
sync::Arc,
};
use dashmap::DashMap;
use jaq_core::{CustomFilter, Definitions, Error, Val};
@ -43,6 +48,17 @@ macro_rules! string_arg {
};
}
macro_rules! int_arg {
($args:expr, $n:expr, $ctx:expr, $val:expr) => {
match $args[$n].run(($ctx.clone(), $val.clone())).next() {
Some(Ok(Val::Int(v))) => Ok(v as _),
Some(Ok(val)) => custom_err(format!("expected int but got {val:?}")),
Some(Err(e)) => Err(e),
None => custom_err("value expected but none found"),
}
};
}
macro_rules! log_action {
($level:expr, $val:expr) => {
match $level.to_ascii_lowercase().as_str() {
@ -170,5 +186,45 @@ pub fn load_watchexec_defs(defs: &mut Definitions) -> miette::Result<()> {
}),
);
trace!("jaq: add read filter");
defs.insert_custom(
"read",
CustomFilter::new(1, {
move |args, (ctx, val)| {
let path = match &val {
Val::Str(v) => v.to_string(),
_ => return_err!(custom_err("expected string (path) but got {val:?}")),
};
let bytes = match int_arg!(args, 0, ctx, &val) {
Ok(v) => v,
Err(e) => return_err!(Err(e)),
};
Box::new(once(Ok(match File::open(&path) {
Ok(file) => {
let buf_reader = BufReader::new(file);
let mut limited = buf_reader.take(bytes);
let mut buffer = String::with_capacity(bytes as _);
match limited.read_to_string(&mut buffer) {
Ok(read) => {
debug!("jaq: read {read} bytes from {path:?}");
Val::Str(buffer.into())
}
Err(err) => {
error!("jaq: failed to read from {path:?}: {err:?}");
Val::Null
}
}
}
Err(err) => {
error!("jaq: failed to open file {path:?}: {err:?}");
Val::Null
}
})))
}
}),
);
Ok(())
}