1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
use crate::color::detect_color; use crate::output::write_error; use clap::Clap; use std::io::{Stdin, StdinLock}; use std::{io, process}; use termcolor::{ColorChoice, StandardStream, StandardStreamLock}; pub const EXIT_CODE_OK: i32 = 0; pub const EXIT_CODE_IO_ERROR: i32 = 1; pub const EXIT_CODE_CLI_ERROR: i32 = 2; pub type Result = io::Result<i32>; pub trait Options: Clap { fn color(&self) -> Option<ColorChoice>; } pub struct Io { stdin: Stdin, stdout: StandardStream, stderr: StandardStream, } impl Io { pub fn new(color: ColorChoice) -> Self { Self { stdin: io::stdin(), stdout: StandardStream::stdout(color), stderr: StandardStream::stderr(color), } } pub fn stdin(&self) -> StdinLock { self.stdin.lock() } pub fn stdout(&self) -> StandardStreamLock { self.stdout.lock() } pub fn stderr(&self) -> StandardStreamLock { self.stderr.lock() } } pub fn exec_run<O, R>(run: R) where O: Options, R: FnOnce(&O, &Io) -> Result, { let options = O::parse(); let color = detect_color(options.color()); let io = Io::new(color); let exit_code = match run(&options, &io) { Ok(exit_code) => exit_code, Err(io_error) => { write_error(&mut io.stderr(), &io_error).expect("Failed to write to stderr!"); EXIT_CODE_IO_ERROR } }; process::exit(exit_code); } #[cfg(test)] mod tests { use super::*; use claim::*; use std::io::{Read, Write}; #[test] fn io() { let io = Io::new(ColorChoice::Never); assert_ok!(io.stdin().read_exact(&mut [])); assert_ok!(io.stdout().flush()); assert_ok!(io.stderr().flush()); } }