refactor: bunpen: config/translate_opts: make the autodetect logic not pollute the hardcoded path ingestion quite so much

This commit is contained in:
2024-08-30 15:20:56 +00:00
parent 67f6026c67
commit 9148b49ba2

View File

@@ -1,6 +1,7 @@
// vim: set shiftwidth=2 : // vim: set shiftwidth=2 :
// ingest literal `cli_opts` into a more computer-friendly form // ingest literal `cli_opts` into a more computer-friendly form
use errors;
use errors::ext; use errors::ext;
use fs; use fs;
use log; use log;
@@ -89,17 +90,38 @@ export fn ingest_cli_opts(opts: cli_opts) (cli_request | help) = {
// if `allow_abs`, then paths with start with `/` are treated as absolute, // if `allow_abs`, then paths with start with `/` are treated as absolute,
// instead of as relative to `base`. // instead of as relative to `base`.
fn ingest_paths(into: *[]path::buffer, from: []str, base: (str | void), allow_abs: bool = false, method: (void | autodetect) = void) void = { fn ingest_paths(into: *[]path::buffer, from: []str, base: (str | void), allow_abs: bool = false, method: (void | autodetect) = void) void = {
// enforce file mode requirements as per autodetect (if specified)
let (allow_nonexistent, allow_file, allow_dir, allow_parent) = match (method) {
case void => yield (true, true, true, false);
case let detect: autodetect => yield switch (detect) {
case autodetect::EXISTING => yield (false, true, true, false);
case autodetect::EXISTING_FILE => yield (false, true, false, false);
case autodetect::EXISTING_FILE_OR_PARENT => yield (false, true, false, true);
case autodetect::EXISTING_OR_PARENT => yield (false, true, true, true);
case autodetect::PARENT => yield (false, false, false, true);
};
};
for (let path_str .. from) { for (let path_str .. from) {
errors::ext::swallow("[config/path] omitting path {}", errors::ext::swallow("[config/path] omitting path {}",
try_as_path(into, path_str, base, allow_abs, method), try_as_path(into, path_str, base, allow_abs, allow_nonexistent, allow_file, allow_dir, allow_parent),
path_str path_str
); );
}; };
}; };
// consider the `path_arg` in the context of the autodetect `method`, and append // consider the `path_arg` in the context of the `allow_*` restrictions,
// either that path, its parent, or neither. // and append either that path, its parent, or neither.
fn try_as_path(into: *[]path::buffer, path_arg: str, base: (str | void), allow_abs: bool, method: (void | autodetect)) (void | fs::error | path::error) = { fn try_as_path(
into: *[]path::buffer,
path_arg: str,
base: (str | void),
allow_abs: bool,
allow_nonexistent: bool,
allow_file: bool,
allow_dir: bool,
allow_parent: bool,
) (void | fs::error | path::error) = {
let path_buf = if (allow_abs && path::abs(path_arg)) { let path_buf = if (allow_abs && path::abs(path_arg)) {
yield path::init(path_arg)?; yield path::init(path_arg)?;
} else match (base) { } else match (base) {
@@ -109,48 +131,38 @@ fn try_as_path(into: *[]path::buffer, path_arg: str, base: (str | void), allow_a
return path::not_prefix; return path::not_prefix;
}; };
// enforce file mode requirements as per autodetect (if specified), if (allow_nonexistent)
// possibly changes `path_buf` to point to its parent. return append(into, path_buf);
match (method) {
case void => void; // try the path
case let detect: autodetect => switch (detect) { let err = match (check_exists(&path_buf, allow_file, allow_dir)) {
case autodetect::EXISTING => case void => return append(into, path_buf);
check_exists(&path_buf, true)?; case let err: (fs::error | path::error) => yield err;
case autodetect::EXISTING_FILE =>
check_exists(&path_buf, false)?;
case autodetect::EXISTING_FILE_OR_PARENT =>
match (check_exists(&path_buf, false)) {
case void => void;
case =>
path::push(&path_buf, "..")?;
check_exists(&path_buf, true)?;
};
case autodetect::EXISTING_OR_PARENT =>
match (check_exists(&path_buf, true)) {
case void => void;
case =>
path::push(&path_buf, "..")?;
check_exists(&path_buf, true)?;
};
case autodetect::PARENT =>
path::push(&path_buf, "..")?;
check_exists(&path_buf, true)?;
};
}; };
append(into, path_buf); // try the parent
if (allow_parent) {
path::push(&path_buf, "..")?;
check_exists(&path_buf, false, true)?;
return append(into, path_buf);
};
return err;
}; };
// ensure that the specified path exists. // ensure that the specified path exists.
// if it exists but not as a directory, succeed only if `dir_ok = true`. // if it exists but not as a directory, succeed only if `dir_ok = true`.
fn check_exists(pathbuf: *path::buffer, dir_ok: bool) (void | fs::error | path::error) = { fn check_exists(pathbuf: *path::buffer, file_ok: bool, dir_ok: bool) (void | fs::error | path::error) = {
if (!file_ok && !dir_ok)
return errors::invalid; // save a syscall for `--bunpen-autodetect parent`
// stat the path. in case of symlinks dereference to the underlying entity. // stat the path. in case of symlinks dereference to the underlying entity.
let dereferenced = fs::realpath(os::cwd, path::string(pathbuf))?; let dereferenced = fs::realpath(os::cwd, path::string(pathbuf))?;
let stat = fs::stat(os::cwd, dereferenced)?; let stat = fs::stat(os::cwd, dereferenced)?;
let is_dir = (stat.mode & rt::S_IFDIR) != 0; let is_dir = (stat.mode & rt::S_IFDIR) != 0;
if (is_dir && !dir_ok) { if (is_dir && dir_ok || !is_dir && file_ok)
log::printfln("[config/path/try] not adding path (wanted a file, but this is a directory): {}", path::string(pathbuf)); return
return fs::wrongtype;
}; log::printfln("[config/path/try] not adding path (exists, but as wrong type): {}", path::string(pathbuf));
return fs::wrongtype;
}; };