yash_env/variable/value.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17use either::{Left, Right};
18use itertools::Itertools;
19use std::borrow::Cow;
20
21/// Value of a variable.
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub enum Value {
24 /// Single string.
25 Scalar(String),
26 /// Array of strings.
27 Array(Vec<String>),
28}
29
30use Value::*;
31
32impl Value {
33 /// Creates a scalar value.
34 #[must_use]
35 pub fn scalar<S: Into<String>>(value: S) -> Self {
36 Scalar(value.into())
37 }
38
39 /// Creates an array value.
40 #[must_use]
41 pub fn array<I, S>(values: I) -> Self
42 where
43 I: IntoIterator<Item = S>,
44 S: Into<String>,
45 {
46 Array(values.into_iter().map(Into::into).collect())
47 }
48
49 /// Splits the value by colons.
50 ///
51 /// If this value is `Scalar`, the value is separated at each occurrence of
52 /// colon (`:`). For `Array`, each array item is returned without further
53 /// splitting the value.
54 ///
55 /// ```
56 /// # use yash_env::variable::Value;
57 /// let scalar = Value::scalar("/usr/local/bin:/usr/bin:/bin");
58 /// let values: Vec<&str> = scalar.split().collect();
59 /// assert_eq!(values, ["/usr/local/bin", "/usr/bin", "/bin"]);
60 /// ```
61 ///
62 /// ```
63 /// # use yash_env::variable::Value;
64 /// let array = Value::array(vec!["foo", "bar"]);
65 /// let values: Vec<&str> = array.split().collect();
66 /// assert_eq!(values, ["foo", "bar"]);
67 /// ```
68 pub fn split(&self) -> impl Iterator<Item = &str> {
69 match self {
70 Scalar(value) => Left(value.split(':')),
71 Array(values) => Right(values.iter().map(String::as_str)),
72 }
73 }
74
75 /// Quotes the value in a format suitable for re-parsing.
76 ///
77 /// This function returns a temporary wrapper of `self`. To obtain a string
78 /// representation of the quoted value, you can use the `Display` or
79 /// `Into<Cow<str>>` implementation for the returned object.
80 ///
81 /// See [`yash_quote`] for details of quoting.
82 ///
83 /// ```
84 /// # use yash_env::variable::Value;
85 /// let scalar = Value::scalar("foo bar");
86 /// assert_eq!(scalar.quote().to_string(), "'foo bar'");
87 /// let array = Value::array(vec!["1", "", "'\\'"]);
88 /// assert_eq!(array.quote().to_string(), r#"(1 '' "'\\'")"#);
89 /// ```
90 pub fn quote(&self) -> QuotedValue<'_> {
91 QuotedValue::from(self)
92 }
93}
94
95/// Converts a string into a scalar value.
96impl From<String> for Value {
97 fn from(value: String) -> Self {
98 Scalar(value)
99 }
100}
101
102/// Converts a string slice to a scalar value.
103impl From<&str> for Value {
104 fn from(value: &str) -> Self {
105 Scalar(value.to_owned())
106 }
107}
108
109/// Converts a vector of strings into an array value.
110impl From<Vec<String>> for Value {
111 fn from(values: Vec<String>) -> Self {
112 Array(values)
113 }
114}
115
116/// Wrapper of [`Value`] for [quoting](Value::quote).
117#[derive(Clone, Copy, Debug)]
118pub struct QuotedValue<'a> {
119 value: &'a Value,
120}
121
122/// Writes a quoted version of the value to the formatter.
123impl std::fmt::Display for QuotedValue<'_> {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 match self.value {
126 Scalar(value) => yash_quote::quoted(value).fmt(f),
127 Array(values) => write!(
128 f,
129 "({})",
130 values
131 .iter()
132 .format_with(" ", |value, f| f(&yash_quote::quoted(value)))
133 ),
134 }
135 }
136}
137
138/// Wraps a value in `QuotedValue`.
139impl<'a> From<&'a Value> for QuotedValue<'a> {
140 fn from(value: &'a Value) -> Self {
141 QuotedValue { value }
142 }
143}
144
145/// Extracts the wrapped reference to the value.
146impl AsRef<Value> for QuotedValue<'_> {
147 fn as_ref(&self) -> &Value {
148 self.value
149 }
150}
151
152/// Constructs a quoted string.
153impl<'a> From<QuotedValue<'a>> for Cow<'a, str> {
154 fn from(value: QuotedValue<'a>) -> Self {
155 match value.value {
156 Scalar(value) => yash_quote::quote(value),
157 Array(_values) => value.to_string().into(),
158 }
159 }
160}