<commit_before>#[macro_use]
extern crate clap;

use clap::{App, AppSettings, Arg};
use derive_more::From;

use diceware::*;

// #[non_exhaustive] // One day
#[derive(From, Debug)]
pub enum Error {
    // External Errors
    Io(std::io::Error),

    // My errors
    InvalidToken,

    // Allows you to add future errors without breaking compatibility
    // for your user's `match` arms.
    // This will eventually be done with #[non_exhaustive]
    #[doc(hidden)]
    __Nonexhaustive,
}

pub type Result<T> = std::result::Result<T, Error>;

fn main() -> Result<()> {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .about(crate_description!())
        .setting(AppSettings::DisableVersion) // no --version flag
        .setting(AppSettings::UnifiedHelpMessage)
        .arg(
            Arg::with_name("SEPARATOR")
                .short("j")
                .long("join")
                .help("Join words using separator [default: \\n]")
                .default_value("\n")
                .hide_default_value(true) // it doesn't print properly
                .takes_value(true),
        )
        .arg(
            Arg::with_name("NUMBER")
                .required(false)
                .help("Sets the number of diceware words to select")
                .default_value("4")
                .index(1),
        )
        .get_matches();

    let number = value_t!(matches.value_of("NUMBER"), u16).unwrap_or_else(|e| e.exit());

    // no longer using a functional word stream
    // it would be nice to do that again sometime.

    // let word_list = get_word_list();

    // let word_stream = iter::repeat_with(|| get_diceware_word(&word_list));
    // let mut words = word_stream.take(number as usize);

    // let separator = matches.value_of("SEPARATOR").unwrap_or("\n");
    // if let Some(word) = words.next() {
    //     print!("{}", word);
    //     for word in words {
    //         print!("{}", separator);
    //         print!("{}", word);
    //     }
    //     println!();
    // }

    let mut words = Vec::<String>::new();

    for _ in 0..number {
        let diceroll = roll();
        let word = get_word(&diceroll).unwrap();
        words.push(word);
    }

    let separator = matches.value_of("SEPARATOR").unwrap_or("\n");
    print!("{}", words.join(separator));
    println!();

    Ok(())
}<commit_msg>Handle invalid rolls.  Adjust CLI argparse code.<commit_after>#[macro_use]
extern crate clap;

use clap::{App, AppSettings, Arg};
use derive_more::From;

use crate::Error::NoWordForRoll;
use diceware::*;

// #[non_exhaustive] // One day
#[derive(From, Debug)]
pub enum Error {
    // External Errors
    Io(std::io::Error),

    // My errors
    NoWordForRoll {
        roll: String,
    },

    // Allows you to add future errors without breaking compatibility
    // for your user's `match` arms.
    // This will eventually be done with #[non_exhaustive]
    #[doc(hidden)]
    __Nonexhaustive,
}

pub type Result<T> = std::result::Result<T, Error>;

fn main() -> Result<()> {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .about(crate_description!())
        .setting(AppSettings::DisableVersion) // no --version flag
        .setting(AppSettings::UnifiedHelpMessage)
        .arg(
            Arg::with_name("SEPARATOR")
                .short("j")
                .long("join")
                .takes_value(true)
                .default_value("\n")
                .hide_default_value(true) // it doesn't print properly
                .help("Join words using separator [default: \\n]"),
        )
        .arg(
            Arg::with_name("NUMBER")
                .required(false)
                .default_value("4")
                .help("Sets the number of diceware words to select"),
        )
        .get_matches();

    let separator = matches.value_of("SEPARATOR").unwrap_or("\n");
    let number = value_t!(matches.value_of("NUMBER"), u16).unwrap_or_else(|e| e.exit());

    // no longer using a functional word stream
    // it would be nice to do that again sometime.

    // let word_list = get_word_list();

    // let word_stream = iter::repeat_with(|| get_diceware_word(&word_list));
    // let mut words = word_stream.take(number as usize);

    // let separator = matches.value_of("SEPARATOR").unwrap_or("\n");
    // if let Some(word) = words.next() {
    //     print!("{}", word);
    //     for word in words {
    //         print!("{}", separator);
    //         print!("{}", word);
    //     }
    //     println!();
    // }

    let mut words = Vec::<String>::new();

    for _ in 0..number {
        let diceroll = roll();
        match get_word(&diceroll) {
            Some(word) => words.push(word),
            None => return Err(NoWordForRoll { roll: diceroll }),
        }
    }

    print!("{}", words.join(separator));
    println!();

    Ok(())
}
{'fraction_non_alphanumeric': 0.1591279543602282, 'fraction_numerical': 0.0018337408312958435, 'mean_word_length': 2.1327377153797062, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2327588', 'n_tokens_mistral': 1529, 'n_tokens_neox': 1496, 'n_words': 356}