fortune/src/main.rs
2023-12-25 16:34:15 -07:00

142 lines
3.0 KiB
Rust

/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
use std::{
env::args,
ffi::OsStr,
fs::{ read, read_dir },
path::{ Path, PathBuf },
process::exit,
};
use arg::Args;
use nanorand::{ WyRand, Rng };
const F_PATH: &str = "/usr/share/fortune";
#[derive(Args, Debug)]
struct Arguments {
_argv0: String,
#[arg(short = "c",)]
c: bool,
#[arg(short = "f")]
f: bool,
cookies: Vec<String>,
}
fn get_cookies(cookies: Option<Vec<String>>) -> Option<Vec<PathBuf>> {
let list = match read_dir(Path::new(F_PATH)) {
Ok(path) => path,
Err(_) => return None,
};
let mut out = list.map(|x| { x.unwrap().path() }).collect::<Vec<PathBuf>>();
match cookies {
Some(cookies) => {
out.clear();
for cookie in cookies.iter() {
out.push(PathBuf::from(format!("{}/{}", F_PATH, cookie)));
}
},
None => {},
};
out.retain(|x| {
let ft = match x.metadata() {
Ok(file) => file.is_file(),
Err(_) => false,
};
x.extension() != Some(OsStr::new("dat")) && ft
});
Some(out)
}
fn tell_fortune(fortunes: String) -> String {
let parsed = fortunes
.split('%')
.collect::<Vec<&str>>();
let mut rnd = WyRand::new();
let fortune = parsed
.get(rnd.generate_range(0_usize..=parsed.len()))
.unwrap()
.trim()
.to_string();
fortune
}
fn main() {
let argv = args().collect::<Vec<String>>();
let opts = match Arguments::from_args(
argv.clone().iter().map(|x| x.as_str())
) {
Ok(args) => args,
Err(_) => {
eprintln!("Usage: {} (-cf)", argv[0]);
exit(1);
},
};
let selections: Option<Vec<String>>;
if opts.cookies.is_empty() { selections = None; }
else { selections = Some(opts.cookies); }
let cookies = match get_cookies(selections) {
Some(fortunes) => fortunes,
None => {
eprintln!("{}: No fortune cookies installed.", argv[0]);
exit(1);
},
};
if opts.f {
for file in &cookies {
println!("{}", file.file_name().unwrap().to_str().unwrap());
}
exit(0);
}
let mut rnd = WyRand::new();
let cookie = &cookies[
rnd.generate_range(0_usize..cookies.len())
];
if opts.c { println!("{:?}\n%", cookie.as_os_str()); }
let fortune = match read(cookie) {
Ok(bytes) => bytes,
Err(err) => {
eprintln!("{}: {}", argv[0], err);
exit(1);
},
};
if let Ok(fortune_text) = String::from_utf8(fortune) {
println!("{}", tell_fortune(fortune_text));
} else { exit(1) }
}