diff --git a/Cargo.toml b/Cargo.toml index 2b6969d..83d9966 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pride" -version = "0.1.5" +version = "0.2.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index b3ab496..8a17e47 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,12 @@ A Rust utility to display pride flags in the terminal. -**This project is under heavy construction! It is subject to major structural and -architectural changes. There are no issues with functionality, but I will continue -to make major changes and refactors until the main roadmap is complete.** +A list of currently implemented flags is available on the [project wiki](https://git.vwolfe.io/valerie/pride/wiki/Flags). -Currently supports a variety of stripe flags. +## Dependencies -Under Construction features: -- ["Complex" Flags](https://git.vwolfe.io/valerie/pride/src/branch/complex) +Some Complex renderers utilize [Powerline's](https://github.com/ryanoasis/powerline-extra-symbols) +slant symbols, and therefore require use of a Powerline font, such as [Fira Code](https://github.com/tonsky/FiraCode). ## Libraries diff --git a/src/color.rs b/src/color.rs index dcec15d..55a7c9e 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,5 +1,5 @@ -use termion::color::{ Fg, Rgb, Reset }; +use termion::color::{ Bg, Fg, Rgb, Reset }; pub type Color = Fg; pub type Colors = Vec>; @@ -8,8 +8,9 @@ pub static BLACK: Color = Fg(Rgb(0x00, 0x00, 0x00)); pub static WHITE: Color = Fg(Rgb(0xFF, 0xFF, 0xFF)); pub static RESET: Fg = Fg(Reset); +pub static RESET_BG: Bg = Bg(Reset); -/// converts a hex integer to Fg(Rgb) +/// produces a termion foreground color from the provided integer pub fn rgb(hex: u32) -> Color { // colors should be 0xrrggbb = 0x__rrggbb; drop the most significant byte let [_, r, g, b] = hex.to_be_bytes(); @@ -17,3 +18,10 @@ pub fn rgb(hex: u32) -> Color { Fg(Rgb(r, g, b)) } +/// produces a termion background color from the provided integer +pub fn bg(hex: u32) -> Bg { + let [_, r, g, b] = hex.to_be_bytes(); + + Bg(Rgb(r, g, b)) +} + diff --git a/src/complex.rs b/src/complex.rs new file mode 100644 index 0000000..c3355cd --- /dev/null +++ b/src/complex.rs @@ -0,0 +1,292 @@ +//! flags that require more complex rendering than just scaling colored stripes + +use termion::{ + terminal_size, + + color::{ Bg, Rgb } +}; + +use crate::{ + color::*, + draw, + flag::{ self, Flag }, + util::{ ansi_len, ansi_substr } +}; + +/// vertically stacking eighths +pub static V_EIGHTH: [char; 7] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇']; +/// horizontally stacking eighths +pub static H_EIGHTH: [char; 7] = ['▏', '▎', '▍', '▌', '▋', '▊', '▉']; + +/// shading by intensity +pub static SHADING: [char; 3] = ['░', '▒', '▓']; + +/// 2/1 slope triangle cut in +pub static TRIANGLE_21: [char; 3] = ['', '🭬', '']; + +/// 2/3 slope slant +pub static SLANT_23: [char; 2] = ['🭒', '🭏']; + +pub fn progress(small: bool) -> Flag { + let red = bg(0xE50000); + let orange = bg(0xFF8D00); + let yellow = bg(0xFFEE00); + let green = bg(0x028121); + let blue = bg(0x004CFF); + let purple = bg(0x770088); + + // we need these colors in both fg & bg; just hold the integers for now + let black: u32 = 0; + let brown: u32 = 0x784F17; + let ltblue: u32 = 0xEAACB8; + let pink: u32 = 0x7ACBF5; + let white: u32 = 0xFFFFFF; + + let (width, height) = if small { (18, 6) } else { terminal_size().unwrap() }; + + // create color slices and line buffer + let stripes = [red, orange, yellow, green, blue, purple]; + let chevrons = [white, ltblue, pink, brown, black]; + let mut lines: Vec = Vec::new(); + + // set up stripe index + let mut index = 0; + + /* ok, coming up with procedure: + * - can't rely on bg_stripes; line count, threshold, etc., will need to happen here + * - line count will always be the largest multiple of 6 smaller than height; c = h - (h % 6) + * - chevrons may have larger widths: TODO calc + * - depth will be funky; line depth will need to use "full" depth; (Df - Dl) / Wc = Ic (TODO verify?) + * - switch stripe index on *absolute* line number rather than n + * - chevron building will be BLOCK.repeat(width) + TRIANGLE_21[0] (fg Ic, bg Ic + 1) + * - chevrons[len - 1] will need unique handling to draw stripes + */ + + // set up constraints + let linecount = height - (height % 6); // largest multiple of 6 smaller than height + let full_depth = width / 3; + let chevron_width = (full_depth / 6) - 1; + let direction_thresh = linecount / 2; + let corner = linecount % 2 == 1; + + let thresh = linecount / 6; // stripe threshold; no bg_stripes call! + let mut line_no = 0; // absolute line number; n is relative + + // chevron helper + let build_chevron = | separator: char | -> String { + let mut output = format!( + "{fg}{bg}{stripe}{separator}", + fg = rgb(chevrons[0]), + bg = bg(chevrons[1]), + stripe = draw::BLOCK.repeat( usize::max(chevron_width as usize * 2, 1) ) + ); + let stripe = draw::BLOCK.repeat(chevron_width as usize); + for i in 1..4 { + output += &format!( + "{fg}{bg}{stripe}{separator}", + fg = rgb(chevrons[i]), + bg = bg(chevrons[i + 1]) + ); + } + output += &format!( + "{fg}{stripe}", + fg = rgb(chevrons[4]) + ); + + output + }; + + // piecewise functions: ascent -> peak -> descent + let mut base = build_chevron(TRIANGLE_21[0]); + let base_length = base.len(); + let display_length = ansi_len(&base) + 1; // chevron width will always stay the same; add 1 for the last separator + for n in 0..direction_thresh { + // advance stripe color at stripe threshold by line number + if line_no != 0 && line_no % thresh == 0 { index += 1; } + + // grab our substring constraints + let start = (direction_thresh - n) as usize - 1; + let diff = display_length - start; + + // take substring of chevron line... + let mut line = ansi_substr(&base, start as usize, base_length); + // ... and add the colored stripe + line += &format!( + "{stripe}{separator}{line}", + stripe = stripes[index], + separator = TRIANGLE_21[0], + line = " ".repeat(width as usize - diff) + ); + + lines.push(line); + line_no += 1; + } + if corner { + if line_no % thresh == 0 { index += 1; } + + let base = build_chevron(TRIANGLE_21[1]); + let mut line = ansi_substr(&base, 0, base_length); + line += &format!( + "{stripe}{separator}{line}", + stripe = stripes[index], + separator = TRIANGLE_21[1], + line = " ".repeat(width as usize - display_length) + ); + + lines.push(line); + line_no += 1; + } + base = build_chevron(TRIANGLE_21[2]); + for n in 0..direction_thresh { + if line_no % thresh == 0 { index += 1; } + if index > 5 { break; } + + let start = n as usize; + let diff = display_length - start; + + let mut line = ansi_substr(&base, start, base_length); + line += &format!( + "{stripe}{separator}{line}", + stripe = stripes[index], + separator = TRIANGLE_21[2], + line = " ".repeat(width as usize - diff) + ); + + lines.push(line); + line_no += 1; + } + + Flag::Lines(lines) +} + +// everything below this point is in alphabetical order + +pub fn aroace(small: bool) -> 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 mut lines: Vec = Vec::new(); + + // set up constraints + let linecount = height - (height % 20); + let aro_thresh = linecount / 5; // threshold for aromantic colors + let ace_thresh = linecount / 4; // threshold for asexual colors + let stripe = draw::BLOCK.repeat((width / 2) as usize); + + let mut aro_index = 0; + let mut ace_index = 0; + for n in 0..linecount { + // switch colors on thresholds + if n != 0 { + if n % aro_thresh == 0 { aro_index += 1; } + if n % ace_thresh == 0 { ace_index += 1; } + } + let line = format!("{}{stripe}{}{stripe}", aro[aro_index], ace[ace_index]); + lines.push(line); + } + + Flag::Lines(lines) +} + +fn demi_orientation_render(middle: Bg, bottom: Bg, width: u16, height: u16) -> Vec { + let white = bg(0xFFFFFF); + + let stripes = vec![white, white, middle, bottom, bottom]; + + // initial stripe output buffer + let mut lines: Vec = draw::bg_stripes(stripes, width, height); + + // assemble triangle cut-in + let linecount = lines.len(); + let depth = linecount / 2; + let corner = linecount % 2 == 1; + // piecewise functions: ascending -> peak -> descending + for n in 0..depth { + let line = lines[n].clone(); + + let replacement = format!("{BLACK}{}{}", draw::BLOCK.repeat(n), TRIANGLE_21[0]); + lines[n] = line.replacen(&" ".repeat(n + 1), &replacement, 1); + } + if corner { + let line = lines[depth].clone(); + + let replacement = format!("{BLACK}{}{}", draw::BLOCK.repeat(depth), TRIANGLE_21[1]); + lines[depth] = line.replacen(&" ".repeat(depth + 1), &replacement, 1); + } + let start = depth + (linecount % 2); + for n in 0..depth { + let line = lines[start + n].clone(); + + let size = depth - n - 1; + let replacement = format!("{BLACK}{}{}", draw::BLOCK.repeat(size), TRIANGLE_21[2]); + lines[start + n] = line.replacen(&" ".repeat(size + 1), &replacement, 1); + } + + lines +} + +pub fn demiromantic(small: bool) -> Flag { + let green = bg(0x3DA542); + let gray = bg(0xD2D2D2); + + let (width, height) = if small { (15, 5) } else { terminal_size().unwrap() }; + let lines = demi_orientation_render(green, gray, width, height); + + Flag::Lines(lines) +} + +pub fn demisexual(small: bool) -> Flag { + let purple = bg(0x832FA8); + let grey = bg(0x7B868C); + + let (width, height) = if small { (15, 5) } else { terminal_size().unwrap() }; + let lines = demi_orientation_render(purple, grey, width, height); + + Flag::Lines(lines) +} + +pub fn disability() { + let gray = bg(0x575757); + + let green: u32 = 0x3AAD7D; + let blue: u32 = 0x79BFE0; + let white: u32 = 0xE8E8E8; + let yellow: u32 = 0xEDDB76; + let red: u32 = 0xCD7281; + + let stripes = [red, yellow, white, blue, green]; + + // 2/3 slant stripes with gray background + +} + +pub fn intersex() -> Flag { + let yellow = bg(0xFFDA00); + let purple = rgb(0x7A00AC); + + let block = " "; + let stripe = block.repeat(9); + let part = block.repeat(4); + + let lines = vec![ + format!("{yellow}{stripe}"), + format!("{part}{purple}O{part}"), + format!("{stripe}") + ]; + + Flag::Lines(lines) +} + +pub fn polyamorous() { + let blue = rgb(0x019FE3); + let magenta = rgb(0xE50051); + let purple = rgb(0x340C46); + let yellow = rgb(0x00FCBF); + + // blue / magenta / purple vert + // WHITE isosceles cutin with yellow heart pointed right +} + diff --git a/src/draw.rs b/src/draw.rs index 2b3255e..7ae7ff6 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -4,76 +4,110 @@ use termion::{ terminal_size, clear, + color::{ Bg, Fg, Rgb }, cursor, input::TermRead, raw::IntoRawMode }; -use crate::color::{ RESET, Colors }; -use crate::flag::BLOCK; +use crate::{ + color::{ RESET, RESET_BG }, + flag::Flag +}; -/// draw a fullscreen stripe flag and hold for keypress -pub fn full(colors: Colors) { - // prepare stdin and stdout +pub static BLOCK: &str = "█"; +pub static UHALF: &str = "▀"; + +pub fn draw_lines(lines: Vec, hold: bool) { let mut stdout = io::stdout().into_raw_mode().unwrap(); - let stdin = io::stdin(); - // get constraints - let count = colors.len(); - let (width, height) = terminal_size().unwrap(); - let thresh = height as usize / count; + let count = lines.len() as u16; + for _ in 0..count { write!(stdout, "\n").ok(); } + write!(stdout, "{}", cursor::Up(count)).ok(); - // clear the terminal - write!(stdout, "{}{}", cursor::Hide, clear::All).ok(); + if hold { write!(stdout, "{}{}", cursor::Hide, clear::All).ok(); } + + let down = cursor::Down(1); + for line in lines { + let left = cursor::Left(line.len() as u16); + write!(stdout, "{line}{left}{down}").ok(); + } + + write!(stdout, "{RESET}{RESET_BG}").ok(); stdout.flush().ok(); + if hold { + let stdin = io::stdin(); + for _ in stdin.keys() { break; } + write!(stdout, "{}", clear::All).ok(); + } + write!(stdout, "{}", cursor::Show).ok(); + stdout.flush().ok(); +} - // build terminal width stripe string - let stripe = BLOCK.repeat(width as usize); +pub fn fg_stripes(colors: Vec>, width: u16, height: u16) -> Vec { + let width = width as usize; + let height = height as usize; + let count = colors.len(); + + let thresh = height / count; + + let stripe = BLOCK.repeat(width); + let mut output = Vec::new(); // create our color index let mut index = 0; - // for every terminal row... - for n in 0..(height as usize) { - // ... increment our index at color change threshold + for n in 0..height { if n != 0 && n % thresh == 0 { index += 1; // and break if out of bounds if index >= count { break; } } - // ... draw the stripe with color at index - write!( - stdout, - "{color}{stripe}{RESET}", - color = colors[index] - ).ok(); + let color = colors[index]; + output.push(format!("{color}{stripe}")); } - // flush stdout - stdout.flush().ok(); - // wait for keypress - for _ in stdin.keys() { break; } - write!(stdout, "{}{}", cursor::Show, clear::All).ok(); - stdout.flush().ok(); + output } - -/// draws a small stripe flag -pub fn small(colors: Colors) { - // prepare stdout - let mut stdout = io::stdout(); - - // get constraints +pub fn bg_stripes(colors: Vec>, width: u16, height: u16) -> Vec { + let width = width as usize; + let height = height as usize; let count = colors.len(); - let width = count * 3; - // build small stripe string - let stripe = BLOCK.repeat(width); + let thresh = height / count; - // print a stripe for all colors - for color in colors { - println!("{color}{stripe}"); + let stripe = " ".repeat(width); + let mut output = Vec::new(); + + let mut index = 0; + for n in 0..height { + if n != 0 && n % thresh == 0 { + index += 1; + if index >= count { break; } + } + let color = colors[index]; + output.push(format!("{color}{stripe}")); + } + + output +} + +impl Flag { + pub fn draw(self, hold: bool) { + 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; + } + fg_stripes(colors, width, height) + }, + Flag::Lines(lines) + => lines + }; + draw_lines(lines, hold); } - // reset our foreground color to play nice and flush stdout - print!("{RESET}"); - stdout.flush().ok(); } diff --git a/src/flag.rs b/src/flag.rs index f8d0b0d..d20247d 100644 --- a/src/flag.rs +++ b/src/flag.rs @@ -3,9 +3,13 @@ use crate::color::*; -pub static BLOCK: &str = "█"; +pub enum Flag { + Stripes(Colors), + Lines(Vec) +} -pub fn pride() -> Colors { + +pub fn pride() -> Flag { let red = rgb(0xE50000); let orange = rgb(0xFF8D00); let yellow = rgb(0xFFEE00); @@ -13,58 +17,58 @@ pub fn pride() -> Colors { let blue = rgb(0x004CFF); let purple = rgb(0x770088); - vec![red, orange, yellow, green, blue, purple] + Flag::Stripes(vec![red, orange, yellow, green, blue, purple]) } -pub fn transgender() -> Colors { +pub fn transgender() -> Flag { let pink = rgb(0x7ACBF5); let blue = rgb(0xEAACB8); - vec![pink, blue, WHITE, blue, pink] + Flag::Stripes(vec![pink, blue, WHITE, blue, pink]) } // everything below here is alphabetical -pub fn agender() -> Colors { +pub fn agender() -> Flag { let gray = rgb(0xB9B9B9); let green = rgb(0xB8F483); - vec![BLACK, gray, WHITE, green, WHITE, gray, BLACK] + Flag::Stripes(vec![BLACK, gray, WHITE, green, WHITE, gray, BLACK]) } -pub fn aromantic() -> Colors { +pub fn aromantic() -> Flag { let green = rgb(0x3BA740); let lime = rgb(0xA8D47A); let grey = rgb(0xABABAB); - vec![green, lime, WHITE, grey, BLACK] + Flag::Stripes(vec![green, lime, WHITE, grey, BLACK]) } -pub fn asexual() -> Colors { +pub fn asexual() -> Flag { let grey = rgb(0xA4A4A4); let purple = rgb(0x810081); - vec![BLACK, grey, WHITE, purple] + Flag::Stripes(vec![BLACK, grey, WHITE, purple]) } -pub fn bigender() -> Colors { +pub fn bigender() -> Flag { let pink = rgb(0xE676A6); let yellow = rgb(0xF9F04C); let purple = rgb(0xAB6BBB); let blue = rgb(0x6D96DC); - vec![pink, yellow, WHITE, purple, blue] + Flag::Stripes(vec![pink, yellow, WHITE, purple, blue]) } -pub fn bisexual() -> Colors { +pub fn bisexual() -> Flag { let magenta = rgb(0xC42A6F); let purple = rgb(0x915392); let blue = rgb(0x1437A2); - vec![magenta, magenta, purple, blue, blue] + Flag::Stripes(vec![magenta, magenta, purple, blue, blue]) } -pub fn gay() -> Colors { +pub fn gay() -> Flag { let green1 = rgb(0x00906D); let green2 = rgb(0x00D1A7); let green3 = rgb(0x7EEBC1); @@ -72,68 +76,68 @@ pub fn gay() -> Colors { let blue2 = rgb(0x5543D3); let blue3 = rgb(0x461280); - vec![green1, green2, green3, WHITE, blue1, blue2, blue3] + Flag::Stripes(vec![green1, green2, green3, WHITE, blue1, blue2, blue3]) } -pub fn genderfluid() -> Colors { +pub fn genderfluid() -> Flag { let pink = rgb(0xFF75A2); let violet = rgb(0xBE18D6); let blue = rgb(0x333EBD); - vec![pink, WHITE, violet, BLACK, blue] + Flag::Stripes(vec![pink, WHITE, violet, BLACK, blue]) } -pub fn gender_nonconforming() -> Colors { +pub fn gender_nonconforming() -> Flag { let purple = rgb(0x50284D); let magenta = rgb(0x96467B); let blue = rgb(0x5C96F7); - vec![purple, purple, magenta, blue, WHITE, blue, magenta, purple, purple] + Flag::Stripes(vec![purple, purple, magenta, blue, WHITE, blue, magenta, purple, purple]) } -pub fn genderqueer() -> Colors { +pub fn genderqueer() -> Flag { let purple = rgb(0xB899DF); let green = rgb(0x6B8E3B); - vec![purple, WHITE, green] + Flag::Stripes(vec![purple, WHITE, green]) } -pub fn gendervoid() -> Colors { +pub fn gendervoid() -> Flag { let navy = rgb(0x08114A); let gray = rgb(0x4A484B); - vec![navy, gray, BLACK, gray, navy] + Flag::Stripes(vec![navy, gray, BLACK, gray, navy]) } -pub fn lesbian() -> Colors { +pub fn lesbian() -> Flag { let red = rgb(0xD62800); let orange = rgb(0xFF9B56); let pink = rgb(0xD462A6); let magenta = rgb(0xA40062); - vec![red, orange, WHITE, pink, magenta] + Flag::Stripes(vec![red, orange, WHITE, pink, magenta]) } -pub fn multigender() -> Colors { +pub fn multigender() -> Flag { let blue = rgb(0x3F47CC); let ltblue = rgb(0x01A4E9); let orange = rgb(0xFB7F27); - vec![blue, ltblue, orange, ltblue, blue] + Flag::Stripes(vec![blue, ltblue, orange, ltblue, blue]) } -pub fn nonbinary() -> Colors { +pub fn nonbinary() -> Flag { let yellow = rgb(0xFFF433); let purple = rgb(0x9B59D0); - vec![yellow, WHITE, purple, BLACK] + Flag::Stripes(vec![yellow, WHITE, purple, BLACK]) } -pub fn pansexual() -> Colors { +pub fn pansexual() -> Flag { let magenta = rgb(0xFF1B8D); let yellow = rgb(0xFFDA00); let cyan = rgb(0x1BB3FF); - vec![magenta, yellow, cyan] + Flag::Stripes(vec![magenta, yellow, cyan]) } diff --git a/src/help.rs b/src/help.rs index 12eb9ae..fb06f4d 100644 --- a/src/help.rs +++ b/src/help.rs @@ -29,6 +29,7 @@ pub fn list_text() { flag list: agender agender pride flag aro, aromantic aromantic pride flag + aroace aromantic-asexual pride flag ace, asexual asexual pride flag bigender bigender pride flag bi, bisexual bisexual pride flag @@ -42,6 +43,7 @@ flag list: nb, nonbinary nonbinary pride flag pan, pansexual pansexual pride flag pride, rainbow six-color rainbow flag + progress progress arrow rainbow flag trans, transgender transgender pride flag"); } @@ -58,7 +60,8 @@ variants: 8-color Gilbert Baker's original 1978 flag with 8 stripes gilbert-baker sex-and-magic - philadelphia The 2017 Philadelphia Pride flag with black and brown stripes"); + philadelphia The 2017 Philadelphia Pride flag with black and brown stripes + progress The 2018 Progess rainbow pride flag designed by Daniel Quasar"); }, "transgender" | "trans" @@ -78,7 +81,9 @@ names: variants: 7-color 7-stripe flag, also designed in 2018 by Emily Gwen"); - } + }, + "progress" + => { println!("Daniel Quasar's 2018 Progress rainbow pride flag.\n\nnames:\n 'progress'"); } _ => help_text() diff --git a/src/main.rs b/src/main.rs index 555f64d..853db1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,17 @@ use std::process::exit; use pico_args::Arguments; mod color; +mod complex; mod draw; mod flag; +<<<<<<< HEAD mod help; +======= +mod util; +>>>>>>> main mod variant; -use crate::color::Colors; +use crate::flag::Flag; static VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -42,7 +47,7 @@ fn main() { let subcommand = args.subcommand().unwrap(); // get color vec from matched flag - let colors: Colors = match subcommand.as_deref() { + let flag: Flag = match subcommand.as_deref() { Some("pride" | "rainbow") | None => { @@ -52,6 +57,8 @@ fn main() { => variant::gilbert_baker(), Some("philadelphia") => variant::philadelphia(), + Some("progress") + => complex::progress(small), _ => flag::pride() } @@ -70,12 +77,24 @@ fn main() { Some("asexual" | "ace") => flag::asexual(), + Some("aroace" | "aromantic-asexual") + => complex::aroace(small), + Some("bigender") => flag::bigender(), Some("bisexual" | "bi") => flag::bisexual(), + Some("demiromantic") + => complex::demiromantic(small), + + Some("demisexual") + => complex::demisexual(small), + +// Some("disability") +// => complex::disability(); + Some("gay" | "mlm") => flag::gay(), @@ -91,6 +110,9 @@ fn main() { Some("gendervoid") => flag::gendervoid(), + Some("intersex") + => complex::intersex(), + Some("lesbian") => { let variant = args.subcommand().unwrap_or(None); @@ -111,11 +133,16 @@ fn main() { Some("pansexual" | "pan") => flag::pansexual(), - _ => { help::help_text(); exit(1) } // (or die) +// Some("poly" | "polyamorous" | "polyamory") +// => complex::polyamorous(), + + Some("progress") + => flag::progress(), + + _ => { help_text(); exit(1) } }; - if small { draw::small(colors); } - else { draw::full(colors); } + flag.draw(!small); } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..cd198d9 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,52 @@ + +/// gets the substring of displayed characters of an ANSI formatted string +pub fn ansi_substr(source: &str, start: usize, end: usize) -> String { + // init output string + let mut output = String::new(); + + // trackers + let mut escaped = false; + let mut index = 0; + for character in source.chars() { + // escape character delimits start and end of ansi sequences + if character == '\u{1B}' { + escaped = true; + output.push(character); + } + // push ALL escaped characters + if escaped { + output.push(character); + // and unset esc on m + if character == 'm' { escaped = false; } + } + // non-escaped characters must obey bounds + else { + if index < start { + index += 1; + continue; + } + + output.push(character); + index += 1; + + if index > end { break; } + } + } + output +} + +/// gets the number of displayed characters in an ANSI formatted string +pub fn ansi_len(source: &str) -> usize { + let mut output = 0; + let mut escaped = false; + + for character in source.chars() { + if character == '\u{1B}' { escaped = true; } + + if !escaped { output += 1; } + else if character == 'm' { escaped = false; } + } + + output +} + diff --git a/src/variant.rs b/src/variant.rs index aee7cd9..91a23a5 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -3,10 +3,10 @@ use crate::{ color::*, - flag + flag::{ self, Flag } }; -pub fn gilbert_baker() -> Colors { +pub fn gilbert_baker() -> Flag { let pink = rgb(0xFF69B4); // sex let red = rgb(0xFF0000); // life let orange = rgb(0xFF8F00); // healing @@ -16,20 +16,26 @@ pub fn gilbert_baker() -> Colors { let indigo = rgb(0x3E0099); // serenity let purple = rgb(0x8F008F); // spirit - vec![pink, red, orange, yellow, green, cyan, indigo, purple] + Flag::Stripes(vec![pink, red, orange, yellow, green, cyan, indigo, purple]) } -pub fn philadelphia() -> Colors { +pub fn philadelphia() -> Flag { let brown = rgb(0x784F17); - let mut output = flag::pride(); - output.insert(0, BLACK); - output.insert(1, brown); + let base = flag::pride(); + let mut colors = match base { + Flag::Stripes(inner) + => inner, + _ + => { panic!("impossible enum variant"); } + }; + colors.insert(0, BLACK); + colors.insert(1, brown); - output + Flag::Stripes(colors) } -pub fn lesbian_7() -> Colors { +pub fn lesbian_7() -> Flag { let orange1 = rgb(0xD52D00); // gender non-conformity let orange2 = rgb(0xEF7627); // independence let orange3 = rgb(0xFF9A56); // community @@ -38,6 +44,6 @@ pub fn lesbian_7() -> Colors { let pink2 = rgb(0xB55690); // love and sex let pink3 = rgb(0xA30262); // femininity - vec![orange1, orange2, orange3, WHITE, pink1, pink2, pink3] + Flag::Stripes(vec![orange1, orange2, orange3, WHITE, pink1, pink2, pink3]) }