mirror of
https://github.com/watchexec/watchexec.git
synced 2024-11-16 00:58:31 +01:00
Add file_meta and file_size filters
This commit is contained in:
parent
309b7583bd
commit
67ceab9eca
2 changed files with 124 additions and 6 deletions
|
@ -800,11 +800,11 @@ pub struct Args {
|
|||
///
|
||||
/// In addition to the jaq stdlib, watchexec adds some custom filter definitions:
|
||||
///
|
||||
/// - 'path | metadata' returns file metadata or null if the file does not exist.
|
||||
/// - 'path | file_meta' returns file metadata or null if the file does not exist.
|
||||
///
|
||||
/// - 'path | filesize' returns the size of the file at path, or null if it does not exist.
|
||||
/// - 'path | file_size' 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.
|
||||
/// - 'path | file_read(bytes)' returns a string with 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.
|
||||
///
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
fs::{metadata, File, FileType, Metadata},
|
||||
io::{BufReader, Read},
|
||||
iter::once,
|
||||
sync::Arc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use jaq_core::{CustomFilter, Definitions, Error, Val};
|
||||
use miette::miette;
|
||||
use serde_json::{json, Value};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
pub fn load_std_defs() -> miette::Result<Definitions> {
|
||||
|
@ -186,9 +188,9 @@ pub fn load_watchexec_defs(defs: &mut Definitions) -> miette::Result<()> {
|
|||
}),
|
||||
);
|
||||
|
||||
trace!("jaq: add read filter");
|
||||
trace!("jaq: add file_read filter");
|
||||
defs.insert_custom(
|
||||
"read",
|
||||
"file_read",
|
||||
CustomFilter::new(1, {
|
||||
move |args, (ctx, val)| {
|
||||
let path = match &val {
|
||||
|
@ -226,5 +228,121 @@ pub fn load_watchexec_defs(defs: &mut Definitions) -> miette::Result<()> {
|
|||
}),
|
||||
);
|
||||
|
||||
trace!("jaq: add file_meta filter");
|
||||
defs.insert_custom(
|
||||
"file_meta",
|
||||
CustomFilter::new(0, {
|
||||
move |_, (_, val)| {
|
||||
let path = match &val {
|
||||
Val::Str(v) => v.to_string(),
|
||||
_ => return_err!(custom_err("expected string (path) but got {val:?}")),
|
||||
};
|
||||
|
||||
Box::new(once(Ok(match metadata(&path) {
|
||||
Ok(meta) => Val::from(json_meta(meta)),
|
||||
Err(err) => {
|
||||
error!("jaq: failed to open {path:?}: {err:?}");
|
||||
Val::Null
|
||||
}
|
||||
})))
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
trace!("jaq: add file_size filter");
|
||||
defs.insert_custom(
|
||||
"file_size",
|
||||
CustomFilter::new(0, {
|
||||
move |_, (_, val)| {
|
||||
let path = match &val {
|
||||
Val::Str(v) => v.to_string(),
|
||||
_ => return_err!(custom_err("expected string (path) but got {val:?}")),
|
||||
};
|
||||
|
||||
Box::new(once(Ok(match metadata(&path) {
|
||||
Ok(meta) => Val::Int(meta.len() as _),
|
||||
Err(err) => {
|
||||
error!("jaq: failed to open {path:?}: {err:?}");
|
||||
Val::Null
|
||||
}
|
||||
})))
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_meta(meta: Metadata) -> Value {
|
||||
let perms = meta.permissions();
|
||||
let mut val = json!({
|
||||
"type": filetype_str(meta.file_type()),
|
||||
"size": meta.len(),
|
||||
"modified": fs_time(meta.modified()),
|
||||
"accessed": fs_time(meta.accessed()),
|
||||
"created": fs_time(meta.created()),
|
||||
"dir": meta.is_dir(),
|
||||
"file": meta.is_file(),
|
||||
"symlink": meta.is_symlink(),
|
||||
"readonly": perms.readonly(),
|
||||
});
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let map = val.as_object_mut().unwrap();
|
||||
map.insert(
|
||||
"mode".to_string(),
|
||||
Value::String(format!("{:o}", perms.mode())),
|
||||
);
|
||||
map.insert("mode_byte".to_string(), Value::from(perms.mode()));
|
||||
map.insert(
|
||||
"executable".to_string(),
|
||||
Value::Bool(perms.mode() & 0o111 != 0),
|
||||
);
|
||||
}
|
||||
|
||||
val
|
||||
}
|
||||
|
||||
fn filetype_str(filetype: FileType) -> &'static str {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
if filetype.is_char_device() {
|
||||
return "char";
|
||||
} else if filetype.is_block_device() {
|
||||
return "block";
|
||||
} else if filetype.is_fifo() {
|
||||
return "fifo";
|
||||
} else if filetype.is_socket() {
|
||||
return "socket";
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::fs::FileTypeExt;
|
||||
if filetype.is_symlink_dir() {
|
||||
return "symdir";
|
||||
} else if filetype.is_symlink_file() {
|
||||
return "symfile";
|
||||
}
|
||||
}
|
||||
|
||||
if filetype.is_dir() {
|
||||
"dir"
|
||||
} else if filetype.is_file() {
|
||||
"file"
|
||||
} else if filetype.is_symlink() {
|
||||
"symlink"
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
fn fs_time(time: std::io::Result<SystemTime>) -> Option<u64> {
|
||||
time.ok()
|
||||
.and_then(|time| time.duration_since(UNIX_EPOCH).ok())
|
||||
.map(|dur| dur.as_secs())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue