1use std::collections::HashMap;
2use std::convert::From;
3use std::env;
4use std::usize;
5
6use crate::output;
7
8struct Escape {
9 what: &'static str,
10 with: &'static str,
11}
12
13#[derive(Clone, Debug, PartialEq)]
15pub struct ExecResult {
16 pub(crate) code: i32,
18 pub(crate) stdout: String,
20}
21
22#[derive(Clone, Debug, PartialEq)]
24pub enum VarValue {
25 Undefined,
27 Str(String),
29 Int(i64),
31 List(Vec<String>),
33 Exec(ExecResult),
35}
36
37impl From<String> for VarValue {
38 fn from(s: String) -> Self {
39 VarValue::Str(s)
40 }
41}
42impl From<&str> for VarValue {
43 fn from(s: &str) -> Self {
44 VarValue::Str(s.to_string())
45 }
46}
47impl From<i64> for VarValue {
48 fn from(i: i64) -> Self {
49 VarValue::Int(i)
50 }
51}
52impl From<i32> for VarValue {
53 fn from(i: i32) -> Self {
54 VarValue::Int(i as i64)
55 }
56}
57impl From<u32> for VarValue {
58 fn from(i: u32) -> Self {
59 VarValue::Int(i as i64)
60 }
61}
62impl From<bool> for VarValue {
63 fn from(b: bool) -> Self {
64 if b {
65 VarValue::Int(1)
66 } else {
67 VarValue::Int(0)
68 }
69 }
70}
71
72impl ToString for VarValue {
73 fn to_string(&self) -> String {
74 match self {
75 VarValue::Undefined => String::new(),
76 VarValue::Str(s) => s.clone(),
77 VarValue::Int(i) => format!("{}", i),
78 VarValue::List(v) => {
79 let mut s = String::new();
80 for it in v.iter() {
81 if !s.is_empty() {
82 s += "\n";
83 }
84 s += it;
85 }
86 s
87 }
88 VarValue::Exec(ex) => {
89 if ex.code == 0 {
90 ex.stdout.to_string()
91 } else {
92 String::new()
93 }
94 }
95 }
96 }
97}
98
99impl VarValue {
100 pub(crate) fn to_flat_string(&self) -> String {
112 match self {
113 VarValue::Undefined => String::new(),
114 VarValue::Str(s) => s.clone(),
115 VarValue::Int(i) => format!("{}", i),
116 VarValue::List(v) => {
117 let mut s = String::new();
118 for it in v.iter() {
119 if !s.is_empty() {
120 s += " ";
121 }
122 s += it;
123 }
124 s
125 }
126 VarValue::Exec(ex) => {
127 if ex.code == 0 {
128 let mut s = String::new();
129 for l in ex.stdout.lines() {
130 if !s.is_empty() {
131 s += " ";
132 }
133 s += l.trim_end();
134 }
135 s
136 } else {
137 String::new()
138 }
139 }
140 }
141 }
142
143 pub(crate) fn is_true(&self) -> bool {
151 match self {
152 VarValue::Undefined => false,
153 VarValue::Int(i) => *i != 0,
154 VarValue::Str(s) => !s.is_empty(),
155 VarValue::List(v) => !v.is_empty() && !v[0].is_empty(),
156 VarValue::Exec(er) => er.code == 0,
157 }
158 }
159
160 pub(crate) fn to_int(&self) -> i64 {
166 match self {
167 VarValue::Undefined => 0,
168 VarValue::Int(i) => *i,
169 VarValue::Str(s) => {
170 if s.is_empty() {
171 0
172 } else {
173 s.parse::<i64>().unwrap_or(0)
174 }
175 }
176 VarValue::List(v) => {
177 if v.is_empty() || v[0].is_empty() {
178 0
179 } else {
180 v[0].parse::<i64>().unwrap_or(0)
181 }
182 }
183 VarValue::Exec(ex) => {
184 if ex.stdout.is_empty() {
185 0
186 } else if let Some(s) = ex.stdout.lines().next() {
187 let strim = s.trim();
188 strim.parse::<i64>().unwrap_or(0)
189 } else {
190 0
191 }
192 }
193 }
194 }
195
196 fn cmp_eq(&self, val: &VarValue) -> bool {
198 match self {
199 VarValue::Undefined => match val {
200 VarValue::Undefined => true,
201 _ => false,
202 },
203 VarValue::List(lst1) => match val {
204 VarValue::List(lst2) => {
205 if lst1.len() != lst2.len() {
206 false
207 } else {
208 for (idx, val) in lst1.iter().enumerate() {
209 if val != &lst2[idx] {
210 return false;
211 }
212 }
213 true
214 }
215 }
216 VarValue::Exec(ex_val) => ex_val.code == 0 && ex_val.stdout.trim() == self.to_string(),
217 VarValue::Str(s) => &self.to_flat_string() == s,
218 VarValue::Int(i) => {
219 if lst1.len() != 1 {
220 false
221 } else {
222 lst1[0] == format!("{}", *i)
223 }
224 }
225 _ => false,
226 },
227 VarValue::Exec(ex) => match val {
228 VarValue::Exec(ex_val) => ex.code == ex_val.code,
229 VarValue::Str(s) => &ex.stdout == s,
230 VarValue::Int(i) => i64::from(ex.code) == *i,
231 VarValue::List(_) => ex.code == 0 && ex.stdout == val.to_string(),
232 _ => false,
233 },
234 VarValue::Str(s) => match val {
235 VarValue::Exec(ex_val) => s == &ex_val.stdout,
236 VarValue::Str(s_val) => s == s_val,
237 VarValue::Int(i) => s == &format!("{}", *i),
238 VarValue::List(_) => s == &val.to_flat_string(),
239 _ => false,
240 },
241 VarValue::Int(i) => match val {
242 VarValue::Exec(ex_val) => *i == i64::from(ex_val.code),
243 VarValue::Str(s_val) => &format!("{}", *i) == s_val,
244 VarValue::Int(i_val) => *i == *i_val,
245 VarValue::List(lst) => {
246 if lst.len() != 1 {
247 false
248 } else {
249 lst[0] == format!("{}", *i)
250 }
251 }
252 _ => false,
253 },
254 }
255 }
256 fn cmp_greater(&self, val: &VarValue) -> bool {
261 match self {
262 VarValue::Undefined => false,
263 VarValue::Exec(ex) => match val {
264 VarValue::Exec(ex_val) => {
265 if ex.code == 0 && ex_val.code != 0 {
266 true
267 } else if ex.code != 0 && ex_val.code == 0 {
268 false
269 } else {
270 ex.code > ex_val.code
271 }
272 }
273 VarValue::Str(s) => ex.stdout > *s,
274 VarValue::Int(i) => i64::from(ex.code) > *i,
275 VarValue::List(_) => ex.code == 0 && ex.stdout > val.to_string(),
276 _ => true,
277 },
278 VarValue::Str(s) => match val {
279 VarValue::Exec(ex_val) => *s > ex_val.stdout,
280 VarValue::Str(s_val) => s > s_val,
281 VarValue::Int(i) => *s > format!("{}", *i),
282 VarValue::List(_) => *s > val.to_flat_string(),
283 _ => true,
284 },
285 VarValue::Int(i) => match val {
286 VarValue::Exec(ex_val) => *i > i64::from(ex_val.code),
287 VarValue::Str(s_val) => format!("{}", *i) > *s_val,
288 VarValue::Int(i_val) => *i > *i_val,
289 VarValue::List(lst) => {
290 if lst.is_empty() {
291 true
292 } else {
293 let vv = lst[0].parse::<i64>().unwrap_or(0i64);
294 *i > vv
295 }
296 }
297 _ => true,
298 },
299 VarValue::List(lst) => match val {
300 VarValue::Exec(ex) => ex.code != 0 || self.to_string() > ex.stdout,
301 VarValue::Str(s) => self.to_flat_string() > *s,
302 VarValue::Int(i) => {
303 if lst.is_empty() {
304 false
305 } else {
306 let vv = lst[0].parse::<i64>().unwrap_or(0i64);
307 vv > *i
308 }
309 }
310 VarValue::List(lst2) => {
311 if lst.len() > lst2.len() {
312 true
313 } else {
314 for (idx, v) in lst.iter().enumerate() {
315 if *v <= lst2[idx] {
316 return false;
317 }
318 }
319 true
320 }
321 }
322 _ => true,
323 },
324 }
325 }
326 fn cmp_less(&self, val: &VarValue) -> bool {
331 match self {
332 VarValue::Undefined => match val {
333 VarValue::Undefined => false,
334 _ => true,
335 },
336 VarValue::Exec(ex) => match val {
337 VarValue::Exec(ex_val) => {
338 if ex.code == 0 && ex_val.code != 0 {
339 true
340 } else if ex.code != 0 && ex_val.code == 0 {
341 false
342 } else {
343 ex.code > ex_val.code
344 }
345 }
346 VarValue::Str(s) => ex.stdout < *s,
347 VarValue::Int(i) => i64::from(ex.code) < *i,
348 VarValue::List(_) => ex.code != 0 || ex.stdout < val.to_string(),
349 _ => false,
350 },
351 VarValue::Str(s) => match val {
352 VarValue::Exec(ex_val) => *s < ex_val.stdout,
353 VarValue::Str(s_val) => s < s_val,
354 VarValue::Int(i) => *s < format!("{}", i),
355 VarValue::List(_) => *s < val.to_flat_string(),
356 _ => false,
357 },
358 VarValue::Int(i) => match val {
359 VarValue::Exec(ex_val) => *i < i64::from(ex_val.code),
360 VarValue::Str(s_val) => format!("{}", i) < *s_val,
361 VarValue::Int(i_val) => i < i_val,
362 VarValue::List(lst) => {
363 if lst.is_empty() {
364 false
365 } else {
366 let vv = lst[0].parse::<i64>().unwrap_or(0i64);
367 *i < vv
368 }
369 }
370 _ => false,
371 },
372 VarValue::List(lst) => match val {
373 VarValue::Exec(ex) => ex.code == 0 && self.to_string() < ex.stdout,
374 VarValue::Str(s) => self.to_flat_string() < *s,
375 VarValue::Int(i) => {
376 if lst.is_empty() {
377 false
378 } else {
379 let vv = lst[0].parse::<i64>().unwrap_or(0i64);
380 vv < *i
381 }
382 }
383 VarValue::List(lst2) => {
384 if lst.len() > lst2.len() {
385 false
386 } else {
387 for (idx, v) in lst.iter().enumerate() {
388 if *v >= lst2[idx] {
389 return false;
390 }
391 }
392 true
393 }
394 }
395 _ => false,
396 },
397 }
398 }
399
400 fn cmp_neq(&self, val: &VarValue) -> bool {
402 !self.cmp_eq(val)
403 }
404 fn cmp_eq_or_greater(&self, val: &VarValue) -> bool {
409 !self.cmp_less(val)
410 }
411 fn cmp_eq_or_less(&self, val: &VarValue) -> bool {
416 !self.cmp_greater(val)
417 }
418 pub(crate) fn cmp(&self, val: &VarValue, cmp_op: &str) -> bool {
420 match cmp_op {
421 "==" => self.cmp_eq(val),
422 "!=" => self.cmp_neq(val),
423 ">" => self.cmp_greater(val),
424 "<" => self.cmp_less(val),
425 ">=" => self.cmp_eq_or_greater(val),
426 "<=" => self.cmp_eq_or_less(val),
427 _ => unreachable!(),
428 }
429 }
430}
431
432pub struct Var {
434 name: String,
436 value: VarValue,
438}
439impl Default for Var {
440 fn default() -> Self {
441 Var { name: String::from(""), value: VarValue::Undefined }
442 }
443}
444
445pub(crate) struct VarMgr {
447 pub(crate) free: Vec<String>,
449 pub(crate) recipe_vars: Vec<Var>,
451 vars: Vec<Var>,
453 verbosity: usize,
455 pub(crate) env: HashMap<String, String>,
457}
458
459impl VarMgr {
460 pub(crate) fn new(verbosity: usize) -> Self {
461 VarMgr { recipe_vars: Vec::new(), vars: Vec::new(), free: Vec::new(), verbosity, env: HashMap::new() }
462 }
463
464 pub(crate) fn set_recipe_var(&mut self, name: &str, val: VarValue) {
466 output!(self.verbosity, 2, "Setting recipe var {}", name);
467 for v in self.recipe_vars.iter_mut() {
468 if v.name == name {
469 output!(self.verbosity, 2, "Changing recipe {} to {:?}", name, val);
470 v.value = val;
471 return;
472 }
473 }
474 output!(self.verbosity, 2, "New recipe var {}: {:?}", name, val);
475 self.recipe_vars.push(Var { name: name.to_string(), value: val });
476 }
477
478 pub(crate) fn set_var(&mut self, name: &str, val: VarValue) {
481 output!(self.verbosity, 2, "Setting a var {}", name);
482 for v in self.recipe_vars.iter_mut() {
483 if v.name == name {
484 output!(self.verbosity, 2, "Changing recipe {} to {:?}", name, val);
485 v.value = val;
486 return;
487 }
488 }
489 for v in self.vars.iter_mut() {
490 if v.name == name {
491 output!(self.verbosity, 2, "Changing var {} to {:?}", name, val);
492 v.value = val;
493 return;
494 }
495 }
496 output!(self.verbosity, 2, "New var {}: {:?}", name, val);
497 self.vars.push(Var { name: name.to_string(), value: val });
498 }
499
500 pub(crate) fn var(&self, name: &str) -> VarValue {
504 for v in self.recipe_vars.iter() {
505 if v.name == name {
506 output!(self.verbosity, 2, "Local recipe var {} found", name);
507 return v.value.clone();
508 }
509 }
510 for v in self.vars.iter() {
511 if v.name == name {
512 output!(self.verbosity, 2, "Global var {} found", name);
513 return v.value.clone();
514 }
515 }
516
517 if let Some(s) = self.env.get(name) {
518 output!(self.verbosity, 2, "Use environment variable from script {}", name);
519 return VarValue::Str(s.to_string());
520 }
521
522 if let Ok(s) = env::var(name) {
523 output!(self.verbosity, 2, "Use environment variable {}", name);
524 return VarValue::Str(s);
525 }
526
527 output!(self.verbosity, 2, "Variable {} not found", name);
528 VarValue::Undefined
529 }
530
531 pub(crate) fn interpolate(&self, in_str: &str, flat: bool) -> String {
556 let mut start_s: usize;
557 let mut start_d: usize;
558 let mut res = String::new();
559 let mut s_ptr = in_str;
560 let escapes: Vec<Escape> = vec![
561 Escape { what: "\\\\", with: "\\" },
562 Escape { what: "\\n", with: "\n" },
563 Escape { what: "\\t", with: "\t" },
564 Escape { what: "\\$", with: "$" },
565 Escape { what: "\\'", with: "'" },
566 Escape { what: "\\\"", with: "\"" },
567 ];
568
569 while !s_ptr.is_empty() {
570 start_d = s_ptr.find('$').unwrap_or(usize::MAX);
571 start_s = s_ptr.find('\\').unwrap_or(usize::MAX);
572
573 if start_s == usize::MAX && start_d == usize::MAX {
574 return res + s_ptr;
575 }
576
577 if start_s == usize::MAX || start_d < start_s {
578 res += &s_ptr[..start_d];
579 s_ptr = &s_ptr[start_d..];
580 if s_ptr.starts_with("$$") {
582 res += "$";
583 s_ptr = &s_ptr["$$".len()..];
584 continue;
585 }
586
587 if !s_ptr.starts_with("${") {
589 res += "$";
590 s_ptr = &s_ptr["$".len()..];
591 continue;
592 }
593
594 s_ptr = &s_ptr["${".len()..];
596 match s_ptr.find('}') {
597 None => return res + "${" + s_ptr,
598 Some(bp) => {
599 let var_name = &s_ptr[..bp];
600 if flat {
601 res += self.var(var_name).to_flat_string().as_str();
602 } else {
603 res += self.var(var_name).to_string().as_str();
604 }
605 s_ptr = &s_ptr[(bp + "}".len())..];
606 }
607 }
608 continue;
609 }
610
611 res += &s_ptr[..start_s];
612 s_ptr = &s_ptr[start_s..];
613 let mut escaped = false;
614 for esc in escapes.iter() {
615 if s_ptr.starts_with(esc.what) {
616 res += esc.with;
617 s_ptr = &s_ptr[esc.what.len()..];
618 escaped = true;
619 break;
620 }
621 }
622 if !escaped {
623 res += "\\";
624 s_ptr = &s_ptr["\\".len()..];
625 }
626 }
627 res
628 }
629}
630
631#[cfg(test)]
632mod var_test {
633 use super::*;
634
635 #[test]
636 fn var_mgr() {
637 let mut v = VarMgr::new(0);
638 v.set_var("abc", VarValue::Int(123));
639 v.recipe_vars.push(Var { name: "def".to_string(), value: VarValue::Int(10) });
640 let v1 = v.var("def");
641 assert_eq!(v1, VarValue::Int(10));
642 let v1 = v.var("abc");
643 assert_eq!(v1, VarValue::Int(123));
644 let v1 = v.var("abc2");
645 assert_eq!(v1, VarValue::Undefined);
646 v.recipe_vars.push(Var { name: "abc".to_string(), value: VarValue::Int(50) });
647 let v1 = v.var("abc");
648 assert_eq!(v1, VarValue::Int(50));
649 v.recipe_vars.clear();
650 let v1 = v.var("abc");
651 assert_eq!(v1, VarValue::Int(123));
652 }
653
654 #[test]
655 fn interpolate_no_matches() {
656 let mut v = VarMgr::new(0);
657 v.set_var("abc", VarValue::Str("123".to_string()));
658 let instr = "text $abc end";
660 let outstr = v.interpolate(instr, false);
661 assert_eq!(instr, &outstr);
662 let instr = "text $${abc} end";
664 let outstr = v.interpolate(instr, false);
665 assert_eq!("text ${abc} end", &outstr);
666 let instr = "text ${abc end";
668 let outstr = v.interpolate(instr, false);
669 assert_eq!(instr, &outstr);
670 let instr = "";
672 let outstr = v.interpolate(instr, false);
673 assert_eq!(instr, &outstr);
674 }
675
676 #[test]
677 fn interpolate_one_match() {
678 let mut v = VarMgr::new(0);
679 v.set_var("abc", VarValue::from("123"));
680 let instr = "text $$${abc} end";
682 let outstr = v.interpolate(instr, false);
683 assert_eq!("text $123 end", &outstr);
684 let instr = "text ${abc} end";
686 let outstr = v.interpolate(instr, false);
687 assert_eq!("text 123 end", &outstr);
688 let instr = "text ${abc2} end";
690 let outstr = v.interpolate(instr, false);
691 assert_eq!("text end", &outstr);
692 let instr = "${abc}";
694 let outstr = v.interpolate(instr, false);
695 assert_eq!("123", &outstr);
696 let instr = "${abcd}";
698 let outstr = v.interpolate(instr, false);
699 assert_eq!("", &outstr);
700 }
701
702 #[test]
703 fn interpolate_few_matches() {
704 let mut v = VarMgr::new(0);
705 v.set_var("abc", VarValue::from("123"));
706 v.set_var("def", VarValue::from("test"));
707 let instr = "text ${def}$$${abc} end";
709 let outstr = v.interpolate(instr, false);
710 assert_eq!("text test$123 end", &outstr);
711 let instr = "text ${abc} end ${def}";
713 let outstr = v.interpolate(instr, false);
714 assert_eq!("text 123 end test", &outstr);
715 let instr = "${def} text ${abc2} end";
717 let outstr = v.interpolate(instr, false);
718 assert_eq!("test text end", &outstr);
719 let instr = "${def}${abc}";
721 let outstr = v.interpolate(instr, false);
722 assert_eq!("test123", &outstr);
723 }
724
725 #[test]
726 fn interpolate_mixed_matches() {
727 let mut v = VarMgr::new(0);
728 v.set_var("abc", VarValue::from("123"));
729 v.set_var("def", VarValue::from("test"));
730 let instr = "text ${def}$${abc} end";
732 let outstr = v.interpolate(instr, false);
733 assert_eq!("text test${abc} end", &outstr);
734 let instr = "text $abc end ${def}";
736 let outstr = v.interpolate(instr, false);
737 assert_eq!("text $abc end test", &outstr);
738 }
739
740 #[test]
741 fn unescaped() {
742 let v = VarMgr::new(0);
743 let ostr = v.interpolate("abcde 12345", false);
744 assert_eq!(&ostr, "abcde 12345");
745 let ostr = v.interpolate("", false);
746 assert_eq!(&ostr, "");
747 let ostr = v.interpolate("1234\\5678\\90", false);
748 assert_eq!(&ostr, "1234\\5678\\90");
749 let ostr = v.interpolate("\\t1234\\\\5678\\n90\\t", false);
750 assert_eq!(&ostr, "\t1234\\5678\n90\t");
751 let instr = "text \\\" test \\'";
753 let outstr = v.interpolate(instr, false);
754 assert_eq!("text \" test '", &outstr);
755 }
756
757 #[test]
758 fn mixed_interpolation() {
759 let mut v = VarMgr::new(0);
760 v.set_var("abc", VarValue::from("123"));
761 v.set_var("def", VarValue::from("test"));
762 let instr = "\\t${def} text ${ab\\nc} end";
764 let outstr = v.interpolate(instr, false);
765 assert_eq!("\ttest text end", &outstr);
766 let instr = "${def}\\ttext ${abc} end";
768 let outstr = v.interpolate(instr, false);
769 assert_eq!("test\ttext 123 end", &outstr);
770 let instr = "\\${def} ${abc} end";
772 let outstr = v.interpolate(instr, false);
773 assert_eq!("${def} 123 end", &outstr);
774 let instr = "$$\\$$${def} $$${abc}$$ end\\$";
775 let outstr = v.interpolate(instr, false);
776 assert_eq!("$$${def} $123$ end$", &outstr);
777 }
778}