2019-04-11 15:23:14 -07:00
|
|
|
use crate::common::*;
|
2018-08-27 16:03:52 -07:00
|
|
|
|
2017-12-02 05:37:10 -08:00
|
|
|
use target;
|
|
|
|
|
2017-12-02 14:59:07 -08:00
|
|
|
lazy_static! {
|
2019-04-11 15:23:14 -07:00
|
|
|
static ref FUNCTIONS: BTreeMap<&'static str, Function> = vec![
|
2018-12-08 14:29:41 -08:00
|
|
|
("arch", Function::Nullary(arch)),
|
|
|
|
("os", Function::Nullary(os)),
|
|
|
|
("os_family", Function::Nullary(os_family)),
|
|
|
|
("env_var", Function::Unary(env_var)),
|
|
|
|
("env_var_or_default", Function::Binary(env_var_or_default)),
|
|
|
|
(
|
|
|
|
"invocation_directory",
|
|
|
|
Function::Nullary(invocation_directory)
|
|
|
|
),
|
|
|
|
]
|
|
|
|
.into_iter()
|
|
|
|
.collect();
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) enum Function {
|
2018-12-08 14:29:41 -08:00
|
|
|
Nullary(fn(&FunctionContext) -> Result<String, String>),
|
|
|
|
Unary(fn(&FunctionContext, &str) -> Result<String, String>),
|
|
|
|
Binary(fn(&FunctionContext, &str, &str) -> Result<String, String>),
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Function {
|
|
|
|
fn argc(&self) -> usize {
|
|
|
|
use self::Function::*;
|
|
|
|
match *self {
|
|
|
|
Nullary(_) => 0,
|
2018-12-08 14:29:41 -08:00
|
|
|
Unary(_) => 1,
|
|
|
|
Binary(_) => 2,
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) fn resolve<'a>(token: &Token<'a>, argc: usize) -> CompilationResult<'a, ()> {
|
2019-04-19 02:40:25 -07:00
|
|
|
let name = token.lexeme();
|
|
|
|
if let Some(function) = FUNCTIONS.get(&name) {
|
|
|
|
use self::Function::*;
|
|
|
|
match (function, argc) {
|
|
|
|
(&Nullary(_), 0) | (&Unary(_), 1) | (&Binary(_), 2) => Ok(()),
|
|
|
|
_ => Err(
|
|
|
|
token.error(CompilationErrorKind::FunctionArgumentCountMismatch {
|
|
|
|
function: name,
|
|
|
|
found: argc,
|
|
|
|
expected: function.argc(),
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(token.error(CompilationErrorKind::UnknownFunction {
|
|
|
|
function: token.lexeme(),
|
|
|
|
}))
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
2017-12-02 05:37:10 -08:00
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) fn evaluate<'a>(
|
2019-11-07 10:55:15 -08:00
|
|
|
function_name: Name<'a>,
|
2019-04-19 02:40:25 -07:00
|
|
|
context: &FunctionContext,
|
|
|
|
arguments: &[String],
|
|
|
|
) -> RunResult<'a, String> {
|
2019-11-07 10:55:15 -08:00
|
|
|
let name = function_name.lexeme();
|
2019-04-19 02:40:25 -07:00
|
|
|
if let Some(function) = FUNCTIONS.get(name) {
|
|
|
|
use self::Function::*;
|
|
|
|
let argc = arguments.len();
|
|
|
|
match (function, argc) {
|
|
|
|
(&Nullary(f), 0) => f(context).map_err(|message| RuntimeError::FunctionCall {
|
2019-11-07 10:55:15 -08:00
|
|
|
function: function_name,
|
2018-12-08 14:29:41 -08:00
|
|
|
message,
|
2019-04-19 02:40:25 -07:00
|
|
|
}),
|
|
|
|
(&Unary(f), 1) => f(context, &arguments[0]).map_err(|message| RuntimeError::FunctionCall {
|
2019-11-07 10:55:15 -08:00
|
|
|
function: function_name,
|
2019-04-19 02:40:25 -07:00
|
|
|
message,
|
|
|
|
}),
|
|
|
|
(&Binary(f), 2) => {
|
|
|
|
f(context, &arguments[0], &arguments[1]).map_err(|message| RuntimeError::FunctionCall {
|
2019-11-07 10:55:15 -08:00
|
|
|
function: function_name,
|
2019-04-19 02:40:25 -07:00
|
|
|
message,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
_ => Err(RuntimeError::Internal {
|
|
|
|
message: format!(
|
|
|
|
"attempted to evaluate function `{}` with {} arguments",
|
|
|
|
name, argc
|
|
|
|
),
|
|
|
|
}),
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
2019-04-19 02:40:25 -07:00
|
|
|
} else {
|
|
|
|
Err(RuntimeError::Internal {
|
|
|
|
message: format!("attempted to evaluate unknown function: `{}`", name),
|
|
|
|
})
|
2017-12-02 14:59:07 -08:00
|
|
|
}
|
2017-12-02 05:37:10 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) fn arch(_context: &FunctionContext) -> Result<String, String> {
|
2017-12-02 14:59:07 -08:00
|
|
|
Ok(target::arch().to_string())
|
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) fn os(_context: &FunctionContext) -> Result<String, String> {
|
2017-12-02 14:59:07 -08:00
|
|
|
Ok(target::os().to_string())
|
2017-12-02 05:37:10 -08:00
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) fn os_family(_context: &FunctionContext) -> Result<String, String> {
|
2017-12-02 14:59:07 -08:00
|
|
|
Ok(target::os_family().to_string())
|
2017-12-02 05:37:10 -08:00
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) fn invocation_directory(context: &FunctionContext) -> Result<String, String> {
|
Gargantuan refactor (#522)
- Instead of changing the current directory with `env::set_current_dir`
to be implicitly inherited by subprocesses, we now use
`Command::current_dir` to set it explicitly. This feels much better,
since we aren't dependent on the implicit state of the process's
current directory.
- Subcommand execution is much improved.
- Added a ton of tests for config parsing, config execution, working
dir, and search dir.
- Error messages are improved. Many more will be colored.
- The Config is now onwed, instead of borrowing from the arguments and
the `clap::ArgMatches` object. This is a huge ergonomic improvement,
especially in tests, and I don't think anyone will notice.
- `--edit` now uses `$VISUAL`, `$EDITOR`, or `vim`, in that order,
matching git, which I think is what most people will expect.
- Added a cute `tmptree!{}` macro, for creating temporary directories
populated with directories and files for tests.
- Admitted that grammer is LL(k) and I don't know what `k` is.
2019-11-09 21:43:20 -08:00
|
|
|
Platform::to_shell_path(context.working_directory, context.invocation_directory)
|
|
|
|
.map_err(|e| format!("Error getting shell path: {}", e))
|
2018-06-19 10:04:03 -07:00
|
|
|
}
|
|
|
|
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) 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-09-21 15:35:03 -07:00
|
|
|
pub(crate) 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> {
|
|
|
|
if let Some(value) = context.dotenv.get(key) {
|
|
|
|
return Ok(value.clone());
|
|
|
|
}
|
|
|
|
|
2017-12-02 14:59:07 -08:00
|
|
|
use std::env::VarError::*;
|
|
|
|
match env::var(key) {
|
|
|
|
Err(NotPresent) => Ok(default.to_string()),
|
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
|
|
|
}
|