rfortune 0.5.5

A Rust-based clone of the classic UNIX 'fortune' command
Documentation
use crate::log::ConsoleLog;
use std::fs;
use std::io::{BufRead, BufReader};
use std::path::Path;

#[derive(Debug)]
pub struct FortuneFile {
    pub title: Option<String>,
    pub quotes: Vec<String>,
}

impl FortuneFile {
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
        let path_ref = path.as_ref();

        let file = match fs::File::open(path_ref) {
            Ok(f) => f,
            Err(e) => {
                ConsoleLog::ko(format!("Failed to open file '{}': {e}", path_ref.display()));
                return Err(format!("Failed to open file: {e}"));
            }
        };

        let reader = BufReader::new(file);

        let mut title: Option<String> = None;
        let mut quotes: Vec<String> = Vec::new();
        let mut current_quote = String::new();
        let mut is_first_line = true;

        for line in reader.lines() {
            let line = match line {
                Ok(l) => l,
                Err(e) => {
                    ConsoleLog::ko(format!(
                        "Failed to read line from '{}': {e}",
                        path_ref.display()
                    ));
                    return Err(format!("Failed to read line: {e}"));
                }
            };

            let trimmed = line.trim();

            if is_first_line && trimmed.starts_with('#') {
                title = Some(trimmed.trim_start_matches('#').trim().to_string());
                is_first_line = false;
                continue;
            }

            if is_first_line && !trimmed.is_empty() {
                is_first_line = false;
            }

            if trimmed == "%" {
                if !current_quote.trim().is_empty() {
                    quotes.push(current_quote.trim().to_string());
                    current_quote.clear();
                }
            } else {
                current_quote.push_str(line.as_str());
                current_quote.push('\n');
            }
        }

        if !current_quote.trim().is_empty() {
            quotes.push(current_quote.trim().to_string());
        }

        if quotes.is_empty() {
            ConsoleLog::warn(format!(
                "No quotes found in '{}'. The file may be empty or incorrectly formatted.",
                path_ref.display()
            ));
            return Err("No quotes found in the file.".to_string());
        }

        Ok(FortuneFile { title, quotes })
    }
}