Dog fact
I just discovered this dog api, which has a facts
endpoint. By default, it seems to give you one random fact. Fun!
❯ curl -s https://dogapi.dog/api/v2/facts | jq '.data[].attributes.body'
"Greyhounds are the fastest dogs on earth, with speeds of up to 45 miles per hour."
I decided to make a version in Rust.
cargo new dogfact
cd dogfact
cargo add reqwest --features=blocking
cargo add serde_json
use serde_json::Value;
const URL: &str = "https://dogapi.dog/api/v2/facts";
fn main() {
let response = reqwest::blocking::get(URL).expect("Could not get request!");
let body = response.text().expect("Could not get text!");
let v: Value = serde_json::from_str(&body).expect("Could not parse JSON!");
println!("{}", v["data"][0]["attributes"]["body"]);
}
But that prints a JSON string (as does the jq
version). I decided print a Rust string instead.
let json_string = v["data"][0]["attributes"]["body"].clone();
let rust_string = serde_json::from_value::<String>(json_string).unwrap();
println!("{rust_string}");
That way, I could pipe the results to cowsay
without worrying about embedded quotes or whatever.
❯ dogfact | cowsay
_________________________________________
/ The oldest dog on record – a Queensland \
| Heeler named Bluey – was 29 years, 5 |
\ months old. /
-----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Cowsay is an old Perl program. You can change the cow to something else if you like. However, I was shocked to find that there was no dog. So I made one.
❯ cat /usr/share/cowsay/cows/dog.cow
$the_cow = <<EOC;
$thoughts |\\|\\
$thoughts .. \\ .
$thoughts o-- \\\\ / @)
v__///\\\\\\\\__/ @
{ }
{ } \\\\\\{ }
<_| <_|
EOC
Now our dog fact can be given to us by a dog!
❯ dogfact | cowsay -f dog
_____________________________________
/ Dachshunds were originally bred for \
\ fighting badgers. /
-------------------------------------
\ |\|\
\ .. \ .
\ o-- \\ / @)
v__///\\\\__/ @
{ }
{ } \\\{ }
<_| <_|
Next, I decided to add the dog and balloon directly. To reformat the text to go in the balloon, we'll use textwrap
cargo add textwrap
Since Rust strings are UTF-8, I used Unicode characters– not just ASCII– to draw the dog and the balloon.
use serde_json::Value;
use std::borrow::Cow;
const COLUMNS: usize = 40;
const H: char = '─';
const V: char = '│';
const UL: char = '╭';
const UR: char = '╮';
const LL: char = '╰';
const LR: char = '╯';
const DH: char = '┬';
const DOG: &[&str] = &[
" │",
" │ ┏╮┏╮",
" │ ╭┛┻┛┻╮ ╭━╮",
" │ ▅━╯▋┈▋┈┃ ╰╮┃",
" ╰─────── ┣━━━━━╯╰━━━━━╮┃┃",
" ╰━━━━┓ ┗╯┃",
" ┃┏┓┏━┳┳┓┏━╯",
" ┗┛┗┛ ┗┛┗┛",
];
const URL: &str = "https://dogapi.dog/api/v2/facts";
fn main() {
let response = reqwest::blocking::get(URL).expect("Could not get request!");
let body = response.text().expect("Could not get text!");
let v: Value = serde_json::from_str(&body).expect("Could not parse JSON!");
let json_string = v["data"][0]["attributes"]["body"].clone();
let rust_string = serde_json::from_value::<String>(json_string).unwrap();
let message = textwrap::wrap(&rust_string, COLUMNS);
let balloon = construct_balloon(&message);
for line in &balloon {
println!("{line}");
}
for line in DOG {
println!("{line}");
}
}
fn construct_balloon(message: &[Cow<str>]) -> Vec<String> {
let max = message
.iter()
.map(|line| line.len())
.max()
.unwrap_or(COLUMNS);
let max = std::cmp::max(max, 5);
let mut tmp = [0; 4];
let h = H.encode_utf8(&mut tmp);
let top_line = h.repeat(max + 2);
let bottom_line = h.repeat(max - 1);
let mut balloon = vec![];
balloon.push(format!(" {UL}{top_line}{UR}"));
for line in message.iter() {
balloon.push(format!(" {V} {:max$} {V}", line));
}
balloon.push(format!(" {LL}{H}{H}{DH}{bottom_line}{LR}"));
balloon
}
Now we can just run dogfact
❯ dogfact
╭──────────────────────────────────────────╮
│ The wetness of a dog's nose is essential │
│ for determining what direction a smell │
│ is coming from. │
╰──┬───────────────────────────────────────╯
│
│ ┏╮┏╮
│ ╭┛┻┛┻╮ ╭━╮
│ ▅━╯▋┈▋┈┃ ╰╮┃
╰─────── ┣━━━━━╯╰━━━━━╮┃┃
╰━━━━┓ ┗╯┃
┃┏┓┏━┳┳┓┏━╯
┗┛┗┛ ┗┛┗┛
Huh, this web page has extra vertical whitespace causing breaks in the lines. Here's what it looks like in my editor.
The Perl cowsay
has a bunch of options. Perhaps I should add some of those. Or, at least, make it optional to draw the dog and balloon. ¯\_(ツ)_/¯