A sheep speaks.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add --think, update README, fix art, add message tail rendering #1

open opened by paeth.xyz targeting main from think
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:dzif5zfmzlbs6cdc67gzlv65/sh.tangled.repo.pull/3mkztbf5tg222
+76 -48
Diff #0
+57 -23
src/main.rs
··· 9 9 10 10 11 11 12 - 12 + const MIN_BUBBLE_SIZE: usize = 20; 13 13 const MAX_BUBBLE_SIZE: usize = 60; 14 14 const MIN_CLONE_INNER: usize = 22; 15 + const DOLLY_MESSAGE_TAIL_POS: &[usize] = &[3]; 16 + const DOLLYS_MESSAGE_TAIL_POS: &[usize] = &[3, 22]; 15 17 16 - const TIPS: &[&str] = &[ 18 + const MUSINGS: &[&str] = &[ 17 19 "I push, therefore I baa.", 18 20 "Identity portable. Repos plural. Wool eternal.", 19 21 ]; ··· 33 35 #[arg(long)] 34 36 clone: bool, 35 37 36 - /// Have Dolly share a random Tangled tip. 38 + /// Have Dolly share what's on her mind. 39 + #[arg(long)] 40 + muse: bool, 41 + 42 + /// Use a thought bubble instead of speech. 37 43 #[arg(long)] 38 - tip: bool, 44 + think: bool, 39 45 } 40 46 41 47 fn main() -> ExitCode { ··· 47 53 48 54 49 55 56 + }; 50 57 58 + let min_inner = if cli.clone { MIN_CLONE_INNER } else { 0 }; 59 + let bubble: String = bubble::render(&message, message_width(), min_inner, cli.think); 51 60 61 + let (art, tail_pos) = if cli.clone { 62 + (DOLLYS, DOLLYS_MESSAGE_TAIL_POS) 63 + } else { 64 + (DOLLY, DOLLY_MESSAGE_TAIL_POS) 65 + }; 66 + let tail = render_message_tail(tail_pos, cli.think); 67 + 68 + // Lock stdout and use BufWriter to group writes to one syscall only. 69 + let stdout = io::stdout(); 70 + let mut out = BufWriter::new(stdout.lock()); 71 + 72 + let _ = writeln!(out, "{bubble}"); 73 + let _ = out.write_all(tail.as_bytes()); 74 + let _ = out.write_all(art.as_bytes()); 52 75 53 - 54 - 55 - 56 - 57 - 58 - 59 - 60 - 61 - 62 - 63 - 64 - 76 + match out.flush() { 65 77 66 78 67 79 ··· 74 86 // Finds where the message text comes from: positional args or piped stdin, 75 87 // or falls back to default string. 76 88 fn resolve_message(cli: &Cli) -> io::Result<String> { 77 - // --tip wins over positional/stdin: the user is explicitly asking for one. 78 - if cli.tip { 79 - return Ok(random_tip().to_string()); 89 + // --muse wins over positional/stdin: the user is explicitly asking for one. 90 + if cli.muse { 91 + return Ok(random_musing().to_string()); 80 92 } 81 93 82 94 // positional args ··· 100 112 Ok(buf.trim_end().to_string()) 101 113 } 102 114 103 - // Picks a tip pseudo-randomly. Uses the system clock as the seed source so we 115 + // Picks a musing pseudo-randomly. Uses the system clock as the seed source so we 104 116 // don't pull in `rand`. 105 - fn random_tip() -> &'static str { 106 - if TIPS.is_empty() { 117 + fn random_musing() -> &'static str { 118 + if MUSINGS.is_empty() { 107 119 return "Baa!"; 108 120 } 109 121 let nanos = SystemTime::now() 110 122 .duration_since(UNIX_EPOCH) 111 123 .map(|d| d.as_millis()) 112 124 .unwrap_or(0); 113 - TIPS[(nanos as usize) % TIPS.len()] 125 + MUSINGS[(nanos as usize) % MUSINGS.len()] 126 + } 127 + 128 + // Renders the two-line tail connecting bubble to Dolly's head. Each anchor 129 + // in `positions` starts a tail char at that column on line 1 and at col+1 on line 2. 130 + fn render_message_tail(positions: &[usize], think: bool) -> String { 131 + let ch = if think { 'o' } else { '\\' }; 132 + let mut out = String::new(); 133 + for offset in 0..2 { 134 + let mut col = 0; 135 + for &pos in positions { 136 + let target = pos + offset; 137 + for _ in col..target { 138 + out.push(' '); 139 + } 140 + out.push(ch); 141 + col = target + 1; 142 + } 143 + out.push('\n'); 144 + } 145 + out 114 146 } 115 147 116 148 // Computes the wrap width for the message inside the bubble. 149 + fn message_width() -> usize { 150 + let term: usize = terminal_size()
+9 -7
README.md
··· 17 17 18 18 19 19 20 + You'll need a Rust toolchain. Install via [rustup](https://rustup.rs) if you don't have one. 20 21 21 - 22 - 23 - 24 - 25 - 26 - 22 + ```sh 23 + git clone https://tangled.org/paeth.xyz/dollysay dollysay 24 + cd dollysay 25 + cargo install --path . 26 + ``` 27 27 28 28 29 29 ··· 33 33 dollysay hello # speak a message 34 34 echo "baa" | dollysay # read from stdin 35 35 dollysay --clone "we are many" # two Dollys, one bubble 36 - dollysay --tip # hear what Dolly has to say 36 + dollysay --think # render the message as a thought bubble 37 + dollysay --muse # hear what Dolly has to say 37 38 ``` 38 39 39 40 Run `dollysay --help` for the full flag list. ··· 47 48 ## TODO 48 49 49 50 - Add Dolly moods! 51 + - add `--deepthink` mode?
+1 -3
dolly.txt
··· 1 - \ 2 - \ 3 - .~``~,,```. 1 + .~``~,,```-. 4 2 ( -~~- ) 5 3 ( // OO \\ ) 6 4 ( \__/ )
-2
dollys.txt
··· 1 - \ \ 2 - \ \ 3 1 .~``~,,```-. .~``~,,```-. 4 2 ( -~~- ) ( -~~- ) 5 3 ( // OO \\ ) ( // OO \\ )
+9 -13
src/bubble.rs
··· 2 2 3 3 // Builds the speech bubble (without trailing newline) and returns 4 4 // it as one String. 5 - pub fn render(text: &str, max_width: usize, min_width: usize) -> String { 5 + pub fn render(text: &str, max_width: usize, min_width: usize, think: bool) -> String { 6 6 let wrapped_text: Vec<String> = textwrap::wrap(text, max_width) 7 7 .into_iter() 8 8 .map(|cow| cow.into_owned()) ··· 27 27 out.push('\n'); 28 28 29 29 if wrapped_text.len() == 1 { 30 - // Single-line: 31 - // _______________________ 32 - // < this is a single line > 33 - // ----------------------- 34 - push_bubble_line(&mut out, &wrapped_text[0], inner, '<', '>'); 30 + // Single-line: < text > for speech, ( text ) for thought. 31 + let (l, r) = if think { ('(', ')') } else { ('<', '>') }; 32 + push_bubble_line(&mut out, &wrapped_text[0], inner, l, r); 35 33 } else { 36 - // Multi-line: 37 - // ___________________________________________________________ 38 - // / this is a multi line text that will require rounding the \ 39 - // | speech bubble corners. since it is more than two lines it | 40 - // \ will need to be extended with walls. / 41 - // ----------------------------------------------------------- 34 + // Multi-line: rounded speech corners + | walls, 35 + // or all parens for thought (cowthink convention). 42 36 let last = wrapped_text.len() - 1; 43 37 for (i, line) in wrapped_text.iter().enumerate() { 44 - let (l, r) = if i == 0 { 38 + let (l, r) = if think { 39 + ('(', ')') 40 + } else if i == 0 { 45 41 ('/', '\\') 46 42 } else if i == last { 47 43 ('\\', '/')

History

1 round 0 comments
sign up or login to add to the discussion
paeth.xyz submitted #0
5 commits
expand
rename --tip to --muse and update accordingly
update README
fix dolly art
add --think mode, move message tail rendering
update README with repo url and --think
merge conflicts detected
expand
  • src/main.rs:13
expand 0 comments