yash_env/variable/
main.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
17//! Module that defines the main `Variable` type.
18
19use super::Expansion;
20use super::Quirk;
21use super::Value;
22use crate::source::Location;
23use std::ops::Deref;
24use thiserror::Error;
25
26/// Definition of a variable.
27///
28/// The methods of `Variable` are designed to be used in a method chain,
29/// but you usually don't create a `Variable` instance directly.
30/// Instead, use [`VariableSet::get_or_new`](super::VariableSet::get_or_new) or
31/// [`Env::get_or_create_variable`](crate::Env::get_or_create_variable) to
32/// create a variable in a variable set and obtain a mutable reference to it
33/// ([`VariableRefMut`]), which allows you to modify the variable.
34#[derive(Clone, Debug, Default, Eq, PartialEq)]
35pub struct Variable {
36    /// Value of the variable.
37    ///
38    /// The value is `None` if the variable has been declared without
39    /// assignment.
40    pub value: Option<Value>,
41
42    /// Optional location where this variable was assigned.
43    ///
44    /// If the current variable value originates from an assignment performed in
45    /// the shell session, `last_assigned_location` is the location of the
46    /// assignment.  Otherwise, `last_assigned_location` is `None`.
47    pub last_assigned_location: Option<Location>,
48
49    /// Whether this variable is exported or not.
50    ///
51    /// An exported variable is also referred to as an _environment variable_.
52    pub is_exported: bool,
53
54    /// Optional location where this variable was made read-only.
55    ///
56    /// If this variable is not read-only, `read_only_location` is `None`.
57    /// Otherwise, `read_only_location` is the location of the simple command
58    /// that executed the `readonly` built-in that made this variable read-only.
59    pub read_only_location: Option<Location>,
60
61    /// Special characteristics of the variable
62    ///
63    /// See [`Quirk`] and [`expand`](Self::expand) for details.
64    pub quirk: Option<Quirk>,
65}
66
67impl Variable {
68    /// Creates a new scalar variable from a string.
69    ///
70    /// The returned variable's `last_assigned_location` and
71    /// `read_only_location` are `None` and `is_exported` is false.
72    #[must_use]
73    pub fn new<S: Into<String>>(value: S) -> Self {
74        Variable {
75            value: Some(Value::scalar(value)),
76            ..Default::default()
77        }
78    }
79
80    /// Creates a new array variable from a string.
81    ///
82    /// The returned variable's `last_assigned_location` and
83    /// `read_only_location` are `None` and `is_exported` is false.
84    #[must_use]
85    pub fn new_array<I, S>(values: I) -> Self
86    where
87        I: IntoIterator<Item = S>,
88        S: Into<String>,
89    {
90        Variable {
91            value: Some(Value::array(values)),
92            ..Default::default()
93        }
94    }
95
96    /// Creates a new empty array variable.
97    ///
98    /// The returned variable's `last_assigned_location` and
99    /// `read_only_location` are `None` and `is_exported` is false.
100    #[must_use]
101    pub fn new_empty_array() -> Self {
102        Self::new_array([] as [&str; 0])
103    }
104
105    /// Sets the last assigned location.
106    ///
107    /// This is a convenience function for doing
108    /// `self.last_assigned_location = Some(location)` in a method chain.
109    #[inline]
110    #[must_use]
111    pub fn set_assigned_location(mut self, location: Location) -> Self {
112        self.last_assigned_location = Some(location);
113        self
114    }
115
116    /// Sets the `is_exported` flag.
117    ///
118    /// This is a convenience function for doing `self.is_exported = true` in a
119    /// method chain.
120    #[inline]
121    #[must_use]
122    pub fn export(mut self) -> Self {
123        self.is_exported = true;
124        self
125    }
126
127    /// Makes the variable read-only.
128    ///
129    /// This is a convenience function for doing
130    /// `self.read_only_location = Some(location)` in a method chain.
131    #[inline]
132    #[must_use]
133    pub fn make_read_only(mut self, location: Location) -> Self {
134        self.read_only_location = Some(location);
135        self
136    }
137
138    /// Whether this variable is read-only or not.
139    #[must_use]
140    pub const fn is_read_only(&self) -> bool {
141        self.read_only_location.is_some()
142    }
143
144    /// Returns the value of this variable, applying any quirk.
145    ///
146    /// If this variable has no [`Quirk`], this function just returns
147    /// `self.value` converted to [`Expansion`]. Otherwise, the effect of the
148    /// quirk is applied to the value and the result is returned.
149    ///
150    /// This function requires the location of the parameter expanding this
151    /// variable, so that `Quirk::LineNumber` can yield the line number of the
152    /// location.
153    #[inline]
154    pub fn expand(&self, location: &Location) -> Expansion<'_> {
155        super::quirk::expand(self, location)
156    }
157}
158
159/// Managed mutable reference to a variable.
160///
161/// This type allows you to mutate a variable in a variable set while
162/// maintaining the invariants of the variable set.
163/// To obtain an instance of `VariableRefMut`, use
164/// [`VariableSet::get_or_new`](super::VariableSet::get_or_new).
165#[derive(Debug, Eq, PartialEq)]
166pub struct VariableRefMut<'a>(&'a mut Variable);
167
168/// Error that occurs when assigning a value to a read-only variable.
169#[derive(Clone, Debug, Eq, Error, PartialEq)]
170#[error("cannot assign to read-only variable")]
171pub struct AssignError {
172    /// Value that was being assigned.
173    pub new_value: Value,
174    /// Location of the failed assignment.
175    pub assigned_location: Option<Location>,
176    /// Location where the variable was made read-only.
177    pub read_only_location: Location,
178}
179
180impl<'a> From<&'a mut Variable> for VariableRefMut<'a> {
181    fn from(variable: &'a mut Variable) -> Self {
182        VariableRefMut(variable)
183    }
184}
185
186impl Deref for VariableRefMut<'_> {
187    type Target = Variable;
188
189    fn deref(&self) -> &Variable {
190        self.0
191    }
192}
193
194impl VariableRefMut<'_> {
195    /// Assigns a value to this variable.
196    ///
197    /// The `value` and `location` operands are set to the `value` and
198    /// `last_assigned_location` fields of this variable, respectively.
199    /// If successful, this function returns the previous value and location.
200    ///
201    /// This function fails if this variable is read-only. In that case, the
202    /// error contains the given operands as well as the location where this
203    /// variable was made read-only.
204    #[inline]
205    pub fn assign<V: Into<Value>, L: Into<Option<Location>>>(
206        &mut self,
207        value: V,
208        location: L,
209    ) -> Result<(Option<Value>, Option<Location>), AssignError> {
210        self.assign_impl(value.into(), location.into())
211    }
212
213    fn assign_impl(
214        &mut self,
215        value: Value,
216        location: Option<Location>,
217    ) -> Result<(Option<Value>, Option<Location>), AssignError> {
218        if let Some(read_only_location) = self.0.read_only_location.clone() {
219            return Err(AssignError {
220                new_value: value,
221                assigned_location: location,
222                read_only_location,
223            });
224        }
225
226        let old_value = self.0.value.replace(value);
227        let old_location = std::mem::replace(&mut self.0.last_assigned_location, location);
228        Ok((old_value, old_location))
229        // TODO Apply quirk
230    }
231
232    /// Sets whether this variable is exported or not.
233    pub fn export(&mut self, is_exported: bool) {
234        self.0.is_exported = is_exported;
235    }
236
237    /// Makes this variable read-only.
238    ///
239    /// The `location` operand is set to the `read_only_location` field of this
240    /// variable unless this variable is already read-only.
241    pub fn make_read_only(&mut self, location: Location) {
242        self.0.read_only_location.get_or_insert(location);
243    }
244
245    /// Sets the quirk of this variable.
246    ///
247    /// This function overwrites any existing quirk of this variable.
248    pub fn set_quirk(&mut self, quirk: Option<Quirk>) {
249        self.0.quirk = quirk;
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[test]
258    fn assigning_values() {
259        let mut var = Variable::default();
260        let mut var = VariableRefMut::from(&mut var);
261        let result = var.assign(Value::scalar("foo value"), None);
262        assert_eq!(result, Ok((None, None)));
263        assert_eq!(*var, Variable::new("foo value"));
264
265        let location = Location::dummy("bar location");
266        let result = var.assign(Value::scalar("bar value"), location.clone());
267        assert_eq!(result, Ok((Some(Value::scalar("foo value")), None)));
268        assert_eq!(var.value, Some(Value::scalar("bar value")));
269        assert_eq!(var.last_assigned_location.as_ref(), Some(&location));
270
271        assert_eq!(
272            var.assign(Value::array(["a", "b", "c"]), None),
273            Ok((Some(Value::scalar("bar value")), Some(location))),
274        );
275        assert_eq!(var.value, Some(Value::array(["a", "b", "c"])));
276    }
277
278    #[test]
279    fn exporting() {
280        let mut var = Variable::default();
281        let mut var = VariableRefMut::from(&mut var);
282        assert!(!var.is_exported);
283        var.export(true);
284        assert!(var.is_exported);
285        var.export(false);
286        assert!(!var.is_exported);
287    }
288
289    #[test]
290    fn making_variables_read_only() {
291        let mut var = Variable::default();
292        let mut var = VariableRefMut::from(&mut var);
293        let location = Location::dummy("read-only location");
294        var.make_read_only(location.clone());
295        assert_eq!(var.read_only_location.as_ref(), Some(&location));
296
297        var.make_read_only(Location::dummy("ignored location"));
298        assert_eq!(var.read_only_location.as_ref(), Some(&location));
299    }
300
301    #[test]
302    fn assigning_to_readonly_variable() {
303        let mut var = Variable::default();
304        let mut var = VariableRefMut::from(&mut var);
305        let assigned_location = Some(Location::dummy("assigned location"));
306        let read_only_location = Location::dummy("read-only location");
307        var.make_read_only(read_only_location.clone());
308        assert_eq!(
309            var.assign(Value::scalar("foo value"), assigned_location.clone()),
310            Err(AssignError {
311                new_value: Value::scalar("foo value"),
312                assigned_location,
313                read_only_location,
314            })
315        )
316    }
317}