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("the stack should have at least one regular context")
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("the stack should have at least one regular context")
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("the stack should have at least one regular context")
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!(
716 self.contexts.len(),
717 1,
718 "the base context should not be popped"
719 );
720 self.contexts.pop();
721 self.all_variables.retain(|_, stack| {
722 stack.pop_if(|vic| {
723 vic.context_index >= self.contexts.len()
726 });
727 !stack.is_empty()
728 })
729 }
730}
731
732impl<'a> Iterator for Iter<'a> {
733 type Item = (&'a str, &'a Variable);
734
735 fn next(&mut self) -> Option<(&'a str, &'a Variable)> {
736 loop {
737 let next = self.inner.next()?;
738 if let Some(variable) = next.1.last() {
739 if variable.context_index >= self.min_context_index {
740 return Some((next.0, &variable.variable));
741 }
742 }
743 }
744 }
745
746 fn size_hint(&self) -> (usize, Option<usize>) {
747 let (_min, max) = self.inner.size_hint();
748 (0, max)
749 }
750}
751
752impl FusedIterator for Iter<'_> {}
753
754mod guard;
755
756pub use self::guard::{ContextGuard, EnvContextGuard};
757
758#[cfg(test)]
759mod tests {
760 use super::*;
761
762 #[test]
763 fn new_variable_in_global_scope() {
764 let mut set = VariableSet::new();
765 set.push_context_impl(Context::default());
766 set.push_context_impl(Context::Volatile);
767
768 let mut var = set.get_or_new("foo", Scope::Global);
769
770 assert_eq!(*var, Variable::default());
771 var.assign("VALUE", None).unwrap();
772 set.assert_normalized();
773 set.pop_context_impl();
774 set.pop_context_impl();
775 assert_eq!(set.get("foo").unwrap().value, Some("VALUE".into()));
777 }
778
779 #[test]
780 fn existing_variable_in_global_scope() {
781 let mut set = VariableSet::new();
782 let mut var = set.get_or_new("foo", Scope::Global);
783 var.assign("ONE", None).unwrap();
784 set.push_context_impl(Context::default());
785 set.push_context_impl(Context::Volatile);
786
787 let mut var = set.get_or_new("foo", Scope::Global);
788
789 assert_eq!(var.value, Some("ONE".into()));
790 var.assign("TWO", Location::dummy("somewhere")).unwrap();
791 set.assert_normalized();
792 set.pop_context_impl();
793 set.pop_context_impl();
794 let var = set.get("foo").unwrap();
796 assert_eq!(var.value, Some("TWO".into()));
797 assert_eq!(
798 var.last_assigned_location,
799 Some(Location::dummy("somewhere")),
800 );
801 }
802
803 #[test]
804 fn new_variable_in_local_scope() {
805 let mut set = VariableSet::new();
807 set.push_context_impl(Context::default());
808
809 let mut var = set.get_or_new("foo", Scope::Local);
810
811 assert_eq!(*var, Variable::default());
812
813 var.assign("OUTER", None).unwrap();
814 set.push_context_impl(Context::default());
815 set.push_context_impl(Context::Volatile);
816
817 let mut var = set.get_or_new("foo", Scope::Local);
818
819 assert_eq!(*var, Variable::default());
820 var.assign("INNER", Location::dummy("location")).unwrap();
821 set.assert_normalized();
822 set.pop_context_impl(); assert_eq!(set.get("foo").unwrap().value, Some("INNER".into()));
824 set.pop_context_impl(); assert_eq!(set.get("foo").unwrap().value, Some("OUTER".into()));
826 set.pop_context_impl(); assert_eq!(set.get("foo"), None);
828 }
829
830 #[test]
831 fn existing_variable_in_local_scope() {
832 let mut set = VariableSet::new();
833 set.push_context_impl(Context::default());
834 let mut var = set.get_or_new("foo", Scope::Local);
835 var.assign("OLD", None).unwrap();
836
837 let mut var = set.get_or_new("foo", Scope::Local);
838
839 assert_eq!(var.value, Some("OLD".into()));
840 var.assign("NEW", None).unwrap();
841 assert_eq!(set.get("foo").unwrap().value, Some("NEW".into()));
842 set.assert_normalized();
843 set.pop_context_impl();
844 assert_eq!(set.get("foo"), None);
845 }
846
847 #[test]
848 fn new_variable_in_volatile_scope() {
849 let mut set = VariableSet::new();
850 set.push_context_impl(Context::Volatile);
851 set.push_context_impl(Context::Volatile);
852
853 let mut var = set.get_or_new("foo", Scope::Volatile);
854
855 assert_eq!(*var, Variable::default());
856 var.assign("VOLATILE", None).unwrap();
857 assert_eq!(set.get("foo").unwrap().value, Some("VOLATILE".into()));
858 set.assert_normalized();
859 set.pop_context_impl();
860 assert_eq!(set.get("foo"), None);
861 }
862
863 #[test]
864 fn cloning_existing_regular_variable_to_volatile_context() {
865 let mut set = VariableSet::new();
866 let mut var = set.get_or_new("foo", Scope::Global);
867 var.assign("VALUE", None).unwrap();
868 var.make_read_only(Location::dummy("read-only location"));
869 let save_var = var.clone();
870 set.push_context_impl(Context::Volatile);
871 set.push_context_impl(Context::Volatile);
872
873 let mut var = set.get_or_new("foo", Scope::Volatile);
874
875 assert_eq!(*var, save_var);
876 var.export(true);
877 assert!(set.get("foo").unwrap().is_exported);
878 set.assert_normalized();
879 set.pop_context_impl();
880 assert_eq!(set.get("foo"), Some(&save_var));
883 }
884
885 #[test]
886 fn existing_variable_in_volatile_scope() {
887 let mut set = VariableSet::new();
888 set.push_context_impl(Context::Volatile);
889 let mut var = set.get_or_new("foo", Scope::Volatile);
890 var.assign("INITIAL", None).unwrap();
891
892 let mut var = set.get_or_new("foo", Scope::Volatile);
893
894 assert_eq!(var.value, Some("INITIAL".into()));
895 var.assign(Value::array(["MODIFIED"]), Location::dummy("somewhere"))
896 .unwrap();
897 assert_eq!(
898 set.get("foo").unwrap().value,
899 Some(Value::array(["MODIFIED"])),
900 );
901 set.assert_normalized();
902 set.pop_context_impl();
903 assert_eq!(set.get("foo"), None);
904 }
905
906 #[test]
907 fn lowering_volatile_variable_to_base_context() {
908 let mut set = VariableSet::new();
909 set.push_context_impl(Context::default());
910 set.push_context_impl(Context::Volatile);
911 let mut var = set.get_or_new("foo", Scope::Volatile);
912 var.assign("DUMMY", None).unwrap();
913 set.push_context_impl(Context::Volatile);
914 let mut var = set.get_or_new("foo", Scope::Volatile);
915 var.assign("VOLATILE", Location::dummy("anywhere")).unwrap();
916 var.export(true);
917
918 let mut var = set.get_or_new("foo", Scope::Global);
919
920 assert_eq!(var.value, Some("VOLATILE".into()));
921 assert_eq!(
922 var.last_assigned_location,
923 Some(Location::dummy("anywhere")),
924 );
925 var.assign("NEW", Location::dummy("somewhere")).unwrap();
926 set.assert_normalized();
927 set.pop_context_impl();
928 set.pop_context_impl();
929 set.pop_context_impl();
930 let var = set.get("foo").unwrap();
933 assert_eq!(var.value, Some("NEW".into()));
934 assert_eq!(
935 var.last_assigned_location,
936 Some(Location::dummy("somewhere")),
937 );
938 assert!(var.is_exported);
940 }
941
942 #[test]
943 fn lowering_volatile_variable_to_middle_regular_context() {
944 let mut set = VariableSet::new();
945 let mut var = set.get_or_new("foo", Scope::Local);
946 var.assign("ONE", None).unwrap();
947 set.push_context_impl(Context::default());
948 let mut var = set.get_or_new("foo", Scope::Local);
949 var.assign("TWO", None).unwrap();
950 set.push_context_impl(Context::default());
951 set.push_context_impl(Context::Volatile);
952 let mut var = set.get_or_new("foo", Scope::Volatile);
953 var.assign("VOLATILE", Location::dummy("anywhere")).unwrap();
954 var.export(true);
955
956 let mut var = set.get_or_new("foo", Scope::Global);
957
958 assert_eq!(var.value, Some("VOLATILE".into()));
959 assert_eq!(
960 var.last_assigned_location,
961 Some(Location::dummy("anywhere")),
962 );
963 var.assign("NEW", Location::dummy("somewhere")).unwrap();
964 set.assert_normalized();
965 set.pop_context_impl();
966 set.pop_context_impl();
967 let var = set.get("foo").unwrap();
969 assert_eq!(var.value, Some("NEW".into()));
970 assert_eq!(
971 var.last_assigned_location,
972 Some(Location::dummy("somewhere")),
973 );
974 assert!(var.is_exported);
976 set.pop_context_impl();
977 let var = set.get("foo").unwrap();
979 assert_eq!(var.value, Some("ONE".into()));
980 }
981
982 #[test]
983 fn lowering_volatile_variable_to_topmost_regular_context_without_existing_variable() {
984 let mut set = VariableSet::new();
985 set.push_context_impl(Context::default());
986 set.push_context_impl(Context::default());
987 set.push_context_impl(Context::Volatile);
988 let mut var = set.get_or_new("foo", Scope::Volatile);
989 var.assign("DUMMY", None).unwrap();
990 set.push_context_impl(Context::Volatile);
991 let mut var = set.get_or_new("foo", Scope::Volatile);
992 var.assign("VOLATILE", Location::dummy("anywhere")).unwrap();
993 var.export(true);
994
995 let mut var = set.get_or_new("foo", Scope::Local);
996
997 assert_eq!(var.value, Some("VOLATILE".into()));
998 assert_eq!(
999 var.last_assigned_location,
1000 Some(Location::dummy("anywhere")),
1001 );
1002 var.assign("NEW", Location::dummy("somewhere")).unwrap();
1003 set.assert_normalized();
1004 set.pop_context_impl();
1005 set.pop_context_impl();
1006 let var = set.get("foo").unwrap();
1009 assert_eq!(var.value, Some("NEW".into()));
1010 assert_eq!(
1011 var.last_assigned_location,
1012 Some(Location::dummy("somewhere")),
1013 );
1014 assert!(var.is_exported);
1016 }
1017
1018 #[test]
1019 fn lowering_volatile_variable_to_topmost_regular_context_overwriting_existing_variable() {
1020 let mut set = VariableSet::new();
1021 set.push_context_impl(Context::default());
1022 set.push_context_impl(Context::default());
1023 let mut var = set.get_or_new("foo", Scope::Local);
1024 var.assign("OLD", None).unwrap();
1025 set.push_context_impl(Context::Volatile);
1026 let mut var = set.get_or_new("foo", Scope::Volatile);
1027 var.assign("DUMMY", None).unwrap();
1028 set.push_context_impl(Context::Volatile);
1029 let mut var = set.get_or_new("foo", Scope::Volatile);
1030 var.assign("VOLATILE", Location::dummy("first")).unwrap();
1031 var.export(true);
1032 set.push_context_impl(Context::Volatile);
1033
1034 let mut var = set.get_or_new("foo", Scope::Local);
1035
1036 assert_eq!(var.value, Some("VOLATILE".into()));
1037 assert_eq!(var.last_assigned_location, Some(Location::dummy("first")));
1038 var.assign("NEW", Location::dummy("second")).unwrap();
1039 set.assert_normalized();
1040 set.pop_context_impl();
1041 set.pop_context_impl();
1042 set.pop_context_impl();
1043 let var = set.get("foo").unwrap();
1046 assert_eq!(var.value, Some("NEW".into()));
1047 assert_eq!(var.last_assigned_location, Some(Location::dummy("second")));
1048 assert!(var.is_exported);
1050 }
1051
1052 #[test]
1053 #[should_panic(expected = "no volatile context to store the variable")]
1054 fn missing_volatile_context() {
1055 let mut set = VariableSet::new();
1056 set.get_or_new("foo", Scope::Volatile);
1057 }
1058
1059 #[test]
1060 fn getting_variables_with_scopes() {
1061 let mut set = VariableSet::new();
1062 set.get_or_new("global", Scope::Global)
1063 .assign("G", None)
1064 .unwrap();
1065 set.push_context_impl(Context::default());
1066 set.get_or_new("local", Scope::Local)
1067 .assign("L", None)
1068 .unwrap();
1069 set.push_context_impl(Context::Volatile);
1070 set.get_or_new("volatile", Scope::Volatile)
1071 .assign("V", None)
1072 .unwrap();
1073
1074 assert_eq!(
1075 set.get_scoped("global", Scope::Global),
1076 Some(&Variable::new("G")),
1077 );
1078 assert_eq!(set.get_scoped("global", Scope::Local), None);
1079 assert_eq!(set.get_scoped("global", Scope::Volatile), None);
1080
1081 assert_eq!(
1082 set.get_scoped("local", Scope::Global),
1083 Some(&Variable::new("L"))
1084 );
1085 assert_eq!(
1086 set.get_scoped("local", Scope::Local),
1087 Some(&Variable::new("L"))
1088 );
1089 assert_eq!(set.get_scoped("local", Scope::Volatile), None);
1090
1091 assert_eq!(
1092 set.get_scoped("volatile", Scope::Global),
1093 Some(&Variable::new("V"))
1094 );
1095 assert_eq!(
1096 set.get_scoped("volatile", Scope::Local),
1097 Some(&Variable::new("V"))
1098 );
1099 assert_eq!(
1100 set.get_scoped("volatile", Scope::Volatile),
1101 Some(&Variable::new("V"))
1102 );
1103 }
1104
1105 #[test]
1106 fn unsetting_nonexisting_variable() {
1107 let mut variables = VariableSet::new();
1108 let result = variables.unset("", Scope::Global).unwrap();
1109 assert_eq!(result, None);
1110 }
1111
1112 #[test]
1113 fn unsetting_variable_with_one_context() {
1114 let mut variables = VariableSet::new();
1115 variables
1116 .get_or_new("foo", Scope::Global)
1117 .assign("X", None)
1118 .unwrap();
1119
1120 let result = variables.unset("foo", Scope::Global).unwrap();
1121 assert_eq!(result, Some(Variable::new("X")));
1122 assert_eq!(variables.get("foo"), None);
1123 }
1124
1125 #[test]
1126 fn unsetting_variables_from_all_contexts() {
1127 let mut variables = VariableSet::new();
1128 variables
1129 .get_or_new("foo", Scope::Global)
1130 .assign("X", None)
1131 .unwrap();
1132 variables.push_context_impl(Context::default());
1133 variables
1134 .get_or_new("foo", Scope::Local)
1135 .assign("Y", None)
1136 .unwrap();
1137 variables.push_context_impl(Context::Volatile);
1138 variables
1139 .get_or_new("foo", Scope::Volatile)
1140 .assign("Z", None)
1141 .unwrap();
1142
1143 let result = variables.unset("foo", Scope::Global).unwrap();
1144 assert_eq!(result, Some(Variable::new("Z")));
1145 assert_eq!(variables.get("foo"), None);
1146 }
1147
1148 #[test]
1149 fn unsetting_variable_from_local_context() {
1150 let mut variables = VariableSet::new();
1151 variables
1152 .get_or_new("foo", Scope::Global)
1153 .assign("A", None)
1154 .unwrap();
1155 variables.push_context_impl(Context::default());
1156 let mut readonly_foo = variables.get_or_new("foo", Scope::Local);
1158 readonly_foo.assign("B", None).unwrap();
1159 readonly_foo.make_read_only(Location::dummy("dummy"));
1160 let readonly_foo = readonly_foo.clone();
1161 variables.push_context_impl(Context::default());
1162 variables
1163 .get_or_new("foo", Scope::Local)
1164 .assign("C", None)
1165 .unwrap();
1166 variables.push_context_impl(Context::Volatile);
1167 variables
1168 .get_or_new("foo", Scope::Volatile)
1169 .assign("D", None)
1170 .unwrap();
1171
1172 let result = variables.unset("foo", Scope::Local).unwrap();
1173 assert_eq!(result, Some(Variable::new("D")));
1174 assert_eq!(variables.get("foo"), Some(&readonly_foo));
1175 }
1176
1177 #[test]
1178 fn unsetting_nonexisting_variable_in_local_context() {
1179 let mut variables = VariableSet::new();
1180 variables
1181 .get_or_new("foo", Scope::Global)
1182 .assign("A", None)
1183 .unwrap();
1184 variables.push_context_impl(Context::default());
1185
1186 let result = variables.unset("foo", Scope::Local).unwrap();
1187 assert_eq!(result, None);
1188 assert_eq!(variables.get("foo"), Some(&Variable::new("A")));
1189 }
1190
1191 #[test]
1192 fn unsetting_variable_from_volatile_context() {
1193 let mut variables = VariableSet::new();
1194 variables
1195 .get_or_new("foo", Scope::Global)
1196 .assign("A", None)
1197 .unwrap();
1198 variables.push_context_impl(Context::default());
1199 variables
1200 .get_or_new("foo", Scope::Local)
1201 .assign("B", None)
1202 .unwrap();
1203 variables.push_context_impl(Context::Volatile);
1204 variables
1205 .get_or_new("foo", Scope::Volatile)
1206 .assign("C", None)
1207 .unwrap();
1208 variables.push_context_impl(Context::Volatile);
1209 variables
1210 .get_or_new("foo", Scope::Volatile)
1211 .assign("D", None)
1212 .unwrap();
1213
1214 let result = variables.unset("foo", Scope::Volatile).unwrap();
1215 assert_eq!(result, Some(Variable::new("D")));
1216 assert_eq!(variables.get("foo"), Some(&Variable::new("B")));
1217 }
1218
1219 #[test]
1220 fn unsetting_nonexisting_variable_in_volatile_context() {
1221 let mut variables = VariableSet::new();
1222 variables
1223 .get_or_new("foo", Scope::Global)
1224 .assign("A", None)
1225 .unwrap();
1226 variables.push_context_impl(Context::Volatile);
1227
1228 let result = variables.unset("foo", Scope::Volatile).unwrap();
1229 assert_eq!(result, None);
1230 assert_eq!(variables.get("foo"), Some(&Variable::new("A")));
1231 }
1232
1233 #[test]
1234 fn unsetting_readonly_variable() {
1235 let read_only_location = &Location::dummy("read-only");
1236 let mut variables = VariableSet::new();
1237 let mut foo = variables.get_or_new("foo", Scope::Global);
1238 foo.assign("A", None).unwrap();
1239 variables.push_context_impl(Context::default());
1240 let mut foo = variables.get_or_new("foo", Scope::Local);
1241 foo.assign("B", None).unwrap();
1242 foo.make_read_only(Location::dummy("dummy"));
1243 variables.push_context_impl(Context::default());
1244 let mut foo = variables.get_or_new("foo", Scope::Local);
1245 foo.assign("C", None).unwrap();
1246 foo.make_read_only(read_only_location.clone());
1247 variables.push_context_impl(Context::default());
1248 let mut foo = variables.get_or_new("foo", Scope::Local);
1249 foo.assign("D", None).unwrap();
1250
1251 let error = variables.unset("foo", Scope::Global).unwrap_err();
1252 assert_eq!(
1253 error,
1254 UnsetError {
1255 name: "foo",
1256 read_only_location
1257 }
1258 );
1259 assert_eq!(variables.get("foo"), Some(&Variable::new("D")));
1260 }
1261
1262 #[test]
1263 #[should_panic(expected = "the base context should not be popped")]
1264 fn cannot_pop_base_context() {
1265 let mut variables = VariableSet::new();
1266 variables.pop_context_impl();
1267 }
1268
1269 fn test_iter<F: FnOnce(&VariableSet)>(f: F) {
1270 let mut set = VariableSet::new();
1271
1272 let mut var = set.get_or_new("global", Scope::Global);
1273 var.assign("global value", None).unwrap();
1274 var.export(true);
1275 let mut var = set.get_or_new("local", Scope::Global);
1276 var.assign("hidden value", None).unwrap();
1277
1278 let mut set = set.push_context(Context::default());
1279
1280 let mut var = set.get_or_new("local", Scope::Local);
1281 var.assign("visible value", None).unwrap();
1282 let mut var = set.get_or_new("volatile", Scope::Local);
1283 var.assign("hidden value", None).unwrap();
1284
1285 let mut set = set.push_context(Context::Volatile);
1286
1287 let mut var = set.get_or_new("volatile", Scope::Volatile);
1288 var.assign("volatile value", None).unwrap();
1289
1290 f(&mut set);
1291 }
1292
1293 #[test]
1294 fn iter_global() {
1295 test_iter(|set| {
1296 let mut v: Vec<_> = set.iter(Scope::Global).collect();
1297 v.sort_unstable_by_key(|&(name, _)| name);
1298 assert_eq!(
1299 v,
1300 [
1301 ("global", &Variable::new("global value").export()),
1302 ("local", &Variable::new("visible value")),
1303 ("volatile", &Variable::new("volatile value"))
1304 ]
1305 );
1306 })
1307 }
1308
1309 #[test]
1310 fn iter_local() {
1311 test_iter(|set| {
1312 let mut v: Vec<_> = set.iter(Scope::Local).collect();
1313 v.sort_unstable_by_key(|&(name, _)| name);
1314 assert_eq!(
1315 v,
1316 [
1317 ("local", &Variable::new("visible value")),
1318 ("volatile", &Variable::new("volatile value"))
1319 ]
1320 );
1321 })
1322 }
1323
1324 #[test]
1325 fn iter_volatile() {
1326 test_iter(|set| {
1327 let mut v: Vec<_> = set.iter(Scope::Volatile).collect();
1328 v.sort_unstable_by_key(|&(name, _)| name);
1329 assert_eq!(v, [("volatile", &Variable::new("volatile value"))]);
1330 })
1331 }
1332
1333 #[test]
1334 fn iter_size_hint() {
1335 test_iter(|set| {
1336 assert_eq!(set.iter(Scope::Global).size_hint(), (0, Some(3)));
1337 assert_eq!(set.iter(Scope::Local).size_hint(), (0, Some(3)));
1338 assert_eq!(set.iter(Scope::Volatile).size_hint(), (0, Some(3)));
1339 })
1340 }
1341
1342 #[test]
1343 fn env_c_strings() {
1344 let mut variables = VariableSet::new();
1345 assert_eq!(variables.env_c_strings(), [] as [CString; 0]);
1346
1347 let mut var = variables.get_or_new("foo", Scope::Global);
1348 var.assign("FOO", None).unwrap();
1349 var.export(true);
1350 let mut var = variables.get_or_new("bar", Scope::Global);
1351 var.assign(Value::array(["BAR"]), None).unwrap();
1352 var.export(true);
1353 let mut var = variables.get_or_new("baz", Scope::Global);
1354 var.assign(Value::array(["1", "two", "3"]), None).unwrap();
1355 var.export(true);
1356 let mut var = variables.get_or_new("null", Scope::Global);
1357 var.assign("not exported", None).unwrap();
1358 variables.get_or_new("none", Scope::Global);
1359
1360 let mut ss = variables.env_c_strings();
1361 ss.sort_unstable();
1362 assert_eq!(
1363 &ss,
1364 &[
1365 c"bar=BAR".to_owned(),
1366 c"baz=1:two:3".to_owned(),
1367 c"foo=FOO".to_owned()
1368 ]
1369 );
1370 }
1371
1372 #[test]
1373 fn env_c_strings_with_equal_in_name() {
1374 let mut variables = VariableSet::new();
1375 let mut var = variables.get_or_new("foo", Scope::Global);
1376 var.assign("FOO", None).unwrap();
1377 var.export(true);
1378 let mut var = variables.get_or_new("foo=bar", Scope::Global);
1379 var.assign("BAR", None).unwrap();
1380 var.export(true);
1381
1382 let mut ss = variables.env_c_strings();
1383 ss.sort_unstable();
1384 assert_eq!(&ss, &[c"foo=FOO".to_owned(),]);
1388 }
1389
1390 #[test]
1391 fn extend_env() {
1392 let mut variables = VariableSet::new();
1393
1394 variables.extend_env([("foo", "FOO"), ("bar", "OK")]);
1395
1396 let foo = variables.get("foo").unwrap();
1397 assert_eq!(foo.value, Some("FOO".into()));
1398 assert!(foo.is_exported);
1399 let bar = variables.get("bar").unwrap();
1400 assert_eq!(bar.value, Some("OK".into()));
1401 assert!(bar.is_exported);
1402 }
1403
1404 #[test]
1405 fn init_lineno() {
1406 let mut variables = VariableSet::new();
1407 variables.init();
1408 let v = variables.get(LINENO).unwrap();
1409 assert_eq!(v.value, None);
1410 assert_eq!(v.quirk, Some(Quirk::LineNumber));
1411 assert_eq!(v.last_assigned_location, None);
1412 assert!(!v.is_exported);
1413 assert_eq!(v.read_only_location, None);
1414 }
1415
1416 #[test]
1417 fn positional_params_in_base_context() {
1418 let mut variables = VariableSet::new();
1419 assert_eq!(variables.positional_params().values, [] as [String; 0]);
1420
1421 let params = variables.positional_params_mut();
1422 params.values.push("foo".to_string());
1423 params.values.push("bar".to_string());
1424
1425 assert_eq!(
1426 variables.positional_params().values,
1427 ["foo".to_string(), "bar".to_string()],
1428 );
1429 }
1430
1431 #[test]
1432 fn positional_params_in_second_regular_context() {
1433 let mut variables = VariableSet::new();
1434 variables.push_context_impl(Context::default());
1435 assert_eq!(variables.positional_params().values, [] as [String; 0]);
1436
1437 let params = variables.positional_params_mut();
1438 params.values.push("1".to_string());
1439
1440 assert_eq!(variables.positional_params().values, ["1".to_string()]);
1441 }
1442
1443 #[test]
1444 fn getting_positional_params_in_volatile_context() {
1445 let mut variables = VariableSet::new();
1446
1447 let params = variables.positional_params_mut();
1448 params.values.push("a".to_string());
1449 params.values.push("b".to_string());
1450 params.values.push("c".to_string());
1451
1452 variables.push_context_impl(Context::Volatile);
1453 assert_eq!(
1454 variables.positional_params().values,
1455 ["a".to_string(), "b".to_string(), "c".to_string()],
1456 );
1457 }
1458
1459 #[test]
1460 fn setting_positional_params_in_volatile_context() {
1461 let mut variables = VariableSet::new();
1462 variables.push_context_impl(Context::Volatile);
1463
1464 let params = variables.positional_params_mut();
1465 params.values.push("x".to_string());
1466
1467 variables.pop_context_impl();
1468 assert_eq!(variables.positional_params().values, ["x".to_string()]);
1469 }
1470}