icmd 0.1.0

A command-line software framework
Documentation
use std::time::Duration;
use std::{any::Any, any::TypeId, fmt::Debug, sync::Arc};
use std::thread;

use crossterm::{cursor, event, execute, terminal};
use downcast_trait::downcast_trait;
use downcast_trait::DowncastTrait;
use parking_lot::RwLock;
use std::mem;

use crate::{
    event::{Event, Listener},
    node::HasListener,
    utils::NodeRef,
};

use super::{
    message::{Content, Message, Participant, RawEvent, Sender},
    ICmdWindow,
};

pub struct GeneralListener {
    pub func: Box<dyn Fn(NodeRef, RawEvent) -> bool + Send + Sync>,
}

impl Debug for GeneralListener {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("GeneralListener").finish()
    }
}

impl<E, T> From<T> for GeneralListener
where
    E: Event,
    T: Listener<EventType = E> + Send + Sync + 'static,
    <E as TryFrom<RawEvent>>::Error: Debug,
{
    fn from(value: T) -> Self {
        let value = Arc::new(RwLock::new(value));
        GeneralListener {
            func: Box::new(move |node, raw| {
                if let Ok(e) = TryFrom::<RawEvent>::try_from(raw) {
                    value.write().on_event(node, e)
                } else {
                    true
                }
            }),
        }
    }
}

#[derive(Debug)]
pub struct Interactor {
    window: Arc<RwLock<ICmdWindow>>,
}

impl Interactor {
    pub fn new(window: Arc<RwLock<ICmdWindow>>) -> Self {
        Interactor { window }
    }

    pub fn launch(&self) {
        while let Ok(event) = event::read() {
            (match event {
                event::Event::Key(key) => {
                    if key.code == event::KeyCode::Char('c')
                        && key.modifiers == event::KeyModifiers::CONTROL
                    {
                        let stdout = &self.window.read().stdout;
                        let mut stdout_ = stdout.lock();
                        execute!(
                            stdout_,
                            cursor::MoveTo(0, 0),
                            terminal::Clear(terminal::ClearType::All)
                        )
                        .unwrap();
                        break;
                    } else {
                        Some(Message {
                            from: TypeId::of::<Self>(),
                            to: TypeId::of::<Dispatcher>(),
                            content: Content::Event(RawEvent::Key(key.code, key.modifiers)),
                        })
                    }
                }
                event::Event::Mouse(me) => Some(Message {
                    from: TypeId::of::<Self>(),
                    to: TypeId::of::<Dispatcher>(),
                    content: Content::Event(RawEvent::Mouse(me.column, me.row, me.kind)),
                }),

                _ => None,
            })
            .map(|msg| self.send(msg));
        }
    }
}

impl Sender for Interactor {
    fn get_window(&self) -> Arc<RwLock<ICmdWindow>> {
        self.window.clone()
    }
}

#[derive(Debug)]
pub struct Dispatcher {
    window: Arc<RwLock<ICmdWindow>>,
}

impl Dispatcher {
    pub fn new(window: Arc<RwLock<ICmdWindow>>) -> Self {
        Dispatcher { window }
    }

    pub fn launch(&self, root: NodeRef) {
        loop {
            if !Participant::update(self, root.clone()) {
                thread::sleep(Duration::from_millis(1));
                break;
            }
        }
    }
}

impl Sender for Dispatcher {
    fn get_window(&self) -> Arc<RwLock<ICmdWindow>> {
        self.window.clone()
    }
}

impl Participant for Dispatcher {
    type Params = NodeRef;
    fn resolve(&self, msg: Message, root: Self::Params) -> bool {
        fn dispatch(root: NodeRef, event: RawEvent) {
            if let Some(node) = downcast_trait!(dyn HasListener, root.as_ref().to_downcast_trait())
            {
                node.get_listeners().read().iter().for_each(|listener| {
                    (listener.func)(root.clone(), event.clone());
                });
                node.children().read().iter().for_each(|child| {
                    dispatch(child.clone(), event.clone());
                });
            };
        }
        match msg.content {
            Content::Event(event) => {
                dispatch(root, event);
                true
            }
            Content::Quit => false,
            _ => true,
        }
    }
}