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}