use std::any;
use std::cell::RefCell;
use std::fmt;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::widgets::Widget;
use taria::Node;
use crate::FrameRecorder;
pub fn sem<'rec, W: Widget>(
rec: &'rec FrameRecorder<'_>,
widget: W,
node: Node,
) -> Semantic<'rec, W> {
Semantic {
widget,
node,
sink: rec.sink(),
}
}
pub struct Semantic<'a, W> {
widget: W,
node: Node,
sink: &'a RefCell<Vec<Node>>,
}
impl<W> fmt::Debug for Semantic<'_, W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Semantic")
.field("widget", &format_args!("{}", any::type_name::<W>()))
.field("id", &self.node.id.0)
.field("role", &self.node.role)
.finish_non_exhaustive()
}
}
impl<W: Widget> Widget for Semantic<'_, W> {
fn render(self, area: Rect, buf: &mut Buffer) {
if let Ok(mut nodes) = self.sink.try_borrow_mut() {
nodes.push(self.node);
}
self.widget.render(area, buf);
}
}
#[cfg(test)]
mod tests {
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::widgets::Widget;
use taria::{Node, Role};
use super::sem;
use crate::test_util::{TestLayer, bind_test_layer};
fn test_layer(label: &str) -> TestLayer {
bind_test_layer("taria-sem-", label)
}
#[test]
fn renders_inner_widget_and_records_node() {
let mut layer = test_layer("semantic");
let rec = layer.frame();
let area = Rect::new(0, 0, 5, 1);
let mut buf = Buffer::empty(area);
sem(&rec, "hello", Node::new("greeting", Role::Text).label("hi")).render(area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["hello"]));
rec.publish();
let root = layer.latest_snapshot().unwrap().root;
assert_eq!(root.children.len(), 1);
assert_eq!(root.children[0].id.0, "greeting");
assert_eq!(root.children[0].label.as_deref(), Some("hi"));
}
#[test]
fn sem_and_push_interleave_in_call_order() {
let mut layer = test_layer("interleave");
let mut rec = layer.frame();
let area = Rect::new(0, 0, 3, 1);
let mut buf = Buffer::empty(area);
rec.push(Node::new("first", Role::Text));
sem(&rec, "x", Node::new("second", Role::Text)).render(area, &mut buf);
rec.push(Node::new("third", Role::Text));
rec.publish();
let root = layer.latest_snapshot().unwrap().root;
let ids: Vec<&str> = root.children.iter().map(|c| c.id.0.as_str()).collect();
assert_eq!(ids, ["first", "second", "third"]);
}
#[test]
fn layer_recorder_and_sem_print_without_their_trees() {
let mut layer = test_layer("debug");
let path = layer.socket_path().to_path_buf();
let printed = format!("{:?}", *layer);
assert_eq!(
printed,
format!(
"TariaLayer {{ app_label: \"debug\", socket_path: {path:?}, enabled: true, \
bind_error: None, seq: Some(0), .. }}"
)
);
let mut rec = layer.frame();
rec.push(Node::new("first", Role::Text).label("not printed"));
let wrapped = sem(
&rec,
"x",
Node::new("second", Role::Button).label("not printed"),
);
assert_eq!(
format!("{wrapped:?}"),
r#"Semantic { widget: &str, id: "second", role: Button, .. }"#
);
let printed = format!("{rec:?}");
assert!(
printed.starts_with("FrameRecorder { layer: TariaLayer {"),
"{printed}"
);
assert!(printed.ends_with("}, recorded: 1, .. }"), "{printed}");
assert!(!printed.contains("not printed"), "{printed}");
let area = Rect::new(0, 0, 1, 1);
wrapped.render(area, &mut Buffer::empty(area));
assert!(format!("{rec:?}").ends_with("}, recorded: 2, .. }"));
let held = rec.sink().borrow_mut();
assert!(format!("{rec:?}").ends_with("}, recorded: <being recorded>, .. }"));
drop(held);
rec.publish();
assert!(format!("{:?}", *layer).contains("seq: Some(1)"));
}
}