disktui 1.3.0

A terminal-based disk management utility built with Rust and Ratatui
Documentation
use ratatui::{
    Frame,
    layout::{Alignment, Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Text},
    widgets::{Block, BorderType, Borders, Clear, Paragraph, Wrap},
};
use tokio::sync::mpsc::UnboundedSender;

use crate::{app::AppResult, event::Event};

#[derive(Debug, Clone)]
pub struct Notification {
    pub message: String,
    pub level: NotificationLevel,
    pub ttl: u16,
}

#[derive(Debug, Clone)]
pub enum NotificationLevel {
    Error,
    Warning,
    Info,
}

impl Notification {
    pub fn render(&self, index: usize, frame: &mut Frame) {
        let (color, title) = match self.level {
            NotificationLevel::Info => (Color::Green, "Info"),
            NotificationLevel::Warning => (Color::Yellow, "Warning"),
            NotificationLevel::Error => (Color::Red, "Error"),
        };

        let mut text = Text::from(vec![
            Line::from(title).style(Style::new().fg(color).add_modifier(Modifier::BOLD)),
        ]);

        text.extend(Text::from(self.message.as_str()));

        let notification_height = u16::try_from(text.height()).unwrap_or(u16::MAX).saturating_add(2);
        let notification_width = u16::try_from(text.width()).unwrap_or(u16::MAX).saturating_add(4);

        let block = Paragraph::new(text)
            .alignment(Alignment::Center)
            .wrap(Wrap { trim: false })
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .style(Style::default())
                    .border_type(BorderType::Thick)
                    .border_style(Style::default().fg(color)),
            );

        let area = notification_rect(
            u16::try_from(index).unwrap_or(u16::MAX),
            notification_height,
            notification_width,
            frame.area(),
        );

        frame.render_widget(Clear, area);
        frame.render_widget(block, area);
    }
    pub fn send(
        message: String,
        level: NotificationLevel,
        sender: &UnboundedSender<Event>,
    ) -> AppResult<()> {
        let ttl = match level {
            NotificationLevel::Error => 100,
            NotificationLevel::Warning => 60,
            NotificationLevel::Info => 40,
        };

        let notif = Notification {
            message,
            level,
            ttl,
        };

        sender.send(Event::Notification(notif))?;

        Ok(())
    }
}

pub fn notification_rect(offset: u16, height: u16, width: u16, r: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints(
            [
                Constraint::Length(height * offset),
                Constraint::Length(height),
                Constraint::Min(1),
            ]
            .as_ref(),
        )
        .split(r);

    Layout::default()
        .direction(Direction::Horizontal)
        .constraints(
            [
                Constraint::Min(1),
                Constraint::Length(width),
                Constraint::Length(2),
            ]
            .as_ref(),
        )
        .split(popup_layout[1])[1]
}