1use std::result;
2
3use thiserror::Error;
4
5pub fn is_invalid(a: &str) -> bool {
6 a.starts_with("---") || a.starts_with("--=")
7}
8
9pub fn is_argument(a: &str) -> bool {
10 !is_invalid(a) && (a == "-" || !a.starts_with('-'))
11}
12
13pub fn is_separator(a: &str) -> bool {
14 a == "--"
15}
16
17pub fn is_flag_or_option(a: &str) -> bool {
18 !is_invalid(a) && !is_argument(a) && !is_separator(a)
19}
20
21#[derive(Error, Debug, PartialEq)]
22pub enum ParseError {
23 #[error("internal, argument can not have argument: {}", arg)]
24 InternalArgumentCanNotHaveArgument { arg: String },
25
26 #[error("internal, separator can not have argument")]
27 InternalSeparatorCanNotHaveArgument,
28
29 #[error("internal, invalid eat count: {}", .eat)]
30 InternalInvalidEatCount { eat: usize },
31
32 #[error("internal, index out of range: {}", .index)]
33 InternalIndexOutOfRange { index: usize },
34
35 #[error("flag can not take argument: {}", .name)]
36 FlagWithArgument { name: String },
37
38 #[error("option missing argument: {}", .name)]
39 OptionWithoutArgument { name: String },
40
41 #[error("invalid argument: {}", .value)]
42 InvalidArgument { value: String },
43
44 #[error("unexpected separator: {}", .value)]
45 UnexpectedSeparator { value: String },
46
47 #[error("unknown flag or option: {}", .name)]
48 UnknownFlagOrOption { name: String },
49
50 #[error("invalid string: {}", .s)]
51 InvalidString { s: String },
52}
53
54fn _s(s: &str) -> String {
55 s.to_string()
56}
57
58pub fn parse<'s, 'a>(
59 argv: &'a [&'s str],
60 index: usize,
61) -> result::Result<(&'s str, Option<&'s str>), ParseError> {
62 if index >= argv.len() {
63 return Err(ParseError::InternalIndexOutOfRange { index: index });
64 }
65
66 let a = argv[index];
67 if is_invalid(a) {
68 Err(ParseError::InvalidString { s: _s(a) })
69 } else if is_argument(a) || is_separator(a) {
70 Ok((a, None))
71 } else if a.starts_with("--") {
72 if let Some(i) = a.find('=') {
73 Ok((&a[..i], Some(&a[i + 1..])))
74 } else {
75 if index + 1 < argv.len() && is_argument(argv[index + 1]) {
76 Ok((a, Some(argv[index + 1])))
77 } else {
78 Ok((a, None))
79 }
80 }
81 } else {
82 assert!(a.starts_with('-'));
83 if a.len() > 2 {
84 Ok((&a[..2], Some(&a[2..])))
85 } else {
86 if index + 1 < argv.len() && is_argument(argv[index + 1]) {
87 Ok((a, Some(argv[index + 1])))
88 } else {
89 Ok((a, None))
90 }
91 }
92 }
93}
94
95pub fn next_index(argv: &[&str], index: usize, eat: usize) -> result::Result<usize, ParseError> {
96 if index >= argv.len() {
97 return Err(ParseError::InternalIndexOutOfRange { index });
98 }
99
100 let a = argv[index];
101 if eat == 0 {
102 if is_invalid(a) {
103 return Err(ParseError::InvalidString { s: _s(a) });
104 } else if is_argument(a) {
105 return Err(ParseError::InvalidArgument { value: _s(a) });
106 } else if is_separator(a) {
107 return Err(ParseError::UnexpectedSeparator { value: _s(a) });
108 } else if is_flag_or_option(a) {
109 if let Some(i) = a.find('=') {
110 return Err(ParseError::UnknownFlagOrOption { name: _s(&a[..i]) });
111 } else {
112 return Err(ParseError::UnknownFlagOrOption { name: _s(a) });
113 }
114 } else {
115 }
116 } else if !(eat == 1 || eat == 2) {
117 return Err(ParseError::InternalInvalidEatCount { eat });
118 }
119
120 let ni = if is_invalid(a) {
121 return Err(ParseError::InvalidString { s: _s(a) });
122 } else if is_argument(a) {
123 if eat == 2 {
124 return Err(ParseError::InternalArgumentCanNotHaveArgument { arg: _s(a) });
125 }
126 index + 1
127 } else if is_separator(a) {
128 if eat == 2 {
129 return Err(ParseError::InternalSeparatorCanNotHaveArgument);
130 } else {
131 assert_eq!(eat, 1);
132 index + 1
133 }
134 } else {
135 assert!(is_flag_or_option(a));
136 if a.starts_with("--") {
137 if let Some(i) = a.find('=') {
138 if eat == 1 {
139 return Err(ParseError::FlagWithArgument { name: _s(&a[..i]) });
140 } else {
141 assert_eq!(eat, 2);
142 index + 1
143 }
144 } else {
145 if eat == 2 {
146 if index + 1 < argv.len() && is_argument(argv[index + 1]) {
147 index + eat
148 } else {
149 return Err(ParseError::OptionWithoutArgument { name: _s(a) });
150 }
151 } else {
152 index + eat
153 }
154 }
155 } else if a.len() > 2 {
156 assert!(a.starts_with('-') && &a[1..2] != "-");
157 if eat == 1 {
158 return Err(ParseError::FlagWithArgument { name: _s(&a[..2]) });
159 } else {
160 assert_eq!(eat, 2);
161 index + 1
162 }
163 } else {
164 if index + 1 < argv.len() && is_argument(argv[index + 1]) {
165 index + eat
166 } else if eat == 2 {
167 return Err(ParseError::OptionWithoutArgument { name: _s(a) });
168 } else {
169 assert_eq!(eat, 1);
170 index + 1
171 }
172 }
173 };
174
175 Ok(ni)
176}
177
178pub fn unwrap_argument<'s>(
179 parse_result: (&'s str, Option<&'s str>),
180) -> result::Result<&'s str, ParseError> {
181 if let Some(a) = parse_result.1 {
182 Ok(a)
183 } else {
184 Err(ParseError::OptionWithoutArgument { name: _s(parse_result.0) })
185 }
186}