1
1
mirror of https://github.com/wez/wezterm.git synced 2024-10-04 02:08:07 +03:00

Improved read_dir and added documentation.

This commit is contained in:
Daniel Kongsgaard 2023-09-27 01:39:14 +02:00
parent d4c75b3d6d
commit 6cb3ca9bb4
2 changed files with 167 additions and 6 deletions

View File

@ -4,7 +4,7 @@ tags:
- utility
- filesystem
---
# `wezterm.read_dir(path)`
# `wezterm.read_dir(path [, callback])`
{{since('20200503-171512-b13ef15f')}}
@ -22,4 +22,121 @@ for _, v in ipairs(wezterm.read_dir '/etc') do
end
```
{{since('nightly')}}
`read_dir` accepts an optional callback function that can be used on each entry
of the directory being read with `read_dir`. The callback function should be of the form
```
function(filepath, meta)
-- do things with the filepath and meta
end
```
where `filepath` is a Lua string of the entry's path and meta is a special `MetaData`
object for the entry.
If you want to use the function to change the output of read_dir, you should make sure
to return values in one of the two following forms:
```
bool,
{ bool, integer },
{ bool, integer, integer },
{ bool, integer, integer, integer },
and so on...
```
If the function returns `true`, then the given `filepath` will be included in the output
of `read_dir`, and if the function returns `false`, then the given `filepath` will be
excluded from the output.
If you return an array `{ bool, integer... }`, then the boolean will have the same meaning
as above, and the integers will be used for sorting (in the given order). (See below.)
If the function returns anything other than boolean or an array starting with a boolean,
or if we don't include the optional function at all, then the default behavior is to include
all `filepath`s.
The `MetaData` object (and thus`meta`) contains information about the entry (either
a directory, a file or a symlink), which can be accessed with the following methods:
* `is_dir` - returns true if the entry is a directory and false otherwise
* `is_file` - returns true if the entry is a file and false otherwise
* `is_symlink` - returns true if the entry is symlink and false otherwise
* `is_readonly` - returns true if the entry is readonly and false otherwise
* `secs_since_modified` - returns an integer with the number of seconds since
the entry was last modified
* `secs_since_accessed` - returns an integer with the number of seconds since
the entry was last accessed
* `secs_since_created` - returns an integer with the number of seconds since
the entry was created
* `bytes` - returns the size of the entry in bytes
### Examples
If we want `read_only` to return the name (not path) of all folders and symlinks that
are not hidden files or folders (i.e., not starting with a `.`) in the home directory,
and then sort them first by the time since we last accessed the entry and thereafter
by the length of the filepath, we can do the following:
```lua
string.startswith = function(str, start)
return str:sub(1, #start) == start
end
string.basename = function(s)
return string.gsub(s, '(.*[/\\])(.*)', '%2')
end
local wezterm = require 'wezterm'
local home = wezterm.home_dir
for _, v in
ipairs(wezterm.read_dir(home, function(filepath, meta)
return {
(meta:is_symlink() or meta:is_dir())
and (not filepath:basename():startswith '.'),
meta:secs_since_accessed(),
#filepath,
}
end))
do
wezterm.log_info('entry: ' .. v:basename())
end
```
Note: The purpose of sorting multiple times is that each sort don't swap equal values,
so if we for example have a lot of entries with the same length filepath, then we can
make sure that entries of each length are additionally sorted by when they were last
accessed.
If we want a list of the path of all files of size at least 1kB and a most 10MB
that we created less than a year ago, and that is sorted from oldest to newest by creation
time, we can do the following:
```lua
local wezterm = require 'wezterm'
local home = wezterm.home_dir
local year_in_secs = 60 * 60 * 24 * 365
local tbl = wezterm.read_dir(home, function(filepath, meta)
return {
meta:is_file()
and (10 ^ 3 < meta:bytes() and meta:bytes() < 10 ^ 7)
and (meta:secs_since_created() < year_in_secs),
-meta:secs_since_created(), -- we do minus to reverse the order
}
end)
wezterm.log_info(tbl)
```
If we just want to list all directories in the home directory, we can do the following:
```lua
local wezterm = require 'wezterm'
local home = wezterm.home_dir
local tbl = wezterm.read_dir(home, function(filepath, meta)
return meta:is_dir()
end)
wezterm.log_info(tbl)
```

View File

@ -78,7 +78,9 @@ async fn read_dir<'lua>(
let mut dir = smol::fs::read_dir(path)
.await
.map_err(mlua::Error::external)?;
let mut entries = vec![];
let mut entries: Vec<String> = vec![];
let mut sort = false; // assume we are not sorting
let mut sort_by_vec: Vec<Vec<i64>> = vec![];
while let Some(entry) = dir.next().await {
let entry = entry.map_err(mlua::Error::external)?;
if let Some(utf8) = entry.path().to_str() {
@ -86,21 +88,40 @@ async fn read_dir<'lua>(
// default behavior is include everything in the directory
let mut include_entry = true;
let mut sort_by: Vec<i64> = vec![];
if let Some(func) = &function {
include_entry = match func.call((utf8.to_string(), MetaData(meta))) {
Ok(mlua::Value::Boolean(b)) => b,
match func.call((utf8.to_string(), MetaData(meta))) {
Ok(mlua::Value::Boolean(b)) => {
include_entry = b;
}
Ok(mlua::Value::Table(tbl)) => {
let mut iter = tbl.sequence_values();
match iter.next() {
Some(Ok(mlua::Value::Boolean(b))) => {
include_entry = b;
}
_ => (),
};
while let Some(Ok(mlua::Value::Integer(i))) = iter.next() {
sort = true;
sort_by.push(i)
}
}
Err(err) => {
return Err(mlua::Error::external(format!(
"the optional read_dir function returns the error: {}",
err
)));
}
_ => true,
_ => (),
}
}
if include_entry {
entries.push(utf8.to_string());
if sort {
sort_by_vec.push(sort_by);
}
} // if include_entry is false, don't add entry to entries
} else {
return Err(mlua::Error::external(anyhow!(
@ -109,7 +130,30 @@ async fn read_dir<'lua>(
)));
}
}
Ok(entries)
if sort {
let mut sorted: Vec<String> = entries.clone();
for i in 0..sort_by_vec[0].len() {
let sort_by_ivec: Vec<i64> = sort_by_vec.iter().map(|v| v[i]).collect();
let mut zipped: Vec<(String, i64)> = entries
.clone()
.into_iter()
.zip(sort_by_ivec.into_iter())
.collect();
zipped.sort_by_key(|pair| pair.1);
sorted = zipped
.iter()
.map(|pair| pair.0.clone())
.map(|s| s.to_string())
.collect();
}
Ok(sorted)
} else {
Ok(entries)
}
}
async fn glob<'lua>(