use std::cell::RefCell;
use std::rc::Rc;
use serde_json::Value;
use super::js_handle::JSHandle;
#[derive(Debug, Clone, Default)]
pub struct ConsoleLocation {
pub url: String,
pub line_number: u32,
pub column_number: u32,
}
pub struct ConsoleMessage {
console_type: String,
text: RefCell<String>,
args: RefCell<Vec<Rc<JSHandle>>>,
location: RefCell<Option<ConsoleLocation>>,
execution_context_id: RefCell<Option<String>>,
serialized_args: RefCell<Vec<Value>>,
}
impl std::fmt::Debug for ConsoleMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConsoleMessage")
.field("console_type", &self.console_type)
.field("text", &self.text.borrow())
.field("arg_count", &self.args.borrow().len())
.finish()
}
}
impl ConsoleMessage {
pub fn new(console_type: impl Into<String>, text: impl Into<String>) -> Self {
Self {
console_type: console_type.into(),
text: RefCell::new(text.into()),
args: RefCell::new(Vec::new()),
location: RefCell::new(None),
execution_context_id: RefCell::new(None),
serialized_args: RefCell::new(Vec::new()),
}
}
pub fn console_type(&self) -> String {
self.console_type.clone()
}
pub fn type_str(&self) -> &str {
&self.console_type
}
pub fn text(&self) -> String {
self.text.borrow().clone()
}
pub fn set_text(&self, t: impl Into<String>) {
*self.text.borrow_mut() = t.into();
}
pub fn args(&self) -> Vec<Rc<JSHandle>> {
self.args.borrow().clone()
}
pub fn add_arg(&self, h: Rc<JSHandle>) {
self.args.borrow_mut().push(h);
}
pub fn arg_count(&self) -> usize {
self.args.borrow().len()
}
pub fn location(&self) -> Option<ConsoleLocation> {
self.location.borrow().clone()
}
pub fn set_location(&self, loc: ConsoleLocation) {
*self.location.borrow_mut() = Some(loc);
}
pub fn execution_context_id(&self) -> Option<String> {
self.execution_context_id.borrow().clone()
}
pub fn set_execution_context_id(&self, id: impl Into<String>) {
*self.execution_context_id.borrow_mut() = Some(id.into());
}
pub fn serialized_args(&self) -> Vec<Value> {
self.serialized_args.borrow().clone()
}
pub fn add_serialized_arg(&self, v: Value) {
self.serialized_args.borrow_mut().push(v);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::frame::ExecutionContext;
fn make_msg() -> ConsoleMessage {
ConsoleMessage::new("log", "hello world")
}
#[test]
fn type_and_text() {
let m = make_msg();
assert_eq!(m.console_type(), "log");
assert_eq!(m.type_str(), "log");
assert_eq!(m.text(), "hello world");
}
#[test]
fn set_text() {
let m = make_msg();
m.set_text("updated");
assert_eq!(m.text(), "updated");
}
#[test]
fn args_start_empty() {
let m = make_msg();
assert_eq!(m.arg_count(), 0);
assert!(m.args().is_empty());
}
#[test]
fn add_arg() {
let m = make_msg();
let ctx = Rc::new(ExecutionContext::new("CTX-1".into()));
let h = Rc::new(JSHandle::new(ctx, "OBJ-1"));
m.add_arg(h);
assert_eq!(m.arg_count(), 1);
assert_eq!(m.args()[0].remote_object_id(), "OBJ-1");
}
#[test]
fn location_round_trip() {
let m = make_msg();
assert!(m.location().is_none());
m.set_location(ConsoleLocation {
url: "https://example.com".into(),
line_number: 42,
column_number: 7,
});
let loc = m.location().unwrap();
assert_eq!(loc.url, "https://example.com");
assert_eq!(loc.line_number, 42);
assert_eq!(loc.column_number, 7);
}
#[test]
fn execution_context_id_round_trip() {
let m = make_msg();
assert!(m.execution_context_id().is_none());
m.set_execution_context_id("CTX-9");
assert_eq!(m.execution_context_id(), Some("CTX-9".into()));
}
#[test]
fn serialized_args_round_trip() {
let m = make_msg();
assert!(m.serialized_args().is_empty());
m.add_serialized_arg(Value::from(1));
m.add_serialized_arg(Value::from("x"));
assert_eq!(m.serialized_args().len(), 2);
}
}