ltrender 0.1.5

a diff based terminal rendering engine
Documentation
use std::time::Instant;

use crate::{
    DrawObject, DrawObjectKey, Drawable, ScreenKey,
    drawable_register::ObjectLifetime,
    error::{AppError, DrawObjectBuilderError},
    rendering::{render_handle::RenderHandle, renderer::RenderModeBehavior},
    terminal_buffer::buffer_and_celldrawer::Shader,
};
pub mod sprite_drawable_builder;
pub use sprite_drawable_builder::SpriteDrawableBuilder;
pub mod circle_drawable_builder;
pub use circle_drawable_builder::CircleDrawableBuilder;
pub mod line_drawable_builder;
pub use line_drawable_builder::LineDrawableBuilder;
pub mod polygon_drawable_builder;
pub use polygon_drawable_builder::PolygonDrawableBuilder;
pub mod rect_drawable_builder;
pub use rect_drawable_builder::RectDrawableBuilder;
pub mod videostream_drawable_builder;
pub use videostream_drawable_builder::VideoStreamDrawableBuilder;

macro_rules! allow_drawable_building {
    ($builder_name:ident, $fn_name:ident) => {
        pub fn $fn_name<F>(mut self, build_fn: F) -> Result<Self, DrawObjectBuilderError>
        where
            F: FnOnce($builder_name) -> $builder_name,
        {
            let builder = build_fn($builder_name::new());
            let drawable = builder.build()?;
            self.drawable = Some(drawable);
            Ok(self)
        }
    };
}

#[macro_export]
macro_rules! handle_field {
    ($fn_name:ident, $field_name:ident, $type:ty) => {
        pub fn $fn_name(mut self, $field_name: $type) -> Self {
            self.$field_name = Some($field_name);
            self
        }
    };
}

#[macro_export]
macro_rules! handle_pointed_field {
    ($fn_name:ident, $field_name:ident) => {
        pub fn $fn_name(mut self, $field_name: impl Into<Point<i32>>) -> Self {
            let point = $field_name.into();
            self.$field_name = Some(point);
            self
        }
    };
}

#[macro_export]
macro_rules! handle_char_field {
    ($fn_name:ident, $field_name:ident) => {
        pub fn $fn_name(mut self, $field_name: impl Into<TerminalChar>) -> Self {
            let point = $field_name.into();
            self.$field_name = Some(point);
            self
        }
    };
}

#[derive(Default)]
pub struct DrawObjectBuilder {
    layer: Option<usize>,
    shaders: Vec<Box<dyn Shader>>,
    drawable: Option<Box<dyn Drawable>>,
    screen_id: Option<ScreenKey>,
    lt: Option<ObjectLifetime>,
}

impl DrawObjectBuilder {
    handle_field!(layer, layer, usize);
    handle_field!(screen, screen_id, usize);
    handle_field!(add_lifetime, lt, ObjectLifetime);
    pub fn shader<T>(mut self, shader: T) -> Self
    where
        T: Shader + 'static,
    {
        self.shaders.push(Box::new(shader));
        self
    }

    pub fn drawable(mut self, drawable: Box<dyn Drawable>) -> Self {
        self.drawable = Some(drawable);
        self
    }

    allow_drawable_building!(SpriteDrawableBuilder, sprite_drawable);
    allow_drawable_building!(CircleDrawableBuilder, circle_drawable);
    allow_drawable_building!(LineDrawableBuilder, line_drawable);
    allow_drawable_building!(PolygonDrawableBuilder, polygon_drawable);
    allow_drawable_building!(RectDrawableBuilder, rect_drawable);
    allow_drawable_building!(VideoStreamDrawableBuilder, videostream_drawable);

    pub fn build<M>(
        &mut self,
        render_handle: &mut RenderHandle<M>,
    ) -> Result<DrawObjectKey, AppError>
    where
        M: RenderModeBehavior,
    {
        let layer = self.layer.ok_or(DrawObjectBuilderError::NoLayerAdded())?;
        let screen_id = self
            .screen_id
            .ok_or(DrawObjectBuilderError::NoScreenAdded())?;

        let drawable = self
            .drawable
            .take()
            .ok_or(DrawObjectBuilderError::NoDrawableAdded())?;

        let lifetime = self.lt.ok_or(DrawObjectBuilderError::NoLifetimeAdded())?;

        render_handle.register_drawable(
            screen_id,
            DrawObject {
                lifetime,
                layer,
                drawable,
                shaders: self.shaders.clone(),
                creation_time: Instant::now(),
            },
        )
    }
}