use crate::context::ResourceContext;
use crate::ui::core::MultiResourceComponent;
use ratatui::{
text::Line,
widgets::{Block, Borders, Paragraph},
};
pub trait StatisticsProvider: Send + Sync + 'static {
fn statistics_lines(&self) -> Vec<Line<'static>>;
}
pub struct StatisticsComponent<T: StatisticsProvider> {
title: String,
_phantom: std::marker::PhantomData<T>,
}
impl<T: StatisticsProvider> StatisticsComponent<T> {
pub fn new() -> Self {
Self::with_title("Statistics")
}
pub fn with_title(title: impl Into<String>) -> Self {
Self {
title: title.into(),
_phantom: std::marker::PhantomData,
}
}
}
impl<T: StatisticsProvider> Default for StatisticsComponent<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: StatisticsProvider> MultiResourceComponent for StatisticsComponent<T> {
type Output = Paragraph<'static>;
fn render_multi(&self, resources: &ResourceContext) -> Option<Self::Output> {
let provider = resources.try_get::<T>()?;
let lines = provider.statistics_lines();
Some(
Paragraph::new(lines).block(
Block::default()
.borders(Borders::ALL)
.title(self.title.clone()),
),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::ResourceContext;
use ratatui::text::Line;
#[derive(Debug, Clone)]
struct TestStats {
value: u32,
}
impl StatisticsProvider for TestStats {
fn statistics_lines(&self) -> Vec<Line<'static>> {
vec![
Line::from(format!("Value: {}", self.value)),
Line::from("Test Stats"),
]
}
}
#[test]
fn test_statistics_component() {
let mut resources = ResourceContext::new();
resources.insert(TestStats { value: 42 });
let component = StatisticsComponent::<TestStats>::new();
let widget = component.render_multi(&resources);
assert!(widget.is_some());
}
#[test]
fn test_statistics_component_missing_resource() {
let resources = ResourceContext::new();
let component = StatisticsComponent::<TestStats>::new();
let widget = component.render_multi(&resources);
assert!(widget.is_none());
}
}