read_dir/
args.rs

1use crate::{Format, ReadDirOptions};
2
3const HELP: &str = r#"  USAGE: rd -[OPTIONS]... [FILE]
4    DESCRIPTION: List files in the given directory
5
6    ARGUMENTS:
7        -A | -D => list dotfiles (files beginning with a '.') aswell as implied entries ('.' and '..'),
8        -a | -d => list dotfiles (files beginning with a '.'),
9        -e => no formatting will be applied; colors will be,
10        -b => no color,
11        -H => no new lines,
12        -l => {
13            files will be displayed vertically and additional metadata will be tacked on
14            the metadata includes (permissions owner group size)
15        }
16        -r => reverse the FINAL file vector,
17        -u => do not apply sorting,
18        -V => list each file on a new line,
19        -Vx => create a vertical matrix with `x` length; x must be greater than 0; see ERRORS,
20
21        -h => print this menu; exit,
22        -v => print the version; exit,
23
24    ERRORS: 
25    Upon an error the program will print the help menu then the error; after it will proceed to exit
26    Possible errors: 
27        * If you add multiple dashes in an argument
28        * If you add an invalid argument
29        * If you add multiple directories
30        * If you pass a number less than or equal to 0 into -Vx
31        * If you pass a directory which you do not have permission to read
32    Internal errors:
33        * If Rust fails to read a directory
34        * If Rust fails to read a file to a string
35        * If Rust fails to read a file entry
36        * If Rust fails to read files metadata 
37        * If Rust failed to open /etc/passwd
38        * If Rust fails to open /etc/group
39    Other:
40        * There is a .expect() which should never occur
41          as an empty file should not be queried
42        * There are a few .expect() calls after parsing a number
43          which should never error since they are checked beforehand"#;
44
45
46pub fn parse_args(args: Vec<String>) -> ReadDirOptions {
47    let mut read_dir_options = ReadDirOptions::new();
48
49    let mut expect_number = false;
50    let mut directory: Option<String> = None;
51    for arg in args {
52        if arg.starts_with("-") {
53            let arg: String = arg.chars().skip(1).collect();
54
55            let args = split(arg);
56
57            for arg in args {
58                match arg.as_str() {
59                    "a" | "d" => {
60                        read_dir_options.dotfiles = true;
61                    }
62
63                    "A" | "D" => {
64                        read_dir_options.dotfiles = true;
65                        read_dir_options.implied = true;
66                    }
67
68                    "b" => {
69                        read_dir_options.colored = false;
70                    }
71
72                    "e" => {
73                        read_dir_options.format = None;
74                    }
75
76                    "H" => {
77                        read_dir_options.format = Some(Format::Horizontal);
78                    }
79
80                    "l" => {
81                        read_dir_options.format = Some(Format::Long);
82                    }
83
84                    "r" => {
85                        read_dir_options.reverse = true;
86                    }
87
88                    "u" => {
89                        read_dir_options.sort = false;
90                    }
91
92                    "V" => {
93                        read_dir_options.format = Some(Format::Vertical);
94                        expect_number = true;
95                        continue;
96                    }
97
98                    "h" => {
99                        println!("{}", HELP);
100                        std::process::exit(0);
101                    }
102
103                    "v" => {
104                        println!(env!("CARGO_PKG_VERSION"));
105                        std::process::exit(0);
106                    }
107
108                    "-" => {
109                        println!("{}", HELP);
110                        eprintln!("Error: please include only one '-'; exiting");
111                        std::process::exit(1);
112                    }
113
114                    arg => {
115                        if arg.chars().all(|c| c.is_numeric()) && expect_number {
116                            read_dir_options.format = Some(Format::VerticalMatrix(
117                                arg.parse()
118                                    .expect("This has already been checked to be a number"),
119                            ));
120                        } else {
121                            println!("{}", HELP);
122                            eprintln!("Error: unknown argument \"{arg}\"; exiting");
123                            std::process::exit(1);
124                        }
125                    }
126                }
127                expect_number = false;
128            }
129        } else {
130            if directory.is_none() {
131                if !std::path::Path::new(&arg).is_dir() {
132                    eprintln!("Error: {} is not a directory; exiting", &arg);
133                    std::process::exit(1);
134                }
135                directory = Some(arg);
136            } else {
137                eprintln!("Error: multiple directory arguments; exiting");
138                std::process::exit(1);
139            }
140        }
141    }
142
143    if let Some(directory) = directory {
144        read_dir_options.directory = directory;
145    }
146
147    read_dir_options
148}
149
150// Custom split that groups numbers together
151// the normal one would take "aV10ru" and output ["a", "V", "10r", "u"]
152// this one instead outputs ["a", "V", "10", "r", "u"]
153fn split(str: String) -> Vec<String> {
154    let mut vec: Vec<String> = Vec::new();
155
156    let mut number = String::new();
157    for char in str.chars() {
158        if char.is_numeric() {
159            number.push(char);
160        } else {
161            if !number.is_empty() {
162                vec.push(number.clone());
163                number.clear();
164            }
165
166            vec.push(char.to_string());
167        }
168    }
169
170    vec
171}