use crate::{Dict, Environment, FALSE_SYMBOL, TRUE_SYMBOL, eval::apply::eval_apply, expand_tilde, value::Value};
pub(crate) fn cd(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if args.len() != 1 {
return Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Cd]: Expected 1 parameter, but {} were passed.",
args.len()
)));
}
match eval_apply(&args[0], env)? {
Value::String(path) => {
let expaned = expand_tilde(path)?;
std::env::set_current_dir(&expaned)
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Cd]: Expected a valid path, but got: `{}` with `{e}`.",
expaned.display()
))
})
.and(Ok(Value::Unit))
}
other => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Cd]: Expected a string as path, but got: `{other}`.",
))),
}
}
pub(crate) fn pwd(args: &[Value], _env: Environment) -> Result<Value, std::sync::Arc<str>> {
if !args.is_empty() {
return Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Pwd]: Expected 0 parameters, but {} were passed.",
args.len()
)));
}
std::env::current_dir()
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Pwd]: Get current path failed with: `{e}`.",
))
})
.map(|path| Value::String(std::sync::Arc::from(path.display().to_string())))
}
pub(crate) fn mkdir(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [path] = args {
match eval_apply(path, env)? {
Value::String(p) => {
let expaned = expand_tilde(p)?;
std::fs::create_dir(expaned)
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Mkdir]: Failed to create directory with: `{e}`"
))
})
.and(Ok(Value::Unit))
}
e => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Mkdir]: Expected a string as path, but got: `{e}`."
))),
}
} else if let [path, is_recursive] = args {
match (
eval_apply(path, env.clone())?,
eval_apply(is_recursive, env)?,
) {
(Value::String(p), r @ Value::Atom(_)) if r == *TRUE_SYMBOL => {
let expaned = expand_tilde(p)?;
std::fs::create_dir_all(expaned)
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Mkdir]: Failed to create directory with: `{e}`"
))
})
.and(Ok(Value::Unit))
}
(Value::String(p), r @ Value::Atom(_)) if r == *FALSE_SYMBOL => {
let expaned = expand_tilde(p)?;
std::fs::create_dir(expaned)
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Mkdir]: Failed to create directory with: `{e}`"
))
})
.and(Ok(Value::Unit))
}
(Value::String(_), e) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Mkdir]: Expected a boolean, but got: `{e}`."
))),
(e, _) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Mkdir]: Expected a string as path, but got: `{e}`."
))),
}
} else {
Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Mkdir]: Expected 1 or 2 parameters, but {} were passed.",
args.len()
)))
}
}
pub(crate) fn rm(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [file_path] = args {
match eval_apply(file_path, env)? {
Value::String(path) => {
let expanded = expand_tilde(path)?;
match std::fs::metadata(&expanded)
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Rm]: Failed to get the metadata of `{}` with: `{e}`",
expanded.display()
))
})?
.is_dir()
{
true => std::fs::remove_dir_all(expanded),
false => std::fs::remove_file(expanded),
}
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Rm]: Failed to remove file with: `{e}`"
))
})
.and(Ok(Value::Unit))
}
e => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Rm]: Expected a string as path, but got: `{e}`."
))),
}
} else {
Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Rm]: Expected 1 parameter, but {} were passed.",
args.len()
)))
}
}
pub(crate) fn glob(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [file_pattern] = args {
match eval_apply(file_pattern, env)? {
Value::String(pat) => Ok(Value::List(
nu_glob::glob(
expand_tilde(pat)?.display().to_string().as_str(),
nu_glob::Uninterruptible,
)
.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::Glob]: Failed to glob files with: `{e}`",
))
})?
.filter_map(|e| {
e.ok()
.map(|p| Value::String(std::sync::Arc::from(p.display().to_string())))
})
.collect::<std::sync::Arc<[Value]>>(),
)),
e => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Glob]: Expected a string as pattern, but got: `{e}`."
))),
}
} else {
Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Glob]: Expected 1 parameter, but {} were passed.",
args.len()
)))
}
}
pub(crate) fn file_meta(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
static TYPE_TAGS: std::sync::LazyLock<[std::sync::Arc<str>; 12]> = std::sync::LazyLock::new(|| {
[
std::sync::Arc::from("MetaData"),
std::sync::Arc::from("kind"),
std::sync::Arc::from("size"),
std::sync::Arc::from("modified"),
std::sync::Arc::from("created"),
std::sync::Arc::from("readonly"),
std::sync::Arc::from("extension"),
std::sync::Arc::from("name"),
std::sync::Arc::from("file"),
std::sync::Arc::from("directory"),
std::sync::Arc::from("symlink"),
std::sync::Arc::from("unknown"),
]
});
if let [file_path] = args {
match eval_apply(file_path, env)? {
Value::String(path) => {
let expanded = expand_tilde(path)?;
let metadata = std::fs::metadata(&expanded).map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::builtin::FileMeta]: Failed to get file metadata with: `{e}`",
))
})?;
let mut data = Dict::with_capacity_and_hasher(9, rustc_hash::FxBuildHasher);
data.insert(
TYPE_TAGS[1].clone(),
std::sync::Arc::new(Value::Atom(if metadata.is_symlink() {
TYPE_TAGS[10].clone()
} else if metadata.is_dir() {
TYPE_TAGS[9].clone()
} else if metadata.is_file() {
TYPE_TAGS[8].clone()
} else {
TYPE_TAGS[11].clone()
})),
);
data.insert(
TYPE_TAGS[2].clone(),
std::sync::Arc::new(Value::Number(metadata.len() as f64)),
);
data.insert(
TYPE_TAGS[3].clone(),
std::sync::Arc::new(match metadata.modified() {
Ok(mt) => match mt.duration_since(std::time::UNIX_EPOCH) {
Ok(t) => Ok(Value::Number(t.as_millis() as f64)),
Err(e) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::FileMeta]: Failed to get the timestamp with: `{e}`"
))),
},
Err(e) => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::FileMeta]: ",
"Failed to get the last modification time with: {}"
),
e
))),
}?),
);
data.insert(
TYPE_TAGS[4].clone(),
std::sync::Arc::new(match metadata.created() {
Ok(mt) => match mt.duration_since(std::time::UNIX_EPOCH) {
Ok(t) => Ok(Value::Number(t.as_millis() as f64)),
Err(e) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::FileMeta]: Failed to get the timestamp with: `{e}`"
))),
},
Err(e) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::FileMeta]: Failed to get the creation time with: `{e}`"
))),
}?),
);
data.insert(
TYPE_TAGS[5].clone(),
std::sync::Arc::new(match metadata.permissions().readonly() {
true => TRUE_SYMBOL.clone(),
false => FALSE_SYMBOL.clone(),
}),
);
data.insert(
TYPE_TAGS[6].clone(),
std::sync::Arc::new(Value::String({
match expanded.extension() {
Some(ex) => std::sync::Arc::from(ex.to_string_lossy()),
None => std::sync::Arc::from(""),
}
})),
);
data.insert(
TYPE_TAGS[7].clone(),
std::sync::Arc::new(Value::String(match expanded.file_name() {
Some(name) => std::sync::Arc::from(name.to_string_lossy()),
None => std::sync::Arc::from(""),
})),
);
Ok(Value::Object(TYPE_TAGS[0].clone(), Box::new(data)))
}
e => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::FileMeta]: Expected a string as file path, but got: `{e}`."
))),
}
} else {
Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::FileMeta]: Expected 1 parameter, but {} were passed.",
args.len()
)))
}
}