Skip to content

Commit

Permalink
Ensure env based functions are available in canonical ctx
Browse files Browse the repository at this point in the history
  • Loading branch information
elkowar committed Dec 5, 2024
1 parent fcb1deb commit b258130
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 67 deletions.
4 changes: 1 addition & 3 deletions src/script/eval_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ impl EvalCtx {
pub fn new_in_mode(mode: EvalMode) -> Result<Self> {
let ctx = Self::new_empty();
stdlib::setup_tag_functions(&ctx)?;
if mode == EvalMode::Local {
stdlib::setup_impure_functions(&ctx)?;
}
stdlib::setup_stdlib(mode, &ctx)?;
Ok(ctx)
}

Expand Down
150 changes: 86 additions & 64 deletions src/script/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,93 @@ use miette::Context as _;
use mlua::Value;
use regex::Regex;

use crate::yolk::EvalMode;

use super::eval_ctx::{EvalCtx, YOLK_TEXT_NAME};

pub fn setup_tag_functions(eval_ctx: &EvalCtx) -> miette::Result<()> {
setup_pure_functions(eval_ctx)?;
pub fn setup_stdlib(eval_mode: EvalMode, eval_ctx: &EvalCtx) -> Result<()> {
setup_environment_stuff(eval_mode, eval_ctx)?;
let globals = eval_ctx.lua().globals();

let inspect = include_str!("lua_lib/inspect.lua");
let inspect = eval_ctx
.lua()
.load(inspect)
.set_name("inspect.lua")
.into_function()
.into_diagnostic()?;
let inspect = eval_ctx
.lua()
.load_from_function::<Value>("inspect", inspect)
.into_diagnostic()?;
globals.set("inspect", inspect).into_diagnostic()?;

eval_ctx.register_fn(
"regex_match",
|_lua, (pattern, haystack): (String, String)| {
Ok(create_regex(&pattern)?.is_match(&haystack))
},
)?;

eval_ctx.register_fn(
"regex_replace",
|_lua, (pattern, haystack, replacement): (String, String, String)| {
Ok(create_regex(&pattern)?
.replace_all(&haystack, &replacement)
.to_string())
},
)?;
Ok(())
}

macro_rules! if_canonical_return {
($eval_mode:expr) => {
if $eval_mode == EvalMode::Canonical {
return Ok(Default::default());
}
};
($eval_mode:expr, $value:expr) => {
if $eval_mode == EvalMode::Canonical {
return Ok($value);
}
};
}

pub fn setup_environment_stuff(eval_mode: EvalMode, eval_ctx: &EvalCtx) -> Result<()> {
eval_ctx.register_fn("command_available", move |_, name: String| {
if_canonical_return!(eval_mode);
Ok(match which::which_all_global(name) {
Ok(mut iter) => iter.next().is_some(),
Err(err) => {
tracing::warn!("Error checking if command is available: {}", err);
false
}
})
})?;
eval_ctx.register_fn("env", move |_, (name, default): (String, String)| {
if_canonical_return!(eval_mode);
Ok(std::env::var(name).unwrap_or(default))
})?;
eval_ctx.register_fn("path_exists", move |_, p: String| {
if_canonical_return!(eval_mode);
Ok(PathBuf::from(p).exists())
})?;
eval_ctx.register_fn("path_is_dir", move |_, p: String| {
if_canonical_return!(eval_mode);
Ok(fs_err::metadata(p).map(|m| m.is_dir()).unwrap_or(false))
})?;
eval_ctx.register_fn("path_is_file", move |_, p: String| {
if_canonical_return!(eval_mode);
Ok(fs_err::metadata(p).map(|m| m.is_file()).unwrap_or(false))
})?;
eval_ctx.register_fn("read_file", move |_, p: String| {
if_canonical_return!(eval_mode);
Ok(fs_err::read_to_string(p).unwrap_or_default())
})?;
Ok(())
}

pub fn setup_tag_functions(eval_ctx: &EvalCtx) -> miette::Result<()> {
/// Simple regex replacement that will refuse to run a non-reversible replacement.
/// If the replacement value is non-reversible, will return the original text and log a warning.
fn tag_text_replace(text: &str, pattern: &str, replacement: &str) -> Result<String> {
Expand Down Expand Up @@ -85,76 +167,16 @@ fn create_regex(s: &str) -> Result<Regex> {
.wrap_err_with(|| format!("Invalid regex: {s}"))
}

pub fn setup_pure_functions(eval_ctx: &EvalCtx) -> Result<()> {
let globals = eval_ctx.lua().globals();

let inspect = include_str!("lua_lib/inspect.lua");
let inspect = eval_ctx
.lua()
.load(inspect)
.set_name("inspect.lua")
.into_function()
.into_diagnostic()?;
let inspect = eval_ctx
.lua()
.load_from_function::<Value>("inspect", inspect)
.into_diagnostic()?;
globals.set("inspect", inspect).into_diagnostic()?;

eval_ctx.register_fn(
"regex_match",
|_lua, (pattern, haystack): (String, String)| {
Ok(create_regex(&pattern)?.is_match(&haystack))
},
)?;

eval_ctx.register_fn(
"regex_replace",
|_lua, (pattern, haystack, replacement): (String, String, String)| {
Ok(create_regex(&pattern)?
.replace_all(&haystack, &replacement)
.to_string())
},
)?;
Ok(())
}

pub fn setup_impure_functions(eval_ctx: &EvalCtx) -> Result<()> {
eval_ctx.register_fn("command_available", |_, name: String| {
Ok(match which::which_all_global(name) {
Ok(mut iter) => iter.next().is_some(),
Err(err) => {
tracing::warn!("Error checking if command is available: {}", err);
false
}
})
})?;
eval_ctx.register_fn("env", |_, (name, default): (String, String)| {
Ok(std::env::var(name).unwrap_or(default))
})?;
eval_ctx.register_fn("path_exists", |_, p: String| Ok(PathBuf::from(p).exists()))?;
eval_ctx.register_fn("path_is_dir", |_, p: String| {
Ok(fs_err::metadata(p).map(|m| m.is_dir()).unwrap_or(false))
})?;
eval_ctx.register_fn("path_is_file", |_, p: String| {
Ok(fs_err::metadata(p).map(|m| m.is_file()).unwrap_or(false))
})?;
eval_ctx.register_fn("read_file", |_, p: String| {
Ok(fs_err::read_to_string(p).unwrap_or_default())
})?;
Ok(())
}

#[cfg(test)]
mod test {
use testresult::TestResult;

use crate::script::eval_ctx::EvalCtx;
use crate::{script::eval_ctx::EvalCtx, yolk::EvalMode};

#[test]
pub fn test_inspect() -> TestResult {
let eval_ctx = EvalCtx::new_empty();
super::setup_pure_functions(&eval_ctx)?;
super::setup_stdlib(EvalMode::Local, &eval_ctx)?;
assert_eq!(
"{ 1, 2 }",
eval_ctx.eval_lua::<String>("test", "inspect({1, 2})")?
Expand Down

0 comments on commit b258130

Please sign in to comment.