From c871781cc9fc09a3e4b12656e4ae637a63f3f325 Mon Sep 17 00:00:00 2001 From: Valerie Date: Fri, 5 Jul 2024 10:17:31 -0400 Subject: [PATCH] added new arbirtrary '--size' flag; removed '--small' flag --- Cargo.toml | 2 +- man/pride.6 | 21 +++++++++++++--- src/complex.rs | 27 ++++++++++++-------- src/draw.rs | 27 +++++++++++--------- src/error.rs | 24 ++++++++++++++++++ src/main.rs | 53 ++++++++++++++++++--------------------- src/state.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 166 insertions(+), 55 deletions(-) create mode 100644 src/error.rs create mode 100644 src/state.rs diff --git a/Cargo.toml b/Cargo.toml index e528210..a3d1968 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pride" -version = "0.3.1" +version = "1.0.0" edition = "2021" authors = [ "Valerie Wolfe " ] description = "Pride flags in the terminal." diff --git a/man/pride.6 b/man/pride.6 index 01836af..84d763f 100644 --- a/man/pride.6 +++ b/man/pride.6 @@ -6,7 +6,8 @@ .Nd shows pride flags in the terminal .Sh SYNOPSIS .Nm -.Op Fl hlvs +.Op Fl hlv +.Op Fl s Ar size .Op Ar flag .Sh DESCRIPTION .Nm @@ -17,6 +18,8 @@ displays pride flags in the terminal using ANSI truecolor sequences. Shows a brief help text. .It Fl l , Fl -list Shows a list of available pride flags. +.It Fl s , Fl -size Ar size +Scales the flag to the given size: 'small' will produce the minimum size; one number sets the width; and two numbers separated by 'x' sets width and height respectively. .It Fl v , Fl -version Shows version information. .It Ar flag @@ -82,10 +85,22 @@ The transgender pride flag designed by Monica Helms in 1999. .Sh EXIT STATUS .Bl -tag -width Ds .It 1 -Unmatched flag name. +Unmatched +.Ar flag +name. .It 2 -Not running in a terminal. +Failed to parse +.Ar size . +.It 3 +The provided +.Ar size +is too small to render. .El +.Sh EXAMPLES +Create a full-width banner flag using tput: +.Pp +.Dl $ pride -s `tput cols` +.Pp .Sh AUTHORS .An -nosplit .An Valerie Wolfe Aq Mt sleeplessval@gmail.com diff --git a/src/complex.rs b/src/complex.rs index 0eb0c01..3bfa2c9 100644 --- a/src/complex.rs +++ b/src/complex.rs @@ -9,7 +9,9 @@ use termion::{ use crate::{ color::*, draw, + error, flag::{ self, Flag }, + state::State, util::{ ansi_len, ansi_substr } }; @@ -27,7 +29,7 @@ pub static TRIANGLE_21: [char; 3] = ['', '🭬', '']; /// 2/3 slope slant pub static SLANT_23: [char; 2] = ['🭒', '🭏']; -pub fn progress(small: bool) -> Flag { +pub fn progress(state: &State) -> Flag { let red = bg(0xE50000); let orange = bg(0xFF8D00); let yellow = bg(0xFFEE00); @@ -42,7 +44,8 @@ pub fn progress(small: bool) -> Flag { let pink: u32 = 0x7ACBF5; let white: u32 = 0xFFFFFF; - let (width, height) = if small { (18, 6) } else { terminal_size().unwrap() }; + let (width, height) = state.size.get(18, 6); + if height < 6 { error::too_small(width, height); } // create color slices and line buffer let stripes = [red, orange, yellow, green, blue, purple]; @@ -151,12 +154,13 @@ pub fn progress(small: bool) -> Flag { // everything below this point is in alphabetical order -pub fn aroace(small: bool) -> Flag { +pub fn aroace_halves(state: &State) -> Flag { // pull colors from aro & ace stripe flags let Flag::Stripes(aro) = flag::aromantic() else { panic!() }; let Flag::Stripes(ace) = flag::asexual() else { panic!() }; - let (width, height) = if small { (60, 20) } else { terminal_size().unwrap() }; + let (width, height) = state.size.get(60, 20); + if height < 20 { error::too_small(width, height); } let mut lines: Vec = Vec::new(); @@ -185,6 +189,8 @@ pub fn aroace(small: bool) -> Flag { fn demi_orientation_render(middle: Bg, bottom: Bg, width: u16, height: u16) -> Vec { let white = bg(0xFFFFFF); + if height < 5 { error::too_small(width, height); } + let stripes = vec![white, white, middle, bottom, bottom]; // initial stripe output buffer @@ -219,21 +225,21 @@ fn demi_orientation_render(middle: Bg, bottom: Bg, width: u16, height: lines } -pub fn demiromantic(small: bool) -> Flag { +pub fn demiromantic(state: &State) -> Flag { let green = bg(0x3DA542); let gray = bg(0xD2D2D2); - let (width, height) = if small { (15, 5) } else { terminal_size().unwrap() }; + let (width, height) = state.size.get(15, 5); let lines = demi_orientation_render(green, gray, width, height); Flag::Lines(lines) } -pub fn demisexual(small: bool) -> Flag { +pub fn demisexual(state: &State) -> Flag { let purple = bg(0x832FA8); let grey = bg(0x7B868C); - let (width, height) = if small { (15, 5) } else { terminal_size().unwrap() }; + let (width, height) = state.size.get(15, 5); let lines = demi_orientation_render(purple, grey, width, height); Flag::Lines(lines) @@ -274,7 +280,7 @@ pub fn intersex() -> Flag { } -pub fn polyamory(small: bool) -> Flag { +pub fn polyamory(state: &State) -> Flag { let blue = rgb(0x019FE3); let magenta = rgb(0xE50051); let purple = rgb(0x340C46); @@ -285,7 +291,8 @@ pub fn polyamory(small: bool) -> Flag { let semicircle = '\u{E0B6}'; let separators = ['\u{E0BE}', '\u{E0BA}']; - let (width, height) = if small { (18, 6) } else { terminal_size().unwrap() }; + let (width, height) = state.size.get(18, 6); + if height < 6 { error::too_small(width, height); } // create stripe array and line buffer let stripes = [magenta, purple]; // only stripes 2 and 3 need tracked diff --git a/src/draw.rs b/src/draw.rs index 3ebf02f..bc4df9c 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -1,6 +1,9 @@ //! render handling code -use std::io::{ self, Write }; +use std::io::{ + self, + Write +}; use termion::{ terminal_size, @@ -9,25 +12,28 @@ use termion::{ color::{ Bg, Fg, Rgb }, cursor, input::TermRead, - raw::IntoRawMode + raw::{ RawTerminal, IntoRawMode } }; use crate::{ color::{ RESET, RESET_BG }, - flag::Flag + error, + flag::Flag, + state::{ Size, State } }; pub static BLOCK: &str = "█"; pub static UHALF: &str = "▀"; /// prints a provided vec of lines to stdout -pub fn draw_lines(lines: Vec, hold: bool) { +pub fn draw_lines(lines: Vec, state: &State) { let mut stdout = io::stdout().into_raw_mode().unwrap(); let count = lines.len() as u16; for _ in 0..count { write!(stdout, "\n").ok(); } write!(stdout, "{}", cursor::Up(count)).ok(); + let hold = state.size == Size::Full; if hold { write!(stdout, "{}{}", cursor::Hide, clear::All).ok(); } let down = cursor::Down(1); @@ -98,22 +104,19 @@ pub fn bg_stripes(colors: Vec>, width: u16, height: u16) -> Vec impl Flag { /// renders a flag to stdout - pub fn draw(self, hold: bool) { + pub fn draw(self, state: &State) { let lines = match self { Flag::Stripes(colors) => { - let (width, height); - if hold { (width, height) = terminal_size().unwrap(); } - else { - height = colors.len() as u16; - width = height * 3; - } + let count = colors.len() as u16; + let (width, height) = state.size.get(count * 3, count); + if height < count { error::too_small(width, height); } fg_stripes(colors, width, height) }, Flag::Lines(lines) => lines }; - draw_lines(lines, hold); + draw_lines(lines, &state); } } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..3c8669a --- /dev/null +++ b/src/error.rs @@ -0,0 +1,24 @@ +use std::process::exit; + +pub fn unmatched_flag(target: String) { + println!("pride: no flag {target}"); + exit(1); +} + + +pub fn size_missing() { + println!("pride: size flag requires a value"); + exit(2); +} + +pub fn size_error(value: &str) { + println!("pride: size '{value}' is invalid"); + exit(2); +} + + +pub fn too_small(width: u16, height: u16) { + println!("pride: this flag must be bigger than {width}x{height}"); + exit(3); +} + diff --git a/src/main.rs b/src/main.rs index cada741..5547a3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,35 @@ //! main method module -use std::{ - io::{ stdout, IsTerminal }, - process::exit -}; - use pico_args::Arguments; mod color; mod complex; mod draw; +mod error; mod flag; mod help; +mod state; mod util; mod variant; -use crate::flag::Flag; +use crate::{ + flag::Flag, + state::State +}; static VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const FLAG_HELP: [&str;2] = [ "-h", "--help" ]; +pub const FLAG_LIST: [&str;2] = [ "-l", "--list" ]; +pub const FLAG_SIZE: [&str;2] = [ "-s", "--size"]; +pub const FLAG_VERSION: [&str;2] = [ "-v", "--version" ]; + fn main() { // collect args let mut args = Arguments::from_env(); // handle help flag - if args.contains(["-h", "--help"]) { + if args.contains(FLAG_HELP) { let target = args.subcommand().unwrap(); if target.is_some() { help::flag_help(&target.unwrap()); } else { help::help_text(); } @@ -32,27 +37,21 @@ fn main() { } // handle list flag - if args.contains(["-l", "--list"]) { + if args.contains(FLAG_LIST) { help::list_text(); return; } // handle version flag - if args.contains(["-v", "--version"]) { + if args.contains(FLAG_VERSION) { println!("pride v{VERSION}"); return; } - if !stdout().is_terminal() { - println!("pride: output must be a terminal"); - exit(2); - } + let state = State::new(&mut args); - // get small flag - let small = args.contains(["-s", "--small"]); - - let subcommand = args.subcommand().unwrap(); - let variant = args.subcommand().unwrap_or(None); + let subcommand = args.subcommand().unwrap(); + let variant = args.subcommand().unwrap(); // get color vec from matched flag let flag: Flag = match subcommand.as_deref() { @@ -65,14 +64,14 @@ fn main() { Some("philadelphia") => variant::philadelphia(), Some("progress") - => complex::progress(small), + => complex::progress(&state), _ => flag::pride() } }, Some("progress") - => complex::progress(small), + => complex::progress(&state), Some("agender") @@ -88,7 +87,7 @@ fn main() { => { match variant.as_deref() { Some("halves" | "side-by-side" | "sbs") - => complex::aroace(small), + => complex::aroace_halves(&state), _ => flag::aroace() } @@ -109,10 +108,10 @@ fn main() { => flag::demigirl(), Some("demiromantic") - => complex::demiromantic(small), + => complex::demiromantic(&state), Some("demisexual") - => complex::demisexual(small), + => complex::demisexual(&state), // Some("disability") // => complex::disability(); @@ -178,15 +177,11 @@ fn main() { _ - => { - println!("pride: no flag '{}'", subcommand.unwrap()); - help::help_text(); - exit(1) - } + => { error::unmatched_flag(subcommand.unwrap()); panic!() } }; // draw flag - flag.draw(!small); + flag.draw(&state); } diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..e86cec5 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,67 @@ +use std::io::{ stdout, IsTerminal }; + +use pico_args::{ Arguments, Error }; +use termion::terminal_size; + +use crate::{ FLAG_SIZE, error }; + +#[derive(PartialEq)] +pub enum Size { + Full, + Small, + Set(u16, u16), + Wide(u16) +} + +impl Size { + pub fn from(value: &str) -> Size { + if value == "small" { return Size::Small; } + let split: Vec<&str> = value.split('x').collect(); + let len = split.len(); + if len == 2 { + if let (Ok(width), Ok(height)) = (str::parse::(split.get(0).unwrap()), str::parse::(split.get(1).unwrap())) { + return Size::Set(width, height); + } + } else if len == 1 { + if let Ok(width) = str::parse::(split.get(0).unwrap()) { + return Size::Wide(width); + } + } + error::size_error(value); + panic!(); + } + pub fn get(&self, width: u16, height: u16) -> (u16, u16) { + match self { + Size::Full => terminal_size().unwrap(), + Size::Set(width, height) => (width.clone(), height.clone()), + Size::Small => (width, height), + Size::Wide(width) => (width.clone(), height) + } + } +} + +pub struct State { + pub size: Size, + pub is_terminal: bool, + pub flag: Option, + pub variant: Option +} + +impl State { + pub fn new(args: &mut Arguments) -> State { + let is_terminal = stdout().is_terminal(); + + let size = match args.value_from_str::<[&str;2], String>(FLAG_SIZE).as_deref() { + Ok(value) => Size::from(value), + Err(Error::MissingOption(_)) | + Err(Error::MissingArgument) => if is_terminal { Size::Full } else { Size::Small }, + _ => { error::size_missing(); panic!() } + }; + + let flag = args.subcommand().unwrap(); + let variant = args.subcommand().unwrap(); + + State { size, is_terminal, flag, variant } + } +} +