From b2581308770d3bf13c7baa86f3a9cebbbdc989a2 Mon Sep 17 00:00:00 2001 From: elkowar Date: Thu, 5 Dec 2024 21:08:47 +0100 Subject: [PATCH] Ensure env based functions are available in canonical ctx --- src/script/eval_ctx.rs | 4 +- src/script/stdlib.rs | 150 +++++++++++++++++++++++------------------ 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/src/script/eval_ctx.rs b/src/script/eval_ctx.rs index 1f8b872..ff7a5bd 100644 --- a/src/script/eval_ctx.rs +++ b/src/script/eval_ctx.rs @@ -36,9 +36,7 @@ impl EvalCtx { pub fn new_in_mode(mode: EvalMode) -> Result { 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) } diff --git a/src/script/stdlib.rs b/src/script/stdlib.rs index 002c30f..2c4d7c8 100644 --- a/src/script/stdlib.rs +++ b/src/script/stdlib.rs @@ -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::("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 { @@ -85,76 +167,16 @@ fn create_regex(s: &str) -> Result { .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::("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::("test", "inspect({1, 2})")?