Skip to main content

flyleaf_core/
array.rs

1//! The operations on an array: an element added at the end in the array's
2//! own style, one removed, one changed.
3
4use toml_edit::{Array, Value};
5
6use crate::{convert, set_value, Kind};
7
8/// Add an element of a kind at the end, in the style of the element before
9/// it.
10///
11/// `toml_edit` writes a pushed element with a default decor, which turns a
12/// multi-line array's last line into `  2,\n3]`. The new element takes the
13/// decor of the one before it instead, so a multi-line array stays one and a
14/// one-line array stays one. Refuses a table, which is not a value.
15pub fn push_element(a: &mut Array, kind: Kind) -> bool {
16    let Some(mut value) = kind.as_value() else {
17        return false;
18    };
19    if let Some(last) = a.iter().last() {
20        *value.decor_mut() = last.decor().clone();
21    }
22    a.push_formatted(value);
23    true
24}
25
26/// Remove the element at an index.
27pub fn remove_element(a: &mut Array, index: usize) -> bool {
28    if index >= a.len() {
29        return false;
30    }
31    a.remove(index);
32    true
33}
34
35/// Change the element at an index, keeping the decor it was written with.
36pub fn set_element(a: &mut Array, index: usize, new: Value) -> bool {
37    match a.get_mut(index) {
38        Some(slot) => {
39            set_value(slot, new);
40            true
41        }
42        None => false,
43    }
44}
45
46/// Change the element at an index to another kind, where it reads as one.
47pub fn convert_element(a: &mut Array, index: usize, to: Kind) -> bool {
48    let Some(slot) = a.get_mut(index) else {
49        return false;
50    };
51    match convert(slot, to) {
52        Some(new) => {
53            set_value(slot, new);
54            true
55        }
56        None => false,
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::{convert_element, push_element, remove_element, set_element};
63    use crate::Kind;
64    use toml_edit::{DocumentMut, Value};
65
66    fn doc(text: &str) -> DocumentMut {
67        text.parse().expect("valid TOML")
68    }
69
70    fn array(d: &mut DocumentMut) -> &mut toml_edit::Array {
71        d["a"].as_array_mut().expect("an array")
72    }
73
74    /// An element added to a one-line array lands on that line, and one
75    /// added to a multi-line array lands on a line of its own with the same
76    /// indentation. Without copying the decor, the second came out as
77    /// `  2,\n3]`, which is what this fails on.
78    #[test]
79    fn an_added_element_takes_the_style_of_the_one_before() {
80        let mut d = doc("a = [1, 2]\n");
81        assert!(push_element(array(&mut d), Kind::Integer));
82        assert_eq!(d.to_string(), "a = [1, 2, 0]\n");
83
84        let mut d = doc("a = [\n  1,\n  2,\n]\n");
85        assert!(push_element(array(&mut d), Kind::Integer));
86        assert_eq!(d.to_string(), "a = [\n  1,\n  2,\n  0,\n]\n");
87
88        let mut d = doc("a = []\n");
89        assert!(push_element(array(&mut d), Kind::Text));
90        assert_eq!(d.to_string(), "a = [\"\"]\n");
91    }
92
93    /// A table is not a value and cannot go in an array.
94    #[test]
95    fn a_table_is_refused() {
96        let mut d = doc("a = [1]\n");
97        assert!(!push_element(array(&mut d), Kind::Table));
98        assert_eq!(d.to_string(), "a = [1]\n");
99    }
100
101    /// Removing an element leaves the others as they were written, and an
102    /// index past the end is refused rather than panicking.
103    #[test]
104    fn an_element_is_removed_in_place() {
105        let mut d = doc("a = [1, 2, 3]   # three\n");
106        assert!(remove_element(array(&mut d), 1));
107        assert_eq!(d.to_string(), "a = [1, 3]   # three\n");
108        assert!(!remove_element(array(&mut d), 5));
109    }
110
111    /// A changed element keeps its spacing, and a converted one is the new
112    /// kind in the old place.
113    #[test]
114    fn an_element_is_changed_or_converted_where_it_is() {
115        let mut d = doc("a = [ 1 , 2 ]\n");
116        assert!(set_element(array(&mut d), 0, Value::from(9)));
117        assert_eq!(d.to_string(), "a = [ 9 , 2 ]\n");
118        assert!(convert_element(array(&mut d), 1, Kind::Text));
119        assert_eq!(d.to_string(), "a = [ 9 , \"2\" ]\n");
120        assert!(!convert_element(array(&mut d), 1, Kind::Boolean));
121        assert!(!set_element(array(&mut d), 7, Value::from(0)));
122    }
123}