why-not 0.1.0

Output a string repeatedly
#![allow(clippy::collapsible_else_if)]

use std::io::{Error, ErrorKind, Result as IoResult};
use std::sync::atomic::{AtomicBool, Ordering};

use rand::{rngs::ThreadRng, seq::SliceRandom, thread_rng};

use crate::config::Config;
use crate::source::get_messages;
use crate::writer::Writer;

mod config;
mod source;
mod writer;

const BUFF_SIZE: usize = 8192;

static SHUTDOWN: AtomicBool = AtomicBool::new(false);

pub fn main() {
    let config = Config::load_from_env();

    let w = Writer::new_from_config(&config);

    if let Err(err) = ctrlc::set_handler(|| SHUTDOWN.store(true, Ordering::Relaxed)) {
        eprintln!("{}", err);
    }

    let res = if config.random {
        print_random_messages(&config, w)
    } else {
        print_message(&config, w)
    };

    if let Err(err) = res {
        if !matches!(
            err.kind(),
            ErrorKind::BrokenPipe | ErrorKind::UnexpectedEof | ErrorKind::Interrupted
        ) {
            eprintln!("{}", err);
        }
    }
}

fn shutdown() -> bool {
    SHUTDOWN.load(Ordering::Relaxed)
}

fn print_message(config: &Config, mut w: Writer) -> IoResult<()> {
    let messages = get_messages(config);

    let message = match messages.first() {
        Some(message) => message.as_str(),
        None => return Err(Error::new(ErrorKind::Other, "Missing messages".to_owned())),
    };

    let mut message = format!("{}\n", message.to_owned());

    if let Some(max) = config.max_lines {
        for _ in 0..max {
            w.write(&message)?;
            if shutdown() {
                break;
            }
        }
    } else {
        if w.supports_multiple_messages() {
            message = repeat_messages(&message);
        }
        while !shutdown() {
            w.write(&message)?;
        }
    }

    Ok(())
}

fn repeat_messages(message: &str) -> String {
    let mut buffer = String::with_capacity(BUFF_SIZE);
    buffer.push_str(message);

    while buffer.len() + message.len() < BUFF_SIZE {
        buffer.push_str(message);
    }

    buffer
}

fn print_random_messages(config: &Config, mut w: Writer) -> IoResult<()> {
    let messages = get_messages(config);
    let mut rng = thread_rng();

    if let Some(max) = config.max_lines {
        for _ in 0..max {
            print_random_message(&messages, &mut w, &mut rng)?;
            if shutdown() {
                break;
            }
        }
    } else {
        while !shutdown() {
            print_random_message(&messages, &mut w, &mut rng)?;
        }
    }

    Ok(())
}

fn print_random_message(messages: &[String], w: &mut Writer, rng: &mut ThreadRng) -> IoResult<()> {
    let message = messages.choose(rng).map(|m| m.as_str()).unwrap_or("");
    w.writeln(message)
}