1use crate::{Input, TokenKind};
2
3pub struct StringInput<I: Iterator<Item = String> = std::env::Args> {
11 current: Option<(usize, usize, TokenKind)>,
12 iter: I,
13 buf: String,
14 ignore_dashes: bool,
15}
16
17impl<I: Iterator<Item = String>> StringInput<I> {
18 pub fn new(mut iter: I) -> Self {
30 match iter.next() {
31 Some(buf) => Self {
32 current: Some(Self::trim_leading_dashes(false, &buf, 0)),
33 iter,
34 buf,
35 ignore_dashes: false,
36 },
37 None => {
38 Self { current: None, iter, buf: String::new(), ignore_dashes: false }
39 }
40 }
41 }
42
43 fn trim_leading_dashes(
44 ignore: bool,
45 string: &str,
46 current: usize,
47 ) -> (usize, usize, TokenKind) {
48 if ignore {
49 (current, current, TokenKind::NoDash)
50 } else if string.starts_with("--") {
51 (current + 2, current, TokenKind::TwoDashes)
52 } else if string.starts_with('-') {
53 (current + 1, current, TokenKind::OneDash)
54 } else {
55 (current, current, TokenKind::NoDash)
56 }
57 }
58
59 fn trim_equals(&self, current: usize, kind: TokenKind) -> (usize, usize, TokenKind) {
60 match kind {
61 TokenKind::NoDash => {}
62 TokenKind::OneDash => {
63 if self.buf[current..].starts_with('=') {
64 return (current + 1, current + 1, TokenKind::AfterEquals);
65 } else {
66 return (current, current, TokenKind::AfterOneDash);
67 }
68 }
69 TokenKind::TwoDashes => {
70 if self.buf[current..].starts_with('=') {
71 return (current + 1, current + 1, TokenKind::AfterEquals);
72 }
73 }
74 TokenKind::AfterOneDash => {
75 if self.buf[current..].starts_with('=') {
76 return (current + 1, current + 1, TokenKind::AfterEquals);
77 }
78 }
79 TokenKind::AfterEquals => {}
80 }
81 (current, current, kind)
82 }
83}
84
85impl<I: Iterator<Item = String>> Input for StringInput<I> {
86 fn current(&self) -> Option<(&str, TokenKind)> {
87 self.current.map(|(i, _, kind)| (&self.buf[i..], kind))
88 }
89
90 fn current_str_with_leading_dashes(&self) -> Option<&str> {
91 self.current.map(|(_, i, _)| &self.buf[i..])
92 }
93
94 fn bump(&mut self, len: usize) -> &str {
95 if let Some((current, _, kind)) = &mut self.current {
96 let current_len = self.buf.len() - *current;
97 if len > current_len {
98 panic!("index bumped out of bounds: {} > {}", len, current_len);
99 }
100
101 let prev_current = *current;
102 *current += len;
103
104 if current_len == len {
105 match self.iter.next() {
106 Some(s) => {
107 self.buf.push_str(&s);
108 self.current = Some(Self::trim_leading_dashes(
109 self.ignore_dashes,
110 &s,
111 *current,
112 ));
113 }
114 None => self.current = None,
115 }
116 } else {
117 let (current, kind) = (*current, *kind);
118 self.current = Some(self.trim_equals(current, kind));
119 }
120
121 &self.buf[prev_current..prev_current + len]
122 } else {
123 panic!("tried to bump index on empty input by {}", len)
124 }
125 }
126
127 fn bump_with_leading_dashes(&mut self, len: usize) -> &str {
128 if let Some((current, cwd, kind)) = &mut self.current {
129 let current_len = self.buf.len() - *cwd;
130 if len > current_len {
131 panic!("index bumped out of bounds: {} > {}", len, current_len);
132 }
133
134 let prev_current = *cwd;
135 *current += len;
136 *cwd += len;
137
138 if current_len == len {
139 match self.iter.next() {
140 Some(s) => {
141 self.buf.push_str(&s);
142 self.current =
143 Some(Self::trim_leading_dashes(self.ignore_dashes, &s, *cwd));
144 }
145 None => self.current = None,
146 }
147 } else {
148 let (current, kind) = (*current, *kind);
149 self.current = Some(self.trim_equals(current, kind));
150 }
151
152 &self.buf[prev_current..prev_current + len]
153 } else {
154 panic!("tried to bump index on empty input by {}", len)
155 }
156 }
157
158 fn bump_argument(&mut self) -> Option<&str> {
159 if let Some((i, _, _)) = self.current {
160 let len = self.buf.len() - i;
161 Some(self.bump(len))
162 } else {
163 None
164 }
165 }
166
167 fn set_ignore_dashes(&mut self, ignore: bool) {
168 self.ignore_dashes = ignore;
169 if let Some((current, cwd, kind)) = &mut self.current {
170 if ignore {
171 *current = *cwd;
172 *kind = TokenKind::NoDash;
173 } else {
174 self.current =
175 Some(Self::trim_leading_dashes(ignore, &self.buf[*current..], *cwd));
176 }
177 }
178 }
179
180 fn ignore_dashes(&self) -> bool {
181 self.ignore_dashes
182 }
183}