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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use rand::prelude::SliceRandom;
use rand::thread_rng;
use std::env::args;
use std::io::{stdin, stdout, Read, Write};
use std::path::Path;

fn main() -> Result<(), String> {
    let mut args: Vec<String> = args().collect();
    args.remove(0);

    if args.len() >= 2 {
        return show_msg(MsgType::InvalidArgumentCount(args.len()));
    }

    let mut shuffle_mode = ShuffleMode::All;

    if args.len() == 1 {
        let option = &args[0];
        match option.to_ascii_lowercase().as_str() {
            "-h" | "--help" => {
                return show_msg(MsgType::Help);
            }
            "-l" | "--line" => {
                shuffle_mode = ShuffleMode::Line;
            }
            "-a" | "--all" => {
                shuffle_mode = ShuffleMode::All;
            }
            _ => {
                return show_msg(MsgType::UnknownOption(option));
            }
        }
    }

    match shuffle_mode {
        ShuffleMode::Line => {
            let stdin = stdin();
            let lines = stdin.lines();
            for line in lines {
                match line {
                    Ok(line) => {
                        println!("{}", shuffle_string(&line));
                    }
                    Err(e) => {
                        return show_msg(MsgType::IoError(e));
                    }
                }
            }
        }
        ShuffleMode::All => {
            let mut s = String::new();
            stdin().read_to_string(&mut s).unwrap();
            print!("{}", shuffle_string(&s));
            stdout().flush().unwrap();
        }
    }
    Ok(())
}

fn shuffle_string(s: &str) -> String {
    let mut rng = thread_rng();

    let mut chars = s.chars().collect::<Vec<_>>();
    chars.shuffle(&mut rng);
    String::from_iter(chars)
}

enum MsgType<'a> {
    Help,
    InvalidArgumentCount(usize),
    UnknownOption(&'a String),
    IoError(std::io::Error),
}

enum ShuffleMode {
    Line,
    All,
}

fn show_msg(msg_type: MsgType) -> Result<(), String> {
    return match msg_type {
        MsgType::Help => {
            let file_path = args().next().unwrap();
            let file_name = Path::new(&file_path).file_name().unwrap().to_str().unwrap();
            let help_msg = format!(
                "Shuffle string read from stdin.
Usage: {} [option]
Options:
  -l, --line  Shuffle string by each line.
  -a, --all  Shuffle all string read from stdin; it's the default mode.
  -h, --help  Show this help.",
                file_name
            );
            println!("{}", help_msg);
            Ok(())
        }
        MsgType::InvalidArgumentCount(count) => {
            return Err(format!("Invalid argument count: {}", count));
        }
        MsgType::UnknownOption(option) => {
            return Err(format!("Unknown option: {}", option));
        }
        MsgType::IoError(e) => {
            return Err(format!("I/O error: {}", e));
        }
    };
}