Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "dev_tools"]
path = dev_tools
url = git@github.com:umpire274/rust_dev_scripts.git
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
# Changelog

## [0.5.2] - 2025-11-04

### Added

- New **`ConsoleLog`** utility for rich, colorized console output with Unicode symbols:
- ℹ `info()` – Informational messages
- ✅ `ok()` – Successful operations
- ⚠️ `warn()` – Warnings and recoverable issues
- ❌ `ko()` – Errors and critical failures
- Integrated `ConsoleLog` throughout configuration, cache, and file management commands for consistent CLI feedback.
- New subcommand **`config edit`** for editing the configuration file directly from the terminal:
- `rfortune config edit` → opens `rfortune.conf` using the system’s default text editor.
- `rfortune config edit --editor <name|path>` → opens it with a specific editor (e.g. `vi`, `nano`, `code`).
- Automatic editor detection logic:
- Checks `$VISUAL` and `$EDITOR` environment variables.
- Falls back to **`nano`** on macOS/Linux and **`notepad`** on Windows.
- Integrated colored console messages (`ConsoleLog`) for clear feedback during editing.

### Changed

- Configuration filename renamed from **`config.yaml`** → **`rfortune.conf`** for better clarity and platform
consistency.
- Updated initialization logic to automatically migrate existing `config.yaml` to the new format (backup saved as
`config.yaml.bak`).
- Unified editor behavior across platforms for a consistent CLI experience.
- Improved user feedback when creating or editing the configuration file.

### Fixed

- Improved user feedback during `config init`, `file init`, and `cache clear` operations to prevent duplicate or missing
log messages.

---

## [0.5.1] - 2025-10-27

### Added
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rfortune"
version = "0.5.1"
version = "0.5.2"
edition = "2024"
authors = ["Umpire274 <umpire274@gmail.com>"]
description = "A Rust-based clone of the classic UNIX 'fortune' command"
Expand All @@ -25,7 +25,7 @@ winresource = "0.1.23"
Icon = "res/rfortune.ico"

[dependencies]
clap = { version = "4.5.50", features = ["derive"] }
clap = { version = "4.5.51", features = ["derive"] }
rand = "0.9.2"
dirs = "6.0.0"
serde = { version = "1.0.228", features = ["derive"] }
Expand Down
52 changes: 39 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ scripting, or just a bit of inspiration.
[![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)
[![GitHub release](https://img.shields.io/github/v/release/umpire274/rfortune)](https://github.com/umpire274/rfortune/releases/latest)

---

## ✨ New in v0.5.2

### 🆕 Configuration system improvements

- Configuration filename renamed from config.yaml → rfortune.conf for better clarity and portability.
- Automatic migration from legacy config.yaml to the new format — the old file is safely backed up as config.yaml.bak.
- Added the new subcommand config edit:
- rfortune config edit → opens rfortune.conf using the system’s default text editor.
- rfortune config edit --editor <name|path> → opens it with a specific editor (e.g. vi, nano, code).
- Automatic detection of preferred editors via $VISUAL and $EDITOR environment variables.
- Default fallback editors are nano (macOS/Linux) and notepad (Windows).

### 🎨 Improved CLI feedback

- Introduced the new ConsoleLog class for rich, colored terminal output:
- ℹ Info, ✅ Success, ⚠️ Warning, ❌ Error
- Unified message style and improved feedback during configuration, cache, and fortune file operations.

---

Expand Down Expand Up @@ -128,30 +147,37 @@ Running `rfortune` without subcommands prints a random fortune from the default

## 🧩 Options & Subcommands

| Command / Option | Description |
|-----------------------|-------------------------------------------------------|
| `-f`, `--file <PATH>` | 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 |
| Command / Option | Description |
|------------------------------|---------------------------------------------------------------------------|
| `-f`, `--file <PATH>` | Use a custom fortune file instead of the default |
| `config init` | Create the configuration file with default options |
| `config edit [--editor <E>]` | Open the configuration file in the system’s default or a specified editor |
| `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 |

---

## 🧪 Examples
## 💡 Examples

```sh
# Print a random fortune from the default file
```bash
# Print a random fortune from the default file (rfortune.dat)
rfortune

# Print a random fortune from a specific file
rfortune --file ~/fortunes/misc

# Initialize configuration file
# Create the default configuration file in the user data directory
rfortune config init

# Create a sample default fortune file
# Open the configuration file in the system’s default text editor
rfortune config edit

# Open the configuration file with a specific editor (e.g. vi, nano, code)
rfortune config edit --editor vi

# Create a sample default fortune file (rfortune.dat)
rfortune file init

# Clear all cached last-used fortunes
Expand Down
1 change: 1 addition & 0 deletions dev_tools
Submodule dev_tools added at 7f8e96
16 changes: 15 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@ 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 \
• `config edit` Edit the configuration file using the system or a chosen editor.\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."
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 config edit\n Open the configuration file in your default system editor.\n\n \
rfortune config edit --editor vi\n Open the configuration file with a specific editor.\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)
Expand Down Expand Up @@ -50,6 +57,13 @@ pub enum Commands {
pub enum ConfigAction {
/// Initialize the configuration file
Init,

/// Edit the configuration file with the system or custom editor
Edit {
/// Specify a custom editor name or path
#[arg(short, long)]
editor: Option<String>,
},
}

#[derive(Subcommand, Debug)]
Expand Down
17 changes: 14 additions & 3 deletions src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
use crate::{config, utils};
use rfortune::log::ConsoleLog;

pub fn run_config_init() {
ConsoleLog::info("Initializing configuration file...");
if let Err(e) = config::init_config_file() {
eprintln!("Error initializing config: {e}");
ConsoleLog::ko(format!("Error initializing config: {e}"));
}
}

pub fn run_file_init() {
ConsoleLog::info("Initializing default fortune file...");
if let Err(e) = config::init_default_file() {
eprintln!("Error initializing fortune file: {e}");
ConsoleLog::ko(format!("Error initializing fortune file: {e}"));
}
}

pub fn run_cache_clear() {
ConsoleLog::info("Clearing cache directory...");
if let Err(e) = utils::clear_cache_dir() {
eprintln!("Error clearing cache: {e}");
ConsoleLog::ko(format!("Error clearing cache: {e}"));
}
}

pub(crate) fn run_config_edit(editor: Option<String>) {
ConsoleLog::info("Clearing cache directory...");
if let Err(e) = config::run_config_edit(editor) {
ConsoleLog::ko(format!("Error opening config in editor: {e}"));
}
}
117 changes: 113 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::log::ConsoleLog;
use dirs::data_dir;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::{env, fs};

#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
Expand All @@ -24,7 +26,7 @@ pub(crate) fn app_dir() -> PathBuf {

pub fn get_config_path() -> PathBuf {
let mut p = app_dir();
p.push("config.yaml");
p.push("rfortune.conf");
p
}

Expand All @@ -40,9 +42,18 @@ pub fn init_config_file() -> std::io::Result<()> {
let dir = app_dir();
fs::create_dir_all(&dir)?;

if let Err(e) = migrate_old_config() {
ConsoleLog::warn(format!("Migration warning: {e}"));
}

let path = get_config_path();
if path.exists() {
return Ok(()); // non sovrascrivere
ConsoleLog::info("Configuration file already exists, skipping.");
return Ok(());
}

if let Err(e) = init_default_file() {
ConsoleLog::ko(format!("Error initializing fortune file: {e}"));
}

let cfg = Config {
Expand All @@ -51,7 +62,10 @@ pub fn init_config_file() -> std::io::Result<()> {
use_cache: Some(true),
};
let yaml = serde_yaml::to_string(&cfg).expect("Failed to serialize config");
fs::write(path, yaml)
fs::write(path, yaml)?;

ConsoleLog::ok("Configuration file successfully created.");
Ok(())
}

/// Crea un file fortune di esempio (rfortune.dat) se assente
Expand Down Expand Up @@ -81,3 +95,98 @@ pub fn load_config() -> Option<Config> {
let content = fs::read_to_string(path).ok()?;
serde_yaml::from_str(&content).ok()
}

/// Tenta di migrare una vecchia configurazione `config.yaml` a `rfortune.conf`
pub fn migrate_old_config() -> std::io::Result<()> {
let dir = app_dir();
let old_path = dir.join("config.yaml");
let new_path = get_config_path();

// Se non c'è un vecchio file o esiste già il nuovo, non fare nulla
if !old_path.exists() || new_path.exists() {
return Ok(());
}

ConsoleLog::info("Old configuration file detected — attempting migration…");

// Legge e prova a deserializzare il vecchio file
match fs::read_to_string(&old_path) {
Ok(content) => match serde_yaml::from_str::<Config>(&content) {
Ok(cfg) => {
// Serializza e salva nel nuovo formato
let yaml =
serde_yaml::to_string(&cfg).expect("Failed to serialize migrated config");
fs::write(&new_path, yaml)?;

// Rinomina il vecchio file come backup
let backup = dir.join("config.yaml.bak");
fs::rename(&old_path, &backup)?;

ConsoleLog::ok(format!(
"Configuration migrated successfully → {:?}",
new_path
));
ConsoleLog::info(format!("Backup saved as {:?}", backup));
}
Err(e) => {
ConsoleLog::ko(format!("Failed to parse old config.yaml: {e}"));
}
},
Err(e) => {
ConsoleLog::ko(format!("Failed to read old config.yaml: {e}"));
}
}

Ok(())
}

/// Apre il file di configurazione in modifica.
/// Se viene specificato `--editor`, usa quello; altrimenti tenta di rilevare l’editor di sistema.
pub fn run_config_edit(editor_arg: Option<String>) -> std::io::Result<()> {
let path: PathBuf = get_config_path();

// Se non esiste, crealo
if !path.exists() {
ConsoleLog::warn("Configuration file not found. Creating a new one...");
if let Err(e) = init_config_file() {
ConsoleLog::ko(format!("Failed to create configuration file: {e}"))
}
}

// 1️⃣ Priorità all'argomento CLI --editor
let editor = if let Some(e) = editor_arg {
e
} else {
// 2️⃣ Poi variabili d'ambiente VISUAL o EDITOR
if let Ok(visual) = env::var("VISUAL") {
visual
} else if let Ok(editor) = env::var("EDITOR") {
editor
} else {
// 3️⃣ Fallback per piattaforma
if cfg!(target_os = "windows") {
"notepad".to_string()
} else {
"nano".to_string()
}
}
};

ConsoleLog::info(format!(
"Opening configuration file with editor: {}",
editor
));

// 4️⃣ Avvia l’editor
let status = Command::new(&editor)
.arg(path.to_string_lossy().to_string())
.status();

match status {
Ok(s) if s.success() => ConsoleLog::ok("Configuration file closed successfully."),
Ok(_) => ConsoleLog::warn("Editor exited with a non-zero status code."),
Err(e) => ConsoleLog::ko(format!("Failed to launch editor '{}': {e}", editor)),
}

Ok(())
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod config;
pub mod loader;
pub mod log;
pub mod utils;
Loading