conch_runtime_pshaw/eval/
fields.rs1use crate::env::{StringWrapper, VariableEnvironment};
2use crate::IFS_DEFAULT;
3use std::borrow::Borrow;
4use std::vec;
5
6lazy_static::lazy_static! {
7 static ref IFS: String = String::from("IFS");
8}
9
10#[derive(PartialEq, Eq, Clone, Debug)]
14pub enum Fields<T> {
15 Zero,
17 Single(T),
19 At(Vec<T>),
21 Star(Vec<T>),
23 Split(Vec<T>),
26}
27
28impl<T: StringWrapper> Fields<T> {
29 pub fn is_null(&self) -> bool {
34 match *self {
35 Fields::Zero => true,
36
37 Fields::Single(ref s) => s.as_str().is_empty(),
38
39 Fields::At(ref v) | Fields::Star(ref v) | Fields::Split(ref v) => {
40 v.iter().all(|s| s.as_str().is_empty())
41 }
42 }
43 }
44
45 pub fn join(self) -> T {
49 match self {
50 Fields::Zero => String::new().into(),
51 Fields::Single(s) => s,
52 Fields::At(v) | Fields::Star(v) | Fields::Split(v) => v
53 .iter()
54 .map(StringWrapper::as_str)
55 .filter(|s| !s.is_empty())
56 .collect::<Vec<&str>>()
57 .join(" ")
58 .into(),
59 }
60 }
61
62 pub fn join_with_ifs<E: ?Sized>(self, env: &E) -> T
68 where
69 E: VariableEnvironment,
70 E::VarName: Borrow<String>,
71 E::Var: Borrow<String>,
72 {
73 match self {
74 Fields::Zero => String::new().into(),
75 Fields::Single(s) => s,
76 Fields::At(v) | Fields::Star(v) | Fields::Split(v) => {
77 let sep = env.var(&IFS).map(|s| s.borrow().as_str()).map_or(" ", |s| {
78 if s.is_empty() {
79 ""
80 } else {
81 &s[0..1]
82 }
83 });
84
85 v.iter()
86 .map(StringWrapper::as_str)
87 .collect::<Vec<_>>()
88 .join(sep)
89 .into()
90 }
91 }
92 }
93
94 pub fn split<E: ?Sized>(self, env: &E) -> Fields<T>
98 where
99 E: VariableEnvironment,
100 E::VarName: Borrow<String>,
101 E::Var: Borrow<String>,
102 {
103 match self {
104 Fields::Zero => Fields::Zero,
105 Fields::Single(f) => split_fields_internal(vec![f], env).into(),
106 Fields::At(fs) => Fields::At(split_fields_internal(fs, env)),
107 Fields::Star(fs) => Fields::Star(split_fields_internal(fs, env)),
108 Fields::Split(fs) => Fields::Split(split_fields_internal(fs, env)),
109 }
110 }
111}
112
113impl<T> From<Vec<T>> for Fields<T> {
115 fn from(mut fields: Vec<T>) -> Self {
116 if fields.is_empty() {
117 Fields::Zero
118 } else if fields.len() == 1 {
119 Fields::Single(fields.pop().unwrap())
120 } else {
121 Fields::Split(fields)
122 }
123 }
124}
125
126impl<T> From<T> for Fields<T> {
127 fn from(t: T) -> Self {
128 Fields::Single(t)
129 }
130}
131
132impl<T> IntoIterator for Fields<T> {
133 type Item = T;
134 type IntoIter = vec::IntoIter<Self::Item>;
135
136 fn into_iter(self) -> Self::IntoIter {
137 let vec = match self {
138 Fields::Zero => vec![],
139 Fields::Single(s) => vec![s],
140 Fields::At(v) | Fields::Star(v) | Fields::Split(v) => v,
141 };
142
143 vec.into_iter()
144 }
145}
146
147fn split_fields_internal<T, E: ?Sized>(words: Vec<T>, env: &E) -> Vec<T>
149where
150 T: StringWrapper,
151 E: VariableEnvironment,
152 E::VarName: Borrow<String>,
153 E::Var: Borrow<String>,
154{
155 let ifs = env.var(&IFS).map_or(IFS_DEFAULT, |s| s.borrow().as_str());
157 if ifs.is_empty() {
158 return words;
159 }
160
161 let whitespace: Vec<char> = ifs.chars().filter(|c| c.is_whitespace()).collect();
162
163 let mut fields = Vec::with_capacity(words.len());
164 'word: for word in words.iter().map(StringWrapper::as_str) {
165 if word.is_empty() {
166 continue;
167 }
168
169 let mut iter = word.chars().enumerate().peekable();
170 loop {
171 let start;
172 loop {
173 match iter.next() {
174 None => continue 'word,
177 Some((idx, c)) => {
178 if whitespace.contains(&c) {
179 continue;
180 } else if ifs.contains(c) {
181 fields.push(String::new().into());
186 } else {
187 start = idx;
189 break;
190 }
191 }
192 }
193 }
194
195 let end;
196 loop {
197 match iter.next() {
198 None => {
199 end = None;
200 break;
201 }
202 Some((idx, c)) => {
203 if ifs.contains(c) {
204 end = Some(idx);
205 break;
206 }
207 }
208 }
209 }
210
211 let field = match end {
212 Some(end) => &word[start..end],
213 None => &word[start..],
214 };
215
216 fields.push(String::from(field).into());
217
218 loop {
222 match iter.peek() {
223 Some(&(_, c)) if whitespace.contains(&c) => {
224 iter.next();
225 }
226 Some(_) | None => break,
227 }
228 }
229 }
230 }
231
232 fields.shrink_to_fit();
233 fields
234}