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}