Skip to main content

sal_text/
rhai.rs

1//! Rhai wrappers for Text module functions
2//!
3//! This module provides Rhai wrappers for the functions in the Text module.
4
5use crate::{TemplateBuilder, TextReplacer, TextReplacerBuilder};
6use rhai::{Array, Engine, EvalAltResult, Map, Position};
7use std::collections::HashMap;
8
9/// Register Text module functions with the Rhai engine
10///
11/// # Arguments
12///
13/// * `engine` - The Rhai engine to register the functions with
14///
15/// # Returns
16///
17/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
18pub fn register_text_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
19    // Register types
20    register_text_types(engine)?;
21
22    // Register TextReplacer constructor
23    engine.register_fn("text_replacer_new", text_replacer_new);
24    engine.register_fn("text_replacer_builder", text_replacer_new); // Alias for backward compatibility
25
26    // Register TextReplacerBuilder instance methods
27    engine.register_fn("pattern", pattern);
28    engine.register_fn("replacement", replacement);
29    engine.register_fn("regex", regex);
30    engine.register_fn("case_insensitive", case_insensitive);
31    engine.register_fn("and", and);
32    engine.register_fn("build", build);
33
34    // Register TextReplacer instance methods
35    engine.register_fn("replace", replace);
36    engine.register_fn("replace_file", replace_file);
37    engine.register_fn("replace_file_in_place", replace_file_in_place);
38    engine.register_fn("replace_file_to", replace_file_to);
39
40    // Register TemplateBuilder constructor
41    engine.register_fn("template_builder_open", template_builder_open);
42
43    // Register TemplateBuilder instance methods
44    engine.register_fn("add_var", add_var_string);
45    engine.register_fn("add_var", add_var_int);
46    engine.register_fn("add_var", add_var_float);
47    engine.register_fn("add_var", add_var_bool);
48    engine.register_fn("add_var", add_var_array);
49    engine.register_fn("add_vars", add_vars);
50    engine.register_fn("render", render);
51    engine.register_fn("render_to_file", render_to_file);
52
53    // Register Fix functions directly from text module
54    engine.register_fn("name_fix", crate::name_fix);
55    engine.register_fn("path_fix", crate::path_fix);
56
57    // Register Dedent functions directly from text module
58    engine.register_fn("dedent", crate::dedent);
59    engine.register_fn("prefix", crate::prefix);
60
61    Ok(())
62}
63
64/// Register Text module types with the Rhai engine
65fn register_text_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
66    // Register TextReplacerBuilder type
67    engine.register_type_with_name::<TextReplacerBuilder>("TextReplacerBuilder");
68
69    // Register TextReplacer type
70    engine.register_type_with_name::<TextReplacer>("TextReplacer");
71
72    // Register TemplateBuilder type
73    engine.register_type_with_name::<TemplateBuilder>("TemplateBuilder");
74
75    Ok(())
76}
77
78// Helper functions for error conversion
79fn io_error_to_rhai_error<T>(result: std::io::Result<T>) -> Result<T, Box<EvalAltResult>> {
80    result.map_err(|e| {
81        Box::new(EvalAltResult::ErrorRuntime(
82            format!("IO error: {}", e).into(),
83            Position::NONE,
84        ))
85    })
86}
87
88fn tera_error_to_rhai_error<T>(result: Result<T, tera::Error>) -> Result<T, Box<EvalAltResult>> {
89    result.map_err(|e| {
90        Box::new(EvalAltResult::ErrorRuntime(
91            format!("Template error: {}", e).into(),
92            Position::NONE,
93        ))
94    })
95}
96
97fn string_error_to_rhai_error<T>(result: Result<T, String>) -> Result<T, Box<EvalAltResult>> {
98    result.map_err(|e| Box::new(EvalAltResult::ErrorRuntime(e.into(), Position::NONE)))
99}
100
101// TextReplacer implementation
102
103/// Creates a new TextReplacerBuilder
104pub fn text_replacer_new() -> TextReplacerBuilder {
105    TextReplacerBuilder::default()
106}
107
108/// Sets the pattern to search for
109pub fn pattern(builder: TextReplacerBuilder, pat: &str) -> TextReplacerBuilder {
110    builder.pattern(pat)
111}
112
113/// Sets the replacement text
114pub fn replacement(builder: TextReplacerBuilder, rep: &str) -> TextReplacerBuilder {
115    builder.replacement(rep)
116}
117
118/// Sets whether to use regex
119pub fn regex(builder: TextReplacerBuilder, yes: bool) -> TextReplacerBuilder {
120    builder.regex(yes)
121}
122
123/// Sets whether the replacement should be case-insensitive
124pub fn case_insensitive(builder: TextReplacerBuilder, yes: bool) -> TextReplacerBuilder {
125    builder.case_insensitive(yes)
126}
127
128/// Adds another replacement operation to the chain and resets the builder for a new operation
129pub fn and(builder: TextReplacerBuilder) -> TextReplacerBuilder {
130    builder.and()
131}
132
133/// Builds the TextReplacer with all configured replacement operations
134pub fn build(builder: TextReplacerBuilder) -> Result<TextReplacer, Box<EvalAltResult>> {
135    string_error_to_rhai_error(builder.build())
136}
137
138/// Applies all configured replacement operations to the input text
139pub fn replace(replacer: &mut TextReplacer, input: &str) -> String {
140    replacer.replace(input)
141}
142
143/// Reads a file, applies all replacements, and returns the result as a string
144pub fn replace_file(replacer: &mut TextReplacer, path: &str) -> Result<String, Box<EvalAltResult>> {
145    io_error_to_rhai_error(replacer.replace_file(path))
146}
147
148/// Reads a file, applies all replacements, and writes the result back to the file
149pub fn replace_file_in_place(
150    replacer: &mut TextReplacer,
151    path: &str,
152) -> Result<(), Box<EvalAltResult>> {
153    io_error_to_rhai_error(replacer.replace_file_in_place(path))
154}
155
156/// Reads a file, applies all replacements, and writes the result to a new file
157pub fn replace_file_to(
158    replacer: &mut TextReplacer,
159    input_path: &str,
160    output_path: &str,
161) -> Result<(), Box<EvalAltResult>> {
162    io_error_to_rhai_error(replacer.replace_file_to(input_path, output_path))
163}
164
165// TemplateBuilder implementation
166
167/// Creates a new TemplateBuilder with the specified template path
168pub fn template_builder_open(template_path: &str) -> Result<TemplateBuilder, Box<EvalAltResult>> {
169    io_error_to_rhai_error(TemplateBuilder::open(template_path))
170}
171
172/// Adds a string variable to the template context
173pub fn add_var_string(builder: TemplateBuilder, name: &str, value: &str) -> TemplateBuilder {
174    builder.add_var(name, value)
175}
176
177/// Adds an integer variable to the template context
178pub fn add_var_int(builder: TemplateBuilder, name: &str, value: i64) -> TemplateBuilder {
179    builder.add_var(name, value)
180}
181
182/// Adds a float variable to the template context
183pub fn add_var_float(builder: TemplateBuilder, name: &str, value: f64) -> TemplateBuilder {
184    builder.add_var(name, value)
185}
186
187/// Adds a boolean variable to the template context
188pub fn add_var_bool(builder: TemplateBuilder, name: &str, value: bool) -> TemplateBuilder {
189    builder.add_var(name, value)
190}
191
192/// Adds an array variable to the template context
193pub fn add_var_array(builder: TemplateBuilder, name: &str, array: Array) -> TemplateBuilder {
194    // Convert Rhai Array to Vec<String>
195    let vec: Vec<String> = array
196        .iter()
197        .filter_map(|v| v.clone().into_string().ok())
198        .collect();
199
200    builder.add_var(name, vec)
201}
202
203/// Adds multiple variables to the template context from a Map
204pub fn add_vars(builder: TemplateBuilder, vars: Map) -> TemplateBuilder {
205    // Convert Rhai Map to Rust HashMap
206    let mut hash_map = HashMap::new();
207
208    for (key, value) in vars.iter() {
209        if let Ok(val_str) = value.clone().into_string() {
210            hash_map.insert(key.to_string(), val_str);
211        }
212    }
213
214    // Add the variables
215    builder.add_vars(hash_map)
216}
217
218/// Renders the template with the current context
219pub fn render(builder: &mut TemplateBuilder) -> Result<String, Box<EvalAltResult>> {
220    tera_error_to_rhai_error(builder.render())
221}
222
223/// Renders the template and writes the result to a file
224pub fn render_to_file(
225    builder: &mut TemplateBuilder,
226    output_path: &str,
227) -> Result<(), Box<EvalAltResult>> {
228    io_error_to_rhai_error(builder.render_to_file(output_path))
229}