1use crate::def_package;
2use crate::module::ModuleFlags;
3use crate::plugin::*;
4use crate::types::dynamic::Tag;
5use crate::{Dynamic, RhaiResult, RhaiResultOf, ERR, INT};
6#[cfg(feature = "no_std")]
7use std::prelude::v1::*;
8
9#[cfg(not(feature = "no_float"))]
10#[cfg(not(feature = "no_std"))]
11use crate::FLOAT;
12
13def_package! {
14 pub LanguageCorePackage(lib) {
16 lib.flags |= ModuleFlags::STANDARD_LIB;
17
18 combine_with_exported_module!(lib, "core", core_functions);
19
20 #[cfg(not(feature = "no_function"))]
21 #[cfg(not(feature = "no_index"))]
22 #[cfg(not(feature = "no_object"))]
23 combine_with_exported_module!(lib, "reflection", reflection_functions);
24 }
25}
26
27#[export_module]
28mod core_functions {
29 #[rhai_fn(name = "exit", volatile, return_raw)]
36 pub fn exit_with_value(value: Dynamic) -> RhaiResult {
37 Err(ERR::Exit(value, Position::NONE).into())
38 }
39 #[rhai_fn(volatile, return_raw)]
46 pub fn exit() -> RhaiResult {
47 Err(ERR::Exit(Dynamic::UNIT, Position::NONE).into())
48 }
49 #[rhai_fn(return_raw)]
64 pub fn take(value: &mut Dynamic) -> RhaiResult {
65 if value.is_read_only() {
66 return Err(
67 ERR::ErrorNonPureMethodCallOnConstant("take".to_string(), Position::NONE).into(),
68 );
69 }
70
71 Ok(std::mem::take(value))
72 }
73 #[rhai_fn(name = "tag", get = "tag", pure)]
85 pub fn get_tag(value: &mut Dynamic) -> INT {
86 value.tag() as INT
87 }
88 #[rhai_fn(name = "set_tag", set = "tag", return_raw)]
100 pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> {
101 const TAG_MIN: Tag = Tag::MIN;
102 const TAG_MAX: Tag = Tag::MAX;
103
104 if tag < TAG_MIN as INT {
105 return Err(ERR::ErrorArithmetic(
106 format!(
107 "{tag} is too small to fit into a tag (must be between {TAG_MIN} and {TAG_MAX})"
108 ),
109 Position::NONE,
110 )
111 .into());
112 }
113 if tag > TAG_MAX as INT {
114 return Err(ERR::ErrorArithmetic(
115 format!(
116 "{tag} is too large to fit into a tag (must be between {TAG_MIN} and {TAG_MAX})"
117 ),
118 Position::NONE,
119 )
120 .into());
121 }
122
123 value.set_tag(tag as Tag);
124 Ok(())
125 }
126
127 #[cfg(not(feature = "no_float"))]
136 #[cfg(not(feature = "no_std"))]
137 #[rhai_fn(name = "sleep", volatile)]
138 pub fn sleep_float(seconds: FLOAT) {
139 if !seconds.is_normal() || seconds.is_sign_negative() {
140 return;
141 }
142
143 #[cfg(not(feature = "f32_float"))]
144 std::thread::sleep(std::time::Duration::from_secs_f64(seconds));
145 #[cfg(feature = "f32_float")]
146 std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
147 }
148 #[cfg(not(feature = "no_std"))]
157 #[rhai_fn(volatile)]
158 pub fn sleep(seconds: INT) {
159 if seconds <= 0 {
160 return;
161 }
162
163 #[allow(clippy::cast_sign_loss)]
164 std::thread::sleep(std::time::Duration::from_secs(seconds as u64));
165 }
166
167 #[cfg(not(feature = "no_index"))]
177 #[cfg(not(feature = "no_object"))]
178 #[cfg(feature = "metadata")]
179 #[rhai_fn(return_raw)]
180 pub fn parse_json(_ctx: NativeCallContext, json: &str) -> RhaiResultOf<Dynamic> {
181 serde_json::from_str(json).map_err(|err| err.to_string().into())
182 }
183}
184
185#[cfg(not(feature = "no_function"))]
186#[cfg(not(feature = "no_index"))]
187#[cfg(not(feature = "no_object"))]
188#[export_module]
189mod reflection_functions {
190 use crate::Array;
191
192 pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array {
194 collect_fn_metadata(&ctx, |_, _, _, _, _| true)
195 }
196 #[rhai_fn(name = "get_fn_metadata_list")]
199 pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> Array {
200 collect_fn_metadata(&ctx, |_, _, n, _, _| n == name)
201 }
202 #[rhai_fn(name = "get_fn_metadata_list")]
205 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
206 pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> Array {
207 if !(0..=crate::MAX_USIZE_INT).contains(¶ms) {
208 return Array::new();
209 }
210
211 collect_fn_metadata(&ctx, |_, _, n, p, _| p == (params as usize) && n == name)
212 }
213}
214
215#[cfg(not(feature = "no_function"))]
216#[cfg(not(feature = "no_index"))]
217#[cfg(not(feature = "no_object"))]
218fn collect_fn_metadata(
219 ctx: &NativeCallContext,
220 filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFuncDef>) -> bool
221 + Copy,
222) -> crate::Array {
223 #[cfg(not(feature = "no_module"))]
224 use crate::Identifier;
225 use crate::{ast::ScriptFuncDef, engine::FN_ANONYMOUS, Array, Map};
226
227 fn make_metadata(
229 engine: &Engine,
230 #[cfg(not(feature = "no_module"))] namespace: Identifier,
231 func: &ScriptFuncDef,
232 ) -> Map {
233 let mut map = Map::new();
234
235 #[cfg(not(feature = "no_module"))]
236 if !namespace.is_empty() {
237 map.insert(
238 "namespace".into(),
239 engine.get_interned_string(namespace).into(),
240 );
241 }
242 map.insert(
243 "name".into(),
244 engine.get_interned_string(func.name.clone()).into(),
245 );
246 map.insert(
247 "access".into(),
248 engine
249 .get_interned_string(match func.access {
250 FnAccess::Public => "public",
251 FnAccess::Private => "private",
252 })
253 .into(),
254 );
255 map.insert(
256 "is_anonymous".into(),
257 func.name.starts_with(FN_ANONYMOUS).into(),
258 );
259 #[cfg(not(feature = "no_object"))]
260 if let Some(ref this_type) = func.this_type {
261 map.insert("this_type".into(), this_type.into());
262 }
263 map.insert(
264 "params".into(),
265 func.params
266 .iter()
267 .map(|p| engine.get_interned_string(p.clone()).into())
268 .collect::<Array>()
269 .into(),
270 );
271 #[cfg(feature = "metadata")]
272 if !func.comments.is_empty() {
273 map.insert(
274 "comments".into(),
275 func.comments
276 .iter()
277 .map(|s| engine.get_interned_string(s).into())
278 .collect::<Array>()
279 .into(),
280 );
281 }
282
283 map
284 }
285
286 let engine = ctx.engine();
287 let mut list = Array::new();
288
289 ctx.iter_namespaces()
290 .flat_map(Module::iter_script_fn)
291 .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
292 .for_each(|(.., f)| {
293 list.push(
294 make_metadata(
295 engine,
296 #[cfg(not(feature = "no_module"))]
297 Identifier::new_const(),
298 f,
299 )
300 .into(),
301 );
302 });
303
304 ctx.engine()
305 .global_modules
306 .iter()
307 .flat_map(|m| m.iter_script_fn())
308 .filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
309 .for_each(|(.., f)| {
310 list.push(
311 make_metadata(
312 engine,
313 #[cfg(not(feature = "no_module"))]
314 Identifier::new_const(),
315 f,
316 )
317 .into(),
318 );
319 });
320
321 #[cfg(not(feature = "no_module"))]
322 ctx.engine()
323 .global_sub_modules
324 .values()
325 .flat_map(|m| m.iter_script_fn())
326 .filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
327 .for_each(|(.., f)| {
328 list.push(
329 make_metadata(
330 engine,
331 #[cfg(not(feature = "no_module"))]
332 Identifier::new_const(),
333 f,
334 )
335 .into(),
336 );
337 });
338
339 #[cfg(not(feature = "no_module"))]
340 {
341 use crate::engine::NAMESPACE_SEPARATOR;
342 use crate::{Shared, SmartString};
343
344 fn scan_module(
346 engine: &Engine,
347 list: &mut Array,
348 namespace: &str,
349 module: &Module,
350 filter: impl Fn(FnNamespace, FnAccess, &str, usize, &Shared<ScriptFuncDef>) -> bool + Copy,
351 ) {
352 module
353 .iter_script_fn()
354 .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
355 .for_each(|(.., f)| list.push(make_metadata(engine, namespace.into(), f).into()));
356 for (name, m) in module.iter_sub_modules() {
357 use std::fmt::Write;
358
359 let mut ns = SmartString::new_const();
360 write!(&mut ns, "{namespace}{NAMESPACE_SEPARATOR}{name}").unwrap();
361 scan_module(engine, list, &ns, m, filter);
362 }
363 }
364
365 for (ns, m) in ctx.global_runtime_state().iter_imports_raw() {
366 scan_module(engine, &mut list, ns, m, filter);
367 }
368 }
369
370 list
371}