use std::num::ParseIntError;
pub use app::App;
pub use event::Event;
pub use preview::PreviewCallback;
use thiserror::Error;
pub use widget::{SkimRender, SkimWidget};
mod app;
mod backend;
pub(crate) mod util;
pub use backend::Tui;
pub mod event;
pub mod header;
mod input;
pub mod item_list;
pub mod layout;
pub mod options;
mod preview;
pub mod statusline;
pub mod widget;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Size {
Percent(u16),
Fixed(u16),
}
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
impl TryFrom<&str> for Direction {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value.to_lowercase().as_str() {
"up" => Ok(Self::Up),
"down" => Ok(Self::Down),
"left" => Ok(Self::Left),
"right" => Ok(Self::Right),
_ => Err("Unknown direction {value}"),
}
}
}
#[derive(Error, Debug, PartialEq, Eq)]
pub enum SizeParseError {
#[error("Error parsing {0}: {1:?}")]
ParseError(String, ParseIntError),
#[error("Invalid percentage {0}")]
InvalidPercent(u16),
}
impl TryFrom<&str> for Size {
type Error = SizeParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.ends_with("%") {
let percent = value
.strip_suffix("%")
.unwrap_or_default()
.parse::<u16>()
.map_err(|e| SizeParseError::ParseError(value.to_string(), e))?;
if percent > 100 {
return Err(SizeParseError::InvalidPercent(percent));
}
Ok(Self::Percent(percent))
} else {
Ok(Self::Fixed(
value
.parse::<u16>()
.map_err(|e| SizeParseError::ParseError(value.to_string(), e))?,
))
}
}
}
impl Default for Size {
fn default() -> Self {
Self::Percent(100)
}
}
#[derive(Default, Clone, Copy)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[allow(missing_docs)]
pub enum BorderType {
#[default]
Plain,
Rounded,
Double,
Thick,
LightDoubleDashed,
HeavyDoubleDashed,
LightTripleDashed,
HeavyTripleDashed,
LightQuadrupleDashed,
HeavyQuadrupleDashed,
QuadrantInside,
QuadrantOutside,
}
impl From<BorderType> for ratatui::widgets::BorderType {
fn from(val: BorderType) -> Self {
match val {
BorderType::Plain => ratatui::widgets::BorderType::Plain,
BorderType::Rounded => ratatui::widgets::BorderType::Rounded,
BorderType::Double => ratatui::widgets::BorderType::Double,
BorderType::Thick => ratatui::widgets::BorderType::Thick,
BorderType::LightDoubleDashed => ratatui::widgets::BorderType::LightDoubleDashed,
BorderType::HeavyDoubleDashed => ratatui::widgets::BorderType::HeavyDoubleDashed,
BorderType::LightTripleDashed => ratatui::widgets::BorderType::LightTripleDashed,
BorderType::HeavyTripleDashed => ratatui::widgets::BorderType::HeavyTripleDashed,
BorderType::LightQuadrupleDashed => ratatui::widgets::BorderType::LightQuadrupleDashed,
BorderType::HeavyQuadrupleDashed => ratatui::widgets::BorderType::HeavyQuadrupleDashed,
BorderType::QuadrantInside => ratatui::widgets::BorderType::QuadrantInside,
BorderType::QuadrantOutside => ratatui::widgets::BorderType::QuadrantOutside,
}
}
}
#[cfg(test)]
mod size_test {
use super::*;
use std::num::IntErrorKind;
#[test]
fn fixed_success() {
assert_eq!(Size::try_from("10"), Ok(Size::Fixed(10u16)));
}
#[test]
fn percent_success() {
assert_eq!(Size::try_from("10%"), Ok(Size::Percent(10u16)));
}
#[test]
fn fixed_neg() {
let SizeParseError::ParseError(err_value, internal_error) = Size::try_from("-10").unwrap_err() else {
panic!();
};
assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);
assert_eq!(err_value, String::from("-10"));
}
#[test]
fn percent_neg() {
let SizeParseError::ParseError(err_value, internal_error) = Size::try_from("-10%").unwrap_err() else {
panic!();
};
assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);
assert_eq!(err_value, String::from("-10%"));
}
#[test]
fn percent_over_100() {
let SizeParseError::InvalidPercent(internal_error) = Size::try_from("110%").unwrap_err() else {
panic!();
};
assert_eq!(internal_error, 110u16);
}
#[test]
fn fixed_invalid_char() {
let SizeParseError::ParseError(value, internal_error) = Size::try_from("1-0").unwrap_err() else {
panic!();
};
assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);
assert_eq!(value, String::from("1-0"));
}
#[test]
fn percent_invalid_char() {
let SizeParseError::ParseError(value, internal_error) = Size::try_from("1-0%").unwrap_err() else {
panic!();
};
assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);
assert_eq!(value, String::from("1-0%"));
}
#[test]
fn percent_empty() {
let SizeParseError::ParseError(value, internal_error) = Size::try_from("%").unwrap_err() else {
panic!();
};
assert_eq!(internal_error.kind(), &IntErrorKind::Empty);
assert_eq!(value, String::from("%"));
}
}