rquickjs_extra_console/
lib.rs

1use std::fmt::Write;
2
3use rquickjs::{class::Trace, function::Rest, Ctx, Error, JsLifetime, Result, Value};
4
5pub use self::formatter::Formatter;
6
7mod formatter;
8
9const TARGET: &str = "console";
10
11/// A console object to print messages to the [`log`] crate.
12///
13/// # Example
14/// ```rust
15/// use rquickjs::{Context, Runtime};
16/// use rquickjs_extra_console::{Console, Formatter};
17///
18/// fn main() {
19///     let rt = Runtime::new().unwrap();
20///     let ctx = Context::full(&rt).unwrap();
21///
22///     ctx.with(|ctx| {
23///         let console = Console::new("hello", Formatter::default());
24///         ctx.globals().set("console", console).unwrap();
25///         ctx.eval::<(), _>("console.log('test')").unwrap();
26///      })
27/// }
28/// ```
29///
30/// [`log`]: https://docs.rs/log
31#[derive(Clone, Trace, JsLifetime)]
32#[rquickjs::class(frozen)]
33pub struct Console {
34    target: String,
35    formatter: Formatter,
36}
37
38impl Console {
39    pub fn new(target: &str, formatter: Formatter) -> Self {
40        Self {
41            target: target.to_string(),
42            formatter,
43        }
44    }
45
46    fn print(&self, level: log::Level, values: Rest<Value<'_>>) -> Result<()> {
47        let mut message = String::new();
48        for (i, value) in values.0.into_iter().enumerate() {
49            if i > 0 {
50                write!(&mut message, ", ").map_err(|_| Error::Unknown)?
51            }
52            self.formatter.format(&mut message, value)?
53        }
54        log::log!(target: &self.target, level, "{message}");
55        Ok(())
56    }
57}
58
59#[rquickjs::methods]
60impl Console {
61    fn debug(&self, values: Rest<Value<'_>>) -> Result<()> {
62        self.print(log::Level::Debug, values)
63    }
64
65    fn log(&self, values: Rest<Value<'_>>) -> Result<()> {
66        self.print(log::Level::Info, values)
67    }
68
69    fn warn(&self, values: Rest<Value<'_>>) -> Result<()> {
70        self.print(log::Level::Warn, values)
71    }
72
73    fn error(&self, values: Rest<Value<'_>>) -> Result<()> {
74        self.print(log::Level::Error, values)
75    }
76}
77
78pub fn init(ctx: &Ctx<'_>) -> Result<()> {
79    let globals = ctx.globals();
80
81    globals.set(
82        "console",
83        Console::new(TARGET, Formatter::builder().max_depth(3).build()),
84    )?;
85
86    Ok(())
87}
88
89#[cfg(test)]
90mod tests {
91    use rquickjs_extra_test::test_with;
92
93    use super::*;
94
95    #[test]
96    fn test_console() {
97        test_with(|ctx| {
98            let console = Console::new("hello", Formatter::default());
99            ctx.globals().set("console", console).unwrap();
100
101            let result = ctx.eval::<(), _>("console.log('test')");
102            assert!(result.is_ok());
103        })
104    }
105}