yash_env/variable/
value.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// This file is part of yash, an extended POSIX shell.
// Copyright (C) 2021 WATANABE Yuki
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use either::{Left, Right};
use itertools::Itertools;
use std::borrow::Cow;

/// Value of a variable.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Value {
    /// Single string.
    Scalar(String),
    /// Array of strings.
    Array(Vec<String>),
}

use Value::*;

impl Value {
    /// Creates a scalar value.
    #[must_use]
    pub fn scalar<S: Into<String>>(value: S) -> Self {
        Scalar(value.into())
    }

    /// Creates an array value.
    #[must_use]
    pub fn array<I, S>(values: I) -> Self
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        Array(values.into_iter().map(Into::into).collect())
    }

    /// Splits the value by colons.
    ///
    /// If this value is `Scalar`, the value is separated at each occurrence of
    /// colon (`:`). For `Array`, each array item is returned without further
    /// splitting the value.
    ///
    /// ```
    /// # use yash_env::variable::Value;
    /// let scalar = Value::scalar("/usr/local/bin:/usr/bin:/bin");
    /// let values: Vec<&str> = scalar.split().collect();
    /// assert_eq!(values, ["/usr/local/bin", "/usr/bin", "/bin"]);
    /// ```
    ///
    /// ```
    /// # use yash_env::variable::Value;
    /// let array = Value::array(vec!["foo", "bar"]);
    /// let values: Vec<&str> = array.split().collect();
    /// assert_eq!(values, ["foo", "bar"]);
    /// ```
    pub fn split(&self) -> impl Iterator<Item = &str> {
        match self {
            Scalar(value) => Left(value.split(':')),
            Array(values) => Right(values.iter().map(String::as_str)),
        }
    }

    /// Quotes the value in a format suitable for re-parsing.
    ///
    /// This function returns a temporary wrapper of `self`. To obtain a string
    /// representation of the quoted value, you can use the `Display` or
    /// `Into<Cow<str>>` implementation for the returned object.
    ///
    /// See [`yash_quote`] for details of quoting.
    ///
    /// ```
    /// # use yash_env::variable::Value;
    /// let scalar = Value::scalar("foo bar");
    /// assert_eq!(scalar.quote().to_string(), "'foo bar'");
    /// let array = Value::array(vec!["1", "", "'\\'"]);
    /// assert_eq!(array.quote().to_string(), r#"(1 '' "'\\'")"#);
    /// ```
    pub fn quote(&self) -> QuotedValue {
        QuotedValue::from(self)
    }
}

/// Converts a string into a scalar value.
impl From<String> for Value {
    fn from(value: String) -> Self {
        Scalar(value)
    }
}

/// Converts a string slice to a scalar value.
impl From<&str> for Value {
    fn from(value: &str) -> Self {
        Scalar(value.to_owned())
    }
}

/// Converts a vector of strings into an array value.
impl From<Vec<String>> for Value {
    fn from(values: Vec<String>) -> Self {
        Array(values)
    }
}

/// Wrapper of [`Value`] for [quoting](Value::quote).
#[derive(Clone, Copy, Debug)]
pub struct QuotedValue<'a> {
    value: &'a Value,
}

/// Writes a quoted version of the value to the formatter.
impl std::fmt::Display for QuotedValue<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.value {
            Scalar(value) => yash_quote::quoted(value).fmt(f),
            Array(values) => write!(
                f,
                "({})",
                values
                    .iter()
                    .format_with(" ", |value, f| f(&yash_quote::quoted(value)))
            ),
        }
    }
}

/// Wraps a value in `QuotedValue`.
impl<'a> From<&'a Value> for QuotedValue<'a> {
    fn from(value: &'a Value) -> Self {
        QuotedValue { value }
    }
}

/// Extracts the wrapped reference to the value.
impl AsRef<Value> for QuotedValue<'_> {
    fn as_ref(&self) -> &Value {
        self.value
    }
}

/// Constructs a quoted string.
impl<'a> From<QuotedValue<'a>> for Cow<'a, str> {
    fn from(value: QuotedValue<'a>) -> Self {
        match value.value {
            Scalar(value) => yash_quote::quote(value),
            Array(_values) => value.to_string().into(),
        }
    }
}