use crate::styles::theme;
use indoc::indoc;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::text::Text;
use ratatui::widgets::Widget;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct DotstateLogo {
size: Size,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Size {
#[default]
Small,
Regular,
Classic,
Narrow,
}
impl DotstateLogo {
#[must_use]
pub const fn new(size: Size) -> Self {
Self { size }
}
#[allow(dead_code)] #[must_use]
pub const fn small() -> Self {
Self::new(Size::Small)
}
#[must_use]
pub const fn regular() -> Self {
Self::new(Size::Regular)
}
#[must_use]
pub const fn classic() -> Self {
Self::new(Size::Classic)
}
#[must_use]
pub const fn narrow() -> Self {
Self::new(Size::Narrow)
}
#[must_use]
pub const fn width(&self) -> u16 {
self.size.width()
}
#[must_use]
pub const fn height(&self) -> u16 {
self.size.height()
}
}
impl Widget for DotstateLogo {
fn render(self, area: Rect, buf: &mut Buffer) {
let logo = self.size.as_str();
Text::raw(logo)
.style(theme().text_style())
.render(area, buf);
}
}
impl Size {
const fn as_str(self) -> &'static str {
match self {
Self::Small => Self::small(),
Self::Regular => Self::regular(),
Self::Classic => Self::classic(),
Self::Narrow => Self::narrow(),
}
}
#[must_use]
pub const fn width(self) -> u16 {
match self {
Self::Small => 16, Self::Regular => 24, Self::Classic => 24, Self::Narrow => 19, }
}
#[must_use]
pub const fn height(self) -> u16 {
match self {
Self::Small => 3,
Self::Regular => 3,
Self::Classic => 3,
Self::Narrow => 3,
}
}
const fn small() -> &'static str {
indoc! {"
▄ ▄▖▄▖▄▖▄▖▄▖▄▖▄▖
▌▌▌▌▐ ▚ ▐ ▌▌▐ ▙▖
▙▘▙▌▐ ▄▌▐ ▛▌▐ ▙▖
"}
}
const fn regular() -> &'static str {
indoc! {"
╺┳┓┏━┓╺┳╸┏━┓╺┳╸┏━┓╺┳╸┏━╸
┃┃┃ ┃ ┃ ┗━┓ ┃ ┣━┫ ┃ ┣╸
╺┻┛┗━┛ ╹ ┗━┛ ╹ ╹ ╹ ╹ ┗━╸
"}
}
const fn classic() -> &'static str {
indoc! {"
╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗
║║║ ║ ║ ╚═╗ ║ ╠═╣ ║ ║╣
═╩╝╚═╝ ╩ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
"}
}
const fn narrow() -> &'static str {
indoc! {"
┳┓┏┓┏┳┓┏┓┏┳┓┏┓┏┳┓┏┓
┃┃┃┃ ┃ ┗┓ ┃ ┣┫ ┃ ┣
┻┛┗┛ ┻ ┗┛ ┻ ┛┗ ┻ ┗┛
"}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_size() {
let logo = DotstateLogo::new(Size::Small);
assert_eq!(logo.size, Size::Small);
}
#[test]
fn default_logo_is_small() {
let logo = DotstateLogo::default();
assert_eq!(logo.size, Size::Small);
}
#[test]
fn small_logo_constant() {
let logo = DotstateLogo::small();
assert_eq!(logo.size, Size::Small);
}
#[test]
fn regular_logo_constant() {
let logo = DotstateLogo::regular();
assert_eq!(logo.size, Size::Regular);
}
#[test]
fn logo_dimensions_match_content() {
for size in [Size::Small, Size::Regular, Size::Classic, Size::Narrow] {
let content = size.as_str();
let lines: Vec<&str> = content.lines().collect();
assert_eq!(
lines.len() as u16,
size.height(),
"{size:?} height mismatch"
);
let max_width = lines.iter().map(|l| l.chars().count()).max().unwrap_or(0) as u16;
assert_eq!(max_width, size.width(), "{size:?} width mismatch");
}
}
}