twilight_command_parser/
arguments.rs1use std::{
2 fmt::{Debug, Formatter, Result as FmtResult},
3 str::CharIndices,
4};
5
6#[derive(Clone)]
8pub struct Arguments<'a> {
9 buf: &'a str,
10 indices: CharIndices<'a>,
11 idx: usize,
12}
13
14impl<'a> Arguments<'a> {
15 pub fn new(buf: &'a str) -> Self {
17 Self {
18 buf: buf.trim(),
19 indices: buf.trim().char_indices(),
20 idx: 0,
21 }
22 }
23
24 pub const fn as_str(&self) -> &'a str {
47 self.buf
48 }
49
50 pub fn into_remainder(self) -> Option<&'a str> {
66 self.buf.get(self.idx..)
67 }
68}
69
70impl<'a> Debug for Arguments<'a> {
71 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
72 f.debug_struct("Arguments")
73 .field("buf", &self.buf)
74 .field("idx", &self.idx)
75 .finish()
76 }
77}
78
79impl<'a> Iterator for Arguments<'a> {
80 type Item = &'a str;
81
82 fn next(&mut self) -> Option<Self::Item> {
84 if self.idx > self.buf.len() {
85 return None;
86 }
87
88 let mut start_idx = self.idx;
89 let mut quoted = false;
90 let mut started = false;
91
92 for (i, ch) in &mut self.indices {
93 if quoted {
94 if ch == '"' {
95 let v = self.buf.get(start_idx..i);
96 self.idx = i + 1;
97
98 return v.map(str::trim);
99 }
100 } else if ch == ' ' {
101 if started {
102 let v = self.buf.get(start_idx..i);
103 self.idx = i + 1;
104
105 return v.map(str::trim);
106 }
107 self.idx = i;
108 start_idx = i;
109 started = true;
110 continue;
111 } else if ch == '"' {
112 start_idx = i + 1;
113 quoted = true;
114 }
115
116 self.idx = i;
117 started = true;
118 }
119
120 self.idx = usize::max_value();
121
122 match self.buf.get(start_idx..) {
123 Some("") | None => None,
124 Some(v) => Some(v.trim()),
125 }
126 }
127}
128
129#[allow(clippy::non_ascii_literal)]
130#[cfg(test)]
131mod tests {
132 use super::Arguments;
133 use static_assertions::assert_impl_all;
134 use std::fmt::Debug;
135
136 assert_impl_all!(Arguments<'_>: Clone, Debug, Iterator, Send, Sync);
137
138 #[test]
139 fn test_as_str() {
140 let args = Arguments::new("foo bar baz");
141 assert_eq!("foo bar baz", args.as_str());
142 }
143
144 #[test]
145 fn test_simple_args() {
146 let mut args = Arguments::new("foo bar baz");
147 assert_eq!(Some("foo"), args.next());
148 assert_eq!(Some("bar"), args.next());
149 assert_eq!(Some("baz"), args.next());
150 assert_eq!(None, args.next());
151 }
152
153 #[test]
154 fn test_quoted_args() {
155 let mut args = Arguments::new(r#"this "is a longer argument" here"#);
156 assert_eq!(Some("this"), args.next());
157 assert_eq!(Some("is a longer argument"), args.next());
158 assert_eq!(Some("here"), args.next());
159 assert_eq!(None, args.next());
160 }
161
162 #[test]
163 fn test_quoted_close_args() {
164 let mut args = Arguments::new(r#""kind of weird""but okay"#);
165 assert_eq!(Some("kind of weird"), args.next());
166 assert_eq!(Some("but okay"), args.next());
167 assert_eq!(None, args.next());
168 }
169
170 #[test]
171 fn test_unicode_chars_1() {
172 let mut args = Arguments::new("๐๐ข๐ nice try");
173 assert_eq!(Some("๐๐ข๐"), args.next());
174 assert_eq!(Some("nice"), args.next());
175 assert_eq!(Some("try"), args.next());
176 assert_eq!(None, args.next());
177 }
178
179 #[test]
180 fn test_unicode_chars_2() {
181 let mut args = Arguments::new("Saighdiรบr rรฉalta what even");
182 assert_eq!(Some("Saighdiรบr"), args.next());
183 assert_eq!(Some("rรฉalta"), args.next());
184 assert_eq!(Some("what"), args.next());
185 assert_eq!(Some("even"), args.next());
186 assert_eq!(None, args.next());
187 }
188
189 #[test]
190 fn test_quoted_unicode_chars() {
191 let mut args = Arguments::new(r#""๐๐ข๐ | CSA" amazing try"#);
192 assert_eq!(Some("๐๐ข๐ | CSA"), args.next());
193 assert_eq!(Some("amazing"), args.next());
194 assert_eq!(Some("try"), args.next());
195 assert_eq!(None, args.next());
196 }
197
198 #[test]
199 fn test_emote() {
200 let mut args = Arguments::new("why an emote ๐");
201 assert_eq!(Some("why"), args.next());
202 assert_eq!(Some("an"), args.next());
203 assert_eq!(Some("emote"), args.next());
204 assert_eq!(Some("๐"), args.next());
205 assert_eq!(None, args.next());
206 }
207
208 #[test]
209 fn test_quoted_emote() {
210 let mut args = Arguments::new(r#"omg "๐ - ๐" kewl"#);
211 assert_eq!(Some("omg"), args.next());
212 assert_eq!(Some("๐ - ๐"), args.next());
213 assert_eq!(Some("kewl"), args.next());
214 assert_eq!(None, args.next());
215 }
216}