use super::{
StdlibConfig, StdlibState, collections, command, network, path, time,
which::{self, WhichConfig, WorkspaceSkipList},
};
use anyhow::Context;
use camino::Utf8Path;
#[cfg(unix)]
use cap_std::fs::FileTypeExt;
use cap_std::{ambient_authority, fs, fs_utf8::Dir};
use minijinja::{Environment, Error, value::Value};
use std::sync::Arc;
use crate::localization::{self, keys};
type FileTest = (&'static str, fn(fs::FileType) -> bool);
pub fn register(env: &mut Environment<'_>) -> anyhow::Result<StdlibState> {
let root = Dir::open_ambient_dir(".", ambient_authority())
.context(localization::message(keys::STDLIB_REGISTER_OPEN_DIR))?;
let cwd = std::env::current_dir()
.context(localization::message(keys::STDLIB_REGISTER_RESOLVE_DIR))?;
let path = camino::Utf8PathBuf::from_path_buf(cwd).map_err(|path| {
anyhow::anyhow!(
"{}",
localization::message(keys::STDLIB_REGISTER_DIR_NON_UTF8)
.with_arg("path", path.display().to_string())
)
})?;
register_with_config(
env,
StdlibConfig::new(root)?.with_workspace_root_path(path)?,
)
}
pub fn register_with_config(
env: &mut Environment<'_>,
config: StdlibConfig,
) -> anyhow::Result<StdlibState> {
let state = StdlibState::default();
register_file_tests(env);
path::register_filters(env, config.home_directory().clone());
collections::register_filters(env);
let which_cache_capacity = config.which_cache_capacity();
let which_skip_dirs = WorkspaceSkipList::from_names(config.workspace_skip_dirs());
let which_cwd = config
.workspace_root_path()
.map(|path| Arc::new(path.to_path_buf()));
let which_path = config.path_override().cloned();
let which_config =
WhichConfig::new(which_cwd, which_path, which_skip_dirs, which_cache_capacity);
which::register(env, which_config);
let impure = state.impure_flag();
let (network_config, command_config) = config.into_components();
network::register_functions(env, Arc::clone(&impure), network_config);
command::register(env, impure, command_config);
time::register_functions(env);
Ok(state)
}
#[must_use]
pub fn value_from_bytes(bytes: Vec<u8>) -> Value {
match String::from_utf8(bytes) {
Ok(text) => Value::from(text),
Err(err) => Value::from_bytes(err.into_bytes()),
}
}
#[cfg(unix)]
const FILE_TESTS: &[FileTest] = &[
("dir", is_dir),
("file", is_file),
("symlink", is_symlink),
("pipe", is_fifo),
("block_device", is_block_device),
("char_device", is_char_device),
("device", is_device),
];
#[cfg(not(unix))]
const FILE_TESTS: &[FileTest] = &[
("dir", is_dir),
("file", is_file),
("symlink", is_symlink),
("pipe", is_fifo),
("block_device", is_block_device),
("char_device", is_char_device),
("device", is_device),
];
fn register_file_tests(env: &mut Environment<'_>) {
for &(name, pred) in FILE_TESTS {
env.add_test(name, move |val: Value| -> Result<bool, Error> {
if let Some(s) = val.as_str() {
return path::file_type_matches(Utf8Path::new(s), pred);
}
Ok(false)
});
}
}
fn is_dir(ft: fs::FileType) -> bool {
ft.is_dir()
}
fn is_file(ft: fs::FileType) -> bool {
ft.is_file()
}
fn is_symlink(ft: fs::FileType) -> bool {
ft.is_symlink()
}
#[cfg(unix)]
fn is_fifo(ft: fs::FileType) -> bool {
ft.is_fifo()
}
#[cfg(not(unix))]
fn is_fifo(_ft: fs::FileType) -> bool {
false
}
#[cfg(unix)]
fn is_block_device(ft: fs::FileType) -> bool {
ft.is_block_device()
}
#[cfg(not(unix))]
fn is_block_device(_ft: fs::FileType) -> bool {
false
}
#[cfg(unix)]
fn is_char_device(ft: fs::FileType) -> bool {
ft.is_char_device()
}
#[cfg(not(unix))]
fn is_char_device(_ft: fs::FileType) -> bool {
false
}
#[cfg(unix)]
fn is_device(ft: fs::FileType) -> bool {
is_block_device(ft) || is_char_device(ft)
}
#[cfg(not(unix))]
fn is_device(_ft: fs::FileType) -> bool {
false
}