2022-05-03 23:05:55 -07:00
|
|
|
#![allow(unknown_lints)]
|
2022-01-30 12:16:10 -08:00
|
|
|
#![allow(clippy::unnecessary_wraps)]
|
|
|
|
|
2019-04-11 15:23:14 -07:00
|
|
|
use crate::common::*;
|
2018-08-27 16:03:52 -07:00
|
|
|
|
2019-12-25 06:12:06 -08:00
|
|
|
use Function::*;
|
2019-11-21 10:14:10 -08:00
|
|
|
pub(crate) enum Function {
|
|
|
|
Nullary(fn(&FunctionContext) -> Result<String, String>),
|
|
|
|
Unary(fn(&FunctionContext, &str) -> Result<String, String>),
|
|
|
|
Binary(fn(&FunctionContext, &str, &str) -> Result<String, String>),
|
2021-10-14 17:00:58 -07:00
|
|
|
BinaryPlus(fn(&FunctionContext, &str, &str, &[String]) -> Result<String, String>),
|
2021-07-03 12:39:45 -07:00
|
|
|
Ternary(fn(&FunctionContext, &str, &str, &str) -> Result<String, String>),
|
2019-11-21 10:14:10 -08:00
|
|
|
}
|
|
|
|
|
2017-12-02 14:59:07 -08:00
|
|
|
lazy_static! {
|
2019-11-21 10:14:10 -08:00
|
|
|
pub(crate) static ref TABLE: BTreeMap<&'static str, Function> = vec![
|
2022-03-02 16:41:48 -08:00
|
|
|
("absolute_path", Unary(absolute_path)),
|
2019-12-25 06:12:06 -08:00
|
|
|
("arch", Nullary(arch)),
|
2021-06-24 23:41:20 -07:00
|
|
|
("clean", Unary(clean)),
|
2019-12-25 06:12:06 -08:00
|
|
|
("env_var", Unary(env_var)),
|
|
|
|
("env_var_or_default", Binary(env_var_or_default)),
|
2022-03-02 16:48:28 -08:00
|
|
|
("error", Unary(error)),
|
2021-06-24 15:55:29 -07:00
|
|
|
("extension", Unary(extension)),
|
2021-06-17 00:56:09 -07:00
|
|
|
("file_name", Unary(file_name)),
|
|
|
|
("file_stem", Unary(file_stem)),
|
2021-06-24 15:55:29 -07:00
|
|
|
("invocation_directory", Nullary(invocation_directory)),
|
2021-10-14 17:00:58 -07:00
|
|
|
("join", BinaryPlus(join)),
|
2021-06-24 15:55:29 -07:00
|
|
|
("just_executable", Nullary(just_executable)),
|
|
|
|
("justfile", Nullary(justfile)),
|
|
|
|
("justfile_directory", Nullary(justfile_directory)),
|
2021-07-03 12:39:45 -07:00
|
|
|
("lowercase", Unary(lowercase)),
|
2021-06-24 15:55:29 -07:00
|
|
|
("os", Nullary(os)),
|
|
|
|
("os_family", Nullary(os_family)),
|
|
|
|
("parent_directory", Unary(parent_directory)),
|
2022-02-21 13:45:30 -08:00
|
|
|
("path_exists", Unary(path_exists)),
|
2021-11-08 11:22:58 -08:00
|
|
|
("quote", Unary(quote)),
|
2021-07-03 12:39:45 -07:00
|
|
|
("replace", Ternary(replace)),
|
2022-05-04 16:18:31 -07:00
|
|
|
("sha256", Unary(sha256)),
|
|
|
|
("sha256_file", Unary(sha256_file)),
|
2021-07-03 12:39:45 -07:00
|
|
|
("trim", Unary(trim)),
|
2021-10-14 00:35:15 -07:00
|
|
|
("trim_end", Unary(trim_end)),
|
2021-10-14 00:03:57 -07:00
|
|
|
("trim_end_match", Binary(trim_end_match)),
|
|
|
|
("trim_end_matches", Binary(trim_end_matches)),
|
2021-10-14 00:35:15 -07:00
|
|
|
("trim_start", Unary(trim_start)),
|
2021-10-14 00:03:57 -07:00
|
|
|
("trim_start_match", Binary(trim_start_match)),
|
|
|
|
("trim_start_matches", Binary(trim_start_matches)),
|
2021-07-03 12:39:45 -07:00
|
|
|
("uppercase", Unary(uppercase)),
|
2022-05-04 16:18:31 -07:00
|
|
|
("uuid", Nullary(uuid)),
|
2021-06-17 00:56:09 -07:00
|
|
|
("without_extension", Unary(without_extension)),
|
2018-12-08 14:29:41 -08:00
|
|
|
]
|
|
|
|
.into_iter()
|
|
|
|
.collect();
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Function {
|
2021-10-14 17:00:58 -07:00
|
|
|
pub(crate) fn argc(&self) -> Range<usize> {
|
2017-12-02 14:59:07 -08:00
|
|
|
match *self {
|
2021-10-14 17:00:58 -07:00
|
|
|
Nullary(_) => 0..0,
|
|
|
|
Unary(_) => 1..1,
|
|
|
|
Binary(_) => 2..2,
|
|
|
|
BinaryPlus(_) => 2..usize::MAX,
|
|
|
|
Ternary(_) => 3..3,
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
|
|
|
}
|
2017-12-02 05:37:10 -08:00
|
|
|
}
|
|
|
|
|
2022-03-02 16:41:48 -08:00
|
|
|
fn absolute_path(context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
let abs_path_unchecked = context.search.working_directory.join(path).lexiclean();
|
|
|
|
match abs_path_unchecked.to_str() {
|
|
|
|
Some(absolute_path) => Ok(absolute_path.to_owned()),
|
|
|
|
None => Err(format!(
|
|
|
|
"Working directory is not valid unicode: {}",
|
|
|
|
context.search.working_directory.display()
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 10:14:10 -08:00
|
|
|
fn arch(_context: &FunctionContext) -> Result<String, String> {
|
2021-02-15 01:18:31 -08:00
|
|
|
Ok(target::arch().to_owned())
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
|
|
|
|
2021-06-24 23:41:20 -07:00
|
|
|
fn clean(_context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
Ok(Path::new(path).lexiclean().to_str().unwrap().to_owned())
|
|
|
|
}
|
|
|
|
|
2019-11-21 10:14:10 -08:00
|
|
|
fn env_var(context: &FunctionContext, key: &str) -> Result<String, String> {
|
2017-12-02 14:59:07 -08:00
|
|
|
use std::env::VarError::*;
|
2018-08-27 16:03:52 -07:00
|
|
|
|
2018-03-17 09:17:41 -07:00
|
|
|
if let Some(value) = context.dotenv.get(key) {
|
|
|
|
return Ok(value.clone());
|
|
|
|
}
|
|
|
|
|
2017-12-02 14:59:07 -08:00
|
|
|
match env::var(key) {
|
|
|
|
Err(NotPresent) => Err(format!("environment variable `{}` not present", key)),
|
2018-12-08 14:29:41 -08:00
|
|
|
Err(NotUnicode(os_string)) => Err(format!(
|
|
|
|
"environment variable `{}` not unicode: {:?}",
|
|
|
|
key, os_string
|
|
|
|
)),
|
2017-12-02 14:59:07 -08:00
|
|
|
Ok(value) => Ok(value),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 10:14:10 -08:00
|
|
|
fn env_var_or_default(
|
2018-03-17 09:17:41 -07:00
|
|
|
context: &FunctionContext,
|
2018-12-08 14:29:41 -08:00
|
|
|
key: &str,
|
|
|
|
default: &str,
|
2018-03-17 09:17:41 -07:00
|
|
|
) -> Result<String, String> {
|
2020-01-15 02:16:13 -08:00
|
|
|
use std::env::VarError::*;
|
|
|
|
|
2018-03-17 09:17:41 -07:00
|
|
|
if let Some(value) = context.dotenv.get(key) {
|
|
|
|
return Ok(value.clone());
|
|
|
|
}
|
|
|
|
|
2017-12-02 14:59:07 -08:00
|
|
|
match env::var(key) {
|
2021-02-15 01:18:31 -08:00
|
|
|
Err(NotPresent) => Ok(default.to_owned()),
|
2018-12-08 14:29:41 -08:00
|
|
|
Err(NotUnicode(os_string)) => Err(format!(
|
|
|
|
"environment variable `{}` not unicode: {:?}",
|
|
|
|
key, os_string
|
|
|
|
)),
|
2017-12-02 14:59:07 -08:00
|
|
|
Ok(value) => Ok(value),
|
|
|
|
}
|
2017-12-02 05:37:10 -08:00
|
|
|
}
|
2021-03-28 15:44:02 -07:00
|
|
|
|
2022-03-02 16:48:28 -08:00
|
|
|
fn error(_context: &FunctionContext, message: &str) -> Result<String, String> {
|
|
|
|
Err(message.to_owned())
|
|
|
|
}
|
|
|
|
|
2021-06-24 15:55:29 -07:00
|
|
|
fn extension(_context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
Utf8Path::new(path)
|
|
|
|
.extension()
|
|
|
|
.map(str::to_owned)
|
|
|
|
.ok_or_else(|| format!("Could not extract extension from `{}`", path))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn file_name(_context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
Utf8Path::new(path)
|
|
|
|
.file_name()
|
|
|
|
.map(str::to_owned)
|
|
|
|
.ok_or_else(|| format!("Could not extract file name from `{}`", path))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn file_stem(_context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
Utf8Path::new(path)
|
|
|
|
.file_stem()
|
|
|
|
.map(str::to_owned)
|
|
|
|
.ok_or_else(|| format!("Could not extract file stem from `{}`", path))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn invocation_directory(context: &FunctionContext) -> Result<String, String> {
|
|
|
|
Platform::convert_native_path(
|
|
|
|
&context.search.working_directory,
|
|
|
|
context.invocation_directory,
|
|
|
|
)
|
|
|
|
.map_err(|e| format!("Error getting shell path: {}", e))
|
|
|
|
}
|
|
|
|
|
2021-10-14 17:00:58 -07:00
|
|
|
fn join(
|
|
|
|
_context: &FunctionContext,
|
|
|
|
base: &str,
|
|
|
|
with: &str,
|
|
|
|
and: &[String],
|
|
|
|
) -> Result<String, String> {
|
|
|
|
let mut result = Utf8Path::new(base).join(with);
|
|
|
|
for arg in and {
|
|
|
|
result.push(arg);
|
|
|
|
}
|
|
|
|
Ok(result.to_string())
|
2021-06-24 15:55:29 -07:00
|
|
|
}
|
|
|
|
|
2021-03-28 15:44:02 -07:00
|
|
|
fn just_executable(_context: &FunctionContext) -> Result<String, String> {
|
|
|
|
let exe_path =
|
|
|
|
std::env::current_exe().map_err(|e| format!("Error getting current executable: {}", e))?;
|
|
|
|
|
|
|
|
exe_path.to_str().map(str::to_owned).ok_or_else(|| {
|
|
|
|
format!(
|
|
|
|
"Executable path is not valid unicode: {}",
|
2022-02-27 19:09:31 -08:00
|
|
|
exe_path.display()
|
2021-03-28 15:44:02 -07:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
2021-06-17 00:56:09 -07:00
|
|
|
|
2021-06-24 15:55:29 -07:00
|
|
|
fn justfile(context: &FunctionContext) -> Result<String, String> {
|
|
|
|
context
|
|
|
|
.search
|
|
|
|
.justfile
|
|
|
|
.to_str()
|
2021-06-17 00:56:09 -07:00
|
|
|
.map(str::to_owned)
|
2021-06-24 15:55:29 -07:00
|
|
|
.ok_or_else(|| {
|
|
|
|
format!(
|
|
|
|
"Justfile path is not valid unicode: {}",
|
2022-02-27 19:09:31 -08:00
|
|
|
context.search.justfile.display()
|
2021-06-24 15:55:29 -07:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn justfile_directory(context: &FunctionContext) -> Result<String, String> {
|
|
|
|
let justfile_directory = context.search.justfile.parent().ok_or_else(|| {
|
|
|
|
format!(
|
|
|
|
"Could not resolve justfile directory. Justfile `{}` had no parent.",
|
|
|
|
context.search.justfile.display()
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
justfile_directory
|
|
|
|
.to_str()
|
|
|
|
.map(str::to_owned)
|
|
|
|
.ok_or_else(|| {
|
|
|
|
format!(
|
|
|
|
"Justfile directory is not valid unicode: {}",
|
2022-02-27 19:09:31 -08:00
|
|
|
justfile_directory.display()
|
2021-06-24 15:55:29 -07:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-07-03 12:39:45 -07:00
|
|
|
fn lowercase(_context: &FunctionContext, s: &str) -> Result<String, String> {
|
|
|
|
Ok(s.to_lowercase())
|
|
|
|
}
|
|
|
|
|
2021-06-24 15:55:29 -07:00
|
|
|
fn os(_context: &FunctionContext) -> Result<String, String> {
|
|
|
|
Ok(target::os().to_owned())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn os_family(_context: &FunctionContext) -> Result<String, String> {
|
2021-08-27 16:36:41 -07:00
|
|
|
Ok(target::family().to_owned())
|
2021-06-17 00:56:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn parent_directory(_context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
Utf8Path::new(path)
|
|
|
|
.parent()
|
|
|
|
.map(Utf8Path::to_string)
|
|
|
|
.ok_or_else(|| format!("Could not extract parent directory from `{}`", path))
|
|
|
|
}
|
|
|
|
|
2022-02-27 19:09:31 -08:00
|
|
|
fn path_exists(context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
Ok(
|
|
|
|
context
|
|
|
|
.search
|
|
|
|
.working_directory
|
|
|
|
.join(path)
|
|
|
|
.exists()
|
|
|
|
.to_string(),
|
|
|
|
)
|
2022-02-21 13:45:30 -08:00
|
|
|
}
|
|
|
|
|
2021-11-08 11:22:58 -08:00
|
|
|
fn quote(_context: &FunctionContext, s: &str) -> Result<String, String> {
|
|
|
|
Ok(format!("'{}'", s.replace('\'', "'\\''")))
|
|
|
|
}
|
|
|
|
|
2021-07-03 12:39:45 -07:00
|
|
|
fn replace(_context: &FunctionContext, s: &str, from: &str, to: &str) -> Result<String, String> {
|
|
|
|
Ok(s.replace(from, to))
|
|
|
|
}
|
|
|
|
|
2022-05-04 16:18:31 -07:00
|
|
|
fn sha256(_context: &FunctionContext, s: &str) -> Result<String, String> {
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
hasher.update(s);
|
|
|
|
let hash = hasher.finalize();
|
|
|
|
Ok(format!("{:x}", hash))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sha256_file(context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
let justpath = context.search.working_directory.join(path);
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
let mut file = std::fs::File::open(&justpath)
|
|
|
|
.map_err(|err| format!("Failed to open file at `{:?}`: {}", &justpath.to_str(), err))?;
|
|
|
|
std::io::copy(&mut file, &mut hasher)
|
|
|
|
.map_err(|err| format!("Failed to read file at `{:?}`: {}", &justpath.to_str(), err))?;
|
|
|
|
let hash = hasher.finalize();
|
|
|
|
Ok(format!("{:x}", hash))
|
|
|
|
}
|
|
|
|
|
2021-07-03 12:39:45 -07:00
|
|
|
fn trim(_context: &FunctionContext, s: &str) -> Result<String, String> {
|
|
|
|
Ok(s.trim().to_owned())
|
|
|
|
}
|
|
|
|
|
2021-10-14 00:35:15 -07:00
|
|
|
fn trim_end(_context: &FunctionContext, s: &str) -> Result<String, String> {
|
|
|
|
Ok(s.trim_end().to_owned())
|
|
|
|
}
|
|
|
|
|
2021-10-14 00:03:57 -07:00
|
|
|
fn trim_end_match(_context: &FunctionContext, s: &str, pat: &str) -> Result<String, String> {
|
|
|
|
Ok(s.strip_suffix(pat).unwrap_or(s).to_owned())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn trim_end_matches(_context: &FunctionContext, s: &str, pat: &str) -> Result<String, String> {
|
|
|
|
Ok(s.trim_end_matches(pat).to_owned())
|
|
|
|
}
|
|
|
|
|
2021-10-14 00:35:15 -07:00
|
|
|
fn trim_start(_context: &FunctionContext, s: &str) -> Result<String, String> {
|
|
|
|
Ok(s.trim_start().to_owned())
|
|
|
|
}
|
|
|
|
|
2021-10-14 00:03:57 -07:00
|
|
|
fn trim_start_match(_context: &FunctionContext, s: &str, pat: &str) -> Result<String, String> {
|
|
|
|
Ok(s.strip_prefix(pat).unwrap_or(s).to_owned())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn trim_start_matches(_context: &FunctionContext, s: &str, pat: &str) -> Result<String, String> {
|
|
|
|
Ok(s.trim_start_matches(pat).to_owned())
|
|
|
|
}
|
|
|
|
|
2021-07-03 12:39:45 -07:00
|
|
|
fn uppercase(_context: &FunctionContext, s: &str) -> Result<String, String> {
|
|
|
|
Ok(s.to_uppercase())
|
|
|
|
}
|
|
|
|
|
2022-05-04 16:18:31 -07:00
|
|
|
fn uuid(_context: &FunctionContext) -> Result<String, String> {
|
|
|
|
Ok(uuid::Uuid::new_v4().to_string())
|
|
|
|
}
|
|
|
|
|
2021-06-17 00:56:09 -07:00
|
|
|
fn without_extension(_context: &FunctionContext, path: &str) -> Result<String, String> {
|
|
|
|
let parent = Utf8Path::new(path)
|
|
|
|
.parent()
|
|
|
|
.ok_or_else(|| format!("Could not extract parent from `{}`", path))?;
|
|
|
|
|
|
|
|
let file_stem = Utf8Path::new(path)
|
|
|
|
.file_stem()
|
|
|
|
.ok_or_else(|| format!("Could not extract file stem from `{}`", path))?;
|
|
|
|
|
|
|
|
Ok(parent.join(file_stem).to_string())
|
|
|
|
}
|