diff --git a/src/critters.rs b/src/critters.rs index 7d790ec..a5eb3fb 100644 --- a/src/critters.rs +++ b/src/critters.rs @@ -1,3 +1,4 @@ +use super::kule::*; use voca_rs::*; pub enum CritterName { @@ -8,7 +9,15 @@ pub enum CritterName { #[derive(Clone)] pub struct CritterTemplate { + // text column where speech line joins speech bubble pub anchor: usize, + /* ascii art critter themself, with special optional formatting strings. all formatters render to one grapheme wide each unless otherwise stated + - $1$2 (left and right eyes) + - $3$4 (left and right tongue) + - $5$6$7 (forward leaning, upwards, and back leaning speech line) + - $8$9 (ansi escape formatting start and formatting stop markers; each renders to zero width) + - $0 object marker. defaults to a single space but can be as many graphemes as you want + */ pub critter: String, } #[derive(Clone)] @@ -25,45 +34,49 @@ pub struct CritterConfig { pub object: String, + pub format: String, + pub template: CritterTemplate, } impl CritterConfig { + // tries to create a critter from relevant strings, falling back to sensible defaults where possible pub fn config_from_string( eyes: &Option, tongue: &Option, line: &Option, object: &Option, + format: &Option, name: &Option, ) -> CritterConfig { let kijetesantakalu = CritterTemplate { anchor: 14, critter: r" - $6 - /__ $6 - / $1$2\ $5 - | |$3$4 - | | - (III|\|| $0" +$8 $9$6 +$8 /__ $9$6 +$8 / $1$2\ $9$5 +$8 | |$3$4 +$8 | | +$8 (III|\|| $9$0" .to_string(), }; let kijetesantakalu_little = CritterTemplate { anchor: 13, critter: r" - $6 - /__ $6 - / $1$2\ $5 - | |$3$4 - (I|\|| $0" +$8 $9$6 +$8 /__ $9$6 +$8 / $1$2\ $9$5 +$8 | |$3$4 +$8 (I|\|| $9$0" .to_string(), }; let soweli = CritterTemplate { anchor: 10, critter: r" - $6 - ___ $6 - $1$2) $5 - |||| $0" +$8 $9$6 +$8 ___ $9$6 +$8 $1$2) $9$5 +$8 |||| $9$0" .to_string(), }; @@ -80,6 +93,8 @@ impl CritterConfig { object: String::from(" "), + format: reset(), // from kule + template: kijetesantakalu, }; @@ -139,6 +154,9 @@ impl CritterConfig { _ => config.object = object.clone(), } } + if let Some(format) = format { + config.format = format.to_string(); + } if let Some(name) = name { match name.as_str() { "kijetesantakalu" => (), @@ -151,6 +169,7 @@ impl CritterConfig { return config; } + // gives a fully formatted version of the critter pub fn format_critter(&self) -> String { return self .template @@ -162,6 +181,8 @@ impl CritterConfig { .replace("$5", &self.right_line) .replace("$6", &self.up_line) .replace("$7", &self.left_line) + .replace("$8", &self.format) + .replace("$9", &reset()) .replace("$0", &self.object); } } diff --git a/src/kule.rs b/src/kule.rs new file mode 100644 index 0000000..0dc1cf4 --- /dev/null +++ b/src/kule.rs @@ -0,0 +1,88 @@ +pub enum Formats { + Plain, + Bright, + Dim, + Italic, + Underline, + Blink, + Reverse, + Strike, +} + +// outputs a code to reset all formatting. equivalent to Formats::Plain.escape(false) +pub fn reset() -> String { + "\x1b[0m".to_string() +} + +impl Formats { + fn escape_code(&self, disable: bool) -> u8 { + let mut code: u8 = match self { + Formats::Plain => 0, + Formats::Bright => 1, + Formats::Dim => 2, + Formats::Italic => 3, + Formats::Underline => 4, + Formats::Blink => 5, + Formats::Reverse => 7, + Formats::Strike => 9, + }; + if code == 0 { + // you can't disable plain + return code; + } else if disable { + code += 20; + } + code + } + pub fn escape(&self, disable: bool) -> String { + format!("\x1b[{}m", self.escape_code(disable)) + } +} +pub enum FourBit { + Black, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White, + BrightBlack, + BrightRed, + BrightGreen, + BrightYellow, + BrightBlue, + BrightMagenta, + BrightCyan, + BrightWhite, +} + +impl FourBit { + fn escape_code(&self, background: bool) -> u8 { + let mut code: u8 = match self { + FourBit::Black => 30, + FourBit::Red => 31, + FourBit::Green => 32, + FourBit::Yellow => 33, + FourBit::Blue => 34, + FourBit::Magenta => 35, + FourBit::Cyan => 36, + FourBit::White => 37, + FourBit::BrightBlack => 90, + FourBit::BrightRed => 91, + FourBit::BrightGreen => 92, + FourBit::BrightYellow => 93, + FourBit::BrightBlue => 94, + FourBit::BrightMagenta => 95, + FourBit::BrightCyan => 96, + FourBit::BrightWhite => 97, + }; + if background { + code += 10; + } + code + } + pub fn escape(&self, background: bool) -> String { + format!("\x1b[{}m", self.escape_code(background)) + } +} diff --git a/src/main.rs b/src/main.rs index 76e5e95..f9ed236 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,20 @@ /* TO DO - kijefiles -- color - other aminals +- pakala */ mod bubbles; mod critters; +mod kule; use clap::Parser; +use kule::Formats; +use kule::FourBit; use std::io; use std::io::Read; - +use voca_rs::*; fn main() { let cli = Args::parse(); let mut text = String::new(); @@ -24,7 +27,8 @@ fn main() { .read_to_string(&mut text) .expect("failed to read input"); } - output(&text, config) + output(&text, config); + println!("{}test{}", FourBit::BrightCyan.escape(false), kule::reset()); } #[derive(Parser, Debug)] @@ -48,6 +52,9 @@ struct Args { #[clap(short = 'W', long)] pakala: Option, + #[clap(short = 'k', long)] + kule: Vec, + // implementation of classic cowsay flags #[clap(short = 'b', long)] ilo: bool, @@ -86,6 +93,7 @@ impl Args { let mut tongue = self.uta.clone(); let mut line = self.palisa.clone(); let mut object = self.ijo.clone(); + let mut format = "".to_string(); let mut name = self.nimi.clone(); if self.ilo { @@ -107,7 +115,81 @@ impl Args { } else if self.lili { eyes = Some("..".to_string()); } - critters::CritterConfig::config_from_string(&eyes, &tongue, &line, &object, &name) + for i in &self.kule { + let lower = query::is_lowercase(&i); + let lower_i = case::lower_case(&i); + format.push_str(&match lower_i.as_str() { + "walo" => { + if lower { + FourBit::White.escape(false) + } else { + FourBit::BrightWhite.escape(false) + } + } + "pimeja" => { + if lower { + FourBit::Black.escape(false) + } else { + FourBit::BrightBlack.escape(false) + } + } + "laso" => { + if lower { + FourBit::Cyan.escape(false) + } else { + FourBit::BrightCyan.escape(false) + } + } + "jelo" => { + if lower { + FourBit::Yellow.escape(false) + } else { + FourBit::BrightYellow.escape(false) + } + } + "loje" => { + if lower { + FourBit::Red.escape(false) + } else { + FourBit::BrightRed.escape(false) + } + } + "kasi" => { + if lower { + FourBit::Green.escape(false) + } else { + FourBit::BrightGreen.escape(false) + } + } + "sewi" => { + if lower { + FourBit::Blue.escape(false) + } else { + FourBit::BrightBlue.escape(false) + } + } + "unu" => { + if lower { + FourBit::Magenta.escape(false) + } else { + FourBit::BrightMagenta.escape(false) + } + } + "suli" => Formats::Bright.escape(false), + "len" => Formats::Dim.escape(false), + "mamamije" => Formats::Italic.escape(false), + "sike" => Formats::Blink.escape(false), + _ => String::new(), + }) + } + critters::CritterConfig::config_from_string( + &eyes, + &tongue, + &line, + &object, + &Some(format), + &name, + ) } }