From 2c93c6cdd8551859dd80c54c3662b3decbdb2e97 Mon Sep 17 00:00:00 2001 From: Alessandro Maestri Date: Sat, 4 Oct 2025 00:32:13 +0200 Subject: [PATCH 1/4] update definition of project and added dependencies --- Cargo.lock | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 9 +++-- 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff85074..ae53b38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,9 +72,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.42" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -82,9 +82,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -137,6 +137,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "getrandom" version = "0.2.16" @@ -160,18 +166,40 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "libc" version = "0.2.164" @@ -275,11 +303,62 @@ dependencies = [ [[package]] name = "rfortune" -version = "0.3.0" +version = "0.5.0" dependencies = [ "clap", "dirs", "rand", + "serde", + "serde_yaml", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", ] [[package]] @@ -325,6 +404,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index d840ed3..eee6ff7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rfortune" -version = "0.3.0" -edition = "2021" +version = "0.5.0" +edition = "2024" authors = ["Umpire274 "] description = "A Rust-based clone of the classic UNIX 'fortune' command" homepage = "https://github.com/umpire274/rFortune" @@ -19,6 +19,9 @@ include = [ ] [dependencies] +clap = { version = "4.5.48", features = ["derive"] } rand = "0.9.2" -clap = { version = "4.5.42", features = ["derive"] } dirs = "6.0.0" +serde = { version = "1.0.228", features = ["derive"] } +serde_yaml = "0.9.33" + From ae87d218b943f83eb0e294b4251231954b57b7d7 Mon Sep 17 00:00:00 2001 From: Alessandro Maestri Date: Sat, 4 Oct 2025 00:35:55 +0200 Subject: [PATCH 2/4] refactor: split cli.rs, commands.rs and config.rs for clearer module structure --- src/cli.rs | 65 +++++++++++++++++++++++++++++++++ src/commands.rs | 19 ++++++++++ src/config.rs | 83 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 87 ++++++++++++++++++-------------------------- src/utils.rs | 80 +++++++++++++++++++--------------------- tests/utils_tests.rs | 4 +- 7 files changed, 244 insertions(+), 95 deletions(-) create mode 100644 src/cli.rs create mode 100644 src/commands.rs create mode 100644 src/config.rs diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..bfb41cc --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,65 @@ +use clap::{Parser, Subcommand}; + +#[derive(Parser, Debug)] +#[command( + name = "rfortune", + version, + about = "Print random quotes from fortune files", + long_about = "rfortune is a Rust implementation of the classic UNIX 'fortune' program.\n\n\ +By default, running `rfortune` prints a random quotation from the default file \ +(rfortune.dat) stored in your user data directory. Quotes inside a fortune file \ +must be separated by a line containing only the '%' character.\n\n\ +You can specify a custom fortune file with `--file`, or manage configuration, \ +fortune files, and cache using subcommands:\n\n \ +• `config init` Create a configuration file with default options.\n \ +• `file init` Create a sample default fortune file (rfortune.dat).\n \ +• `cache clear` Remove all cached last-used fortunes.\n\n\ +This makes it easy to test, customize and extend your fortune collections \ +while preserving the spirit of the original UNIX command.", + after_help = "EXAMPLES:\n rfortune\n Print a random fortune from the default file (rfortune.dat).\n\n rfortune --file ~/fortunes/misc\n Print a random fortune from the file ~/fortunes/misc.\n\n rfortune config init\n Create a default configuration file in the user data directory.\n\n rfortune file init\n Create a sample fortune file (rfortune.dat) in the user data directory.\n\n rfortune cache clear\n Remove all cached last-used fortunes." +)] +pub struct Cli { + /// Fortune file to use instead of the default (rfortune.dat) + #[arg(short, long)] + pub file: Option, + + #[command(subcommand)] + pub command: Option, +} + +#[derive(Subcommand, Debug)] +pub enum Commands { + /// Manage configuration + Config { + #[command(subcommand)] + action: ConfigAction, + }, + /// Manage fortune files + File { + #[command(subcommand)] + action: FileAction, + }, + /// Manage cache + Cache { + #[command(subcommand)] + action: CacheAction, + }, +} + +#[derive(Subcommand, Debug)] +pub enum ConfigAction { + /// Initialize the configuration file + Init, +} + +#[derive(Subcommand, Debug)] +pub enum FileAction { + /// Create a sample default fortune file (rfortune.dat) + Init, +} + +#[derive(Subcommand, Debug)] +pub enum CacheAction { + /// Clear the cache directory + Clear, +} diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..d284f24 --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,19 @@ +use crate::{config, utils}; + +pub fn run_config_init() { + if let Err(e) = config::init_config_file() { + eprintln!("Error initializing config: {e}"); + } +} + +pub fn run_file_init() { + if let Err(e) = config::init_default_file() { + eprintln!("Error initializing fortune file: {e}"); + } +} + +pub fn run_cache_clear() { + if let Err(e) = utils::clear_cache_dir() { + eprintln!("Error clearing cache: {e}"); + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..1db1a9c --- /dev/null +++ b/src/config.rs @@ -0,0 +1,83 @@ +use dirs::data_dir; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Config { + pub default_file: Option, + pub print_title: Option, + pub use_cache: Option, +} + +pub(crate) fn app_dir() -> PathBuf { + let mut base = data_dir().unwrap_or_else(|| { + // fallback molto conservativo + let mut p = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + p.push(".rfortune"); + p + }); + base.push("rfortune"); + // NB: non creiamo qui la directory per non sorprendere l'utente in chiamate read-only + base +} + +pub fn get_config_path() -> PathBuf { + let mut p = app_dir(); + p.push("config.yaml"); + p +} + +/// Percorso di default del file fortune dell'app (rfortune.dat) +pub fn get_default_path() -> PathBuf { + let mut p = app_dir(); + p.push("rfortune.dat"); + p +} + +/// Crea un file di configurazione minimale (se non esiste) +pub fn init_config_file() -> std::io::Result<()> { + let dir = app_dir(); + fs::create_dir_all(&dir)?; + + let path = get_config_path(); + if path.exists() { + return Ok(()); // non sovrascrivere + } + + let cfg = Config { + default_file: Some(get_default_path().to_string_lossy().to_string()), + print_title: Some(true), + use_cache: Some(true), + }; + let yaml = serde_yaml::to_string(&cfg).expect("Failed to serialize config"); + fs::write(path, yaml) +} + +/// Crea un file fortune di esempio (rfortune.dat) se assente +pub fn init_default_file() -> std::io::Result<()> { + let dir = app_dir(); + fs::create_dir_all(&dir)?; + + let path = get_default_path(); + if path.exists() { + return Ok(()); // non sovrascrivere + } + + let sample = r#"Fortune favors the bold. +— Publius Vergilius Maro +% +Premature optimization is the root of all evil. +— Donald Knuth +% +In Rust we trust. +"#; + fs::write(path, sample) +} + +/// Carica la configurazione se presente +pub fn load_config() -> Option { + let path = get_config_path(); + let content = fs::read_to_string(path).ok()?; + serde_yaml::from_str(&content).ok() +} diff --git a/src/lib.rs b/src/lib.rs index 138e20b..7d7a9e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ +pub mod config; pub mod loader; pub mod utils; diff --git a/src/main.rs b/src/main.rs index 5b55b98..aab1a72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,61 +1,46 @@ -use clap::{Arg, Command}; -use std::env; -use std::path::PathBuf; +use clap::Parser; +use rfortune::{config, loader, utils}; -use rfortune::loader::FortuneFile; -use rfortune::utils::{get_default_path, init_default_file, print_random}; +mod cli; +mod commands; -const VERSION: &str = env!("CARGO_PKG_VERSION"); +use cli::{CacheAction, Cli, Commands, ConfigAction, FileAction}; fn main() { - let matches = Command::new("rFortune") - .version(VERSION) - .author("Alessandro Maestri ") - .about("A Rust-based clone of the classic 'fortune' tool") - .arg( - Arg::new("file") - .short('f') - .long("file") - .value_name("FILE") - .help("Path to the .dat fortune file") - .num_args(1), - ) - .arg( - Arg::new("init") - .long("init") - .help("Initialize default fortune file and directory") - .action(clap::ArgAction::SetTrue), - ) - .arg( - Arg::new("clear-cache") - .long("clear-cache") - .help("Delete all cached quote history") - .action(clap::ArgAction::SetTrue), - ) - .get_matches(); + let cli = Cli::parse(); - if *matches.get_one::("clear-cache").unwrap_or(&false) { - match rfortune::utils::clear_cache_dir() { - Ok(_) => println!("Cache cleared successfully."), - Err(e) => eprintln!("Error clearing cache: {e}"), + match cli.command { + Some(Commands::Config { + action: ConfigAction::Init, + }) => { + commands::run_config_init(); } - return; - } - - if matches.get_flag("init") { - if let Err(e) = init_default_file() { - eprintln!("Initialization error: {e}"); + Some(Commands::File { + action: FileAction::Init, + }) => { + commands::run_file_init(); } - return; - } - - let filepath = matches - .get_one::("file") - .map(PathBuf::from) - .unwrap_or_else(get_default_path); + Some(Commands::Cache { + action: CacheAction::Clear, + }) => { + commands::run_cache_clear(); + } + None => { + // Comportamento standard: stampa una citazione casuale + let file_path = if let Some(path) = cli.file { + std::path::PathBuf::from(path) + } else { + config::get_default_path() + }; - match FortuneFile::from_file(&filepath) { - Ok(fortune_file) => print_random(&fortune_file, &filepath), - Err(err) => eprintln!("Error: {err}"), + match loader::FortuneFile::from_file(&file_path) { + Ok(fortune_file) => { + if let Err(e) = utils::print_random(&fortune_file, &file_path) { + eprintln!("Error: {e}"); + } + } + Err(e) => eprintln!("Error loading fortune file: {e}"), + } + } } } diff --git a/src/utils.rs b/src/utils.rs index e173841..68ebf27 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,7 @@ +use crate::config; use crate::loader::FortuneFile; use rand::seq::IndexedRandom; use std::fs; -use std::fs::{create_dir_all, File}; use std::io::Write; use std::path::{Path, PathBuf}; @@ -10,17 +10,28 @@ pub fn random_quote(quotes: &[String]) -> &str { quotes.choose(&mut rng).map(|s| s.as_str()).unwrap() } -pub fn print_random(fortune_file: &FortuneFile, file_path: &Path) { - let cache_path = get_cache_path(file_path); - let last_used = read_last_cache(&cache_path); - let quote = random_nonrepeating(&fortune_file.quotes, last_used.as_deref()); +pub fn print_random(fortune_file: &FortuneFile, file_path: &Path) -> Result<(), String> { + // esempio: recupera la citazione casuale + let quote = if fortune_file.quotes.is_empty() { + return Err("No quotes found in the file.".to_string()); + } else { + random_nonrepeating(&fortune_file.quotes, None) + }; + // eventuale stampa titolo se presente if let Some(title) = &fortune_file.title { - println!("{title}\n"); + println!("({title})"); } - println!("{quote}"); - write_last_cache(&cache_path, quote); + // stampa il contenuto + println!("{}", quote); + + // qui salvi anche in cache se necessario + if let Err(e) = save_last_cache(file_path, quote) { + eprintln!("Warning: could not update cache: {e}"); + } + + Ok(()) } pub fn get_cache_path(dat_path: &Path) -> PathBuf { @@ -59,45 +70,30 @@ pub fn random_nonrepeating<'a>(quotes: &'a [String], last: Option<&str>) -> &'a } } -pub fn get_default_path() -> PathBuf { - if cfg!(target_os = "windows") { - if let Some(home_dir) = dirs::data_dir() { - return home_dir.join("rfortune").join("rfortunes.dat"); - } - PathBuf::from("C:\\Users\\Public\\rfortune\\rfortunes.dat") - } else { - PathBuf::from("/usr/local/share/rfortune/rfortunes.dat") - } +/// Directory della cache: .../rfortune/cache +fn get_cache_dir() -> PathBuf { + let mut p = config::app_dir(); + p.push("rfortune"); + p.push("cache"); + p } -pub fn init_default_file() -> Result<(), String> { - let path = get_default_path(); - if let Some(parent) = path.parent() { - create_dir_all(parent).map_err(|e| format!("Failed to create directory: {e}"))?; +/// Svuota completamente la cache +pub fn clear_cache_dir() -> std::io::Result<()> { + let dir = get_cache_dir(); + if dir.exists() { + fs::remove_dir_all(&dir)?; } - - let mut file = File::create(&path).map_err(|e| format!("Failed to create file: {e}"))?; - let sample = "%\nThe best way to get a good idea is to get a lot of ideas.\n%\nDo or do not. There is no try.\n%\nTo iterate is human, to recurse divine.\n%\n"; - file.write_all(sample.as_bytes()) - .map_err(|e| format!("Failed to write to file: {e}"))?; - - println!("Initialized default fortune file at: {}", path.display()); Ok(()) } -pub fn clear_cache_dir() -> Result<(), String> { - if let Some(mut cache_dir) = dirs::data_local_dir() { - cache_dir.push("rfortune"); - cache_dir.push("cache"); - - if cache_dir.exists() { - fs::remove_dir_all(&cache_dir) - .map_err(|e| format!("Failed to remove cache directory: {e}"))?; - Ok(()) - } else { - Ok(()) // Nessuna directory da cancellare - } - } else { - Err("Unable to determine system data directory.".to_string()) +/// Salva l’ultima citazione usata in un file di cache +pub fn save_last_cache(file_path: &Path, quote: &str) -> std::io::Result<()> { + let cache_path = get_cache_path(file_path); + if let Some(parent) = cache_path.parent() { + fs::create_dir_all(parent)?; // assicura che la cartella esista } + let mut f = fs::File::create(cache_path)?; + f.write_all(quote.as_bytes())?; + Ok(()) } diff --git a/tests/utils_tests.rs b/tests/utils_tests.rs index d5982a8..32ca7e4 100644 --- a/tests/utils_tests.rs +++ b/tests/utils_tests.rs @@ -26,7 +26,7 @@ fn test_print_random_output() { path.push("test_quotes.dat"); // Verifica solo che non panichi (output testato altrove) - print_random(&fortune_file, &path); + let _ = print_random(&fortune_file, &path); } #[test] @@ -41,7 +41,7 @@ fn test_cache_read_write() { temp_path.push("test_cache.dat"); // Prima esecuzione: salva una citazione - print_random(&fortune_file, &temp_path); + let _ = print_random(&fortune_file, &temp_path); // Leggi da cache let cache_path = get_cache_path(&temp_path); From 133f8ae04b74e926e247375c01970b03d180183a Mon Sep 17 00:00:00 2001 From: Alessandro Maestri Date: Sat, 4 Oct 2025 00:41:17 +0200 Subject: [PATCH 3/4] Updated CHANGELOG.md and README.md --- CHANGELOG.md | 40 ++++++++++++++++++++++++--- README.md | 77 +++++++++++++++++++++++++--------------------------- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ac4c3..c66eb03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,51 @@ # Changelog +## [v0.5.0] - 2025-10-04 + +### 🔧 Refactoring + +- Split CLI definition into a dedicated `cli.rs` module +- Added a new `commands.rs` module to handle subcommand actions (`config init`, `file init`, `cache clear`) +- Extracted configuration logic from `utils.rs` into a new `config.rs` module +- Simplified `main.rs` to only parse CLI input and dispatch commands + +### ✨ CLI Improvements + +- Introduced subcommands: + - `config init` → initialize the configuration file + - `file init` → create a sample default fortune file (`rfortune.dat`) + - `cache clear` → clear the cache directory +- Enhanced `--help` output with a detailed `long_about` description and usage examples + +### ✅ Misc + +- Clearer module boundaries (`cli`, `commands`, `config`, `utils`, `loader`) +- Improved maintainability and readability of the codebase + +--- + ## [v0.3.0] - 2025-07-30 ### 🔧 Refactoring + - Removed `fortune.rs` and consolidated logic into new `utils.rs` - Updated `main.rs` and `lib.rs` to use `rfortune` crate structure consistently ### 🧪 Unit Testing + - Added unit tests for `loader` module (parsing `.dat` files) - Added unit tests for `utils` module, including `random_quote` and `print_random` - Fixed the signature of `print_random()` to accept file path for cache handling ### 💾 Cache Support + - Implemented cache system to avoid repeating the same fortune twice - Cache is stored in user-specific system path (`$XDG_DATA_HOME/rfortune/cache/` or `%APPDATA%\rfortune\cache\`) - Added tests for reading/writing cache and ensuring uniqueness of quotes - Introduced `--clear-cache` flag to manually delete the entire cache directory ### ✅ Misc + - Ensured full cross-platform compatibility (Linux, macOS, Windows) - Cleaned up unused code and improved module boundaries @@ -26,9 +54,11 @@ ## [v0.2.2] - 2025-07-29 ### Changed + - Updated the `README.md` in the `homebrew-rfortune` tap repository to include installation instructions via Homebrew. ### Notes + - No changes to the binary or functionality of `rfortune` itself. --- @@ -36,19 +66,21 @@ ## [v0.2.1] - 2025-07-29 ### Added + - Support for publishing `rfortune` to [crates.io](https://crates.io/crates/rfortune) - Updated `Cargo.toml` with metadata required by crates.io: - - Package description, authors, license, keywords, categories - - Repository and homepage URLs - - Included files for packaging + - Package description, authors, license, keywords, categories + - Repository and homepage URLs + - Included files for packaging ### Notes + - This version does not introduce new features or changes to functionality. - Users can now install `rfortune` directly via: ```bash cargo install rfortune ``` - + --- ## [v0.2.0] - 2025-07-27 diff --git a/README.md b/README.md index 8e440bd..322eaad 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,10 @@ It displays a random quote or witty phrase from a plain text file, making it per scripting, or just a bit of inspiration. ![CI](https://github.com/umpire274/rfortune/actions/workflows/ci.yml/badge.svg) -[![Licenza MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) +[![License MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20Intel%20%7C%20macOS%20Apple%20Silicon-blue)](https://github.com/umpire274/rFortune/releases) -[![Versione](https://img.shields.io/badge/version-0.3.0-orange)](https://github.com/umpire274/rfortune/releases/tag/v0.3.0) +[![GitHub release](https://img.shields.io/github/v/release/umpire274/rfortune)](https://github.com/umpire274/rfortune/releases/latest) + --- @@ -20,7 +21,7 @@ scripting, or just a bit of inspiration. - 🌹 UTF-8 support for multilingual content - 🧩 Easily extensible - 🧠 Built-in cache system to avoid showing the same fortune twice in a row -- ✨ Supports various command-line options including file selection, cache management, version display, and more +- ✨ New CLI with subcommands for config, file initialization and cache management --- @@ -29,15 +30,17 @@ scripting, or just a bit of inspiration. [![Packaging status](https://repology.org/badge/vertical-allrepos/rfortune.svg)](https://repology.org/project/rfortune/versions) ### 🐧 AUR (Arch Linux) + [![AUR](https://img.shields.io/aur/version/rfortune)](https://aur.archlinux.org/packages/rfortune) ```bash yay -S rfortune -# oppure +# or paru -S rfortune ``` ### 🍺 Homebrew (macOS/Linux) + [![Homebrew Tap](https://img.shields.io/badge/homebrew-tap-brightgreen)](https://github.com/umpire274/homebrew-rfortune) ```bash @@ -45,15 +48,8 @@ brew tap umpire274/rfortune brew install rfortune ``` -### 🪟 Scoop (Windows) -[![Scoop](https://img.shields.io/badge/scoop-rfortune-blue)](https://github.com/ScoopInstaller/Main/pull/XXX) - -```powershell -scoop bucket add main -scoop install rfortune -``` - ### 🦀 Crates.io (Rust) + [![Crates.io](https://img.shields.io/crates/v/rfortune)](https://crates.io/crates/rfortune) ```bash @@ -116,53 +112,54 @@ gpg --fingerprint 423FABCE0A1921FB --- -## 🔐 Initialization (optional) - -To create the default fortune directory and a starter `rfortunes.dat` file: +## 🚀 Usage -```bash -rfortune --init +```sh +rfortune [OPTIONS] +rfortune ``` -- On **Linux/macOS**: creates `/usr/local/share/rfortune/rfortunes.dat` -- On **Windows**: creates `%APPDATA%\rfortune\rfortunes.dat` +Running `rfortune` without subcommands prints a random fortune from the default file (`rfortune.dat`). --- -## 🚀 Usage +## 🧩 Options & Subcommands -```sh -rfortune [--file path/to/quotes.dat] -``` +| Command / Option | Description | +|-----------------------|-------------------------------------------------------| +| `-f`, `--file ` | Use a custom fortune file instead of the default | +| `config init` | Create the configuration file with default options | +| `file init` | Create a sample default fortune file (`rfortune.dat`) | +| `cache clear` | Remove all cached last-used fortunes | +| `-V`, `--version` | Show version information | +| `-h`, `--help` | Show help message | --- -## 🧩 Options +## 🧪 Examples -| Option | Description | -|-------------------|--------------------------------------------| -| `-f`, `--file` | Use a custom file of fortunes | -| `--init` | Create the default directory and test file | -| `--clear-cache` | Delete all cached quote history | -| `-V`, `--version` | Show version | -| `-h`, `--help` | Show help message | +```sh +# Print a random fortune from the default file +rfortune -> If no file is specified, the program defaults to platform-specific location. +# Print a random fortune from a specific file +rfortune --file ~/fortunes/misc ---- +# Initialize configuration file +rfortune config init -## 🧪 Example +# Create a sample default fortune file +rfortune file init -```sh -$ rfortune -Never trust a computer you can't throw out a window. — Steve Wozniak +# Clear all cached last-used fortunes +rfortune cache clear ``` --- ## 📁 File Format -Each fortune must be on one or more lines separated by '%', like so: +Each fortune must be on one or more lines separated by `%`, like so: ```txt % @@ -174,7 +171,8 @@ To iterate is human, to recurse divine. % ``` -You may optionally add a title at the top of the file by starting the first line with #. The title will be printed before the random quote: +You may optionally add a title at the top of the file by starting the first line with #. The title will be printed +before the random quote: ```txt # Murphy's Laws @@ -205,4 +203,3 @@ to fork the repo and contribute. ## 🙌 Acknowledgments Inspired by the classic BSD fortune program. Built with ❤️ in Rust. - From d2f9aa19154cd799c50965485676a3072bb3c9bd Mon Sep 17 00:00:00 2001 From: Alessandro Maestri Date: Sat, 4 Oct 2025 00:46:11 +0200 Subject: [PATCH 4/4] fix[rust.yml] load fmt and clippy --- .github/workflows/rust.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c07d42d..a5b9d82 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,19 +10,24 @@ jobs: check: name: Lint & Test runs-on: ubuntu-latest - + steps: - name: Checkout uses: actions/checkout@v4 - + - name: Install Rust uses: dtolnay/rust-toolchain@stable - + + - name: Install rustfmt and clippy components + run: | + rustup component add rustfmt + rustup component add clippy + - name: Format Check run: cargo fmt --all -- --check - + - name: Clippy Check run: cargo clippy --all-targets --all-features -- -D warnings - + - name: Run Tests run: cargo test --all