chimes_utils/utils/
script_engine.rs1use std::cell::RefCell;
2
3use std::{fs::write, sync::Mutex};
4
5use base64::prelude::*;
6use futures_util::future::LocalBoxFuture;
7use rhai::Scope;
8use serde_json::{Map, Value};
9use tera::{Context, Tera};
10
11use crate::{get_local_timestamp, ChimesError};
12
13fn base64_decode(base: &str, urlsafe: bool) -> Result<Vec<u8>, ChimesError> {
14 let newt = base.replace('\n', "");
15 let newt_str = newt.as_str();
16 if urlsafe {
18 match BASE64_URL_SAFE_NO_PAD.decode(newt_str) {
19 Ok(vs) => Ok(vs),
20 Err(err) => {
21 log::info!("Decode URL_SAFE_NO_PAD the base64 with error {}", err);
22 match BASE64_URL_SAFE.decode(newt_str) {
23 Ok(vs) => Ok(vs),
24 Err(err) => {
25 log::info!("Decode URL_SAFE the base64 with error {}", err);
26 Err(ChimesError::custom(100010, err.to_string()))
27 }
28 }
29 }
30 }
31 } else {
32 match BASE64_STANDARD_NO_PAD.decode(newt_str) {
33 Ok(vs) => Ok(vs),
34 Err(err) => {
35 log::info!("Decode STANDARD_NO_PAD the base64 with error {}", err);
36 match BASE64_STANDARD.decode(newt_str) {
37 Ok(vs) => Ok(vs),
38 Err(err) => {
39 log::info!("Decode STANDARD the base64 with error {}", err);
40 Err(ChimesError::custom(100010, err.to_string()))
41 }
42 }
43 }
44 }
45 }
46}
47
48fn base64_encode(content: Vec<u8>) -> String {
49 BASE64_STANDARD.encode(content)
50}
51
52pub fn write_file(path: &String, content: &Vec<u8>) {
53 match write(path, content) {
54 Ok(_) => {}
55 Err(err) => {
56 log::info!("Write file with an error {}", err);
57 }
58 }
59}
60
61pub fn translate_dict(_name: String, _value: String) -> String {
62 String::new()
63}
64
65pub trait DictTranslate {
66 fn translate(&self, name: &str, val: &str) -> String;
67 fn reload_all_dicts(&'static self) -> LocalBoxFuture<'static, Result<(), ChimesError>>;
68}
69
70pub struct Dummy {}
71
72impl DictTranslate for Dummy {
73 fn translate(&self, _name: &str, _val: &str) -> String {
74 String::new()
75 }
76
77 fn reload_all_dicts(&'static self) -> LocalBoxFuture<'static, Result<(), ChimesError>> {
78 Box::pin(async move { Ok(()) })
79 }
80}
81
82pub struct Translation {
83 h: Box<dyn DictTranslate>,
84}
85
86impl Translation {
87 pub fn new() -> Self {
88 Self {
89 h: Box::new(Dummy {}),
90 }
91 }
92
93 pub fn replace(&mut self, dt: impl DictTranslate + 'static) {
94 self.h = Box::new(dt);
95 }
96}
97
98impl Default for Translation {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104unsafe impl Send for Translation {}
105unsafe impl Sync for Translation {}
106
107lazy_static! {
108 pub static ref FUNCTION_TRANSLATE_DICT: Mutex<RefCell<Translation>> =
109 Mutex::new(RefCell::new(Translation::new()));
110}
111
112pub fn set_translate_dict_fn(func: impl DictTranslate + 'static) {
113 FUNCTION_TRANSLATE_DICT
114 .lock()
115 .unwrap()
116 .borrow_mut()
117 .replace(func);
118}
119
120pub fn get_translate_dict_fn() -> &'static Translation {
121 unsafe { &*FUNCTION_TRANSLATE_DICT.lock().unwrap().as_ptr() }
122}
123
124pub async fn reload_translate_dicts() {
125 match get_translate_dict_fn().h.reload_all_dicts().await {
127 Ok(_) => {
128 log::info!("Reload dicts successfully.");
129 }
130 Err(err) => {
131 log::info!("Failed: {} ", err);
132 }
133 }
134}
135
136pub fn script_eval(
137 script: &str,
138 ctx: &Map<String, Value>,
139) -> Result<std::string::String, ChimesError> {
140 let mut engine = rhai::Engine::new();
141 let mut scope = Scope::new();
142
143 engine.register_fn("timestamp", get_local_timestamp);
144
145 engine.register_fn("snowflake_id", move |prefix: &str| {
146 let new_id = rbatis::snowflake::new_snowflake_id();
147 format!("{}_{}", prefix, new_id)
148 });
149
150 engine.register_fn("base64_encode", move |content: Vec<u8>| {
151 base64_encode(content)
152 });
153
154 engine.register_fn(
155 "base64_decode_std",
156 move |content: String| match base64_decode(content.as_str(), false) {
157 Ok(ts) => ts,
158 Err(_) => vec![],
159 },
160 );
161
162 engine.register_fn(
163 "base64_decode_url",
164 move |content: String| match base64_decode(content.as_str(), true) {
165 Ok(ts) => ts,
166 Err(_) => vec![],
167 },
168 );
169
170 engine.register_fn("write_file", move |name: &str, content: Vec<u8>| {
171 write_file(&name.to_string(), &content);
172 });
173
174 engine.register_fn("translate_dict", move |name: &str, val: &str| {
175 get_translate_dict_fn().h.translate(name, val)
176 });
177
178 let json = Value::Object(ctx.clone()).to_string();
179
180 match engine.parse_json(json.as_str(), true) {
181 Ok(dynval) => {
182 scope.push("ctx", dynval);
183 }
184 Err(_) => {
185 log::info!("could not convert to rhai::Dynamic. ");
186 }
187 };
188
189 log::debug!("Script: {}", script);
190
191 let tt = match engine.eval_with_scope::<String>(&mut scope, script) {
192 Ok(t) => t,
193 Err(err) => {
194 log::info!("error on execute the script: {}", err.to_string());
195 return Err(ChimesError::custom(210, err.to_string()));
196 }
197 };
198 Ok(tt)
199}
200
201pub fn template_eval(script: &str, ctx: Value) -> Result<String, ChimesError> {
202 let mut tera = match Tera::new("templates/**/*.tera") {
203 Ok(t) => t,
204 Err(err) => {
205 log::info!(
206 "Could not found tera context {}, then use default Tera.",
207 err
208 );
209 Tera::default()
211 }
212 };
213
214 let context = match Context::from_serialize(&ctx) {
215 Ok(c) => c,
216 Err(_) => Context::new(),
217 };
218
219 match tera.render_str(script, &context) {
220 Ok(text) => Ok(text),
221 Err(err) => Err(ChimesError::custom(410, err.to_string())),
222 }
223}