use std::any::Any;
use crate::console::Renderable;
pub fn gilt_cast<T: Renderable + 'static>(value: Box<dyn Any>) -> Option<Box<T>> {
value.downcast::<T>().ok()
}
pub fn is_type<T: 'static>(value: &dyn Any) -> bool {
value.is::<T>()
}
pub trait GiltCast: Sized + 'static {
fn __gilt__(self) -> Box<dyn Renderable>;
}
pub trait IntoRenderable {
fn into_renderable(self) -> Box<dyn Renderable>;
}
impl<T: GiltCast> IntoRenderable for T {
fn into_renderable(self) -> Box<dyn Renderable> {
self.__gilt__()
}
}
pub trait RenderableExt: Renderable + Sized + 'static {
fn into_boxed_renderable(self) -> RenderableBox;
}
impl<T: Renderable + 'static> RenderableExt for T {
fn into_boxed_renderable(self) -> RenderableBox {
RenderableBox::new(self)
}
}
pub struct RenderableBox {
inner: Box<dyn Renderable>,
}
impl RenderableBox {
pub fn new<R: Renderable + 'static>(renderable: R) -> Self {
Self {
inner: Box::new(renderable),
}
}
pub fn as_renderable(&self) -> &dyn Renderable {
&*self.inner
}
pub fn into_inner(self) -> Box<dyn Renderable> {
self.inner
}
}
impl Renderable for RenderableBox {
fn gilt_console(
&self,
console: &crate::console::Console,
options: &crate::console::ConsoleOptions,
) -> Vec<crate::segment::Segment> {
self.inner.gilt_console(console, options)
}
}
pub fn as_renderable_ref<T: Renderable>(value: &T) -> &dyn Renderable {
value
}
pub fn as_renderable_mut<T: Renderable>(value: &mut T) -> &mut dyn Renderable {
value
}
#[macro_export]
macro_rules! derive_gilt_cast {
($item:item) => {
$item
};
}
#[macro_export]
macro_rules! gilt_cast_impl {
($type:ty => |$this:ident| $body:expr) => {
impl $crate::protocol::GiltCast for $type {
fn __gilt__(self) -> Box<dyn $crate::console::Renderable> {
let $this = self;
$body
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[test]
fn test_gilt_cast_success() {
let text = Text::from("Hello, World!");
let boxed: Box<dyn Any> = Box::new(text);
let cast_result = gilt_cast::<Text>(boxed);
assert!(cast_result.is_some());
}
#[test]
fn test_gilt_cast_failure() {
let text = Text::from("Hello");
let boxed: Box<dyn Any> = Box::new(text);
let cast_result = gilt_cast::<Panel>(boxed);
assert!(cast_result.is_none());
}
#[test]
fn test_gilt_cast_with_panel() {
let panel = Panel::new(Text::from("Content"));
let boxed: Box<dyn Any> = Box::new(panel);
let cast_result = gilt_cast::<Panel>(boxed);
assert!(cast_result.is_some());
}
struct TestData {
value: i32,
}
impl GiltCast for TestData {
fn __gilt__(self) -> Box<dyn Renderable> {
Box::new(Panel::new(Text::from(format!("Value: {}", self.value))))
}
}
#[test]
fn test_gilt_cast_trait() {
let data = TestData { value: 42 };
let renderable = data.into_renderable();
let mut console = crate::console::Console::builder().width(80).build();
console.begin_capture();
console.print(&*renderable);
let output = console.end_capture();
assert!(output.contains("Value: 42"));
}
#[test]
fn test_into_renderable_blanket_impl() {
struct SimpleData(&'static str);
impl GiltCast for SimpleData {
fn __gilt__(self) -> Box<dyn Renderable> {
Box::new(Text::from(self.0))
}
}
let data = SimpleData("Test");
let _renderable: Box<dyn Renderable> = data.into_renderable();
}
#[test]
fn test_renderable_box() {
let text = Text::from("Boxed text");
let boxed = RenderableBox::new(text);
let mut console = crate::console::Console::builder().width(80).build();
console.begin_capture();
console.print(&boxed);
let output = console.end_capture();
assert!(output.contains("Boxed text"));
}
#[test]
fn test_renderable_box_from_panel() {
let panel = Panel::new(Text::from("Panel content"));
let boxed = RenderableBox::new(panel);
let inner = boxed.into_inner();
let mut console = crate::console::Console::builder().width(80).build();
console.begin_capture();
console.print(&*inner);
let output = console.end_capture();
assert!(output.contains("Panel content"));
}
#[test]
fn test_renderable_ext() {
let text = Text::from("Extended");
let boxed = text.into_boxed_renderable();
let mut console = crate::console::Console::builder().width(80).build();
console.begin_capture();
console.print(&boxed);
let output = console.end_capture();
assert!(output.contains("Extended"));
}
#[test]
fn test_is_type() {
let text: Box<dyn Any> = Box::new(Text::from("Test"));
assert!(is_type::<Text>(&*text));
assert!(!is_type::<Panel>(&*text));
}
#[test]
fn test_as_renderable_ref() {
let text = Text::from("Reference");
let renderable_ref = as_renderable_ref(&text);
let mut console = crate::console::Console::builder().width(80).build();
console.begin_capture();
console.print(renderable_ref);
let output = console.end_capture();
assert!(output.contains("Reference"));
}
#[test]
fn test_gilt_cast_impl_macro() {
struct QuickData {
x: i32,
y: i32,
}
gilt_cast_impl! { QuickData => |p|
Box::new(Text::from(format!("Point: ({}, {})", p.x, p.y)))
}
let data = QuickData { x: 10, y: 20 };
let renderable = data.into_renderable();
let mut console = crate::console::Console::builder().width(80).build();
console.begin_capture();
console.print(&*renderable);
let output = console.end_capture();
assert!(output.contains("Point: (10, 20)"));
}
#[test]
fn test_collection_of_boxes() {
let items: Vec<RenderableBox> = vec![
RenderableBox::new(Text::from("Item 1")),
RenderableBox::new(Panel::new(Text::from("Item 2"))),
RenderableBox::new(Rule::with_title("Item 3")),
];
let mut console = crate::console::Console::builder().width(80).build();
console.begin_capture();
for item in &items {
console.print(item);
}
let output = console.end_capture();
assert!(output.contains("Item 1"));
assert!(output.contains("Item 2"));
assert!(output.contains("Item 3"));
}
}