devela/lang/ffi/js/
namespace.rs

1// devela::lang::ffi::js::namespace
2//
3//! Defines the [`Js`] namespace.
4//
5
6use devela::{JsConsole, Str, js_int32, js_uint32};
7#[cfg(all(feature = "alloc", unsafe··))]
8use devela::{String, Vec};
9
10#[doc = crate::TAG_NAMESPACE!()]
11/// Javascript-related operations.
12///
13/// See also: [`Web`][crate::Web].
14#[derive(Debug)]
15pub struct Js;
16
17#[rustfmt::skip]
18impl Js {
19    /// Returns the [`JsConsole`] namespace struct.
20    pub fn console() -> JsConsole { JsConsole }
21}
22
23/// Strings
24impl Js {
25    /// Reads a JS string into a Rust `&str` backed by the given `buffer`,
26    /// truncating if the buffer is too small.
27    ///
28    /// # Panic
29    /// Panics if the result is not valid UTF-8.
30    #[inline(always)]
31    pub fn read_str(
32        buffer: &mut [u8],
33        mut write_fn: impl FnMut(*mut u8, js_uint32) -> js_int32,
34    ) -> &str {
35        let ptr = buffer.as_mut_ptr();
36        let cap = buffer.len() as js_uint32;
37        let len = write_fn(ptr, cap) as usize;
38        Str::from_utf8(&buffer[..len]).expect("Valid UTF-8") // IMPROVE
39    }
40
41    /// Allocates a `String` by calling a JS-backed FFI fn that writes a UTF-8 string into WASM memory.
42    /// Allocates a new `String` from JS (auto-sizing).
43    ///
44    /// - Uses a dynamic buffer, starting with 128 bytes of `capacity`.
45    /// - Retries with exact required capacity if truncation is detected.
46    #[inline(always)]
47    #[cfg(not(feature = "safe_lang"))]
48    #[cfg(all(feature = "alloc", unsafe··))]
49    #[cfg_attr(nightly_doc, doc(cfg(all(feature = "alloc", unsafe··))))]
50    pub fn read_string(write_fn: impl FnMut(*mut u8, js_uint32) -> js_int32) -> String {
51        Js::read_string_capped(128, false, write_fn)
52    }
53
54    /// Allocates a `String` by calling a JS-backed FFI fn that writes a UTF-8 string into WASM memory.
55    ///
56    /// - Uses a dynamic buffer, starting with the given `capacity`.
57    /// - Retries with exact required capacity if truncation is detected (unless `truncate = true`).
58    /// - Assumes the FFI fn returns `js_int32`: positive = bytes written, negative = required size.
59    #[cfg(not(feature = "safe_lang"))]
60    #[cfg(all(feature = "alloc", unsafe··))]
61    #[cfg_attr(nightly_doc, doc(cfg(all(feature = "alloc", unsafe··))))]
62    pub fn read_string_capped(
63        mut cap: js_uint32,
64        truncate: bool,
65        mut write_fn: impl FnMut(*mut u8, js_uint32) -> js_int32,
66    ) -> String {
67        loop {
68            let mut vec = Vec::with_capacity(cap as usize);
69            let ptr = vec.as_mut_ptr();
70            let result = write_fn(ptr, cap);
71            if !truncate && result < 0 {
72                cap = (-result) as js_uint32;
73                continue;
74            }
75            unsafe {
76                vec.set_len(result as usize);
77                return String::from_utf8_unchecked(vec);
78            }
79        }
80    }
81}