yash_semantics/expansion/
phrase.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2022 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//! Array of fields as an intermediate expansion result
18//!
19//! This module defines [`Phrase`], a data type for holding intermediate
20//! expansion results. A phrase is an array of (optional, possibly zero) fields,
21//! and a field is a string of attributed characters ([`AttrChar`]).
22//!
23//! The most general form of a phrase is represented by `Vec<Vec<AttrChar>>`.
24//! However, many expansion results in a phrase of only one field. The `Phrase`
25//! type can represent a single-field phrase by only holding a `Vec<AttrChar>`
26//! or `AttrChar` instance to reduce memory allocation overheads.
27//!
28//! You can join two or more phrases into a single by using the
29//! [`append`](Phrase::append) method and the `+` and `+=` operators. Phrase
30//! concatenation is not the same as appending a `Vec<Vec<AttrChar>>` to
31//! another. When phrases are joined, the last field of the first phrase and the
32//! first field of the second phrase are concatenated while other fields remain
33//! intact.
34
35use crate::expansion::attr::AttrChar;
36use crate::expansion::attr::Origin;
37use std::iter::FusedIterator;
38use std::ops::Add;
39use std::ops::AddAssign;
40use yash_env::variable::IFS;
41use yash_env::variable::Value;
42use yash_env::variable::VariableSet;
43
44/// Array of fields with optimized data structure
45///
46/// See the [module documentation](self).
47#[derive(Clone, Debug, Eq)]
48pub enum Phrase {
49    /// Phrase having one field containing one character
50    ///
51    /// `Phrase::Char(c)` is equivalent to `Phrase::Field(vec![c])`, which in
52    /// turn equals `Phrase::Full(vec![vec![c]])`.
53    Char(AttrChar),
54    /// Phrase made up of one field
55    ///
56    /// `Phrase::Field(chars)` is equivalent to `Phrase::Full(vec![chars])`.
57    Field(Vec<AttrChar>),
58    /// Phrase containing any number of fields
59    Full(Vec<Vec<AttrChar>>),
60}
61
62use Phrase::*;
63
64impl PartialEq for Phrase {
65    fn eq(&self, other: &Phrase) -> bool {
66        match (self, other) {
67            (Char(left), Char(right)) => left == right,
68            (Field(left), Field(right)) => left == right,
69            (Full(left), Full(right)) => left == right,
70            (Char(c), Field(f)) | (Field(f), Char(c)) => [*c].as_slice() == f.as_slice(),
71            (Char(c), Full(v)) | (Full(v), Char(c)) => {
72                matches!(v.as_slice(), [f] if [*c].as_slice() == f.as_slice())
73            }
74            (Field(f), Full(v)) | (Full(v), Field(f)) => {
75                matches!(v.as_slice(), [fv] if f == fv)
76            }
77        }
78    }
79}
80
81impl Phrase {
82    /// Returns a phrase containing no fields.
83    ///
84    /// This function requires no heap allocation.
85    #[inline]
86    #[must_use]
87    pub fn zero_fields() -> Self {
88        Full(Vec::new())
89    }
90
91    /// Returns a phrase containing one empty field.
92    ///
93    /// This function requires no heap allocation.
94    #[inline]
95    #[must_use]
96    pub fn one_empty_field() -> Self {
97        Field(Vec::new())
98    }
99
100    /// Tests whether the phrase has no fields.
101    #[must_use]
102    pub fn is_zero_fields(&self) -> bool {
103        matches!(self, Full(fields) if fields.is_empty())
104    }
105
106    /// Returns the number of fields in the phrase.
107    #[must_use]
108    pub fn field_count(&self) -> usize {
109        match self {
110            Char(_) | Field(_) => 1,
111            Full(fields) => fields.len(),
112        }
113    }
114
115    /// Moves all fields of `other` into `self`, leaving `other` empty.
116    ///
117    /// This function joins two phrases into one. It concatenates the last field
118    /// of `self` and the first field of `other`. Other fields are left intact
119    /// in the new phrase.
120    ///
121    /// ```
122    /// # use yash_semantics::expansion::{attr::{AttrChar, Origin}, phrase::Phrase};
123    /// # let a = AttrChar {
124    /// #     value: 'a',
125    /// #     origin: Origin::Literal,
126    /// #     is_quoted: false,
127    /// #     is_quoting: false,
128    /// # };
129    /// # let b = AttrChar { value: 'b', ..a };
130    /// # let c = AttrChar { value: 'c', ..a };
131    /// # let d = AttrChar { value: 'd', ..a };
132    /// # let e = AttrChar { value: 'e', ..a };
133    /// # let f = AttrChar { value: 'f', ..a };
134    /// let mut left = Phrase::Full(vec![vec![a], vec![b], vec![c]]);
135    /// let mut right = Phrase::Full(vec![vec![d], vec![e], vec![f]]);
136    /// left.append(&mut right);
137    /// assert_eq!(
138    ///     left,
139    ///     Phrase::Full(vec![vec![a], vec![b], vec![c, d], vec![e], vec![f]])
140    /// );
141    /// assert!(right.is_zero_fields());
142    /// ```
143    ///
144    /// That implies joining two single-field phrases results in another
145    /// single-field phrase.
146    ///
147    /// ```
148    /// # use yash_semantics::expansion::{attr::{AttrChar, Origin}, phrase::Phrase};
149    /// # let a = AttrChar {
150    /// #     value: 'a',
151    /// #     origin: Origin::Literal,
152    /// #     is_quoted: false,
153    /// #     is_quoting: false,
154    /// # };
155    /// # let b = AttrChar { value: 'b', ..a };
156    /// # let c = AttrChar { value: 'c', ..a };
157    /// # let d = AttrChar { value: 'd', ..a };
158    /// # let e = AttrChar { value: 'e', ..a };
159    /// # let f = AttrChar { value: 'f', ..a };
160    /// let mut left = Phrase::Field(vec![a, b, c]);
161    /// let mut right = Phrase::Field(vec![d, e, f]);
162    /// left.append(&mut right);
163    /// assert_eq!(left, Phrase::Field(vec![a, b, c, d, e, f]));
164    /// assert!(right.is_zero_fields());
165    /// ```
166    ///
167    /// ```
168    /// # use yash_semantics::expansion::{attr::{AttrChar, Origin}, phrase::Phrase};
169    /// # let a = AttrChar {
170    /// #     value: 'a',
171    /// #     origin: Origin::Literal,
172    /// #     is_quoted: false,
173    /// #     is_quoting: false,
174    /// # };
175    /// # let b = AttrChar { value: 'b', ..a };
176    /// let mut left = Phrase::Char(a);
177    /// let mut right = Phrase::Char(b);
178    /// left.append(&mut right);
179    /// assert_eq!(left, Phrase::Field(vec![a, b]));
180    /// assert!(right.is_zero_fields());
181    /// ```
182    ///
183    /// ```
184    /// # use yash_semantics::expansion::phrase::Phrase;
185    /// let mut left = Phrase::one_empty_field();
186    /// let mut right = Phrase::one_empty_field();
187    /// left.append(&mut right);
188    /// assert_eq!(left, Phrase::one_empty_field());
189    /// assert_eq!(right, Phrase::zero_fields());
190    /// ```
191    ///
192    /// If either phrase is zero fields, the result is the other.
193    pub fn append(&mut self, other: &mut Phrase) {
194        match (&mut *self, &mut *other) {
195            (Char(left), Char(right)) => {
196                *self = Field(vec![*left, *right]);
197                *other = Phrase::zero_fields();
198            }
199            (Char(left), Field(right)) => {
200                right.insert(0, *left);
201                *self = std::mem::replace(other, Phrase::zero_fields());
202            }
203            (Field(left), Char(right)) => {
204                left.push(*right);
205                *other = Phrase::zero_fields();
206            }
207            (Field(left), Field(right)) => {
208                left.append(right);
209                *other = Phrase::zero_fields();
210            }
211            (left, Full(right)) => {
212                if let Some(right_first) = right.first_mut() {
213                    match left {
214                        Char(left) => {
215                            right_first.insert(0, *left);
216                            *self = std::mem::replace(other, Phrase::zero_fields());
217                        }
218                        Field(left) => {
219                            left.append(right_first);
220                            std::mem::swap(left, right_first);
221                            *self = std::mem::replace(other, Phrase::zero_fields());
222                        }
223                        Full(left) => {
224                            if let Some(left_last) = left.last_mut() {
225                                left_last.append(right_first);
226                                left.extend(right.drain(1..));
227                                right.clear();
228                            } else {
229                                std::mem::swap(left, right);
230                            }
231                        }
232                    }
233                } else {
234                    // Nothing to do
235                }
236            }
237            (Full(left), right) => {
238                if let Some(left_last) = left.last_mut() {
239                    match right {
240                        Char(right) => left_last.push(*right),
241                        Field(right) => left_last.append(right),
242                        Full(_right) => unreachable!(),
243                    }
244                    *other = Phrase::zero_fields();
245                } else {
246                    std::mem::swap(self, other);
247                }
248            }
249        }
250    }
251
252    /// Joins this phrase into a single field, separated by the first IFS character.
253    ///
254    /// This function joins `self` into a single field, separating each original
255    /// field by the first character of variable `IFS`. If the variable is not
256    /// set, fields are separated by a space. If the variable is set but has an
257    /// empty value, fields are joined without separation.
258    pub fn ifs_join(self, vars: &VariableSet) -> Vec<AttrChar> {
259        match self {
260            Char(c) => vec![c],
261            Field(field) => field,
262            Full(mut fields) => match fields.len() {
263                0 => vec![],
264                1 => fields.swap_remove(0),
265                _ => {
266                    let separator = match vars.get(IFS).and_then(|v| v.value.as_ref()) {
267                        Some(Value::Scalar(value)) => value.chars().next(),
268                        Some(Value::Array(values)) => {
269                            values.first().and_then(|value| value.chars().next())
270                        }
271                        None => Some(' '),
272                    }
273                    .map(|c| AttrChar {
274                        value: c,
275                        origin: Origin::SoftExpansion,
276                        is_quoted: false,
277                        is_quoting: false,
278                    });
279
280                    let mut i = fields.into_iter();
281                    let mut result = i.next().unwrap();
282                    result.reserve_exact(
283                        i.as_slice().iter().map(|field| field.len()).sum::<usize>()
284                            + i.as_slice().len(),
285                    );
286                    for field in i {
287                        if let Some(separator) = separator {
288                            result.push(separator);
289                        }
290                        result.extend(field);
291                    }
292                    result
293                }
294            },
295        }
296    }
297
298    /// Applies a function to every character in the phrase.
299    pub fn for_each_char_mut<F>(&mut self, mut f: F)
300    where
301        F: FnMut(&mut AttrChar),
302    {
303        match self {
304            Char(c) => f(c),
305            Field(field) => field.iter_mut().for_each(f),
306            Full(fields) => fields.iter_mut().flatten().for_each(f),
307        }
308    }
309}
310
311impl From<AttrChar> for Phrase {
312    #[inline]
313    fn from(c: AttrChar) -> Self {
314        Char(c)
315    }
316}
317
318impl From<Vec<AttrChar>> for Phrase {
319    #[inline]
320    fn from(chars: Vec<AttrChar>) -> Self {
321        Field(chars)
322    }
323}
324
325impl From<Vec<Vec<AttrChar>>> for Phrase {
326    #[inline]
327    fn from(fields: Vec<Vec<AttrChar>>) -> Self {
328        Full(fields)
329    }
330}
331
332impl From<Phrase> for Vec<Vec<AttrChar>> {
333    fn from(phrase: Phrase) -> Self {
334        match phrase {
335            Char(c) => vec![vec![c]],
336            Field(f) => vec![f],
337            Full(v) => v,
338        }
339    }
340}
341
342/// Private implementation detail of [`IntoIter`]
343#[derive(Clone, Debug)]
344enum IntoIterState {
345    None,
346    Char(AttrChar),
347    Field(Vec<AttrChar>),
348    Full(std::vec::IntoIter<Vec<AttrChar>>),
349}
350
351/// Iterator of fields
352///
353/// You can turn a [`Phrase`] into an iterator by
354/// [`into_iter`](Phrase::into_iter).
355#[derive(Clone, Debug)]
356pub struct IntoIter(IntoIterState);
357
358impl Iterator for IntoIter {
359    type Item = Vec<AttrChar>;
360    fn next(&mut self) -> Option<Vec<AttrChar>> {
361        match &mut self.0 {
362            IntoIterState::None => None,
363            IntoIterState::Char(c) => {
364                let f = vec![*c];
365                self.0 = IntoIterState::None;
366                Some(f)
367            }
368            IntoIterState::Field(f) => {
369                let f = std::mem::take(f);
370                self.0 = IntoIterState::None;
371                Some(f)
372            }
373            IntoIterState::Full(i) => i.next(),
374        }
375    }
376
377    fn size_hint(&self) -> (usize, Option<usize>) {
378        match &self.0 {
379            IntoIterState::None => (0, Some(0)),
380            IntoIterState::Char(_) | IntoIterState::Field(_) => (1, Some(1)),
381            IntoIterState::Full(i) => i.size_hint(),
382        }
383    }
384
385    #[inline]
386    fn count(self) -> usize {
387        self.len()
388    }
389}
390
391impl DoubleEndedIterator for IntoIter {
392    fn next_back(&mut self) -> Option<Vec<AttrChar>> {
393        match &mut self.0 {
394            IntoIterState::None => None,
395            IntoIterState::Char(c) => {
396                let f = vec![*c];
397                self.0 = IntoIterState::None;
398                Some(f)
399            }
400            IntoIterState::Field(f) => {
401                let f = std::mem::take(f);
402                self.0 = IntoIterState::None;
403                Some(f)
404            }
405            IntoIterState::Full(i) => i.next_back(),
406        }
407    }
408}
409
410impl ExactSizeIterator for IntoIter {}
411
412impl FusedIterator for IntoIter {}
413
414impl IntoIterator for Phrase {
415    type Item = Vec<AttrChar>;
416    type IntoIter = IntoIter;
417    fn into_iter(self) -> IntoIter {
418        IntoIter(match self {
419            Char(c) => IntoIterState::Char(c),
420            Field(f) => IntoIterState::Field(f),
421            Full(f) => IntoIterState::Full(f.into_iter()),
422        })
423    }
424}
425
426// We do not implement FromIterator or Extend for Phrase because their behavior
427// would be confusing compared with that of AddAssign and Add.
428// You should directly manipulate Vec<Vec<AttrChar>>.
429
430/// See [`Phrase::append`].
431impl AddAssign for Phrase {
432    fn add_assign(&mut self, mut other: Phrase) {
433        self.append(&mut other)
434    }
435}
436
437/// See [`Phrase::append`].
438impl Add for Phrase {
439    type Output = Phrase;
440    #[inline]
441    fn add(mut self, other: Phrase) -> Self {
442        self.add_assign(other);
443        self
444    }
445}
446
447// We do not implement std::iter::Sum for Phrase because it is not obvious
448// what the sum of an empty iterator should be.
449
450#[cfg(test)]
451mod tests {
452    use super::*;
453    use yash_env::variable::Scope;
454
455    #[test]
456    fn partial_eq() {
457        let c1 = AttrChar {
458            value: 'a',
459            origin: Origin::Literal,
460            is_quoted: false,
461            is_quoting: false,
462        };
463        let c2 = AttrChar { value: 'b', ..c1 };
464        let f0 = vec![];
465        let f1 = vec![c1];
466        let f2 = vec![c2];
467        let f3 = vec![c1, c2];
468        let ve = vec![];
469        let v0 = vec![f0.clone()];
470        let v1 = vec![f1.clone()];
471        let v2 = vec![f2.clone()];
472        let v3 = vec![f3.clone()];
473        let v4 = vec![f1.clone(), f2.clone()];
474
475        let c1 = Char(c1);
476        let c2 = Char(c2);
477        let f0 = Field(f0);
478        let f1 = Field(f1);
479        let f2 = Field(f2);
480        let f3 = Field(f3);
481        let ve = Full(ve);
482        let v0 = Full(v0);
483        let v1 = Full(v1);
484        let v2 = Full(v2);
485        let v3 = Full(v3);
486        let v4 = Full(v4);
487
488        assert_eq!(c1, c1);
489        assert_eq!(c2, c2);
490        assert_ne!(c1, c2);
491        assert_ne!(c2, c1);
492
493        assert_eq!(f0, f0);
494        assert_eq!(f1, f1);
495        assert_eq!(f3, f3);
496        assert_ne!(f0, f1);
497        assert_ne!(f1, f2);
498        assert_ne!(f2, f3);
499        assert_ne!(f3, f0);
500
501        assert_eq!(ve, ve);
502        assert_eq!(v2, v2);
503        assert_eq!(v4, v4);
504        assert_ne!(ve, v0);
505        assert_ne!(v0, v1);
506        assert_ne!(v1, v2);
507        assert_ne!(v2, v3);
508        assert_ne!(v3, v4);
509        assert_ne!(v4, ve);
510
511        assert_eq!(c1, f1);
512        assert_eq!(c2, f2);
513        assert_ne!(c1, f2);
514        assert_ne!(c2, f3);
515        assert_ne!(c2, f0);
516
517        assert_eq!(f1, c1);
518        assert_eq!(f2, c2);
519        assert_ne!(f2, c1);
520        assert_ne!(f3, c2);
521        assert_ne!(f0, c2);
522
523        assert_eq!(c1, v1);
524        assert_eq!(c2, v2);
525        assert_ne!(c1, v2);
526        assert_ne!(c2, v3);
527        assert_ne!(c1, v4);
528        assert_ne!(c2, ve);
529
530        assert_eq!(v1, c1);
531        assert_eq!(v2, c2);
532        assert_ne!(v2, c1);
533        assert_ne!(v3, c2);
534        assert_ne!(v4, c1);
535        assert_ne!(ve, c2);
536
537        assert_eq!(f0, v0);
538        assert_eq!(f1, v1);
539        assert_eq!(f2, v2);
540        assert_eq!(f3, v3);
541        assert_ne!(f0, ve);
542        assert_ne!(f1, v0);
543        assert_ne!(f2, v1);
544        assert_ne!(f3, v2);
545        assert_ne!(f3, v4);
546
547        assert_eq!(v0, f0);
548        assert_eq!(v1, f1);
549        assert_eq!(v2, f2);
550        assert_eq!(v3, f3);
551        assert_ne!(ve, f0);
552        assert_ne!(v0, f1);
553        assert_ne!(v1, f2);
554        assert_ne!(v2, f3);
555        assert_ne!(v4, f3);
556    }
557
558    #[test]
559    fn is_zero_fields() {
560        let c = AttrChar {
561            value: 'a',
562            origin: Origin::Literal,
563            is_quoted: false,
564            is_quoting: false,
565        };
566
567        assert!(Full(vec![]).is_zero_fields());
568
569        assert!(!Char(c).is_zero_fields());
570        assert!(!Field(vec![]).is_zero_fields());
571        assert!(!Field(vec![c]).is_zero_fields());
572        assert!(!Field(vec![c, c]).is_zero_fields());
573        assert!(!Full(vec![vec![]]).is_zero_fields());
574        assert!(!Full(vec![vec![c]]).is_zero_fields());
575        assert!(!Full(vec![vec![], vec![]]).is_zero_fields());
576    }
577
578    #[test]
579    fn field_count() {
580        let c = AttrChar {
581            value: 'a',
582            origin: Origin::Literal,
583            is_quoted: false,
584            is_quoting: false,
585        };
586        assert_eq!(Char(c).field_count(), 1);
587        assert_eq!(Field(vec![c]).field_count(), 1);
588        assert_eq!(Phrase::zero_fields().field_count(), 0);
589        assert_eq!(Phrase::one_empty_field().field_count(), 1);
590        assert_eq!(Full(vec![vec![c]]).field_count(), 1);
591        assert_eq!(Full(vec![vec![c, c]]).field_count(), 1);
592        assert_eq!(Full(vec![vec![c], vec![c]]).field_count(), 2);
593    }
594
595    #[test]
596    fn into_iter_size_hint() {
597        let c = AttrChar {
598            value: 'x',
599            origin: Origin::Literal,
600            is_quoted: false,
601            is_quoting: false,
602        };
603
604        let mut i = Char(c).into_iter();
605        assert_eq!(i.size_hint(), (1, Some(1)));
606        i.next();
607        assert_eq!(i.size_hint(), (0, Some(0)));
608
609        let mut i = Field(vec![c, c]).into_iter();
610        assert_eq!(i.size_hint(), (1, Some(1)));
611        i.next();
612        assert_eq!(i.size_hint(), (0, Some(0)));
613
614        let mut i = Full(vec![vec![c], vec![c, c, c], vec![c, c]]).into_iter();
615        assert_eq!(i.size_hint(), (3, Some(3)));
616        i.next();
617        assert_eq!(i.size_hint(), (2, Some(2)));
618        i.next();
619        assert_eq!(i.size_hint(), (1, Some(1)));
620        i.next();
621        assert_eq!(i.size_hint(), (0, Some(0)));
622    }
623
624    #[test]
625    fn into_iter_count() {
626        let c = AttrChar {
627            value: 'x',
628            origin: Origin::Literal,
629            is_quoted: false,
630            is_quoting: false,
631        };
632
633        let i = Char(c).into_iter();
634        assert_eq!(i.count(), 1);
635
636        let mut i = Char(c).into_iter();
637        i.next();
638        assert_eq!(i.count(), 0);
639
640        let i = Field(vec![c, c]).into_iter();
641        assert_eq!(i.count(), 1);
642
643        let mut i = Field(vec![c, c]).into_iter();
644        i.next();
645        assert_eq!(i.count(), 0);
646
647        let mut i = Full(vec![vec![c], vec![c, c, c], vec![c, c]]).into_iter();
648        i.next();
649        assert_eq!(i.count(), 2);
650
651        let mut i = Full(vec![vec![c], vec![c, c, c], vec![c, c]]).into_iter();
652        i.next();
653        i.next();
654        i.next();
655        assert_eq!(i.count(), 0);
656    }
657
658    #[test]
659    fn into_iter_fused() {
660        let c = AttrChar {
661            value: 'x',
662            origin: Origin::Literal,
663            is_quoted: false,
664            is_quoting: false,
665        };
666
667        let mut i = Char(c).into_iter();
668        assert_eq!(i.next(), Some(vec![c]));
669        assert_eq!(i.next(), None);
670        assert_eq!(i.next(), None);
671
672        let mut i = Field(vec![c, c]).into_iter();
673        assert_eq!(i.next(), Some(vec![c, c]));
674        assert_eq!(i.next(), None);
675        assert_eq!(i.next(), None);
676
677        let mut i = Full(vec![vec![c], vec![c, c, c], vec![c, c]]).into_iter();
678        assert_eq!(i.next(), Some(vec![c]));
679        assert_eq!(i.next(), Some(vec![c, c, c]));
680        assert_eq!(i.next(), Some(vec![c, c]));
681        assert_eq!(i.next(), None);
682        assert_eq!(i.next(), None);
683    }
684
685    #[test]
686    fn into_iter_back_fused() {
687        let c = AttrChar {
688            value: 'x',
689            origin: Origin::Literal,
690            is_quoted: false,
691            is_quoting: false,
692        };
693
694        let mut i = Char(c).into_iter();
695        assert_eq!(i.next_back(), Some(vec![c]));
696        assert_eq!(i.next_back(), None);
697        assert_eq!(i.next_back(), None);
698
699        let mut i = Field(vec![c, c]).into_iter();
700        assert_eq!(i.next_back(), Some(vec![c, c]));
701        assert_eq!(i.next_back(), None);
702        assert_eq!(i.next_back(), None);
703
704        let mut i = Full(vec![vec![c], vec![c, c, c], vec![c, c]]).into_iter();
705        assert_eq!(i.next_back(), Some(vec![c, c]));
706        assert_eq!(i.next_back(), Some(vec![c, c, c]));
707        assert_eq!(i.next_back(), Some(vec![c]));
708        assert_eq!(i.next_back(), None);
709        assert_eq!(i.next_back(), None);
710    }
711
712    #[test]
713    fn append_empty_empty() {
714        let mut left = Phrase::zero_fields();
715        let mut right = Phrase::zero_fields();
716        left.append(&mut right);
717        assert_eq!(left, Phrase::zero_fields());
718        assert_eq!(right, Phrase::zero_fields());
719
720        let mut left = Phrase::one_empty_field();
721        let mut right = Phrase::zero_fields();
722        left.append(&mut right);
723        assert_eq!(left, Phrase::one_empty_field());
724        assert_eq!(right, Phrase::zero_fields());
725
726        let mut left = Phrase::zero_fields();
727        let mut right = Phrase::one_empty_field();
728        left.append(&mut right);
729        assert_eq!(left, Phrase::one_empty_field());
730        assert_eq!(right, Phrase::zero_fields());
731
732        // See also doc test for Phrase::append
733    }
734
735    #[test]
736    fn append_empty_char() {
737        let phrase = Char(AttrChar {
738            value: 'a',
739            origin: Origin::Literal,
740            is_quoted: false,
741            is_quoting: false,
742        });
743        for mut left in [
744            Phrase::zero_fields(),
745            Phrase::one_empty_field(),
746            Full(vec![]),
747            Full(vec![vec![]]),
748        ] {
749            let mut right = phrase.clone();
750            left.append(&mut right);
751            assert_eq!(left, phrase);
752            assert_eq!(right, Phrase::zero_fields());
753        }
754    }
755
756    #[test]
757    fn append_char_empty() {
758        let phrase = Char(AttrChar {
759            value: 'a',
760            origin: Origin::Literal,
761            is_quoted: false,
762            is_quoting: false,
763        });
764        for mut right in [
765            Phrase::zero_fields(),
766            Phrase::one_empty_field(),
767            Full(vec![]),
768            Full(vec![vec![]]),
769        ] {
770            let mut left = phrase.clone();
771            left.append(&mut right);
772            assert_eq!(left, phrase);
773            assert_eq!(right, Phrase::zero_fields());
774        }
775    }
776
777    // See the doc test in Phrase::append
778    // #[test]
779    // fn append_char_char() {}
780
781    #[test]
782    fn append_empty_field() {
783        let a = AttrChar {
784            value: 'a',
785            origin: Origin::Literal,
786            is_quoted: false,
787            is_quoting: false,
788        };
789        let b = AttrChar { value: 'b', ..a };
790        let c = AttrChar { value: 'c', ..a };
791        let phrase = Field(vec![a, b, c]);
792        for mut left in [
793            Phrase::zero_fields(),
794            Phrase::one_empty_field(),
795            Full(vec![]),
796            Full(vec![vec![]]),
797        ] {
798            let mut right = phrase.clone();
799            left.append(&mut right);
800            assert_eq!(left, phrase);
801            assert_eq!(right, Phrase::zero_fields());
802        }
803    }
804
805    #[test]
806    fn append_field_empty() {
807        let a = AttrChar {
808            value: 'a',
809            origin: Origin::Literal,
810            is_quoted: false,
811            is_quoting: false,
812        };
813        let b = AttrChar { value: 'b', ..a };
814        let c = AttrChar { value: 'c', ..a };
815        let phrase = Field(vec![a, b, c]);
816        for mut right in [
817            Phrase::zero_fields(),
818            Phrase::one_empty_field(),
819            Full(vec![]),
820            Full(vec![vec![]]),
821        ] {
822            let mut left = phrase.clone();
823            left.append(&mut right);
824            assert_eq!(left, phrase);
825            assert_eq!(right, Phrase::zero_fields());
826        }
827    }
828
829    #[test]
830    fn append_char_field() {
831        let a = AttrChar {
832            value: 'a',
833            origin: Origin::Literal,
834            is_quoted: false,
835            is_quoting: false,
836        };
837        let b = AttrChar { value: 'b', ..a };
838        let c = AttrChar { value: 'c', ..a };
839        let mut left = Char(a);
840        let mut right = Field(vec![b, c]);
841        left.append(&mut right);
842        assert_eq!(left, Field(vec![a, b, c]));
843        assert_eq!(right, Phrase::zero_fields());
844    }
845
846    #[test]
847    fn append_field_char() {
848        let a = AttrChar {
849            value: 'a',
850            origin: Origin::Literal,
851            is_quoted: false,
852            is_quoting: false,
853        };
854        let b = AttrChar { value: 'b', ..a };
855        let c = AttrChar { value: 'c', ..a };
856        let mut left = Field(vec![a, b]);
857        let mut right = Char(c);
858        left.append(&mut right);
859        assert_eq!(left, Field(vec![a, b, c]));
860        assert_eq!(right, Phrase::zero_fields());
861    }
862
863    // See the doc test in Phrase::append
864    // #[test]
865    // fn append_field_field() {}
866
867    #[test]
868    fn append_empty_full() {
869        let a = AttrChar {
870            value: 'a',
871            origin: Origin::Literal,
872            is_quoted: false,
873            is_quoting: false,
874        };
875        let b = AttrChar { value: 'b', ..a };
876        let c = AttrChar { value: 'c', ..a };
877        let phrase = Full(vec![vec![a], vec![b], vec![c]]);
878        for mut left in [
879            Phrase::zero_fields(),
880            Phrase::one_empty_field(),
881            Full(vec![]),
882            Full(vec![vec![]]),
883        ] {
884            let mut right = phrase.clone();
885            left.append(&mut right);
886            assert_eq!(left, phrase);
887            assert_eq!(right, Phrase::zero_fields());
888        }
889    }
890
891    #[test]
892    fn append_full_empty() {
893        let a = AttrChar {
894            value: 'a',
895            origin: Origin::Literal,
896            is_quoted: false,
897            is_quoting: false,
898        };
899        let b = AttrChar { value: 'b', ..a };
900        let c = AttrChar { value: 'c', ..a };
901        let phrase = Full(vec![vec![a], vec![b], vec![c]]);
902        for mut right in [
903            Phrase::zero_fields(),
904            Phrase::one_empty_field(),
905            Full(vec![]),
906            Full(vec![vec![]]),
907        ] {
908            let mut left = phrase.clone();
909            left.append(&mut right);
910            assert_eq!(left, phrase);
911            assert_eq!(right, Phrase::zero_fields());
912        }
913    }
914
915    #[test]
916    fn append_char_full() {
917        let a = AttrChar {
918            value: 'a',
919            origin: Origin::Literal,
920            is_quoted: false,
921            is_quoting: false,
922        };
923        let b = AttrChar { value: 'b', ..a };
924        let c = AttrChar { value: 'c', ..a };
925        let mut left = Char(a);
926        let mut right = Full(vec![vec![b], vec![c]]);
927        left.append(&mut right);
928        assert_eq!(left, Full(vec![vec![a, b], vec![c]]));
929        assert_eq!(right, Phrase::zero_fields());
930    }
931
932    #[test]
933    fn append_full_char() {
934        let a = AttrChar {
935            value: 'a',
936            origin: Origin::Literal,
937            is_quoted: false,
938            is_quoting: false,
939        };
940        let b = AttrChar { value: 'b', ..a };
941        let c = AttrChar { value: 'c', ..a };
942        let mut left = Full(vec![vec![a], vec![b]]);
943        let mut right = Char(c);
944        left.append(&mut right);
945        assert_eq!(left, Full(vec![vec![a], vec![b, c]]));
946        assert_eq!(right, Phrase::zero_fields());
947    }
948
949    #[test]
950    fn append_field_full() {
951        let a = AttrChar {
952            value: 'a',
953            origin: Origin::Literal,
954            is_quoted: false,
955            is_quoting: false,
956        };
957        let b = AttrChar { value: 'b', ..a };
958        let c = AttrChar { value: 'c', ..a };
959        let mut left = Field(vec![a]);
960        let mut right = Full(vec![vec![b], vec![c]]);
961        left.append(&mut right);
962        assert_eq!(left, Full(vec![vec![a, b], vec![c]]));
963        assert_eq!(right, Phrase::zero_fields());
964    }
965
966    #[test]
967    fn append_full_field() {
968        let a = AttrChar {
969            value: 'a',
970            origin: Origin::Literal,
971            is_quoted: false,
972            is_quoting: false,
973        };
974        let b = AttrChar { value: 'b', ..a };
975        let c = AttrChar { value: 'c', ..a };
976        let mut left = Full(vec![vec![a], vec![b]]);
977        let mut right = Field(vec![c]);
978        left.append(&mut right);
979        assert_eq!(left, Full(vec![vec![a], vec![b, c]]));
980        assert_eq!(right, Phrase::zero_fields());
981    }
982
983    // See the doc test in Phrase::append
984    // #[test]
985    // fn append_full_full() {}
986
987    fn dummy_field(chars: &str) -> Vec<AttrChar> {
988        chars
989            .chars()
990            .map(|c| AttrChar {
991                value: c,
992                origin: Origin::SoftExpansion,
993                is_quoted: false,
994                is_quoting: false,
995            })
996            .collect()
997    }
998
999    #[test]
1000    fn ifs_join_char() {
1001        let a = AttrChar {
1002            value: 'a',
1003            origin: Origin::Literal,
1004            is_quoted: false,
1005            is_quoting: false,
1006        };
1007        let phrase = Char(a);
1008        let field = phrase.ifs_join(&VariableSet::new());
1009        assert_eq!(field, [a]);
1010    }
1011
1012    #[test]
1013    fn ifs_join_field() {
1014        let field_in = dummy_field("abc");
1015        let phrase = Field(field_in.clone());
1016        let field_out = phrase.ifs_join(&VariableSet::new());
1017        assert_eq!(field_out, field_in);
1018    }
1019
1020    #[test]
1021    fn ifs_join_full_empty() {
1022        let phrase = Full(vec![]);
1023        let field = phrase.ifs_join(&VariableSet::new());
1024        assert_eq!(field, []);
1025    }
1026
1027    #[test]
1028    fn ifs_join_full_one() {
1029        let field_in = dummy_field("foo");
1030        let phrase = Full(vec![field_in.clone()]);
1031        let field_out = phrase.ifs_join(&VariableSet::new());
1032        assert_eq!(field_out, field_in);
1033    }
1034
1035    #[test]
1036    fn ifs_join_full_unset_ifs() {
1037        let phrase = Full(vec![vec![], vec![]]);
1038        let field = phrase.ifs_join(&VariableSet::new());
1039        assert_eq!(field, dummy_field(" "));
1040
1041        let phrase = Full(vec![
1042            dummy_field("foo"),
1043            dummy_field("bar"),
1044            dummy_field("baz"),
1045        ]);
1046        let field = phrase.ifs_join(&VariableSet::new());
1047        assert_eq!(field, dummy_field("foo bar baz"));
1048    }
1049
1050    #[test]
1051    fn ifs_join_full_unassigned_ifs() {
1052        let mut vars = VariableSet::new();
1053        vars.get_or_new(IFS, Scope::Global);
1054        let phrase = Full(vec![
1055            dummy_field("foo"),
1056            dummy_field("bar"),
1057            dummy_field("baz"),
1058        ]);
1059        let field = phrase.ifs_join(&vars);
1060        assert_eq!(field, dummy_field("foo bar baz"));
1061    }
1062
1063    #[test]
1064    fn ifs_join_full_scalar_ifs() {
1065        let mut vars = VariableSet::new();
1066        vars.get_or_new(IFS, Scope::Global)
1067            .assign("!?", None)
1068            .unwrap();
1069        let phrase = Full(vec![
1070            dummy_field("foo"),
1071            dummy_field("bar"),
1072            dummy_field("baz"),
1073        ]);
1074        let field = phrase.ifs_join(&vars);
1075        assert_eq!(field, dummy_field("foo!bar!baz"));
1076    }
1077
1078    #[test]
1079    fn ifs_join_full_array_ifs() {
1080        let mut vars = VariableSet::new();
1081        vars.get_or_new(IFS, Scope::Global)
1082            .assign(Value::array(["-+", "abc"]), None)
1083            .unwrap();
1084        let phrase = Full(vec![
1085            dummy_field("foo"),
1086            dummy_field("bar"),
1087            dummy_field("baz"),
1088        ]);
1089        let field = phrase.ifs_join(&vars);
1090        assert_eq!(field, dummy_field("foo-bar-baz"));
1091    }
1092
1093    #[test]
1094    fn ifs_join_full_empty_scalar_ifs() {
1095        let mut vars = VariableSet::new();
1096        vars.get_or_new(IFS, Scope::Global)
1097            .assign("", None)
1098            .unwrap();
1099        let phrase = Full(vec![
1100            dummy_field("foo"),
1101            dummy_field("bar"),
1102            dummy_field("baz"),
1103        ]);
1104        let field = phrase.ifs_join(&vars);
1105        assert_eq!(field, dummy_field("foobarbaz"));
1106    }
1107
1108    #[test]
1109    fn ifs_join_full_empty_array_ifs() {
1110        let mut vars = VariableSet::new();
1111        vars.get_or_new(IFS, Scope::Global)
1112            .assign(Value::Array(vec![]), None)
1113            .unwrap();
1114        let phrase = Full(vec![
1115            dummy_field("foo"),
1116            dummy_field("bar"),
1117            dummy_field("baz"),
1118        ]);
1119        let field = phrase.ifs_join(&vars);
1120        assert_eq!(field, dummy_field("foobarbaz"));
1121
1122        vars.get_or_new(IFS, Scope::Global)
1123            .assign(Value::array(["", "abc"]), None)
1124            .unwrap();
1125        let phrase = Full(vec![
1126            dummy_field("foo"),
1127            dummy_field("bar"),
1128            dummy_field("baz"),
1129        ]);
1130        let field = phrase.ifs_join(&vars);
1131        assert_eq!(field, dummy_field("foobarbaz"));
1132    }
1133}