1use crate::ast::{self, CompleteStyle, Delimiter, Expression, Name, Tree};
4use crate::color::*;
5use crate::git::Stats;
6
7use std::{fmt, io};
8
9#[derive(Debug)]
11pub enum InterpreterErr {
12 UnexpectedArgs { exp: Expression },
13 WriteError(io::Error),
14}
15
16impl From<io::Error> for InterpreterErr {
17 fn from(e: io::Error) -> Self {
18 InterpreterErr::WriteError(e)
19 }
20}
21
22type Result<T = bool> = std::result::Result<T, InterpreterErr>;
23
24#[derive(Debug, PartialEq, Eq, Default, Clone)]
26pub struct Interpreter {
27 stats: Stats,
28 allow_color: bool,
29 bash_prompt: bool,
30 command_queue: Vec<WriteCommand>,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34enum WriteCommand {
35 WriteContext(CompleteStyle),
36 WriteStr(&'static str),
37 #[allow(unused)] WriteString(String),
39}
40
41impl Interpreter {
42 pub fn new(stats: Stats, allow_color: bool, bash_prompt: bool) -> Interpreter {
44 Interpreter {
45 stats,
46 allow_color,
47 bash_prompt,
48 command_queue: Vec::with_capacity(32),
49 }
50 }
51
52 fn drain_queue(&mut self, i: usize) {
53 self.command_queue.truncate(self.command_queue.len() - i);
54 }
55
56 fn queue_str(&mut self, s: &'static str) {
57 self.command_queue.push(WriteCommand::WriteStr(s));
58 }
59
60 #[inline(always)]
61 fn with_command<F, C, W>(&mut self, w: &mut W, c: WriteCommand, execute: F, close: C) -> Result
62 where
63 W: io::Write,
64 F: Fn(&mut Self, &mut W) -> Result,
65 C: Fn(&mut Self, &mut W) -> Result<()>,
66 {
67 self.command_queue.push(c);
68 execute(self, w).and_then(|wrote| {
69 if wrote {
70 close(self, w).map(|_| true)
71 } else {
72 self.command_queue.pop();
73 Ok(false)
74 }
75 })
76 }
77
78 pub fn evaluate<W: io::Write>(&mut self, exps: &Tree, w: &mut W) -> Result<()> {
80 if self.allow_color {
81 self.queue_str(if self.bash_prompt {
82 "\u{01}\x1B[0m\u{02}"
83 } else {
84 "\x1B[0m"
85 });
86 }
87
88 if self.interpret_tree(w, &exps, CompleteStyle::default())? && self.allow_color {
89 if self.bash_prompt {
90 write!(w, "\u{01}\x1B[0m\u{02}")?;
91 } else {
92 write!(w, "\x1B[0m")?;
93 }
94 }
95
96 self.command_queue.clear();
97
98 Ok(())
99 }
100
101 #[inline(always)]
102 fn write_queue<W: io::Write>(&mut self, w: &mut W) -> Result<()> {
103 for command in self.command_queue.drain(..) {
104 use WriteCommand::*;
105 match command {
106 WriteString(s) => write!(w, "{}", s)?,
107 WriteContext(c) => c.write_to(w, self.bash_prompt)?,
108 WriteStr(s) => write!(w, "{}", s)?,
109 }
110 }
111
112 Ok(())
113 }
114
115 fn interpret_tree<W: io::Write>(
116 &mut self,
117 w: &mut W,
118 exps: &Tree,
119 context: CompleteStyle,
120 ) -> Result {
121 use Expression::*;
122 let mut wrote = false;
123 let mut separator_count = 0;
124 for e in &exps.0 {
125 match e {
126 Separator(s) => {
127 if wrote {
130 separator_count += 1;
131 self.queue_str(s.as_str());
132 }
133 }
134 e => {
135 if self.interpret(w, &e, context)? {
139 wrote = true;
140 } else {
141 self.drain_queue(separator_count);
142 }
143 separator_count = 0;
144 }
145 }
146 }
147 self.drain_queue(separator_count);
148 Ok(wrote)
149 }
150
151 fn interpret<W: io::Write>(
152 &mut self,
153 w: &mut W,
154 exp: &Expression,
155 ctx: CompleteStyle,
156 ) -> Result {
157 use ast::Expression::*;
158
159 match exp {
160 Named { name, ref sub } => self.interpret_named(w, *name, sub, ctx),
161 Group { d, ref sub } => self.interpret_group(w, *d, sub, ctx),
162 Format { ref style, ref sub } => self.interpret_format(w, *style, sub, ctx),
163 Literal(ref literal) => {
164 self.write_queue(w)?;
165 write!(w, "{}", literal)?;
166 Ok(true)
167 }
168 Separator(_) => unreachable!("Separator must be handled in tree interpreter"),
169 }
170 }
171
172 fn interpret_group<W: io::Write>(
173 &mut self,
174 w: &mut W,
175 d: Delimiter,
176 sub: &Tree,
177 style: CompleteStyle,
178 ) -> Result {
179 if sub.0.len() > 0 {
180 self.with_command(
181 w,
182 WriteCommand::WriteStr(d.left()),
183 |i, w| i.interpret_tree(w, &sub, style),
184 |_, w| write!(w, "{}", d.right()).map_err(|e| e.into()),
185 )
186 } else {
187 Ok(false)
188 }
189 }
190
191 #[inline(always)]
192 fn optional_prefix<W: io::Write, V1: fmt::Display + Empty, V2: fmt::Display>(
193 &mut self,
194 w: &mut W,
195 sub: &Tree,
196 val: V1,
197 prefix: V2,
198 ctx: CompleteStyle,
199 ) -> Result {
200 if val.is_empty() {
201 return Ok(false);
202 }
203
204 self.write_queue(w)?;
205
206 if sub.0.is_empty() {
207 write!(w, "{}{}", prefix, val)?;
208 return Ok(true);
209 }
210
211 if self.interpret_tree(w, sub, ctx)? {
212 write!(w, "{}", val)?;
213 } else {
214 write!(w, "{}{}", prefix, val)?;
215 }
216
217 Ok(true)
218 }
219
220 #[inline(always)]
221 fn interpret_literal<W: io::Write>(&mut self, w: &mut W, sub: &Tree, s: &str) -> Result {
222 if sub.0.is_empty() {
223 write!(w, "{}", s)?;
224 Ok(true)
225 } else {
226 Err(InterpreterErr::UnexpectedArgs {
227 exp: Expression::Named {
228 name: Name::Quote,
229 sub: sub.clone(),
230 },
231 })
232 }
233 }
234
235 #[inline(always)]
236 fn interpret_named<W: io::Write>(
237 &mut self,
238 w: &mut W,
239 name: Name,
240 sub: &Tree,
241 ctx: CompleteStyle,
242 ) -> Result {
243 use ast::Name::*;
244 match name {
245 Branch => self.optional_prefix(w, sub, self.stats.branch.clone(), "", ctx),
246 Remote => self.optional_prefix(w, sub, self.stats.remote.clone(), "", ctx),
247 Ahead => self.optional_prefix(w, sub, self.stats.ahead, "+", ctx),
248 Behind => self.optional_prefix(w, sub, self.stats.behind, "-", ctx),
249 Conflict => self.optional_prefix(w, sub, self.stats.conflicts, "U", ctx),
250 Added => self.optional_prefix(w, sub, self.stats.added_staged, "A", ctx),
251 Untracked => self.optional_prefix(w, sub, self.stats.untracked, "?", ctx),
252 Modified => self.optional_prefix(w, sub, self.stats.modified_staged, "M", ctx),
253 Unstaged => self.optional_prefix(w, sub, self.stats.modified, "M", ctx),
254 Deleted => self.optional_prefix(w, sub, self.stats.deleted, "D", ctx),
255 DeletedStaged => self.optional_prefix(w, sub, self.stats.deleted_staged, "D", ctx),
256 Renamed => self.optional_prefix(w, sub, self.stats.renamed, "R", ctx),
257 Stashed => self.optional_prefix(w, sub, self.stats.stashes, "H", ctx),
258 Quote => self.interpret_literal(w, sub, "'"),
259 }
260 }
261
262 fn interpret_format<W: io::Write>(
263 &mut self,
264 w: &mut W,
265 style: CompleteStyle,
266 sub: &Tree,
267 mut context: CompleteStyle,
268 ) -> Result {
269 let prev = context;
270 context += style;
271
272 self.with_command(
273 w,
274 WriteCommand::WriteContext(context),
275 |i, w| i.interpret_tree(w, sub, context),
276 |i, w| {
277 prev.write_difference(w, &context, i.bash_prompt)
278 .map_err(|e| e.into())
279 },
280 )
281 }
282}
283
284trait Empty {
288 fn is_empty(&self) -> bool;
289}
290
291impl Empty for u16 {
292 fn is_empty(&self) -> bool {
293 *self == 0
294 }
295}
296
297impl Empty for str {
298 fn is_empty(&self) -> bool {
299 self.is_empty()
300 }
301}
302
303impl Empty for String {
304 fn is_empty(&self) -> bool {
305 self.is_empty()
306 }
307}
308
309impl<T> Empty for Vec<T> {
310 fn is_empty(&self) -> bool {
311 self.is_empty()
312 }
313}
314
315#[cfg(test)]
316mod test {
317
318 use super::*;
319 use crate::git::Stats;
320 use ast;
321 use ast::{Delimiter, Expression, Name, Tree};
322 use proptest::arbitrary::any;
323 use proptest::collection::vec;
324 use proptest::strategy::Strategy;
325
326 proptest! {
327 #[test]
328 fn empty_stats_empty_result(
329 name in ast::arb_name()
330 .prop_filter("Quote is never empty".to_owned(),
331 |n| *n != Name::Quote)
332 ) {
333
334 let stats: Stats = Default::default();
335
336 let mut interpreter = Interpreter::new(stats, false, false);
337
338 let exp = Expression::Named { name, sub: Tree::new() };
339
340 let mut output = Vec::new();
341 match interpreter.evaluate(&Tree(vec![exp.clone()]), &mut output) {
342 Ok(()) => {
343 println!("interpreted {} as {} ({:?})", exp, String::from_utf8_lossy(&output), output);
344 assert!(output.is_empty())
345 },
346 Err(e) => {
347 println!("{:?}", e);
348 panic!("Error in proptest")
349 }
350 }
351 }
352
353 #[test]
354 fn empty_group_empty_result(
355 name in ast::arb_name()
356 .prop_filter("Quote is never empty".to_owned(),
357 |n| *n != Name::Quote)
358 ) {
359 let stats = Stats::default();
360 let interior = Expression::Named { name, sub: Tree::new(), };
361 let exp = Expression::Group {
362 d: Delimiter::Curly,
363 sub: Tree(vec![interior]),
364 };
365
366 let mut interpreter = Interpreter::new(stats, false, false);
367
368 let mut output = Vec::with_capacity(32);
369 match interpreter.evaluate(&Tree(vec![exp.clone()]), &mut output) {
370 Ok(()) => {
371 println!(
372 "interpreted {} as \"{}\" ({:?})",
373 exp,
374 String::from_utf8(output.clone()).unwrap(),
375 output
376 );
377 prop_assert!(output.is_empty());
378 }
379 Err(e) => {
380 println!("{:?} printing {}", e, String::from_utf8(output).unwrap());
381 prop_assert!(false, "Failed to interpret tree");
382 }
383 }
384 }
385
386 #[test]
387 fn empty_format_empty_result(
388 name in ast::arb_name()
389 .prop_filter("Quote is never empty".to_owned(),
390 |n| *n != Name::Quote),
391 style in vec(ast::arb_style(), 1..10),
392 bash_prompt in any::<bool>()
393 ) {
394 let stats = Stats::default();
395 let interior = Expression::Named { name, sub: Tree::new(), };
396 let exp = Expression::Format {
397 style: style.iter().collect(),
398 sub: Tree(vec![interior]),
399 };
400
401 let mut interpreter = Interpreter::new(stats, true, bash_prompt);
402 let mut output = Vec::with_capacity(32);
403 match interpreter.evaluate(&Tree(vec![exp.clone()]), &mut output) {
404 Ok(()) => {
405 println!(
406 "interpreted {} as {} ({:?})",
407 exp,
408 String::from_utf8(output.clone()).unwrap(),
409 output
410 );
411 prop_assert!(output.is_empty());
412 }
413 Err(e) => {
414 println!("{:?} printing {}", e, String::from_utf8(output.clone()).unwrap());
415 prop_assert!(false, "Failed to interpret tree");
416 }
417 }
418 }
419 }
420}