use std::fmt::{Display, Formatter, Result};
pub mod banner;
pub struct CStyle(pub &'static str);
impl Display for CStyle {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{}", self.0)
}
}
pub const RESET_COLOR: &str = "\x1b[0m";
pub trait Stylable {
fn print(&self);
fn println(&self);
fn show(&self, style: &CStyle) {
print!("{}", self.wrap_reset(style));
}
fn showln(&self, style: &CStyle) {
println!("{}", self.wrap_reset(style));
}
fn style(&self, style: &CStyle) -> String {
self.wrap_reset(style)
}
fn wrap_reset(&self, style: &CStyle) -> String;
fn in_bold(&self) -> String;
fn in_dim(&self) -> String;
fn in_italic(&self) -> String;
fn in_underline(&self) -> String;
fn in_blink(&self) -> String;
fn in_gray(&self) -> String;
fn in_red(&self) -> String;
fn in_green(&self) -> String;
fn in_yellow(&self) -> String;
fn in_blue(&self) -> String;
fn in_magenta(&self) -> String;
fn in_cyan(&self) -> String;
fn in_white(&self) -> String;
fn in_orange(&self) -> String;
fn in_pink(&self) -> String;
fn in_purple(&self) -> String;
fn on_gray(&self) -> String;
fn on_red(&self) -> String;
fn on_green(&self) -> String;
fn on_yellow(&self) -> String;
fn on_blue(&self) -> String;
fn on_magenta(&self) -> String;
fn on_cyan(&self) -> String;
fn on_white(&self) -> String;
fn on_orange(&self) -> String;
fn on_pink(&self) -> String;
fn on_purple(&self) -> String;
fn subtle(&self) -> String;
fn as_info(&self) -> String;
fn as_success(&self) -> String;
fn as_warning(&self) -> String;
fn as_error(&self) -> String;
fn as_debug(&self) -> String;
fn with_nostyle(&self) -> String;
fn vibrant(&self) -> String;
fn print_positioned<F>(&self, x: i32, y: i32, func: F)
where
F: Fn(&str) -> String;
}
impl<T: Display> Stylable for T {
fn print(&self) {
print!("{}", self);
}
fn println(&self) {
println!("{}", self);
}
fn wrap_reset(&self, style: &CStyle) -> String {
format!("{}{}{}", style, self, RESET_COLOR)
}
fn in_bold(&self) -> String { self.wrap_reset(&CStyle("\x1b[1m")) }
fn in_dim(&self) -> String { self.wrap_reset(&CStyle("\x1b[2m")) }
fn in_italic(&self) -> String { self.wrap_reset(&CStyle("\x1b[3m")) }
fn in_underline(&self) -> String { self.wrap_reset(&CStyle("\x1b[4m")) }
fn in_blink(&self) -> String { self.wrap_reset(&CStyle("\x1b[5m")) }
fn in_gray(&self) -> String { self.wrap_reset(&CStyle("\x1b[90m")) }
fn in_red(&self) -> String { self.wrap_reset(&CStyle("\x1b[91m")) }
fn in_green(&self) -> String { self.wrap_reset(&CStyle("\x1b[92m")) }
fn in_yellow(&self) -> String { self.wrap_reset(&CStyle("\x1b[93m")) }
fn in_blue(&self) -> String { self.wrap_reset(&CStyle("\x1b[94m")) }
fn in_magenta(&self) -> String { self.wrap_reset(&CStyle("\x1b[95m")) }
fn in_cyan(&self) -> String { self.wrap_reset(&CStyle("\x1b[96m")) }
fn in_white(&self) -> String { self.wrap_reset(&CStyle("\x1b[97m")) }
fn in_orange(&self) -> String { self.wrap_reset(&CStyle("\x1b[38;5;208m")) }
fn in_pink(&self) -> String { self.wrap_reset(&CStyle("\x1b[38;5;205m")) }
fn in_purple(&self) -> String { self.wrap_reset(&CStyle("\x1b[38;5;129m")) }
fn on_gray(&self) -> String { self.wrap_reset(&CStyle("\x1b[100m")) }
fn on_red(&self) -> String { self.wrap_reset(&CStyle("\x1b[101m")) }
fn on_green(&self) -> String { self.wrap_reset(&CStyle("\x1b[102m")) }
fn on_yellow(&self) -> String { self.wrap_reset(&CStyle("\x1b[103m")) }
fn on_blue(&self) -> String { self.wrap_reset(&CStyle("\x1b[104m")) }
fn on_magenta(&self) -> String { self.wrap_reset(&CStyle("\x1b[105m")) }
fn on_cyan(&self) -> String { self.wrap_reset(&CStyle("\x1b[106m")) }
fn on_white(&self) -> String { self.wrap_reset(&CStyle("\x1b[107m")) }
fn on_orange(&self) -> String { self.wrap_reset(&CStyle("\x1b[48;5;208m")) }
fn on_pink(&self) -> String { self.wrap_reset(&CStyle("\x1b[48;5;205m")) }
fn on_purple(&self) -> String { self.wrap_reset(&CStyle("\x1b[48;5;129m")) }
fn subtle(&self) -> String { self.wrap_reset(&CStyle("\x1b[90m")) }
fn as_info(&self) -> String { self.wrap_reset(&CStyle("\x1b[96m")) }
fn as_success(&self) -> String { self.wrap_reset(&CStyle("\x1b[92m")) }
fn as_warning(&self) -> String { self.wrap_reset(&CStyle("\x1b[93m")) }
fn as_error(&self) -> String { self.wrap_reset(&CStyle("\x1b[91m")) }
fn as_debug(&self) -> String { self.wrap_reset(&CStyle("\x1b[95m")) }
fn with_nostyle(&self) -> String { format!("{}", self) }
fn vibrant(&self) -> String {
let mut result = String::new();
for c in self.to_string().chars() {
if c == ' ' {
result.push(c);
} else {
let color = rand::random::<u8>() % 216 + 16; result.push_str(&format!("\x1b[38;5;{}m{}\x1b[0m", color, c));
}
}
result
}
fn print_positioned<F>(&self, x: i32, y: i32, func: F)
where
F: Fn(&str) -> String,
{
if x < 0 {
print!("\x1b[{}A", x.abs());
}
if x > 0 {
print!("\x1b[{}B", x);
}
if y < 0 {
print!("\x1b[{}D", y.abs());
}
if y > 0 {
print!("\x1b[{}C", y);
}
print!("{}", func(&self.to_string()));
if x < 0 {
print!("\x1b[{}B", x.abs());
}
if x > 0 {
print!("\x1b[{}A", x);
}
if y < 0 {
print!("\x1b[{}C", y.abs());
}
if y > 0 {
print!("\x1b[{}D", y);
}
io::stdout().flush().unwrap();
}
}
pub use super::*;
pub use ask::*;
use std::io::{self, Write};
use std::sync::RwLock;
pub fn print_positioned(x: i16, y: i16, text: impl Into<String>) {
let text = text.into();
let (xor, yor) = cursor::position().unwrap();
let adjusted_x = x + xor as i16;
let adjusted_y = y + yor as i16;
print!("\x1b[{};{}H{}", adjusted_x, adjusted_y, text);
print!("\x1b[{};{}H", xor, yor);
}
pub fn reset_line() {
print!("\x1b[0G");
}
pub fn move_up(n: i16) {
print!("\x1b[{}A", n);
}
pub fn move_down(n: i16) {
print!("\x1b[{}B", n);
}
pub fn move_right(n: i16) {
print!("\x1b[{}C", n);
}
pub fn move_left(n: i16) {
print!("\x1b[{}D", n);
}
pub fn clear_line() {
print!("\x1b[2K");
}
pub fn clear_screen() {
print!("\x1b[2J");
}
pub fn enable_raw_mode() -> std::io::Result<()> {
io::stdout().write_all(b"\x1b[?25l")?;
io::stdout().flush()?;
Ok(())
}
pub fn disable_raw_mode() -> std::io::Result<()> {
io::stdout().write_all(b"\x1b[?25h")?;
io::stdout().flush()?;
Ok(())
}
lazy_static::lazy_static! {
static ref MAX_WIDTH: RwLock<usize> = RwLock::new(60);
}
pub fn get_max_width() -> usize {
*MAX_WIDTH.read().unwrap()
}
pub fn set_max_width(width: usize) {
*MAX_WIDTH.write().unwrap() = width;
}
pub fn wrap_text(text: impl Into<String>, width: usize) -> String {
let mut wrapped = textwrap::fill(&text.into(), width);
wrapped = wrapped.trim_end().to_string();
wrapped
}
pub fn divider() {
print!("\x1b[0G");
println!("\x1b[90m{}\x1b[0m", "─".repeat(get_max_width()));
}
#[macro_export]
macro_rules! divider {
() => {
$crate::divider();
};
($($arg:tt)*) => {
{
use std::io::{self, Write};
let stdout = io::stdout();
let mut stdout = stdout.lock();
let width = $crate::get_max_width();
let mut left = String::new();
let mut center = String::new();
let mut right = String::new();
let mut chars = "─".to_string();
let mut args = vec![$($arg)*];
let mut has_left = false;
let mut has_right = false;
while let Some(arg) = args.pop() {
match arg {
"-" => {
if center.is_empty() {
has_left = true;
} else {
has_right = true;
}
}
"/" => {
if left.is_empty() {
left.push('╭');
} else {
right.push('╮');
}
}
"\\" => {
if left.is_empty() {
left.push('╰');
} else {
right.push('╯');
}
}
"=" => {
chars = "═".to_string();
}
s => {
if center.is_empty() {
center = s.to_string();
} else {
center = format!("{} {}", center, s.to_string());
}
}
}
}
let content_width = center.len();
let mut left_width = 0;
let mut right_width = 0;
if has_left && has_right {
let remaining_width = width - content_width - 2;
if remaining_width >= 2 {
left_width = remaining_width / 2;
right_width = remaining_width - left_width;
}
} else if has_left {
left_width = width - content_width - 1;
} else if has_right {
right_width = width - content_width - 1;
} else {
let remaining_width = width - content_width;
if remaining_width >= 2 {
left_width = remaining_width / 2;
right_width = remaining_width - left_width;
}
}
left = chars.repeat(left_width) + &left;
right = right + &chars.repeat(right_width);
let output = if has_left && has_right {
format!("{} {} {}", left, center, right)
} else if has_left {
format!("{} {}", left, center)
} else if has_right {
format!("{} {}", center, right)
} else {
format!("{}{}{}", left, center, right)
};
write!(stdout, "\x1b[0G\x1b[90m{:\u{2500}<width$}\x1b[0m\n", output, width = width).unwrap();
}
};
}
pub fn divider_vibrant() {
print!("\x1b[0G");
let mut s = String::new();
for _ in 0..get_max_width() {
let color = rand::random::<u8>() % 216 + 16; s.push_str(format!("\x1b[38;5;{}m{}\x1b[0m", color, "—").as_str());
}
println!("{}", s);
}
pub fn print_framed(x: i16, y: i16, w: i16, h: i16, text: impl Into<String>) {
let (xor, yor) = cursor::position().unwrap();
let text = text.into();
let width = w as usize;
let height = h as usize;
let wrapped = wrap_text(text, width);
let mut lines = wrapped.lines();
for i in 0..height {
if let Some(line) = lines.next() {
print!("\x1b[{};{}H{}", x + i as i16, y, line);
}
}
print!("\x1b[{};{}H", xor, yor);
}
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
fn pad_text(text: impl Into<String>) -> String {
let text = text.into();
if text.starts_with(' ') {
format!(" {}", text)
} else {
format!(" {}", text)
}
}
pub trait Pickable {
fn get_title(&self) -> String;
fn get_description(&self) -> String;
}
#[macro_export]
macro_rules! show {
($arg:expr) => {
print!("{}", $arg);
};
($style:ident, $value:expr) => {
print!("{}{}{}", $crate::$style, $value, $crate::RESET_COLOR);
};
($style:ident, $value:expr, $($rest:tt)*) => {
$crate::show!($style, $value);
$crate::show!($($rest)*);
};
}
#[macro_export]
macro_rules! showln {
($arg:expr) => {
println!("{}", $arg);
};
($style:ident, $value:expr) => {
println!("{}{}{}", $crate::$style, $value, $crate::RESET_COLOR);
};
($style:ident, $value:expr, $($rest:tt)*) => {
$crate::show!($style, $value);
$crate::showln!($($rest)*);
};
}
#[macro_export]
macro_rules! render {
($arg:expr) => {
$arg.to_string()
};
($style:ident, $value:expr) => {
format!("{}{}{}", $crate::$style, $value, $crate::RESET_COLOR)
};
($style:ident, $value:expr, $($rest:tt)*) => {
format!("{}{}{}", $crate::$style, $value, $crate::RESET_COLOR) + &$crate::render!($($rest)*)
};
}
pub use textwrap::*;
#[macro_export]
macro_rules! paragraph {
($title_style:ident, $title:expr, $text_style:ident, $text:expr) => {
let title = $title.to_string();
showln!($title_style, "╭─ ", $title_style, title, $title_style, " ─");
let text = $text.to_string();
let width = $crate::get_max_width() - 3;
let mut wrapped = $crate::wrap_text(&text, width);
wrapped = wrapped.trim_end().to_string();
for line in wrapped.lines() {
showln!($title_style, "│ ", $text_style, line);
}
showln!($title_style, "╰─",$title_style, "─".repeat(width));
};
}
#[macro_export]
macro_rules! tree {
($val:expr) => {
fn display_tree(value: &Value, indent: usize) {
match value {
Value::Object(map) => {
println!(
"{}╭─ {}object{}",
"│ ".repeat(indent),
$crate::purple_bold,
$crate::RESET_COLOR
);
for (key, value) in map {
println!("{}{} ", "│ ".repeat(indent + 1), key);
display_tree(value, indent + 1);
}
println!("{}╰─—", "│ ".repeat(indent));
}
Value::Array(array) => {
println!(
"{}╭─ {}array{}",
"│ ".repeat(indent),
$crate::purple_bold,
$crate::RESET_COLOR
);
for (index, value) in array.iter().enumerate() {
println!("{}{}: ", "│ ".repeat(indent + 1), index);
display_tree(value, indent + 1);
}
println!("{}╰─—", "│ ".repeat(indent));
}
Value::String(string) => {
println!(
"{}{}{}{}",
"│ ".repeat(indent),
$crate::green,
string,
$crate::RESET_COLOR
);
}
Value::Number(number) => {
println!(
"{}{}{}{}",
"│ ".repeat(indent),
$crate::yellow,
number,
$crate::RESET_COLOR
);
}
Value::Bool(boolean) => {
println!(
"{}{}{}{}",
"│ ".repeat(indent),
$crate::blue,
boolean,
$crate::RESET_COLOR
);
}
Value::Null => {
println!(
"{}{}{}{}",
"│ ".repeat(indent),
$crate::gray,
"null",
$crate::RESET_COLOR
);
}
}
}
display_tree(&$val, 0);
};
}
pub use ask::*;
pub mod ask;
pub fn code_to_char(code: &KeyCode) -> char {
match code {
KeyCode::Char(c) => *c,
KeyCode::Enter => '\n',
KeyCode::Backspace => '\x08',
KeyCode::Esc => '\x1b',
KeyCode::Tab => '\t',
KeyCode::Up => '↑',
KeyCode::Down => '↓',
KeyCode::Left => '←',
KeyCode::Right => '→',
_ => ' ',
}
}