mirror of
https://github.com/watchexec/watchexec.git
synced 2024-10-05 01:21:32 +02:00
Add file_meta and file_size filters
This commit is contained in:
parent
309b7583bd
commit
67ceab9eca
@ -800,11 +800,11 @@ pub struct Args {
|
|||||||
///
|
///
|
||||||
/// In addition to the jaq stdlib, watchexec adds some custom filter definitions:
|
/// 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
|
/// 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.
|
/// read the whole file at once to encourage limiting the amount of data read and processed.
|
||||||
///
|
///
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::{metadata, File, FileType, Metadata},
|
||||||
io::{BufReader, Read},
|
io::{BufReader, Read},
|
||||||
iter::once,
|
iter::once,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use jaq_core::{CustomFilter, Definitions, Error, Val};
|
use jaq_core::{CustomFilter, Definitions, Error, Val};
|
||||||
use miette::miette;
|
use miette::miette;
|
||||||
|
use serde_json::{json, Value};
|
||||||
use tracing::{debug, error, info, trace, warn};
|
use tracing::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
pub fn load_std_defs() -> miette::Result<Definitions> {
|
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(
|
defs.insert_custom(
|
||||||
"read",
|
"file_read",
|
||||||
CustomFilter::new(1, {
|
CustomFilter::new(1, {
|
||||||
move |args, (ctx, val)| {
|
move |args, (ctx, val)| {
|
||||||
let path = match &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(())
|
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 New Issue
Block a user