1#[cfg(doc)]
98use crate::Env;
99use crate::semantics::Field;
100use crate::source::Location;
101use itertools::Itertools;
102use std::borrow::Borrow;
103use std::collections::HashMap;
104use std::collections::hash_map::Entry::{Occupied, Vacant};
105use std::ffi::CString;
106use std::fmt::Write;
107use std::hash::Hash;
108use std::iter::FusedIterator;
109use thiserror::Error;
110
111mod value;
112
113pub use self::value::QuotedValue;
114pub use self::value::Value::{self, Array, Scalar};
115
116mod quirk;
117
118pub use self::quirk::Expansion;
119pub use self::quirk::Quirk;
120
121mod main;
122
123pub use self::main::AssignError;
124pub use self::main::Variable;
125pub use self::main::VariableRefMut;
126
127mod constants;
128
129pub use self::constants::*;
131
132#[derive(Clone, Debug, Eq, PartialEq)]
133struct VariableInContext {
134 variable: Variable,
135 context_index: usize,
136}
137
138#[derive(Clone, Debug, Default, Eq, PartialEq)]
140pub struct PositionalParams {
141 pub values: Vec<String>,
143 pub last_modified_location: Option<Location>,
145}
146
147impl PositionalParams {
148 pub fn from_fields<I>(fields: I) -> Self
155 where
156 I: IntoIterator<Item = Field>,
157 {
158 let mut fields = fields.into_iter();
159 let last_modified_location = fields.next().map(|field| field.origin);
160 let values = fields.map(|field| field.value).collect();
161 Self {
162 values,
163 last_modified_location,
164 }
165 }
166}
167
168#[derive(Clone, Debug, Eq, PartialEq)]
176pub enum Context {
177 Regular { positional_params: PositionalParams },
183
184 Volatile,
189}
190
191impl Default for Context {
192 fn default() -> Self {
193 Context::Regular {
194 positional_params: Default::default(),
195 }
196 }
197}
198
199#[derive(Clone, Debug, Eq, PartialEq)]
203pub struct VariableSet {
204 all_variables: HashMap<String, Vec<VariableInContext>>,
213
214 contexts: Vec<Context>,
219}
220
221impl Default for VariableSet {
222 fn default() -> Self {
223 VariableSet {
224 all_variables: Default::default(),
225 contexts: vec![Context::default()],
226 }
227 }
228}
229
230#[derive(Clone, Copy, Debug, Eq, PartialEq)]
235pub enum Scope {
236 Global,
237 Local,
238 Volatile,
239}
240
241#[derive(Clone, Debug, Eq, Error, PartialEq)]
243#[error("cannot unset read-only variable `{name}`")]
244pub struct UnsetError<'a> {
245 pub name: &'a str,
247 pub read_only_location: &'a Location,
249}
250
251#[derive(Clone, Debug)]
255pub struct Iter<'a> {
256 inner: std::collections::hash_map::Iter<'a, String, Vec<VariableInContext>>,
257 min_context_index: usize,
258}
259
260impl VariableSet {
261 #[must_use]
263 pub fn new() -> VariableSet {
264 Default::default()
265 }
266
267 #[must_use]
278 pub fn get<N>(&self, name: &N) -> Option<&Variable>
279 where
280 String: Borrow<N>,
281 N: Hash + Eq + ?Sized,
282 {
283 Some(&self.all_variables.get(name)?.last()?.variable)
284 }
285
286 fn index_of_topmost_regular_context(contexts: &[Context]) -> usize {
288 contexts
289 .iter()
290 .rposition(|context| matches!(context, Context::Regular { .. }))
291 .expect("base context has gone")
292 }
293
294 fn index_of_context(scope: Scope, contexts: &[Context]) -> usize {
296 match scope {
297 Scope::Global => 0,
298 Scope::Local => Self::index_of_topmost_regular_context(contexts),
299 Scope::Volatile => Self::index_of_topmost_regular_context(contexts) + 1,
300 }
301 }
302
303 #[must_use]
321 pub fn get_scoped<N>(&self, name: &N, scope: Scope) -> Option<&Variable>
322 where
323 String: Borrow<N>,
324 N: Hash + Eq + ?Sized,
325 {
326 let index = Self::index_of_context(scope, &self.contexts);
327 self.all_variables
328 .get(name)?
329 .last()
330 .filter(|vic| vic.context_index >= index)
331 .map(|vic| &vic.variable)
332 }
333
334 #[inline]
381 pub fn get_or_new<S: Into<String>>(&mut self, name: S, scope: Scope) -> VariableRefMut<'_> {
382 self.get_or_new_impl(name.into(), scope)
383 }
384
385 fn get_or_new_impl(&mut self, name: String, scope: Scope) -> VariableRefMut<'_> {
386 let stack = match self.all_variables.entry(name) {
387 Vacant(vacant) => vacant.insert(Vec::new()),
388 Occupied(occupied) => occupied.into_mut(),
389 };
390 let context_index = match scope {
391 Scope::Global => 0,
392 Scope::Local => Self::index_of_topmost_regular_context(&self.contexts),
393 Scope::Volatile => self.contexts.len() - 1,
394 };
395
396 match scope {
397 Scope::Global | Scope::Local => 'branch: {
398 let mut removed_volatile_variable = None;
399
400 while let Some(var) = stack.last_mut() {
404 if var.context_index < context_index {
405 break;
406 }
407 match self.contexts[var.context_index] {
408 Context::Regular { .. } => {
409 if let Some(removed_volatile_variable) = removed_volatile_variable {
410 var.variable = removed_volatile_variable;
411 }
412 break 'branch;
413 }
414 Context::Volatile => {
415 removed_volatile_variable.get_or_insert(stack.pop().unwrap().variable);
416 }
417 }
418 }
419
420 stack.push(VariableInContext {
421 variable: removed_volatile_variable.unwrap_or_default(),
422 context_index,
423 });
424 }
425
426 Scope::Volatile => {
427 assert_eq!(
428 self.contexts[context_index],
429 Context::Volatile,
430 "no volatile context to store the variable",
431 );
432 if let Some(var) = stack.last() {
433 if var.context_index != context_index {
434 stack.push(VariableInContext {
435 variable: var.variable.clone(),
436 context_index,
437 });
438 }
439 } else {
440 stack.push(VariableInContext {
441 variable: Variable::default(),
442 context_index,
443 });
444 }
445 }
446 }
447
448 VariableRefMut::from(&mut stack.last_mut().unwrap().variable)
449 }
450
451 #[cfg(test)]
453 fn assert_normalized(&self) {
454 for context in self.all_variables.values() {
455 for vars in context.windows(2) {
456 assert!(
457 vars[0].context_index < vars[1].context_index,
458 "invalid context index: {vars:?}",
459 );
460 }
461 if let Some(last) = context.last() {
462 assert!(
463 last.context_index < self.contexts.len(),
464 "invalid context index: {last:?}",
465 );
466 }
467 }
468 }
469
470 #[must_use]
479 pub fn get_scalar<N>(&self, name: &N) -> Option<&str>
480 where
481 String: Borrow<N>,
482 N: Hash + Eq + ?Sized,
483 {
484 fn inner(var: &Variable) -> Option<&str> {
485 match var.value.as_ref()? {
486 Scalar(value) => Some(value),
487 Array(_) => None,
488 }
489 }
490 inner(self.get(name)?)
491 }
492
493 pub fn unset<'a>(
518 &'a mut self,
519 name: &'a str,
520 scope: Scope,
521 ) -> Result<Option<Variable>, UnsetError<'a>> {
522 let Some(stack) = self.all_variables.get_mut(name) else {
523 return Ok(None);
524 };
525
526 let index = Self::index_of_context(scope, &self.contexts);
528
529 if let Some(read_only_position) = stack[index..]
544 .iter()
545 .rposition(|vic| vic.variable.is_read_only())
546 {
547 let read_only_index = index + read_only_position;
548 let read_only_location = &stack[read_only_index].variable.read_only_location;
549 return Err(UnsetError {
550 name,
551 read_only_location: read_only_location.as_ref().unwrap(),
552 });
553 }
554
555 Ok(stack.drain(index..).next_back().map(|vic| vic.variable))
556 }
557
558 pub fn iter(&self, scope: Scope) -> Iter<'_> {
572 Iter {
573 inner: self.all_variables.iter(),
574 min_context_index: Self::index_of_context(scope, &self.contexts),
575 }
576 }
577
578 #[must_use]
592 pub fn env_c_strings(&self) -> Vec<CString> {
593 self.all_variables
594 .iter()
595 .filter_map(|(name, vars)| {
596 let var = &vars.last()?.variable;
597 if !var.is_exported || name.contains('=') {
598 return None;
599 }
600 let value = var.value.as_ref()?;
601 let mut result = name.clone();
602 result.push('=');
603 match value {
604 Scalar(value) => result.push_str(value),
605 Array(values) => write!(result, "{}", values.iter().format(":")).ok()?,
606 }
607 CString::new(result).ok()
609 })
610 .collect()
611 }
612
613 pub fn extend_env<I, K, V>(&mut self, vars: I)
622 where
623 I: IntoIterator<Item = (K, V)>,
624 K: Into<String>,
625 V: Into<String>,
626 {
627 for (name, value) in vars {
628 let mut var = self.get_or_new(name, Scope::Global);
629 if var.assign(value.into(), None).is_ok() {
630 var.export(true)
631 }
632 }
633 }
634
635 pub fn init(&mut self) {
654 const VARIABLES: &[(&str, &str)] = &[
655 (IFS, IFS_INITIAL_VALUE),
656 (OPTIND, OPTIND_INITIAL_VALUE),
657 (PS1, PS1_INITIAL_VALUE_NON_ROOT),
658 (PS2, PS2_INITIAL_VALUE),
659 (PS4, PS4_INITIAL_VALUE),
660 ];
661 for &(name, value) in VARIABLES {
662 self.get_or_new(name, Scope::Global)
663 .assign(value, None)
664 .ok();
665 }
666
667 self.get_or_new(LINENO, Scope::Global)
668 .set_quirk(Some(Quirk::LineNumber))
669 }
670
671 #[must_use]
680 pub fn positional_params(&self) -> &PositionalParams {
681 self.contexts
682 .iter()
683 .rev()
684 .find_map(|context| match context {
685 Context::Regular { positional_params } => Some(positional_params),
686 Context::Volatile => None,
687 })
688 .expect("base context has gone")
689 }
690
691 #[must_use]
698 pub fn positional_params_mut(&mut self) -> &mut PositionalParams {
699 self.contexts
700 .iter_mut()
701 .rev()
702 .find_map(|context| match context {
703 Context::Regular { positional_params } => Some(positional_params),
704 Context::Volatile => None,
705 })
706 .expect("base context has gone")
707 }
708
709 fn push_context_impl(&mut self, context: Context) {
710 self.contexts.push(context);
711 }
712
713 fn pop_context_impl(&mut self) {
714 debug_assert!(!self.contexts.is_empty());
715 assert_ne!(self.contexts.len(), 1, "cannot pop the base context");
716 self.contexts.pop();
717 self.all_variables.retain(|_, stack| {
718 stack.pop_if(|vic| {
719 vic.context_index >= self.contexts.len()
722 });
723 !stack.is_empty()
724 })
725 }
726}
727
728impl<'a> Iterator for Iter<'a> {
729 type Item = (&'a str, &'a Variable);
730
731 fn next(&mut self) -> Option<(&'a str, &'a Variable)> {
732 loop {
733 let next = self.inner.next()?;
734 if let Some(variable) = next.1.last() {
735 if variable.context_index >= self.min_context_index {
736 return Some((next.0, &variable.variable));
737 }
738 }
739 }
740 }
741
742 fn size_hint(&self) -> (usize, Option<usize>) {
743 let (_min, max) = self.inner.size_hint();
744 (0, max)
745 }
746}
747
748impl FusedIterator for Iter<'_> {}
749
750mod guard;
751
752pub use self::guard::{ContextGuard, EnvContextGuard};
753
754#[cfg(test)]
755mod tests {
756 use super::*;
757
758 #[test]
759 fn new_variable_in_global_scope() {
760 let mut set = VariableSet::new();
761 set.push_context_impl(Context::default());
762 set.push_context_impl(Context::Volatile);
763
764 let mut var = set.get_or_new("foo", Scope::Global);
765
766 assert_eq!(*var, Variable::default());
767 var.assign("VALUE", None).unwrap();
768 set.assert_normalized();
769 set.pop_context_impl();
770 set.pop_context_impl();
771 assert_eq!(set.get("foo").unwrap().value, Some("VALUE".into()));
773 }
774
775 #[test]
776 fn existing_variable_in_global_scope() {
777 let mut set = VariableSet::new();
778 let mut var = set.get_or_new("foo", Scope::Global);
779 var.assign("ONE", None).unwrap();
780 set.push_context_impl(Context::default());
781 set.push_context_impl(Context::Volatile);
782
783 let mut var = set.get_or_new("foo", Scope::Global);
784
785 assert_eq!(var.value, Some("ONE".into()));
786 var.assign("TWO", Location::dummy("somewhere")).unwrap();
787 set.assert_normalized();
788 set.pop_context_impl();
789 set.pop_context_impl();
790 let var = set.get("foo").unwrap();
792 assert_eq!(var.value, Some("TWO".into()));
793 assert_eq!(
794 var.last_assigned_location,
795 Some(Location::dummy("somewhere")),
796 );
797 }
798
799 #[test]
800 fn new_variable_in_local_scope() {
801 let mut set = VariableSet::new();
803 set.push_context_impl(Context::default());
804
805 let mut var = set.get_or_new("foo", Scope::Local);
806
807 assert_eq!(*var, Variable::default());
808
809 var.assign("OUTER", None).unwrap();
810 set.push_context_impl(Context::default());
811 set.push_context_impl(Context::Volatile);
812
813 let mut var = set.get_or_new("foo", Scope::Local);
814
815 assert_eq!(*var, Variable::default());
816 var.assign("INNER", Location::dummy("location")).unwrap();
817 set.assert_normalized();
818 set.pop_context_impl(); assert_eq!(set.get("foo").unwrap().value, Some("INNER".into()));
820 set.pop_context_impl(); assert_eq!(set.get("foo").unwrap().value, Some("OUTER".into()));
822 set.pop_context_impl(); assert_eq!(set.get("foo"), None);
824 }
825
826 #[test]
827 fn existing_variable_in_local_scope() {
828 let mut set = VariableSet::new();
829 set.push_context_impl(Context::default());
830 let mut var = set.get_or_new("foo", Scope::Local);
831 var.assign("OLD", None).unwrap();
832
833 let mut var = set.get_or_new("foo", Scope::Local);
834
835 assert_eq!(var.value, Some("OLD".into()));
836 var.assign("NEW", None).unwrap();
837 assert_eq!(set.get("foo").unwrap().value, Some("NEW".into()));
838 set.assert_normalized();
839 set.pop_context_impl();
840 assert_eq!(set.get("foo"), None);
841 }
842
843 #[test]
844 fn new_variable_in_volatile_scope() {
845 let mut set = VariableSet::new();
846 set.push_context_impl(Context::Volatile);
847 set.push_context_impl(Context::Volatile);
848
849 let mut var = set.get_or_new("foo", Scope::Volatile);
850
851 assert_eq!(*var, Variable::default());
852 var.assign("VOLATILE", None).unwrap();
853 assert_eq!(set.get("foo").unwrap().value, Some("VOLATILE".into()));
854 set.assert_normalized();
855 set.pop_context_impl();
856 assert_eq!(set.get("foo"), None);
857 }
858
859 #[test]
860 fn cloning_existing_regular_variable_to_volatile_context() {
861 let mut set = VariableSet::new();
862 let mut var = set.get_or_new("foo", Scope::Global);
863 var.assign("VALUE", None).unwrap();
864 var.make_read_only(Location::dummy("read-only location"));
865 let save_var = var.clone();
866 set.push_context_impl(Context::Volatile);
867 set.push_context_impl(Context::Volatile);
868
869 let mut var = set.get_or_new("foo", Scope::Volatile);
870
871 assert_eq!(*var, save_var);
872 var.export(true);
873 assert!(set.get("foo").unwrap().is_exported);
874 set.assert_normalized();
875 set.pop_context_impl();
876 assert_eq!(set.get("foo"), Some(&save_var));
879 }
880
881 #[test]
882 fn existing_variable_in_volatile_scope() {
883 let mut set = VariableSet::new();
884 set.push_context_impl(Context::Volatile);
885 let mut var = set.get_or_new("foo", Scope::Volatile);
886 var.assign("INITIAL", None).unwrap();
887
888 let mut var = set.get_or_new("foo", Scope::Volatile);
889
890 assert_eq!(var.value, Some("INITIAL".into()));
891 var.assign(Value::array(["MODIFIED"]), Location::dummy("somewhere"))
892 .unwrap();
893 assert_eq!(
894 set.get("foo").unwrap().value,
895 Some(Value::array(["MODIFIED"])),
896 );
897 set.assert_normalized();
898 set.pop_context_impl();
899 assert_eq!(set.get("foo"), None);
900 }
901
902 #[test]
903 fn lowering_volatile_variable_to_base_context() {
904 let mut set = VariableSet::new();
905 set.push_context_impl(Context::default());
906 set.push_context_impl(Context::Volatile);
907 let mut var = set.get_or_new("foo", Scope::Volatile);
908 var.assign("DUMMY", None).unwrap();
909 set.push_context_impl(Context::Volatile);
910 let mut var = set.get_or_new("foo", Scope::Volatile);
911 var.assign("VOLATILE", Location::dummy("anywhere")).unwrap();
912 var.export(true);
913
914 let mut var = set.get_or_new("foo", Scope::Global);
915
916 assert_eq!(var.value, Some("VOLATILE".into()));
917 assert_eq!(
918 var.last_assigned_location,
919 Some(Location::dummy("anywhere")),
920 );
921 var.assign("NEW", Location::dummy("somewhere")).unwrap();
922 set.assert_normalized();
923 set.pop_context_impl();
924 set.pop_context_impl();
925 set.pop_context_impl();
926 let var = set.get("foo").unwrap();
929 assert_eq!(var.value, Some("NEW".into()));
930 assert_eq!(
931 var.last_assigned_location,
932 Some(Location::dummy("somewhere")),
933 );
934 assert!(var.is_exported);
936 }
937
938 #[test]
939 fn lowering_volatile_variable_to_middle_regular_context() {
940 let mut set = VariableSet::new();
941 let mut var = set.get_or_new("foo", Scope::Local);
942 var.assign("ONE", None).unwrap();
943 set.push_context_impl(Context::default());
944 let mut var = set.get_or_new("foo", Scope::Local);
945 var.assign("TWO", None).unwrap();
946 set.push_context_impl(Context::default());
947 set.push_context_impl(Context::Volatile);
948 let mut var = set.get_or_new("foo", Scope::Volatile);
949 var.assign("VOLATILE", Location::dummy("anywhere")).unwrap();
950 var.export(true);
951
952 let mut var = set.get_or_new("foo", Scope::Global);
953
954 assert_eq!(var.value, Some("VOLATILE".into()));
955 assert_eq!(
956 var.last_assigned_location,
957 Some(Location::dummy("anywhere")),
958 );
959 var.assign("NEW", Location::dummy("somewhere")).unwrap();
960 set.assert_normalized();
961 set.pop_context_impl();
962 set.pop_context_impl();
963 let var = set.get("foo").unwrap();
965 assert_eq!(var.value, Some("NEW".into()));
966 assert_eq!(
967 var.last_assigned_location,
968 Some(Location::dummy("somewhere")),
969 );
970 assert!(var.is_exported);
972 set.pop_context_impl();
973 let var = set.get("foo").unwrap();
975 assert_eq!(var.value, Some("ONE".into()));
976 }
977
978 #[test]
979 fn lowering_volatile_variable_to_topmost_regular_context_without_existing_variable() {
980 let mut set = VariableSet::new();
981 set.push_context_impl(Context::default());
982 set.push_context_impl(Context::default());
983 set.push_context_impl(Context::Volatile);
984 let mut var = set.get_or_new("foo", Scope::Volatile);
985 var.assign("DUMMY", None).unwrap();
986 set.push_context_impl(Context::Volatile);
987 let mut var = set.get_or_new("foo", Scope::Volatile);
988 var.assign("VOLATILE", Location::dummy("anywhere")).unwrap();
989 var.export(true);
990
991 let mut var = set.get_or_new("foo", Scope::Local);
992
993 assert_eq!(var.value, Some("VOLATILE".into()));
994 assert_eq!(
995 var.last_assigned_location,
996 Some(Location::dummy("anywhere")),
997 );
998 var.assign("NEW", Location::dummy("somewhere")).unwrap();
999 set.assert_normalized();
1000 set.pop_context_impl();
1001 set.pop_context_impl();
1002 let var = set.get("foo").unwrap();
1005 assert_eq!(var.value, Some("NEW".into()));
1006 assert_eq!(
1007 var.last_assigned_location,
1008 Some(Location::dummy("somewhere")),
1009 );
1010 assert!(var.is_exported);
1012 }
1013
1014 #[test]
1015 fn lowering_volatile_variable_to_topmost_regular_context_overwriting_existing_variable() {
1016 let mut set = VariableSet::new();
1017 set.push_context_impl(Context::default());
1018 set.push_context_impl(Context::default());
1019 let mut var = set.get_or_new("foo", Scope::Local);
1020 var.assign("OLD", None).unwrap();
1021 set.push_context_impl(Context::Volatile);
1022 let mut var = set.get_or_new("foo", Scope::Volatile);
1023 var.assign("DUMMY", None).unwrap();
1024 set.push_context_impl(Context::Volatile);
1025 let mut var = set.get_or_new("foo", Scope::Volatile);
1026 var.assign("VOLATILE", Location::dummy("first")).unwrap();
1027 var.export(true);
1028 set.push_context_impl(Context::Volatile);
1029
1030 let mut var = set.get_or_new("foo", Scope::Local);
1031
1032 assert_eq!(var.value, Some("VOLATILE".into()));
1033 assert_eq!(var.last_assigned_location, Some(Location::dummy("first")));
1034 var.assign("NEW", Location::dummy("second")).unwrap();
1035 set.assert_normalized();
1036 set.pop_context_impl();
1037 set.pop_context_impl();
1038 set.pop_context_impl();
1039 let var = set.get("foo").unwrap();
1042 assert_eq!(var.value, Some("NEW".into()));
1043 assert_eq!(var.last_assigned_location, Some(Location::dummy("second")));
1044 assert!(var.is_exported);
1046 }
1047
1048 #[test]
1049 #[should_panic(expected = "no volatile context to store the variable")]
1050 fn missing_volatile_context() {
1051 let mut set = VariableSet::new();
1052 set.get_or_new("foo", Scope::Volatile);
1053 }
1054
1055 #[test]
1056 fn getting_variables_with_scopes() {
1057 let mut set = VariableSet::new();
1058 set.get_or_new("global", Scope::Global)
1059 .assign("G", None)
1060 .unwrap();
1061 set.push_context_impl(Context::default());
1062 set.get_or_new("local", Scope::Local)
1063 .assign("L", None)
1064 .unwrap();
1065 set.push_context_impl(Context::Volatile);
1066 set.get_or_new("volatile", Scope::Volatile)
1067 .assign("V", None)
1068 .unwrap();
1069
1070 assert_eq!(
1071 set.get_scoped("global", Scope::Global),
1072 Some(&Variable::new("G")),
1073 );
1074 assert_eq!(set.get_scoped("global", Scope::Local), None);
1075 assert_eq!(set.get_scoped("global", Scope::Volatile), None);
1076
1077 assert_eq!(
1078 set.get_scoped("local", Scope::Global),
1079 Some(&Variable::new("L"))
1080 );
1081 assert_eq!(
1082 set.get_scoped("local", Scope::Local),
1083 Some(&Variable::new("L"))
1084 );
1085 assert_eq!(set.get_scoped("local", Scope::Volatile), None);
1086
1087 assert_eq!(
1088 set.get_scoped("volatile", Scope::Global),
1089 Some(&Variable::new("V"))
1090 );
1091 assert_eq!(
1092 set.get_scoped("volatile", Scope::Local),
1093 Some(&Variable::new("V"))
1094 );
1095 assert_eq!(
1096 set.get_scoped("volatile", Scope::Volatile),
1097 Some(&Variable::new("V"))
1098 );
1099 }
1100
1101 #[test]
1102 fn unsetting_nonexisting_variable() {
1103 let mut variables = VariableSet::new();
1104 let result = variables.unset("", Scope::Global).unwrap();
1105 assert_eq!(result, None);
1106 }
1107
1108 #[test]
1109 fn unsetting_variable_with_one_context() {
1110 let mut variables = VariableSet::new();
1111 variables
1112 .get_or_new("foo", Scope::Global)
1113 .assign("X", None)
1114 .unwrap();
1115
1116 let result = variables.unset("foo", Scope::Global).unwrap();
1117 assert_eq!(result, Some(Variable::new("X")));
1118 assert_eq!(variables.get("foo"), None);
1119 }
1120
1121 #[test]
1122 fn unsetting_variables_from_all_contexts() {
1123 let mut variables = VariableSet::new();
1124 variables
1125 .get_or_new("foo", Scope::Global)
1126 .assign("X", None)
1127 .unwrap();
1128 variables.push_context_impl(Context::default());
1129 variables
1130 .get_or_new("foo", Scope::Local)
1131 .assign("Y", None)
1132 .unwrap();
1133 variables.push_context_impl(Context::Volatile);
1134 variables
1135 .get_or_new("foo", Scope::Volatile)
1136 .assign("Z", None)
1137 .unwrap();
1138
1139 let result = variables.unset("foo", Scope::Global).unwrap();
1140 assert_eq!(result, Some(Variable::new("Z")));
1141 assert_eq!(variables.get("foo"), None);
1142 }
1143
1144 #[test]
1145 fn unsetting_variable_from_local_context() {
1146 let mut variables = VariableSet::new();
1147 variables
1148 .get_or_new("foo", Scope::Global)
1149 .assign("A", None)
1150 .unwrap();
1151 variables.push_context_impl(Context::default());
1152 let mut readonly_foo = variables.get_or_new("foo", Scope::Local);
1154 readonly_foo.assign("B", None).unwrap();
1155 readonly_foo.make_read_only(Location::dummy("dummy"));
1156 let readonly_foo = readonly_foo.clone();
1157 variables.push_context_impl(Context::default());
1158 variables
1159 .get_or_new("foo", Scope::Local)
1160 .assign("C", None)
1161 .unwrap();
1162 variables.push_context_impl(Context::Volatile);
1163 variables
1164 .get_or_new("foo", Scope::Volatile)
1165 .assign("D", None)
1166 .unwrap();
1167
1168 let result = variables.unset("foo", Scope::Local).unwrap();
1169 assert_eq!(result, Some(Variable::new("D")));
1170 assert_eq!(variables.get("foo"), Some(&readonly_foo));
1171 }
1172
1173 #[test]
1174 fn unsetting_nonexisting_variable_in_local_context() {
1175 let mut variables = VariableSet::new();
1176 variables
1177 .get_or_new("foo", Scope::Global)
1178 .assign("A", None)
1179 .unwrap();
1180 variables.push_context_impl(Context::default());
1181
1182 let result = variables.unset("foo", Scope::Local).unwrap();
1183 assert_eq!(result, None);
1184 assert_eq!(variables.get("foo"), Some(&Variable::new("A")));
1185 }
1186
1187 #[test]
1188 fn unsetting_variable_from_volatile_context() {
1189 let mut variables = VariableSet::new();
1190 variables
1191 .get_or_new("foo", Scope::Global)
1192 .assign("A", None)
1193 .unwrap();
1194 variables.push_context_impl(Context::default());
1195 variables
1196 .get_or_new("foo", Scope::Local)
1197 .assign("B", None)
1198 .unwrap();
1199 variables.push_context_impl(Context::Volatile);
1200 variables
1201 .get_or_new("foo", Scope::Volatile)
1202 .assign("C", None)
1203 .unwrap();
1204 variables.push_context_impl(Context::Volatile);
1205 variables
1206 .get_or_new("foo", Scope::Volatile)
1207 .assign("D", None)
1208 .unwrap();
1209
1210 let result = variables.unset("foo", Scope::Volatile).unwrap();
1211 assert_eq!(result, Some(Variable::new("D")));
1212 assert_eq!(variables.get("foo"), Some(&Variable::new("B")));
1213 }
1214
1215 #[test]
1216 fn unsetting_nonexisting_variable_in_volatile_context() {
1217 let mut variables = VariableSet::new();
1218 variables
1219 .get_or_new("foo", Scope::Global)
1220 .assign("A", None)
1221 .unwrap();
1222 variables.push_context_impl(Context::Volatile);
1223
1224 let result = variables.unset("foo", Scope::Volatile).unwrap();
1225 assert_eq!(result, None);
1226 assert_eq!(variables.get("foo"), Some(&Variable::new("A")));
1227 }
1228
1229 #[test]
1230 fn unsetting_readonly_variable() {
1231 let read_only_location = &Location::dummy("read-only");
1232 let mut variables = VariableSet::new();
1233 let mut foo = variables.get_or_new("foo", Scope::Global);
1234 foo.assign("A", None).unwrap();
1235 variables.push_context_impl(Context::default());
1236 let mut foo = variables.get_or_new("foo", Scope::Local);
1237 foo.assign("B", None).unwrap();
1238 foo.make_read_only(Location::dummy("dummy"));
1239 variables.push_context_impl(Context::default());
1240 let mut foo = variables.get_or_new("foo", Scope::Local);
1241 foo.assign("C", None).unwrap();
1242 foo.make_read_only(read_only_location.clone());
1243 variables.push_context_impl(Context::default());
1244 let mut foo = variables.get_or_new("foo", Scope::Local);
1245 foo.assign("D", None).unwrap();
1246
1247 let error = variables.unset("foo", Scope::Global).unwrap_err();
1248 assert_eq!(
1249 error,
1250 UnsetError {
1251 name: "foo",
1252 read_only_location
1253 }
1254 );
1255 assert_eq!(variables.get("foo"), Some(&Variable::new("D")));
1256 }
1257
1258 #[test]
1259 #[should_panic(expected = "cannot pop the base context")]
1260 fn cannot_pop_base_context() {
1261 let mut variables = VariableSet::new();
1262 variables.pop_context_impl();
1263 }
1264
1265 fn test_iter<F: FnOnce(&VariableSet)>(f: F) {
1266 let mut set = VariableSet::new();
1267
1268 let mut var = set.get_or_new("global", Scope::Global);
1269 var.assign("global value", None).unwrap();
1270 var.export(true);
1271 let mut var = set.get_or_new("local", Scope::Global);
1272 var.assign("hidden value", None).unwrap();
1273
1274 let mut set = set.push_context(Context::default());
1275
1276 let mut var = set.get_or_new("local", Scope::Local);
1277 var.assign("visible value", None).unwrap();
1278 let mut var = set.get_or_new("volatile", Scope::Local);
1279 var.assign("hidden value", None).unwrap();
1280
1281 let mut set = set.push_context(Context::Volatile);
1282
1283 let mut var = set.get_or_new("volatile", Scope::Volatile);
1284 var.assign("volatile value", None).unwrap();
1285
1286 f(&mut set);
1287 }
1288
1289 #[test]
1290 fn iter_global() {
1291 test_iter(|set| {
1292 let mut v: Vec<_> = set.iter(Scope::Global).collect();
1293 v.sort_unstable_by_key(|&(name, _)| name);
1294 assert_eq!(
1295 v,
1296 [
1297 ("global", &Variable::new("global value").export()),
1298 ("local", &Variable::new("visible value")),
1299 ("volatile", &Variable::new("volatile value"))
1300 ]
1301 );
1302 })
1303 }
1304
1305 #[test]
1306 fn iter_local() {
1307 test_iter(|set| {
1308 let mut v: Vec<_> = set.iter(Scope::Local).collect();
1309 v.sort_unstable_by_key(|&(name, _)| name);
1310 assert_eq!(
1311 v,
1312 [
1313 ("local", &Variable::new("visible value")),
1314 ("volatile", &Variable::new("volatile value"))
1315 ]
1316 );
1317 })
1318 }
1319
1320 #[test]
1321 fn iter_volatile() {
1322 test_iter(|set| {
1323 let mut v: Vec<_> = set.iter(Scope::Volatile).collect();
1324 v.sort_unstable_by_key(|&(name, _)| name);
1325 assert_eq!(v, [("volatile", &Variable::new("volatile value"))]);
1326 })
1327 }
1328
1329 #[test]
1330 fn iter_size_hint() {
1331 test_iter(|set| {
1332 assert_eq!(set.iter(Scope::Global).size_hint(), (0, Some(3)));
1333 assert_eq!(set.iter(Scope::Local).size_hint(), (0, Some(3)));
1334 assert_eq!(set.iter(Scope::Volatile).size_hint(), (0, Some(3)));
1335 })
1336 }
1337
1338 #[test]
1339 fn env_c_strings() {
1340 let mut variables = VariableSet::new();
1341 assert_eq!(variables.env_c_strings(), [] as [CString; 0]);
1342
1343 let mut var = variables.get_or_new("foo", Scope::Global);
1344 var.assign("FOO", None).unwrap();
1345 var.export(true);
1346 let mut var = variables.get_or_new("bar", Scope::Global);
1347 var.assign(Value::array(["BAR"]), None).unwrap();
1348 var.export(true);
1349 let mut var = variables.get_or_new("baz", Scope::Global);
1350 var.assign(Value::array(["1", "two", "3"]), None).unwrap();
1351 var.export(true);
1352 let mut var = variables.get_or_new("null", Scope::Global);
1353 var.assign("not exported", None).unwrap();
1354 variables.get_or_new("none", Scope::Global);
1355
1356 let mut ss = variables.env_c_strings();
1357 ss.sort_unstable();
1358 assert_eq!(
1359 &ss,
1360 &[
1361 c"bar=BAR".to_owned(),
1362 c"baz=1:two:3".to_owned(),
1363 c"foo=FOO".to_owned()
1364 ]
1365 );
1366 }
1367
1368 #[test]
1369 fn env_c_strings_with_equal_in_name() {
1370 let mut variables = VariableSet::new();
1371 let mut var = variables.get_or_new("foo", Scope::Global);
1372 var.assign("FOO", None).unwrap();
1373 var.export(true);
1374 let mut var = variables.get_or_new("foo=bar", Scope::Global);
1375 var.assign("BAR", None).unwrap();
1376 var.export(true);
1377
1378 let mut ss = variables.env_c_strings();
1379 ss.sort_unstable();
1380 assert_eq!(&ss, &[c"foo=FOO".to_owned(),]);
1384 }
1385
1386 #[test]
1387 fn extend_env() {
1388 let mut variables = VariableSet::new();
1389
1390 variables.extend_env([("foo", "FOO"), ("bar", "OK")]);
1391
1392 let foo = variables.get("foo").unwrap();
1393 assert_eq!(foo.value, Some("FOO".into()));
1394 assert!(foo.is_exported);
1395 let bar = variables.get("bar").unwrap();
1396 assert_eq!(bar.value, Some("OK".into()));
1397 assert!(bar.is_exported);
1398 }
1399
1400 #[test]
1401 fn init_lineno() {
1402 let mut variables = VariableSet::new();
1403 variables.init();
1404 let v = variables.get(LINENO).unwrap();
1405 assert_eq!(v.value, None);
1406 assert_eq!(v.quirk, Some(Quirk::LineNumber));
1407 assert_eq!(v.last_assigned_location, None);
1408 assert!(!v.is_exported);
1409 assert_eq!(v.read_only_location, None);
1410 }
1411
1412 #[test]
1413 fn positional_params_in_base_context() {
1414 let mut variables = VariableSet::new();
1415 assert_eq!(variables.positional_params().values, [] as [String; 0]);
1416
1417 let params = variables.positional_params_mut();
1418 params.values.push("foo".to_string());
1419 params.values.push("bar".to_string());
1420
1421 assert_eq!(
1422 variables.positional_params().values,
1423 ["foo".to_string(), "bar".to_string()],
1424 );
1425 }
1426
1427 #[test]
1428 fn positional_params_in_second_regular_context() {
1429 let mut variables = VariableSet::new();
1430 variables.push_context_impl(Context::default());
1431 assert_eq!(variables.positional_params().values, [] as [String; 0]);
1432
1433 let params = variables.positional_params_mut();
1434 params.values.push("1".to_string());
1435
1436 assert_eq!(variables.positional_params().values, ["1".to_string()]);
1437 }
1438
1439 #[test]
1440 fn getting_positional_params_in_volatile_context() {
1441 let mut variables = VariableSet::new();
1442
1443 let params = variables.positional_params_mut();
1444 params.values.push("a".to_string());
1445 params.values.push("b".to_string());
1446 params.values.push("c".to_string());
1447
1448 variables.push_context_impl(Context::Volatile);
1449 assert_eq!(
1450 variables.positional_params().values,
1451 ["a".to_string(), "b".to_string(), "c".to_string()],
1452 );
1453 }
1454
1455 #[test]
1456 fn setting_positional_params_in_volatile_context() {
1457 let mut variables = VariableSet::new();
1458 variables.push_context_impl(Context::Volatile);
1459
1460 let params = variables.positional_params_mut();
1461 params.values.push("x".to_string());
1462
1463 variables.pop_context_impl();
1464 assert_eq!(variables.positional_params().values, ["x".to_string()]);
1465 }
1466}