1use std::borrow::Cow;
4use std::collections::BTreeMap;
5use std::fmt::{Display, Write};
6
7use crate::shell::Shell;
8use crate::{error, escape};
9
10#[derive(Clone, Debug)]
12pub struct ShellVariable {
13 value: ShellValue,
15 exported: bool,
17 readonly: bool,
19 enumerable: bool,
21 transform_on_update: ShellVariableUpdateTransform,
23 trace: bool,
25 treat_as_integer: bool,
27 treat_as_nameref: bool,
29}
30
31#[derive(Clone, Copy, Debug)]
33pub enum ShellVariableUpdateTransform {
34 None,
36 Lowercase,
38 Uppercase,
40 Capitalize,
42}
43
44impl Default for ShellVariable {
45 fn default() -> Self {
46 Self {
47 value: ShellValue::String(String::new()),
48 exported: false,
49 readonly: false,
50 enumerable: true,
51 transform_on_update: ShellVariableUpdateTransform::None,
52 trace: false,
53 treat_as_integer: false,
54 treat_as_nameref: false,
55 }
56 }
57}
58
59impl ShellVariable {
60 pub fn new(value: ShellValue) -> Self {
66 Self {
67 value,
68 ..Self::default()
69 }
70 }
71
72 pub const fn value(&self) -> &ShellValue {
74 &self.value
75 }
76
77 pub const fn is_exported(&self) -> bool {
79 self.exported
80 }
81
82 pub const fn export(&mut self) -> &mut Self {
84 self.exported = true;
85 self
86 }
87
88 pub const fn unexport(&mut self) -> &mut Self {
90 self.exported = false;
91 self
92 }
93
94 pub const fn is_readonly(&self) -> bool {
96 self.readonly
97 }
98
99 pub const fn set_readonly(&mut self) -> &mut Self {
101 self.readonly = true;
102 self
103 }
104
105 pub const fn unset_readonly(&mut self) -> Result<&mut Self, error::Error> {
107 if self.readonly {
108 return Err(error::Error::ReadonlyVariable);
109 }
110
111 self.readonly = false;
112 Ok(self)
113 }
114
115 pub const fn is_trace_enabled(&self) -> bool {
117 self.trace
118 }
119
120 pub const fn enable_trace(&mut self) -> &mut Self {
122 self.trace = true;
123 self
124 }
125
126 pub const fn disable_trace(&mut self) -> &mut Self {
128 self.trace = false;
129 self
130 }
131
132 pub const fn is_enumerable(&self) -> bool {
134 self.enumerable
135 }
136
137 pub const fn hide_from_enumeration(&mut self) -> &mut Self {
139 self.enumerable = false;
140 self
141 }
142
143 pub const fn get_update_transform(&self) -> ShellVariableUpdateTransform {
145 self.transform_on_update
146 }
147
148 pub const fn set_update_transform(&mut self, transform: ShellVariableUpdateTransform) {
150 self.transform_on_update = transform;
151 }
152
153 pub const fn is_treated_as_integer(&self) -> bool {
155 self.treat_as_integer
156 }
157
158 pub const fn treat_as_integer(&mut self) -> &mut Self {
160 self.treat_as_integer = true;
161 self
162 }
163
164 pub const fn unset_treat_as_integer(&mut self) -> &mut Self {
166 self.treat_as_integer = false;
167 self
168 }
169
170 pub const fn is_treated_as_nameref(&self) -> bool {
172 self.treat_as_nameref
173 }
174
175 pub const fn treat_as_nameref(&mut self) -> &mut Self {
177 self.treat_as_nameref = true;
178 self
179 }
180
181 pub const fn unset_treat_as_nameref(&mut self) -> &mut Self {
183 self.treat_as_nameref = false;
184 self
185 }
186
187 pub fn convert_to_indexed_array(&mut self) -> Result<(), error::Error> {
189 match self.value() {
190 ShellValue::IndexedArray(_) => Ok(()),
191 ShellValue::AssociativeArray(_) => {
192 Err(error::Error::ConvertingAssociativeArrayToIndexedArray)
193 }
194 _ => {
195 let mut new_values = BTreeMap::new();
196 new_values.insert(
197 0,
198 self.value.to_cow_str_without_dynamic_support().to_string(),
199 );
200 self.value = ShellValue::IndexedArray(new_values);
201 Ok(())
202 }
203 }
204 }
205
206 pub fn convert_to_associative_array(&mut self) -> Result<(), error::Error> {
208 match self.value() {
209 ShellValue::AssociativeArray(_) => Ok(()),
210 ShellValue::IndexedArray(_) => {
211 Err(error::Error::ConvertingIndexedArrayToAssociativeArray)
212 }
213 _ => {
214 let mut new_values: BTreeMap<String, String> = BTreeMap::new();
215 new_values.insert(
216 String::from("0"),
217 self.value.to_cow_str_without_dynamic_support().to_string(),
218 );
219 self.value = ShellValue::AssociativeArray(new_values);
220 Ok(())
221 }
222 }
223 }
224
225 #[allow(clippy::too_many_lines)]
232 pub fn assign(&mut self, value: ShellValueLiteral, append: bool) -> Result<(), error::Error> {
233 if self.is_readonly() {
234 return Err(error::Error::ReadonlyVariable);
235 }
236
237 let value = self.convert_value_literal_for_assignment(value);
238
239 if append {
240 match (&self.value, &value) {
241 (ShellValue::Unset(_), ShellValueLiteral::Array(_))
244 | (
245 ShellValue::Unset(
246 ShellValueUnsetType::IndexedArray | ShellValueUnsetType::AssociativeArray,
247 ),
248 _,
249 ) => {
250 self.assign(ShellValueLiteral::Array(ArrayLiteral(vec![])), false)?;
251 }
252 (ShellValue::Unset(_), ShellValueLiteral::Scalar(_)) => {
256 self.assign(ShellValueLiteral::Scalar(String::new()), false)?;
257 }
258 (ShellValue::String(_), ShellValueLiteral::Array(_)) => {
261 self.convert_to_indexed_array()?;
262 }
263 _ => (),
264 }
265
266 let treat_as_int = self.is_treated_as_integer();
267 let update_transform = self.get_update_transform();
268
269 match &mut self.value {
270 ShellValue::String(base) => match value {
271 ShellValueLiteral::Scalar(suffix) => {
272 if treat_as_int {
273 let int_value = base.parse::<i64>().unwrap_or(0)
274 + suffix.parse::<i64>().unwrap_or(0);
275 base.clear();
276 base.push_str(int_value.to_string().as_str());
277 } else {
278 base.push_str(suffix.as_str());
279 Self::apply_value_transforms(base, treat_as_int, update_transform);
280 }
281 Ok(())
282 }
283 ShellValueLiteral::Array(_) => {
284 Ok(())
286 }
287 },
288 ShellValue::IndexedArray(existing_values) => match value {
289 ShellValueLiteral::Scalar(new_value) => {
290 self.assign_at_index(String::from("0"), new_value, append)
291 }
292 ShellValueLiteral::Array(new_values) => {
293 ShellValue::update_indexed_array_from_literals(existing_values, new_values);
294 Ok(())
295 }
296 },
297 ShellValue::AssociativeArray(existing_values) => match value {
298 ShellValueLiteral::Scalar(new_value) => {
299 self.assign_at_index(String::from("0"), new_value, append)
300 }
301 ShellValueLiteral::Array(new_values) => {
302 ShellValue::update_associative_array_from_literals(
303 existing_values,
304 new_values,
305 )
306 }
307 },
308 ShellValue::Unset(_) => unreachable!("covered in conversion above"),
309 ShellValue::Dynamic { .. } => Ok(()),
311 }
312 } else {
313 match (&self.value, value) {
314 (
317 ShellValue::IndexedArray(_)
318 | ShellValue::AssociativeArray(_)
319 | ShellValue::Unset(
320 ShellValueUnsetType::AssociativeArray | ShellValueUnsetType::IndexedArray,
321 ),
322 ShellValueLiteral::Scalar(s),
323 ) => self.assign_at_index(String::from("0"), s, false),
324
325 (
329 ShellValue::IndexedArray(_)
330 | ShellValue::Unset(
331 ShellValueUnsetType::IndexedArray | ShellValueUnsetType::Untyped,
332 )
333 | ShellValue::String(_)
334 | ShellValue::Dynamic { .. },
335 ShellValueLiteral::Array(literal_values),
336 ) => {
337 self.value = ShellValue::indexed_array_from_literals(literal_values);
338 Ok(())
339 }
340
341 (
344 ShellValue::AssociativeArray(_)
345 | ShellValue::Unset(ShellValueUnsetType::AssociativeArray),
346 ShellValueLiteral::Array(literal_values),
347 ) => {
348 self.value = ShellValue::associative_array_from_literals(literal_values)?;
349 Ok(())
350 }
351
352 (ShellValue::Dynamic { .. }, _) => Ok(()),
355
356 (ShellValue::String(_) | ShellValue::Unset(_), ShellValueLiteral::Scalar(s)) => {
358 self.value = ShellValue::String(s);
359 Ok(())
360 }
361 }
362 }
363 }
364
365 #[allow(clippy::needless_pass_by_value)]
375 pub fn assign_at_index(
376 &mut self,
377 array_index: String,
378 value: String,
379 append: bool,
380 ) -> Result<(), error::Error> {
381 match &self.value {
382 ShellValue::Unset(_) => {
383 self.assign(ShellValueLiteral::Array(ArrayLiteral(vec![])), false)?;
384 }
385 ShellValue::String(_) => {
386 self.convert_to_indexed_array()?;
387 }
388 _ => (),
389 }
390
391 let treat_as_int = self.is_treated_as_integer();
392 let value = self.convert_value_str_for_assignment(value);
393
394 match &mut self.value {
395 ShellValue::IndexedArray(arr) => {
396 let key: u64 = array_index.parse().unwrap_or(0);
397
398 if append {
399 let existing_value = arr.get(&key).map_or_else(|| "", |v| v.as_str());
400
401 let mut new_value;
402 if treat_as_int {
403 new_value = (existing_value.parse::<i64>().unwrap_or(0)
404 + value.parse::<i64>().unwrap_or(0))
405 .to_string();
406 } else {
407 new_value = existing_value.to_owned();
408 new_value.push_str(value.as_str());
409 }
410
411 arr.insert(key, new_value);
412 } else {
413 arr.insert(key, value);
414 }
415
416 Ok(())
417 }
418 ShellValue::AssociativeArray(arr) => {
419 if append {
420 let existing_value = arr
421 .get(array_index.as_str())
422 .map_or_else(|| "", |v| v.as_str());
423
424 let mut new_value;
425 if treat_as_int {
426 new_value = (existing_value.parse::<i64>().unwrap_or(0)
427 + value.parse::<i64>().unwrap_or(0))
428 .to_string();
429 } else {
430 new_value = existing_value.to_owned();
431 new_value.push_str(value.as_str());
432 }
433
434 arr.insert(array_index, new_value.to_string());
435 } else {
436 arr.insert(array_index, value);
437 }
438 Ok(())
439 }
440 _ => {
441 tracing::error!("assigning to index {array_index} of {:?}", self.value);
442 error::unimp("assigning to index of non-array variable")
443 }
444 }
445 }
446
447 fn convert_value_literal_for_assignment(&self, value: ShellValueLiteral) -> ShellValueLiteral {
448 match value {
449 ShellValueLiteral::Scalar(s) => {
450 ShellValueLiteral::Scalar(self.convert_value_str_for_assignment(s))
451 }
452 ShellValueLiteral::Array(literals) => ShellValueLiteral::Array(ArrayLiteral(
453 literals
454 .0
455 .into_iter()
456 .map(|(k, v)| (k, self.convert_value_str_for_assignment(v)))
457 .collect(),
458 )),
459 }
460 }
461
462 fn convert_value_str_for_assignment(&self, mut s: String) -> String {
463 Self::apply_value_transforms(
464 &mut s,
465 self.is_treated_as_integer(),
466 self.get_update_transform(),
467 );
468
469 s
470 }
471
472 fn apply_value_transforms(
473 s: &mut String,
474 treat_as_int: bool,
475 update_transform: ShellVariableUpdateTransform,
476 ) {
477 if treat_as_int {
478 *s = (*s).parse::<i64>().unwrap_or(0).to_string();
479 } else {
480 match update_transform {
481 ShellVariableUpdateTransform::None => (),
482 ShellVariableUpdateTransform::Lowercase => *s = (*s).to_lowercase(),
483 ShellVariableUpdateTransform::Uppercase => *s = (*s).to_uppercase(),
484 ShellVariableUpdateTransform::Capitalize => {
485 *s = s.to_lowercase();
487 if let Some(c) = s.chars().next() {
488 s.replace_range(0..1, &c.to_uppercase().to_string());
489 }
490 }
491 }
492 }
493 }
494
495 pub fn unset_index(&mut self, index: &str) -> Result<bool, error::Error> {
502 match &mut self.value {
503 ShellValue::Unset(ty) => match ty {
504 ShellValueUnsetType::Untyped => Err(error::Error::NotArray),
505 ShellValueUnsetType::AssociativeArray | ShellValueUnsetType::IndexedArray => {
506 Ok(false)
507 }
508 },
509 ShellValue::String(_) => Err(error::Error::NotArray),
510 ShellValue::AssociativeArray(values) => Ok(values.remove(index).is_some()),
511 ShellValue::IndexedArray(values) => {
512 let key = index.parse::<u64>().unwrap_or(0);
513 Ok(values.remove(&key).is_some())
514 }
515 ShellValue::Dynamic { .. } => Ok(false),
516 }
517 }
518
519 pub(crate) fn resolve_value(&self, shell: &Shell) -> ShellValue {
525 match &self.value {
527 ShellValue::Dynamic { getter, .. } => getter(shell),
528 _ => self.value.clone(),
529 }
530 }
531
532 pub fn get_attribute_flags(&self, shell: &Shell) -> String {
534 let value = self.resolve_value(shell);
535
536 let mut result = String::new();
537
538 if matches!(
539 value,
540 ShellValue::IndexedArray(_) | ShellValue::Unset(ShellValueUnsetType::IndexedArray)
541 ) {
542 result.push('a');
543 }
544 if matches!(
545 value,
546 ShellValue::AssociativeArray(_)
547 | ShellValue::Unset(ShellValueUnsetType::AssociativeArray)
548 ) {
549 result.push('A');
550 }
551 if matches!(
552 self.get_update_transform(),
553 ShellVariableUpdateTransform::Capitalize
554 ) {
555 result.push('c');
556 }
557 if self.is_treated_as_integer() {
558 result.push('i');
559 }
560 if self.is_treated_as_nameref() {
561 result.push('n');
562 }
563 if self.is_readonly() {
564 result.push('r');
565 }
566 if matches!(
567 self.get_update_transform(),
568 ShellVariableUpdateTransform::Lowercase
569 ) {
570 result.push('l');
571 }
572 if self.is_trace_enabled() {
573 result.push('t');
574 }
575 if matches!(
576 self.get_update_transform(),
577 ShellVariableUpdateTransform::Uppercase
578 ) {
579 result.push('u');
580 }
581 if self.is_exported() {
582 result.push('x');
583 }
584
585 result
586 }
587}
588
589type DynamicValueGetter = fn(&Shell) -> ShellValue;
590type DynamicValueSetter = fn(&Shell) -> ();
591
592#[derive(Clone, Debug)]
594pub enum ShellValue {
595 Unset(ShellValueUnsetType),
597 String(String),
599 AssociativeArray(BTreeMap<String, String>),
601 IndexedArray(BTreeMap<u64, String>),
603 Dynamic {
605 getter: DynamicValueGetter,
607 setter: DynamicValueSetter,
609 },
610}
611
612#[derive(Clone, Debug)]
614pub enum ShellValueUnsetType {
615 Untyped,
617 AssociativeArray,
619 IndexedArray,
621}
622
623#[derive(Clone, Debug)]
625pub enum ShellValueLiteral {
626 Scalar(String),
628 Array(ArrayLiteral),
630}
631
632impl ShellValueLiteral {
633 pub(crate) fn fmt_for_tracing(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
634 match self {
635 Self::Scalar(s) => Self::fmt_scalar_for_tracing(s.as_str(), f),
636 Self::Array(elements) => {
637 write!(f, "(")?;
638 for (i, (key, value)) in elements.0.iter().enumerate() {
639 if i > 0 {
640 write!(f, " ")?;
641 }
642 if let Some(key) = key {
643 write!(f, "[")?;
644 Self::fmt_scalar_for_tracing(key.as_str(), f)?;
645 write!(f, "]=")?;
646 }
647 Self::fmt_scalar_for_tracing(value.as_str(), f)?;
648 }
649 write!(f, ")")
650 }
651 }
652 }
653
654 fn fmt_scalar_for_tracing(s: &str, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
655 let processed = escape::quote_if_needed(s, escape::QuoteMode::SingleQuote);
656 write!(f, "{processed}")
657 }
658}
659
660impl Display for ShellValueLiteral {
661 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
662 self.fmt_for_tracing(f)
663 }
664}
665
666impl From<&str> for ShellValueLiteral {
667 fn from(value: &str) -> Self {
668 Self::Scalar(value.to_owned())
669 }
670}
671
672impl From<String> for ShellValueLiteral {
673 fn from(value: String) -> Self {
674 Self::Scalar(value)
675 }
676}
677
678impl From<Vec<&str>> for ShellValueLiteral {
679 fn from(value: Vec<&str>) -> Self {
680 Self::Array(ArrayLiteral(
681 value.into_iter().map(|s| (None, s.to_owned())).collect(),
682 ))
683 }
684}
685
686#[derive(Clone, Debug)]
688pub struct ArrayLiteral(pub Vec<(Option<String>, String)>);
689
690#[derive(Copy, Clone, Debug)]
692pub enum FormatStyle {
693 Basic,
695 DeclarePrint,
697}
698
699impl ShellValue {
700 pub const fn is_array(&self) -> bool {
702 matches!(
703 self,
704 Self::IndexedArray(_)
705 | Self::AssociativeArray(_)
706 | Self::Unset(
707 ShellValueUnsetType::IndexedArray | ShellValueUnsetType::AssociativeArray
708 )
709 )
710 }
711
712 pub const fn is_set(&self) -> bool {
714 !matches!(self, Self::Unset(_))
715 }
716
717 pub fn indexed_array_from_strings<S>(values: S) -> Self
723 where
724 S: IntoIterator<Item = String>,
725 {
726 let mut owned_values = BTreeMap::new();
727 for (i, value) in values.into_iter().enumerate() {
728 owned_values.insert(i as u64, value);
729 }
730
731 Self::IndexedArray(owned_values)
732 }
733
734 pub fn indexed_array_from_strs(values: &[&str]) -> Self {
740 let mut owned_values = BTreeMap::new();
741 for (i, value) in values.iter().enumerate() {
742 owned_values.insert(i as u64, (*value).to_string());
743 }
744
745 Self::IndexedArray(owned_values)
746 }
747
748 pub fn indexed_array_from_literals(literals: ArrayLiteral) -> Self {
754 let mut values = BTreeMap::new();
755 Self::update_indexed_array_from_literals(&mut values, literals);
756
757 Self::IndexedArray(values)
758 }
759
760 fn update_indexed_array_from_literals(
761 existing_values: &mut BTreeMap<u64, String>,
762 literal_values: ArrayLiteral,
763 ) {
764 let mut new_key = if let Some((largest_index, _)) = existing_values.last_key_value() {
765 largest_index + 1
766 } else {
767 0
768 };
769
770 for (key, value) in literal_values.0 {
771 if let Some(key) = key {
772 new_key = key.parse().unwrap_or(0);
773 }
774
775 existing_values.insert(new_key, value);
776 new_key += 1;
777 }
778 }
779
780 pub fn associative_array_from_literals(literals: ArrayLiteral) -> Result<Self, error::Error> {
786 let mut values = BTreeMap::new();
787 Self::update_associative_array_from_literals(&mut values, literals)?;
788
789 Ok(Self::AssociativeArray(values))
790 }
791
792 fn update_associative_array_from_literals(
793 existing_values: &mut BTreeMap<String, String>,
794 literal_values: ArrayLiteral,
795 ) -> Result<(), error::Error> {
796 let mut current_key = None;
797 for (key, value) in literal_values.0 {
798 if let Some(current_key) = current_key.take() {
799 if key.is_some() {
800 return error::unimp("misaligned keys/values in associative array literal");
801 } else {
802 existing_values.insert(current_key, value);
803 }
804 } else if let Some(key) = key {
805 existing_values.insert(key, value);
806 } else {
807 current_key = Some(value);
808 }
809 }
810
811 if let Some(current_key) = current_key {
812 existing_values.insert(current_key, String::new());
813 }
814
815 Ok(())
816 }
817
818 pub fn format(&self, style: FormatStyle, shell: &Shell) -> Result<Cow<'_, str>, error::Error> {
824 match self {
825 Self::Unset(_) => Ok("".into()),
826 Self::String(s) => match style {
827 FormatStyle::Basic => Ok(escape::quote_if_needed(
828 s.as_str(),
829 escape::QuoteMode::SingleQuote,
830 )),
831 FormatStyle::DeclarePrint => {
832 Ok(escape::force_quote(s.as_str(), escape::QuoteMode::DoubleQuote).into())
833 }
834 },
835 Self::AssociativeArray(values) => {
836 let mut result = String::new();
837 result.push('(');
838
839 for (key, value) in values {
840 let formatted_key =
841 escape::quote_if_needed(key.as_str(), escape::QuoteMode::DoubleQuote);
842 let formatted_value =
843 escape::force_quote(value.as_str(), escape::QuoteMode::DoubleQuote);
844
845 write!(result, "[{formatted_key}]={formatted_value} ")?;
849 }
850
851 result.push(')');
852 Ok(result.into())
853 }
854 Self::IndexedArray(values) => {
855 let mut result = String::new();
856 result.push('(');
857
858 for (i, (key, value)) in values.iter().enumerate() {
859 if i > 0 {
860 result.push(' ');
861 }
862
863 let formatted_value =
864 escape::force_quote(value.as_str(), escape::QuoteMode::DoubleQuote);
865 write!(result, "[{key}]={formatted_value}")?;
866 }
867
868 result.push(')');
869 Ok(result.into())
870 }
871 Self::Dynamic { getter, .. } => {
872 let dynamic_value = getter(shell);
873 let result = dynamic_value.format(style, shell)?.to_string();
874 Ok(result.into())
875 }
876 }
877 }
878
879 pub fn get_at(&self, index: &str, shell: &Shell) -> Result<Option<Cow<'_, str>>, error::Error> {
885 match self {
886 Self::Unset(_) => Ok(None),
887 Self::String(s) => {
888 if index.parse::<u64>().unwrap_or(0) == 0 {
889 Ok(Some(Cow::Borrowed(s)))
890 } else {
891 Ok(None)
892 }
893 }
894 Self::AssociativeArray(values) => {
895 Ok(values.get(index).map(|s| Cow::Borrowed(s.as_str())))
896 }
897 Self::IndexedArray(values) => {
898 let mut index_value = index.parse::<i64>().unwrap_or(0);
899
900 #[allow(clippy::cast_possible_wrap)]
901 if index_value < 0 {
902 index_value += values.len() as i64;
903 if index_value < 0 {
904 return Err(error::Error::ArrayIndexOutOfRange);
905 }
906 }
907
908 #[allow(clippy::cast_sign_loss)]
911 let index_value = index_value as u64;
912
913 Ok(values.get(&index_value).map(|s| Cow::Borrowed(s.as_str())))
914 }
915 Self::Dynamic { getter, .. } => {
916 let dynamic_value = getter(shell);
917 let result = dynamic_value.get_at(index, shell)?;
918 Ok(result.map(|s| s.to_string().into()))
919 }
920 }
921 }
922
923 pub fn get_element_keys(&self, shell: &Shell) -> Vec<String> {
925 match self {
926 Self::Unset(_) => vec![],
927 Self::String(_) => vec!["0".to_owned()],
928 Self::AssociativeArray(array) => array.keys().map(|k| k.to_owned()).collect(),
929 Self::IndexedArray(array) => array.keys().map(|k| k.to_string()).collect(),
930 Self::Dynamic { getter, .. } => getter(shell).get_element_keys(shell),
931 }
932 }
933
934 pub fn get_element_values(&self, shell: &Shell) -> Vec<String> {
936 match self {
937 Self::Unset(_) => vec![],
938 Self::String(s) => vec![s.to_owned()],
939 Self::AssociativeArray(array) => array.values().map(|v| v.to_owned()).collect(),
940 Self::IndexedArray(array) => array.values().map(|v| v.to_owned()).collect(),
941 Self::Dynamic { getter, .. } => getter(shell).get_element_values(shell),
942 }
943 }
944
945 pub fn to_cow_str(&self, shell: &Shell) -> Cow<'_, str> {
947 self.try_get_cow_str(shell).unwrap_or(Cow::Borrowed(""))
948 }
949
950 fn to_cow_str_without_dynamic_support(&self) -> Cow<'_, str> {
951 self.try_get_cow_str_without_dynamic_support()
952 .unwrap_or(Cow::Borrowed(""))
953 }
954
955 pub fn try_get_cow_str(&self, shell: &Shell) -> Option<Cow<'_, str>> {
958 match self {
959 Self::Dynamic { getter, .. } => {
960 let dynamic_value = getter(shell);
961 dynamic_value
962 .try_get_cow_str(shell)
963 .map(|s| s.to_string().into())
964 }
965 _ => self.try_get_cow_str_without_dynamic_support(),
966 }
967 }
968
969 fn try_get_cow_str_without_dynamic_support(&self) -> Option<Cow<'_, str>> {
970 match self {
971 Self::Unset(_) => None,
972 Self::String(s) => Some(Cow::Borrowed(s.as_str())),
973 Self::AssociativeArray(values) => values.get("0").map(|s| Cow::Borrowed(s.as_str())),
974 Self::IndexedArray(values) => values.get(&0).map(|s| Cow::Borrowed(s.as_str())),
975 Self::Dynamic { .. } => None,
976 }
977 }
978
979 pub fn to_assignable_str(&self, index: Option<&str>, shell: &Shell) -> String {
985 match self {
986 Self::Unset(_) => String::new(),
987 Self::String(s) => escape::force_quote(s.as_str(), escape::QuoteMode::SingleQuote),
988 Self::AssociativeArray(_) | Self::IndexedArray(_) => {
989 if let Some(index) = index {
990 if let Ok(Some(value)) = self.get_at(index, shell) {
991 escape::force_quote(value.as_ref(), escape::QuoteMode::SingleQuote)
992 } else {
993 String::new()
994 }
995 } else {
996 self.format(FormatStyle::DeclarePrint, shell)
997 .unwrap()
998 .into_owned()
999 }
1000 }
1001 Self::Dynamic { getter, .. } => getter(shell).to_assignable_str(index, shell),
1002 }
1003 }
1004}
1005
1006impl From<&str> for ShellValue {
1007 fn from(value: &str) -> Self {
1008 Self::String(value.to_owned())
1009 }
1010}
1011
1012impl From<&String> for ShellValue {
1013 fn from(value: &String) -> Self {
1014 Self::String(value.clone())
1015 }
1016}
1017
1018impl From<String> for ShellValue {
1019 fn from(value: String) -> Self {
1020 Self::String(value)
1021 }
1022}
1023
1024impl From<Vec<String>> for ShellValue {
1025 fn from(values: Vec<String>) -> Self {
1026 Self::indexed_array_from_strings(values)
1027 }
1028}
1029
1030impl From<Vec<&str>> for ShellValue {
1031 fn from(values: Vec<&str>) -> Self {
1032 Self::indexed_array_from_strs(values.as_slice())
1033 }
1034}