cyndikator 0.1.0

A cli rss reader
use crate::db::Database;
use crossterm::{event::read, terminal};
use std::io::{stdout, Write};

use draw::*;
use record::Cache;

mod draw;
mod inter;
mod record;

pub struct View {
    db: Database,
}

pub enum Action {
    Delete,
    Undo,
    Open,
    Noop,
    Quit,
}

impl View {
    pub fn new(db: Database) -> View {
        View { db }
    }

    fn render(self, out: &mut impl Write) -> eyre::Result<()> {
        let (_, height) = terminal::size()?;
        let mut cache = Cache::new(self.db);
        let mut inter = inter::Inter::new(height, 0);

        loop {
            let selected = inter.offset;
            let entries = cache.window(inter.base, height as u32)?;
            Full { selected, entries }.draw(out)?;
            out.flush()?;

            let e = read()?;

            match inter.interact(&e)? {
                Action::Open => {
                    if let Some(url) = &entries
                        .get(inter.offset as usize)
                        .and_then(|e| e.url.as_ref())
                    {
                        let _ = open::that_in_background(url);
                    }
                }

                Action::Delete => {
                    cache.delete(inter.offset as usize);
                }

                Action::Undo => {
                    cache.undo();
                }

                Action::Noop => (),
                Action::Quit => break,
            };

            let total = cache.total();
            if inter.offset as u32 + inter.base >= total {
                inter.base = total.checked_sub(inter.height as u32).unwrap_or(0);

                inter.offset = total
                    .checked_sub(inter.base)
                    .unwrap_or(0)
                    .checked_sub(1)
                    .unwrap_or(0) as u16;
            }
            inter.offset = inter.offset.min(cache.total() as u16 - 1);
        }

        Ok(())
    }

    pub fn interact(self) -> eyre::Result<()> {
        let mut out = stdout();

        let raw = Raw::default();

        Clear.draw(&mut out)?;
        ShowCur(false).draw(&mut out)?;

        let res = self.render(&mut out);

        let _ = Clear.draw(&mut out);
        let _ = ShowCur(true).draw(&mut out);

        drop(raw);
        out.flush()?;

        res
    }
}

struct Raw;

impl Default for Raw {
    fn default() -> Raw {
        let _ = terminal::enable_raw_mode();

        Raw
    }
}

impl Drop for Raw {
    fn drop(&mut self) {
        let _ = terminal::disable_raw_mode();
    }
}