netscanner 0.6.43

Network Scanner
use color_eyre::eyre::Result;
use color_eyre::owo_colors::OwoColorize;
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::style::Stylize;
use ratatui::{prelude::*, widgets::*};
use ratatui::{
    layout::Position,
    text::{Line, Span},
    widgets::Paragraph,
};
use serde::{Deserialize, Serialize};
use strum::{EnumCount, IntoEnumIterator};
use tokio::sync::mpsc::UnboundedSender;

use super::{Component, Frame};
use crate::{
    action::Action,
    config::DEFAULT_BORDER_STYLE,
    config::{Config, KeyBindings},
    enums::TabsEnum,
    layout::get_vertical_layout,
};

#[derive(Default)]
pub struct Tabs {
    action_tx: Option<UnboundedSender<Action>>,
    config: Config,
    tab_index: usize,
}

impl Tabs {
    pub fn new() -> Self {
        Self {
            action_tx: None,
            config: Config::default(),
            tab_index: 0,
        }
    }

    fn make_tabs(&self) -> Paragraph<'_> {
        let enum_titles: Vec<Span> =
            TabsEnum::iter()
                .enumerate()
                .fold(Vec::new(), |mut title_spans, (idx, p)| {
                    let index_str = idx + 1;

                    let s1 = "(".yellow();
                    let s2 = format!("{}", index_str).red();
                    let s3 = ")".yellow();
                    let mut s4 = format!("{} ", p).dark_gray().bold();
                    if idx == self.tab_index {
                        s4 = format!("{} ", p).green().bold();
                    }

                    title_spans.push(s1);
                    title_spans.push(s2);
                    title_spans.push(s3);
                    title_spans.push(s4);
                    title_spans
                });

        let title = Line::from(vec![
            "|".yellow(),
            "Tab".red().bold(),
            "s|".yellow(),
        ]).right_aligned();

        let arrrow = String::from(char::from_u32(0x25bc).unwrap_or('>'));
        let b = Block::default()
            .title(title)
            .title_bottom(Line::from(vec!["|".yellow(), arrrow.green(), "|".yellow()]).centered())
            .borders(Borders::ALL)
            .border_type(DEFAULT_BORDER_STYLE)
            .padding(Padding::new(1, 0, 0, 0))
            .border_style(Style::default().fg(Color::Rgb(100, 100, 100)));

        Paragraph::new(Line::from(enum_titles)).block(b)
    }

    fn next_tab(&mut self) {
        self.tab_index = (self.tab_index + 1) % TabsEnum::COUNT;
        if let Some(ref action_tx) = self.action_tx {
            let tab_enum = TabsEnum::iter().nth(self.tab_index).unwrap();
            action_tx.send(Action::TabChange(tab_enum)).unwrap();
        }
    }
}

impl Component for Tabs {
    fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
        self.action_tx = Some(tx);
        Ok(())
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }

    fn register_config_handler(&mut self, config: Config) -> Result<()> {
        self.config = config;
        Ok(())
    }

    fn update(&mut self, action: Action) -> Result<Option<Action>> {
        match action {
            Action::Tab => {
                self.next_tab();
            }

            Action::TabChange(tab_enum) => TabsEnum::iter().enumerate().for_each(|(idx, t)| {
                if tab_enum == t {
                    self.tab_index = idx;
                }
            }),

            _ => {}
        }
        Ok(None)
    }

    fn draw(&mut self, f: &mut Frame<'_>, area: Rect) -> Result<()> {
        let layout = get_vertical_layout(area);
        let mut rect = layout.tabs;
        rect.y += 1;

        let tabs = self.make_tabs();
        f.render_widget(tabs, rect);

        Ok(())
    }
}