/* * Copyright (c) 2023 Emma Tebibyte * 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, } fn get_cookies(cookies: Option>) -> Option> { 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::>(); 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::>(); 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::>(); 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>; 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) } }