Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hooks: ability to call scripts #831

Merged
merged 2 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/src/commands/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ pub fn execute(matches: &ArgMatches) -> Result<Report, Error> {
let name = matches
.get_one::<String>("name")
.expect("name to be set as required");
Hooks::run_file(&ctx, name)
Hooks::run_file(&ctx, name).map(|(report, _)| report)
}
37 changes: 35 additions & 2 deletions bin/src/modules/hook/libraries/hemtt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use hemtt_common::version::Version;
use rhai::plugin::{
export_module, Dynamic, FnNamespace, FuncRegistration, Module, NativeCallContext, PluginFunc,
RhaiResult, TypeId,
export_module, mem, Dynamic, FnNamespace, FuncRegistration, ImmutableString, Module,
NativeCallContext, PluginFunc, RhaiResult, TypeId,
};

use crate::context::Context;
Expand All @@ -11,6 +11,7 @@ use super::project::RhaiProject;
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone)]
pub struct RhaiHemtt {
ctx: Context,
version: Version,
project: RhaiProject,
folder: String,
Expand All @@ -19,6 +20,7 @@ pub struct RhaiHemtt {
impl RhaiHemtt {
pub fn new(ctx: &Context) -> Self {
Self {
ctx: ctx.clone(),
version: Version::try_from(env!("HEMTT_VERSION"))
.expect("hemtt version should be valid"),
project: RhaiProject::new(ctx),
Expand All @@ -31,6 +33,14 @@ impl RhaiHemtt {
#[allow(clippy::unwrap_used)] // coming from rhai codegen
#[export_module]
pub mod project_functions {
use rhai::EvalAltResult;

use crate::{
modules::{hook::error::bhe1_script_not_found::ScriptNotFound, Hooks},
report::Report,
Error,
};

#[rhai_fn(global, pure)]
pub fn version(hemtt: &mut RhaiHemtt) -> Version {
hemtt.version.clone()
Expand Down Expand Up @@ -65,4 +75,27 @@ pub mod project_functions {
pub fn is_release(hemtt: &mut RhaiHemtt) -> bool {
hemtt.folder == "release"
}

#[rhai_fn(global, pure, return_raw)]
pub fn script(hemtt: &mut RhaiHemtt, name: &str) -> Result<Dynamic, Box<EvalAltResult>> {
fn inner_script(hemtt: &mut RhaiHemtt, name: &str) -> Result<(Report, Dynamic), Error> {
let scripts = hemtt.ctx.workspace_path().join(".hemtt")?.join("scripts")?;
let path = scripts.join(name)?.with_extension("rhai")?;
trace!("running script: {}", path.as_str());
if !path.exists()? {
return Ok((
{
let mut report = Report::new();
report.push(ScriptNotFound::code(name.to_owned(), &scripts)?);
report
},
Dynamic::UNIT,
));
}
Hooks::run(&hemtt.ctx, path, false)
}
inner_script(hemtt, name)
.map_err(|e| e.to_string().into())
.map(|(_, d)| d)
}
}
27 changes: 16 additions & 11 deletions bin/src/modules/hook/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::sync::{Arc, Mutex};

use ::rhai::{packages::Package, Engine, Scope};
use hemtt_workspace::WorkspacePath;
use rhai::Dynamic;

use crate::{context::Context, error::Error, report::Report};

Expand Down Expand Up @@ -102,7 +103,7 @@ impl Hooks {
"Running hook: {}",
file.as_str().trim_start_matches("/.hemtt/hooks/")
);
report.merge(Self::run(ctx, file, vfs)?);
report.merge(Self::run(ctx, file, vfs)?.0);
ctx.config().version().invalidate();
}
Ok(report)
Expand All @@ -117,22 +118,22 @@ impl Hooks {
///
/// # Panics
/// If a file path is not a valid [`OsStr`] (UTF-8)
pub fn run_file(ctx: &Context, name: &str) -> Result<Report, Error> {
pub fn run_file(ctx: &Context, name: &str) -> Result<(Report, Dynamic), Error> {
let mut report = Report::new();
let scripts = ctx.workspace_path().join(".hemtt")?.join("scripts")?;
let path = scripts.join(name)?.with_extension("rhai")?;
trace!("running script: {}", path.as_str());
if !path.exists()? {
report.push(ScriptNotFound::code(name.to_owned(), &scripts)?);
return Ok(report);
return Ok((report, Dynamic::UNIT));
}
let res = Self::run(ctx, path, false);
ctx.config().version().invalidate();
res
}

#[allow(clippy::needless_pass_by_value)] // rhai things
fn run(ctx: &Context, path: WorkspacePath, vfs: bool) -> Result<Report, Error> {
fn run(ctx: &Context, path: WorkspacePath, vfs: bool) -> Result<(Report, Dynamic), Error> {
let mut report = Report::new();
let mut engine = engine(vfs);
let mut scope = scope(ctx, vfs)?;
Expand Down Expand Up @@ -171,14 +172,18 @@ impl Hooks {
.lock()
.expect("told_to_fail mutex poisoned") = true;
});
if let Err(e) = engine.run_with_scope(&mut scope, &path.read_to_string()?) {
report.push(RuntimeError::code(path, &e));
return Ok(report);
}
if *told_to_fail.lock().expect("told_to_fail mutex poisoned") {
report.push(ScriptFatal::code(name));
match engine.eval_with_scope(&mut scope, &path.read_to_string()?) {
Err(e) => {
report.push(RuntimeError::code(path, &e));
Ok((report, Dynamic::UNIT))
}
Ok(ret) => {
if *told_to_fail.lock().expect("told_to_fail mutex poisoned") {
report.push(ScriptFatal::code(name));
}
Ok((report, ret))
}
}
Ok(report)
}
}

Expand Down
4 changes: 4 additions & 0 deletions bin/tests/bravo/.hemtt/hooks/post_release/02_test_rhs.rhai
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ if !include.is_file() {
if include.is_dir() {
fatal("{} is a directory", include);
}

if HEMTT.script("test") != 42 {
fatal("HEMTT.script(\"test\") != 42");
}
1 change: 1 addition & 0 deletions bin/tests/bravo/.hemtt/scripts/test.rhai
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
print("Just a test");
print(date("[year]-[month]-[day] [hour]:[minute]:[second]"));
print(date("[year repr:last_two][month][day]"));
42
22 changes: 22 additions & 0 deletions book/rhai/scripts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,25 @@ The files are located in the `.hemtt/scripts` folder, and are written in [Rhai](
They have access to all the same [libraries](../library/index.md) as [hooks](../hooks.md), but only use the [real file system](../library/filesystem.md#hemtt_rfs---real-file-system), since they run outside of the build process.

Scripts are useful for automating tasks that are not part of the build process, such as creating a new addon, or updating the version number.

## Calling from Hooks

Scripts can be called from other scripts or from hooks using `HEMTT.script(<script>)`. The script will still only have access to the real file system.

The script can return a value that will be passed back to the hook.

**.hemtt/scripts/value.rhai**

```js
1 + 1 * 2
```

**.hemtt/hooks/post_release/print_value.rhai**

```js
let value = HEMTT.script("value");
if value != 3 {
fatal("Value is not 3");
}
println(value)
```
2 changes: 1 addition & 1 deletion libs/workspace/src/reporting/diagnostic/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl Label {
.chars()
.next()
.map_or(true, |c| c.is_lowercase() || !c.is_alphabetic()),
"All label messages should be lowercase (except for text copied from the source)"
"All label messages should be lowercase (except for text copied from the source), got: {message:?}",
);
label = label.with_message(message.clone());
}
Expand Down
Loading