use crate::context::ResourceContext;
use crate::ui::core::Component;
use ratatui::widgets::{Block, Borders, Paragraph};
pub trait HeaderContext: Send + Sync + 'static {
fn turn(&self) -> u32;
fn max_turns(&self) -> u32;
fn mode(&self) -> String;
}
pub struct HeaderComponent<T: HeaderContext> {
title: String,
_phantom: std::marker::PhantomData<T>,
}
impl<T: HeaderContext> HeaderComponent<T> {
pub fn new() -> Self {
Self::with_title("Status")
}
pub fn with_title(title: impl Into<String>) -> Self {
Self {
title: title.into(),
_phantom: std::marker::PhantomData,
}
}
}
impl<T: HeaderContext> Default for HeaderComponent<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: HeaderContext> Component for HeaderComponent<T> {
type Context = T;
type Output = Paragraph<'static>;
fn render(&self, resources: &ResourceContext) -> Option<Self::Output> {
let ctx = resources.try_get::<T>()?;
let header_text = format!(
"Turn {}/{} | Mode: {}",
ctx.turn(),
ctx.max_turns(),
ctx.mode()
);
Some(
Paragraph::new(header_text).block(
Block::default()
.borders(Borders::ALL)
.title(self.title.clone()),
),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::ResourceContext;
#[derive(Debug, Clone)]
struct TestContext {
turn: u32,
max_turns: u32,
mode: String,
}
impl HeaderContext for TestContext {
fn turn(&self) -> u32 {
self.turn
}
fn max_turns(&self) -> u32 {
self.max_turns
}
fn mode(&self) -> String {
self.mode.clone()
}
}
#[test]
fn test_header_component() {
let mut resources = ResourceContext::new();
resources.insert(TestContext {
turn: 5,
max_turns: 20,
mode: "Test Mode".to_string(),
});
let component = HeaderComponent::<TestContext>::new();
let widget = component.render(&resources);
assert!(widget.is_some());
}
#[test]
fn test_header_component_missing_resource() {
let resources = ResourceContext::new();
let component = HeaderComponent::<TestContext>::new();
let widget = component.render(&resources);
assert!(widget.is_none());
}
#[test]
fn test_header_component_custom_title() {
let mut resources = ResourceContext::new();
resources.insert(TestContext {
turn: 1,
max_turns: 10,
mode: "Custom".to_string(),
});
let component = HeaderComponent::<TestContext>::with_title("Custom Title");
let widget = component.render(&resources);
assert!(widget.is_some());
}
}