Compare commits

...

8 commits

10 changed files with 328 additions and 213 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "pride" name = "pride"
version = "0.3.1" version = "0.4.1"
edition = "2021" edition = "2021"
authors = [ "Valerie Wolfe <sleeplessval@gmail.com>" ] authors = [ "Valerie Wolfe <sleeplessval@gmail.com>" ]
description = "Pride flags in the terminal." description = "Pride flags in the terminal."

View file

@ -6,7 +6,8 @@
.Nd shows pride flags in the terminal .Nd shows pride flags in the terminal
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl hlvs .Op Fl hlv
.Op Fl s Op Ar size
.Op Ar flag .Op Ar flag
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@ -17,6 +18,8 @@ displays pride flags in the terminal using ANSI truecolor sequences.
Shows a brief help text. Shows a brief help text.
.It Fl l , Fl -list .It Fl l , Fl -list
Shows a list of available pride flags. Shows a list of available pride flags.
.It Fl s , Fl -size Op Ar size
Scales the flag to the given size: no value or '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 .It Fl v , Fl -version
Shows version information. Shows version information.
.It Ar flag .It Ar flag
@ -47,7 +50,7 @@ The gay men pride flag designed by @gayflagblog@tumblr.com in 2019.
.It Ic gender-nonconforming, gendernonconforming, gnc .It Ic gender-nonconforming, gendernonconforming, gnc
.It Ic genderqueer .It Ic genderqueer
.It Ic gendervoid .It Ic gendervoid
.It Ic lesbian .It Ic lesbian, wlw
The 5-stripe lesbian pride flag designed by Emily Gwen in 2018. The 5-stripe lesbian pride flag designed by Emily Gwen in 2018.
.Pp .Pp
VARIANTS VARIANTS
@ -82,10 +85,22 @@ The transgender pride flag designed by Monica Helms in 1999.
.Sh EXIT STATUS .Sh EXIT STATUS
.Bl -tag -width Ds .Bl -tag -width Ds
.It 1 .It 1
Unmatched flag name. Unmatched
.Ar flag
name.
.It 2 .It 2
Not running in a terminal. Failed to parse
.Ar size .
.It 3
The provided
.Ar size
is too small to render.
.El .El
.Sh EXAMPLES
Create a full-width banner flag using tput:
.Pp
.Dl $ pride -s `tput cols`
.Pp
.Sh AUTHORS .Sh AUTHORS
.An -nosplit .An -nosplit
.An Valerie Wolfe Aq Mt sleeplessval@gmail.com .An Valerie Wolfe Aq Mt sleeplessval@gmail.com

254
sbom.xml
View file

@ -1,13 +1,13 @@
{ {
"SPDXID": "SPDXRef-DOCUMENT", "SPDXID": "SPDXRef-DOCUMENT",
"creationInfo": { "creationInfo": {
"created": "2024-03-14T21:10:21.186Z", "created": "2024-07-05T20:16:51.403Z",
"creators": [ "creators": [
"Tool: cargo-sbom-v0.8.4" "Tool: cargo-sbom-v0.8.4"
] ]
}, },
"dataLicense": "CC0-1.0", "dataLicense": "CC0-1.0",
"documentNamespace": "https://spdx.org/spdxdocs/pride-4bb3085e-02b0-4995-aab3-241ff2d827ee", "documentNamespace": "https://spdx.org/spdxdocs/pride-6150510e-b518-43f0-aa53-3e2795909cc2",
"files": [ "files": [
{ {
"SPDXID": "SPDXRef-File-pride", "SPDXID": "SPDXRef-File-pride",
@ -20,88 +20,6 @@
], ],
"name": "pride", "name": "pride",
"packages": [ "packages": [
{
"SPDXID": "SPDXRef-Package-numtoa-0.1.0",
"description": "Convert numbers into stack-allocated byte arrays",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/numtoa@0.1.0",
"referenceType": "purl"
}
],
"licenseConcluded": "MIT OR Apache-2.0",
"licenseDeclared": "MIT OR Apache-2.0",
"name": "numtoa",
"versionInfo": "0.1.0"
},
{
"SPDXID": "SPDXRef-Package-termion-2.0.3",
"description": "A bindless library for manipulating terminals.",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/termion@2.0.3",
"referenceType": "purl"
}
],
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"name": "termion",
"versionInfo": "2.0.3"
},
{
"SPDXID": "SPDXRef-Package-pico-args-0.5.0",
"description": "An ultra simple CLI arguments parser.",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/pico-args@0.5.0",
"referenceType": "purl"
}
],
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"name": "pico-args",
"versionInfo": "0.5.0"
},
{
"SPDXID": "SPDXRef-Package-libc-0.2.151",
"description": "Raw FFI bindings to platform libraries like libc.\n",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/libc@0.2.151",
"referenceType": "purl"
}
],
"homepage": "https://github.com/rust-lang/libc",
"licenseConcluded": "MIT OR Apache-2.0",
"licenseDeclared": "MIT OR Apache-2.0",
"name": "libc",
"versionInfo": "0.2.151"
},
{
"SPDXID": "SPDXRef-Package-bitflags-2.4.1",
"description": "A macro to generate structures which behave like bitflags.\n",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/bitflags@2.4.1",
"referenceType": "purl"
}
],
"homepage": "https://github.com/bitflags/bitflags",
"licenseConcluded": "MIT OR Apache-2.0",
"licenseDeclared": "MIT OR Apache-2.0",
"name": "bitflags",
"versionInfo": "2.4.1"
},
{ {
"SPDXID": "SPDXRef-Package-redox_syscall-0.4.1", "SPDXID": "SPDXRef-Package-redox_syscall-0.4.1",
"description": "A Rust library to access raw Redox system calls", "description": "A Rust library to access raw Redox system calls",
@ -118,6 +36,39 @@
"name": "redox_syscall", "name": "redox_syscall",
"versionInfo": "0.4.1" "versionInfo": "0.4.1"
}, },
{
"SPDXID": "SPDXRef-Package-libredox-0.0.2",
"description": "Redox stable ABI",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/libredox@0.0.2",
"referenceType": "purl"
}
],
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"name": "libredox",
"versionInfo": "0.0.2"
},
{
"SPDXID": "SPDXRef-Package-libc-0.2.155",
"description": "Raw FFI bindings to platform libraries like libc.\n",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/libc@0.2.155",
"referenceType": "purl"
}
],
"homepage": "https://github.com/rust-lang/libc",
"licenseConcluded": "MIT OR Apache-2.0",
"licenseDeclared": "MIT OR Apache-2.0",
"name": "libc",
"versionInfo": "0.2.155"
},
{ {
"SPDXID": "SPDXRef-Package-bitflags-1.3.2", "SPDXID": "SPDXRef-Package-bitflags-1.3.2",
"description": "A macro to generate structures which behave like bitflags.\n", "description": "A macro to generate structures which behave like bitflags.\n",
@ -135,16 +86,6 @@
"name": "bitflags", "name": "bitflags",
"versionInfo": "1.3.2" "versionInfo": "1.3.2"
}, },
{
"SPDXID": "SPDXRef-Package-pride-0.3.1",
"description": "Pride flags in the terminal.",
"downloadLocation": "NONE",
"homepage": "https://git.vwolfe.io/valerie/pride",
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"name": "pride",
"versionInfo": "0.3.1"
},
{ {
"SPDXID": "SPDXRef-Package-redox_termios-0.1.3", "SPDXID": "SPDXRef-Package-redox_termios-0.1.3",
"description": "A Rust library to access Redox termios functions", "description": "A Rust library to access Redox termios functions",
@ -162,42 +103,101 @@
"versionInfo": "0.1.3" "versionInfo": "0.1.3"
}, },
{ {
"SPDXID": "SPDXRef-Package-libredox-0.0.2", "SPDXID": "SPDXRef-Package-numtoa-0.1.0",
"description": "Redox stable ABI", "description": "Convert numbers into stack-allocated byte arrays",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index", "downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [ "externalRefs": [
{ {
"referenceCategory": "PACKAGE-MANAGER", "referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/libredox@0.0.2", "referenceLocator": "pkg:cargo/numtoa@0.1.0",
"referenceType": "purl"
}
],
"licenseConcluded": "MIT OR Apache-2.0",
"licenseDeclared": "MIT OR Apache-2.0",
"name": "numtoa",
"versionInfo": "0.1.0"
},
{
"SPDXID": "SPDXRef-Package-termion-3.0.0",
"description": "A bindless library for manipulating terminals.",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/termion@3.0.0",
"referenceType": "purl" "referenceType": "purl"
} }
], ],
"licenseConcluded": "MIT", "licenseConcluded": "MIT",
"licenseDeclared": "MIT", "licenseDeclared": "MIT",
"name": "libredox", "name": "termion",
"versionInfo": "0.0.2" "versionInfo": "3.0.0"
},
{
"SPDXID": "SPDXRef-Package-bitflags-2.5.0",
"description": "A macro to generate structures which behave like bitflags.\n",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/bitflags@2.5.0",
"referenceType": "purl"
}
],
"homepage": "https://github.com/bitflags/bitflags",
"licenseConcluded": "MIT OR Apache-2.0",
"licenseDeclared": "MIT OR Apache-2.0",
"name": "bitflags",
"versionInfo": "2.5.0"
},
{
"SPDXID": "SPDXRef-Package-pride-0.4.1",
"description": "Pride flags in the terminal.",
"downloadLocation": "NONE",
"homepage": "https://git.vwolfe.io/valerie/pride",
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"name": "pride",
"versionInfo": "0.4.1"
},
{
"SPDXID": "SPDXRef-Package-pico-args-0.5.0",
"description": "An ultra simple CLI arguments parser.",
"downloadLocation": "registry+https://github.com/rust-lang/crates.io-index",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:cargo/pico-args@0.5.0",
"referenceType": "purl"
}
],
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"name": "pico-args",
"versionInfo": "0.5.0"
} }
], ],
"relationships": [ "relationships": [
{ {
"relatedSpdxElement": "SPDXRef-Package-libc-0.2.151", "relatedSpdxElement": "SPDXRef-Package-numtoa-0.1.0",
"relationshipType": "DEPENDS_ON", "relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-termion-2.0.3" "spdxElementId": "SPDXRef-Package-termion-3.0.0"
}, },
{ {
"relatedSpdxElement": "SPDXRef-Package-libc-0.2.151", "relatedSpdxElement": "SPDXRef-Package-pico-args-0.5.0",
"relationshipType": "DEPENDS_ON", "relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-libredox-0.0.2" "spdxElementId": "SPDXRef-Package-pride-0.4.1"
}, },
{ {
"relatedSpdxElement": "SPDXRef-Package-bitflags-2.4.1", "relatedSpdxElement": "SPDXRef-Package-libc-0.2.155",
"relationshipType": "DEPENDS_ON", "relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-libredox-0.0.2" "spdxElementId": "SPDXRef-Package-termion-3.0.0"
}, },
{ {
"relatedSpdxElement": "SPDXRef-Package-pride-0.3.1", "relatedSpdxElement": "SPDXRef-Package-termion-3.0.0",
"relationshipType": "GENERATED_FROM", "relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-File-pride" "spdxElementId": "SPDXRef-Package-pride-0.4.1"
}, },
{ {
"relatedSpdxElement": "SPDXRef-Package-bitflags-1.3.2", "relatedSpdxElement": "SPDXRef-Package-bitflags-1.3.2",
@ -205,34 +205,34 @@
"spdxElementId": "SPDXRef-Package-redox_syscall-0.4.1" "spdxElementId": "SPDXRef-Package-redox_syscall-0.4.1"
}, },
{ {
"relatedSpdxElement": "SPDXRef-Package-pico-args-0.5.0", "relatedSpdxElement": "SPDXRef-Package-pride-0.4.1",
"relationshipType": "DEPENDS_ON", "relationshipType": "GENERATED_FROM",
"spdxElementId": "SPDXRef-Package-pride-0.3.1" "spdxElementId": "SPDXRef-File-pride"
},
{
"relatedSpdxElement": "SPDXRef-Package-redox_termios-0.1.3",
"relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-termion-2.0.3"
},
{
"relatedSpdxElement": "SPDXRef-Package-numtoa-0.1.0",
"relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-termion-2.0.3"
},
{
"relatedSpdxElement": "SPDXRef-Package-termion-2.0.3",
"relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-pride-0.3.1"
}, },
{ {
"relatedSpdxElement": "SPDXRef-Package-libredox-0.0.2", "relatedSpdxElement": "SPDXRef-Package-libredox-0.0.2",
"relationshipType": "DEPENDS_ON", "relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-termion-2.0.3" "spdxElementId": "SPDXRef-Package-termion-3.0.0"
}, },
{ {
"relatedSpdxElement": "SPDXRef-Package-redox_syscall-0.4.1", "relatedSpdxElement": "SPDXRef-Package-redox_syscall-0.4.1",
"relationshipType": "DEPENDS_ON", "relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-libredox-0.0.2" "spdxElementId": "SPDXRef-Package-libredox-0.0.2"
},
{
"relatedSpdxElement": "SPDXRef-Package-redox_termios-0.1.3",
"relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-termion-3.0.0"
},
{
"relatedSpdxElement": "SPDXRef-Package-libc-0.2.155",
"relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-libredox-0.0.2"
},
{
"relatedSpdxElement": "SPDXRef-Package-bitflags-2.5.0",
"relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-Package-libredox-0.0.2"
} }
], ],
"spdxVersion": "SPDX-2.3" "spdxVersion": "SPDX-2.3"

View file

@ -9,7 +9,9 @@ use termion::{
use crate::{ use crate::{
color::*, color::*,
draw, draw,
error,
flag::{ self, Flag }, flag::{ self, Flag },
state::State,
util::{ ansi_len, ansi_substr } util::{ ansi_len, ansi_substr }
}; };
@ -27,7 +29,7 @@ pub static TRIANGLE_21: [char; 3] = ['', '🭬', ''];
/// 2/3 slope slant /// 2/3 slope slant
pub static SLANT_23: [char; 2] = ['🭒', '🭏']; pub static SLANT_23: [char; 2] = ['🭒', '🭏'];
pub fn progress(small: bool) -> Flag { pub fn progress(state: &State) -> Flag {
let red = bg(0xE50000); let red = bg(0xE50000);
let orange = bg(0xFF8D00); let orange = bg(0xFF8D00);
let yellow = bg(0xFFEE00); let yellow = bg(0xFFEE00);
@ -42,7 +44,8 @@ pub fn progress(small: bool) -> Flag {
let pink: u32 = 0x7ACBF5; let pink: u32 = 0x7ACBF5;
let white: u32 = 0xFFFFFF; 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 || width < 6 { error::too_small(width, height); }
// create color slices and line buffer // create color slices and line buffer
let stripes = [red, orange, yellow, green, blue, purple]; let stripes = [red, orange, yellow, green, blue, purple];
@ -55,7 +58,7 @@ pub fn progress(small: bool) -> Flag {
// set up constraints // set up constraints
let linecount = height - (height % 6); // largest multiple of 6 smaller than height let linecount = height - (height % 6); // largest multiple of 6 smaller than height
let full_depth = width / 3; let full_depth = width / 3;
let chevron_width = (full_depth / 6) - 1; let chevron_width = if full_depth > 6 { (full_depth / 6) - 1 } else { 0 };
let direction_thresh = linecount / 2; let direction_thresh = linecount / 2;
let corner = linecount % 2 == 1; let corner = linecount % 2 == 1;
@ -96,17 +99,14 @@ pub fn progress(small: bool) -> Flag {
// grab our substring constraints // grab our substring constraints
let start = (direction_thresh - n) as usize - 1; let start = (direction_thresh - n) as usize - 1;
let diff = display_length - start; let diff = if display_length >= start { display_length - start } else { 0 };
// take substring of chevron line... // take substring of chevron line...
let mut line = ansi_substr(&base, start as usize, base_length); let mut line = ansi_substr(&base, start as usize, base_length);
line += &stripes[index].to_string();
if diff > 0 { line.push(TRIANGLE_21[0]); }
// ... and add the colored stripe // ... and add the colored stripe
line += &format!( line += &" ".repeat(width as usize - diff);
"{stripe}{separator}{line}",
stripe = stripes[index],
separator = TRIANGLE_21[0],
line = " ".repeat(width as usize - diff)
);
lines.push(line); lines.push(line);
line_no += 1; line_no += 1;
@ -132,15 +132,12 @@ pub fn progress(small: bool) -> Flag {
if index > 5 { break; } if index > 5 { break; }
let start = n as usize; let start = n as usize;
let diff = display_length - start; let diff = if display_length >= start { display_length - start } else { 0 };
let mut line = ansi_substr(&base, start, base_length); let mut line = ansi_substr(&base, start, base_length);
line += &format!( line += &stripes[index].to_string();
"{stripe}{separator}{line}", if diff > 0 { line.push(TRIANGLE_21[2]); }
stripe = stripes[index], line += &" ".repeat(width as usize - diff);
separator = TRIANGLE_21[2],
line = " ".repeat(width as usize - diff)
);
lines.push(line); lines.push(line);
line_no += 1; line_no += 1;
@ -151,12 +148,13 @@ pub fn progress(small: bool) -> Flag {
// everything below this point is in alphabetical order // 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 // pull colors from aro & ace stripe flags
let Flag::Stripes(aro) = flag::aromantic() else { panic!() }; let Flag::Stripes(aro) = flag::aromantic() else { panic!() };
let Flag::Stripes(ace) = flag::asexual() 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<String> = Vec::new(); let mut lines: Vec<String> = Vec::new();
@ -185,6 +183,8 @@ pub fn aroace(small: bool) -> Flag {
fn demi_orientation_render(middle: Bg<Rgb>, bottom: Bg<Rgb>, width: u16, height: u16) -> Vec<String> { fn demi_orientation_render(middle: Bg<Rgb>, bottom: Bg<Rgb>, width: u16, height: u16) -> Vec<String> {
let white = bg(0xFFFFFF); let white = bg(0xFFFFFF);
if height < 5 { error::too_small(width, height); }
let stripes = vec![white, white, middle, bottom, bottom]; let stripes = vec![white, white, middle, bottom, bottom];
// initial stripe output buffer // initial stripe output buffer
@ -219,21 +219,21 @@ fn demi_orientation_render(middle: Bg<Rgb>, bottom: Bg<Rgb>, width: u16, height:
lines lines
} }
pub fn demiromantic(small: bool) -> Flag { pub fn demiromantic(state: &State) -> Flag {
let green = bg(0x3DA542); let green = bg(0x3DA542);
let gray = bg(0xD2D2D2); 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); let lines = demi_orientation_render(green, gray, width, height);
Flag::Lines(lines) Flag::Lines(lines)
} }
pub fn demisexual(small: bool) -> Flag { pub fn demisexual(state: &State) -> Flag {
let purple = bg(0x832FA8); let purple = bg(0x832FA8);
let grey = bg(0x7B868C); 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); let lines = demi_orientation_render(purple, grey, width, height);
Flag::Lines(lines) Flag::Lines(lines)
@ -274,7 +274,7 @@ pub fn intersex() -> Flag {
} }
pub fn polyamory(small: bool) -> Flag { pub fn polyamory(state: &State) -> Flag {
let blue = rgb(0x019FE3); let blue = rgb(0x019FE3);
let magenta = rgb(0xE50051); let magenta = rgb(0xE50051);
let purple = rgb(0x340C46); let purple = rgb(0x340C46);
@ -285,7 +285,8 @@ pub fn polyamory(small: bool) -> Flag {
let semicircle = '\u{E0B6}'; let semicircle = '\u{E0B6}';
let separators = ['\u{E0BE}', '\u{E0BA}']; 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 // create stripe array and line buffer
let stripes = [magenta, purple]; // only stripes 2 and 3 need tracked let stripes = [magenta, purple]; // only stripes 2 and 3 need tracked

View file

@ -1,6 +1,9 @@
//! render handling code //! render handling code
use std::io::{ self, Write }; use std::io::{
self,
Write
};
use termion::{ use termion::{
terminal_size, terminal_size,
@ -9,44 +12,58 @@ use termion::{
color::{ Bg, Fg, Rgb }, color::{ Bg, Fg, Rgb },
cursor, cursor,
input::TermRead, input::TermRead,
raw::IntoRawMode raw::{ RawTerminal, IntoRawMode }
}; };
use crate::{ use crate::{
color::{ RESET, RESET_BG }, color::{ RESET, RESET_BG },
flag::Flag error,
flag::Flag,
state::{ Size, State }
}; };
pub static BLOCK: &str = ""; pub static BLOCK: &str = "";
pub static UHALF: &str = ""; pub static UHALF: &str = "";
/// prints a provided vec of lines to stdout /// prints a provided vec of lines to stdout
pub fn draw_lines(lines: Vec<String>, hold: bool) { pub fn draw_full(lines: Vec<String>) {
let mut stdout = io::stdout().into_raw_mode().unwrap(); let mut stdout = io::stdout().into_raw_mode().unwrap();
// get in position for draw
let count = lines.len() as u16; let count = lines.len() as u16;
for _ in 0..count { write!(stdout, "\n").ok(); } for _ in 0..count { write!(stdout, "\n").ok(); }
write!(stdout, "{}", cursor::Up(count)).ok(); write!(stdout, "{}", cursor::Up(count)).ok();
if hold { write!(stdout, "{}{}", cursor::Hide, clear::All).ok(); } // clear screen and hide cursor
write!(stdout, "{}{}", cursor::Hide, clear::All).ok();
// write lines
let down = cursor::Down(1); let down = cursor::Down(1);
for line in lines { for line in lines {
let left = cursor::Left(line.len() as u16); let left = cursor::Left(line.len() as u16);
write!(stdout, "{line}{left}{down}").ok(); write!(stdout, "{line}{left}{down}").ok();
} }
// clear formatting and flush buffer
write!(stdout, "{RESET}{RESET_BG}").ok(); write!(stdout, "{RESET}{RESET_BG}").ok();
stdout.flush().ok(); stdout.flush().ok();
if hold {
let stdin = io::stdin(); // hold for input
for _ in stdin.keys() { break; } let stdin = io::stdin();
write!(stdout, "{}", clear::All).ok(); for _ in stdin.keys() { break; }
}
write!(stdout, "{}", cursor::Show).ok(); // clear and show cursor
write!(stdout, "{}{}", clear::All, cursor::Show).ok();
stdout.flush().ok(); stdout.flush().ok();
} }
pub fn draw_lines(lines: Vec<String>, state: &State) {
match state.size {
Size::Full => draw_full(lines),
_ => for line in lines { println!("{line}{RESET}{RESET_BG}") }
}
}
/// generates lines for foreground colors provided as a vec of strings for the draw_lines method /// generates lines for foreground colors provided as a vec of strings for the draw_lines method
pub fn fg_stripes(colors: Vec<Fg<Rgb>>, width: u16, height: u16) -> Vec<String> { pub fn fg_stripes(colors: Vec<Fg<Rgb>>, width: u16, height: u16) -> Vec<String> {
let width = width as usize; let width = width as usize;
@ -98,22 +115,19 @@ pub fn bg_stripes(colors: Vec<Bg<Rgb>>, width: u16, height: u16) -> Vec<String>
impl Flag { impl Flag {
/// renders a flag to stdout /// renders a flag to stdout
pub fn draw(self, hold: bool) { pub fn draw(self, state: &State) {
let lines = match self { let lines = match self {
Flag::Stripes(colors) Flag::Stripes(colors)
=> { => {
let (width, height); let count = colors.len() as u16;
if hold { (width, height) = terminal_size().unwrap(); } let (width, height) = state.size.get(count * 3, count);
else { if height < count { error::too_small(width, height); }
height = colors.len() as u16;
width = height * 3;
}
fg_stripes(colors, width, height) fg_stripes(colors, width, height)
}, },
Flag::Lines(lines) Flag::Lines(lines)
=> lines => lines
}; };
draw_lines(lines, hold); draw_lines(lines, &state);
} }
} }

24
src/error.rs Normal file
View file

@ -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);
}

View file

@ -13,13 +13,13 @@ Display pride flags in the terminal.
usage: pride [flags] [name] usage: pride [flags] [name]
args: args:
<name> The pride flag to display <name> The pride flag to display
flags: flags:
-h, --help Shows this help text -h, --help Shows this help text
--version Show version information --version Show version information
-l, --list Prints a list of printable flags -l, --list Prints a list of printable flags
-s, --small Prints a small version without holding -s, --size [size] Sets the size of the output flag.
Use 'pride --list' to see a list of printable flags Use 'pride --list' to see a list of printable flags
@ -135,7 +135,7 @@ names:
println!("The 5-stripe lesbian flag designed by Emily Gwen in 2018. println!("The 5-stripe lesbian flag designed by Emily Gwen in 2018.
names: names:
'lesbian' 'lesbian', 'wlw'
variants: variants:
7-color 7-stripe flag, also designed in 2018 by Emily Gwen"); 7-color 7-stripe flag, also designed in 2018 by Emily Gwen");

View file

@ -1,30 +1,35 @@
//! main method module //! main method module
use std::{
io::{ stdout, IsTerminal },
process::exit
};
use pico_args::Arguments; use pico_args::Arguments;
mod color; mod color;
mod complex; mod complex;
mod draw; mod draw;
mod error;
mod flag; mod flag;
mod help; mod help;
mod state;
mod util; mod util;
mod variant; mod variant;
use crate::flag::Flag; use crate::{
flag::Flag,
state::State
};
static VERSION: &str = env!("CARGO_PKG_VERSION"); 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() { fn main() {
// collect args // collect args
let mut args = Arguments::from_env(); let mut args = Arguments::from_env();
// handle help flag // handle help flag
if args.contains(["-h", "--help"]) { if args.contains(FLAG_HELP) {
let target = args.subcommand().unwrap(); let target = args.subcommand().unwrap();
if target.is_some() { help::flag_help(&target.unwrap()); } if target.is_some() { help::flag_help(&target.unwrap()); }
else { help::help_text(); } else { help::help_text(); }
@ -32,27 +37,21 @@ fn main() {
} }
// handle list flag // handle list flag
if args.contains(["-l", "--list"]) { if args.contains(FLAG_LIST) {
help::list_text(); help::list_text();
return; return;
} }
// handle version flag // handle version flag
if args.contains(["-v", "--version"]) { if args.contains(FLAG_VERSION) {
println!("pride v{VERSION}"); println!("pride v{VERSION}");
return; return;
} }
if !stdout().is_terminal() { let state = State::new(&mut args);
println!("pride: output must be a terminal");
exit(2);
}
// get small flag let subcommand = args.subcommand().unwrap();
let small = args.contains(["-s", "--small"]); let variant = args.subcommand().unwrap();
let subcommand = args.subcommand().unwrap();
let variant = args.subcommand().unwrap_or(None);
// get color vec from matched flag // get color vec from matched flag
let flag: Flag = match subcommand.as_deref() { let flag: Flag = match subcommand.as_deref() {
@ -65,14 +64,14 @@ fn main() {
Some("philadelphia") Some("philadelphia")
=> variant::philadelphia(), => variant::philadelphia(),
Some("progress") Some("progress")
=> complex::progress(small), => complex::progress(&state),
_ _
=> flag::pride() => flag::pride()
} }
}, },
Some("progress") Some("progress")
=> complex::progress(small), => complex::progress(&state),
Some("agender") Some("agender")
@ -88,7 +87,7 @@ fn main() {
=> { => {
match variant.as_deref() { match variant.as_deref() {
Some("halves" | "side-by-side" | "sbs") Some("halves" | "side-by-side" | "sbs")
=> complex::aroace(small), => complex::aroace_halves(&state),
_ _
=> flag::aroace() => flag::aroace()
} }
@ -109,10 +108,10 @@ fn main() {
=> flag::demigirl(), => flag::demigirl(),
Some("demiromantic") Some("demiromantic")
=> complex::demiromantic(small), => complex::demiromantic(&state),
Some("demisexual") Some("demisexual")
=> complex::demisexual(small), => complex::demisexual(&state),
// Some("disability") // Some("disability")
// => complex::disability(); // => complex::disability();
@ -138,7 +137,7 @@ fn main() {
// => complex::intersex(), // => complex::intersex(),
Some("lesbian") Some("lesbian" | "wlw")
=> { => {
match variant.as_deref() { match variant.as_deref() {
Some("7-color") Some("7-color")
@ -178,15 +177,11 @@ fn main() {
_ _
=> { => { error::unmatched_flag(subcommand.unwrap()); panic!() }
println!("pride: no flag '{}'", subcommand.unwrap());
help::help_text();
exit(1)
}
}; };
// draw flag // draw flag
flag.draw(!small); flag.draw(&state);
} }

66
src/state.rs Normal file
View file

@ -0,0 +1,66 @@
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::<u16>(split.get(0).unwrap()), str::parse::<u16>(split.get(1).unwrap())) {
return Size::Set(width, height);
}
} else if len == 1 {
if let Ok(width) = str::parse::<u16>(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,
}
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 },
Err(Error::OptionWithoutAValue(_)) => Size::Small,
_ => { error::size_missing(); panic!() }
};
State { size, is_terminal }
}
}

View file

@ -27,7 +27,7 @@ pub fn philadelphia() -> Flag {
Flag::Stripes(inner) Flag::Stripes(inner)
=> inner, => inner,
_ _
=> { panic!("impossible enum variant"); } => panic!("impossible enum variant")
}; };
colors.insert(0, BLACK); colors.insert(0, BLACK);
colors.insert(1, brown); colors.insert(1, brown);