Skip to main content

guix_scheme/
record.rs

1//! Generic view over `(record-name (field value) ...)` forms.
2
3use scheme_edit::{list, sym, Item, Node};
4
5/// Read-only view of a record-shaped list node.
6pub struct RecordView<'a> {
7    node: &'a Node,
8}
9
10impl<'a> RecordView<'a> {
11    /// `None` unless NODE is a list with a symbol head.
12    pub fn new(node: &'a Node) -> Option<Self> {
13        node.head_symbol()?;
14        Some(Self { node })
15    }
16
17    pub fn record_name(&self) -> &'a str {
18        // new() guarantees a symbol head.
19        self.node.head_symbol().unwrap_or_default()
20    }
21
22    /// Value node of the first `(name value)` child.
23    pub fn field(&self, name: &str) -> Option<&'a Node> {
24        field_value(self.node, name)
25    }
26
27    pub fn symbol_field(&self, name: &str) -> Option<&'a str> {
28        self.field(name)?.as_symbol()
29    }
30
31    pub fn string_field(&self, name: &str) -> Option<String> {
32        self.field(name)?.as_string_lit()
33    }
34}
35
36/// Mutable view: same getters plus field editing.
37pub struct RecordViewMut<'a> {
38    node: &'a mut Node,
39}
40
41impl<'a> RecordViewMut<'a> {
42    /// `None` unless NODE is a list with a symbol head.
43    pub fn new(node: &'a mut Node) -> Option<Self> {
44        node.head_symbol()?;
45        Some(Self { node })
46    }
47
48    pub fn record_name(&self) -> &str {
49        self.node.head_symbol().unwrap_or_default()
50    }
51
52    pub fn field(&self, name: &str) -> Option<&Node> {
53        field_value(self.node, name)
54    }
55
56    pub fn symbol_field(&self, name: &str) -> Option<&str> {
57        self.field(name)?.as_symbol()
58    }
59
60    pub fn string_field(&self, name: &str) -> Option<String> {
61        self.field(name)?.as_string_lit()
62    }
63
64    /// Mutable value node of the first `(name value)` child, for
65    /// in-place edits inside field expressions.
66    pub fn field_mut(&mut self, name: &str) -> Option<&mut Node> {
67        let idx = self.node.position_of(|n| n.head_symbol() == Some(name))?;
68        self.node.data_child_mut(idx)?.data_child_mut(1)
69    }
70
71    /// Replace the value of `(name value)` in place, or append a new
72    /// `(name value)` child when the field is absent.
73    pub fn set_field(&mut self, name: &str, value: Node) {
74        if let Node::List { items, .. } = &mut *self.node {
75            for item in items.iter_mut() {
76                if let Item::Node(n) = item {
77                    if n.head_symbol() == Some(name) {
78                        if n.data_len() >= 2 {
79                            n.replace_child(1, value);
80                        } else {
81                            n.push_child(value);
82                        }
83                        return;
84                    }
85                }
86            }
87        }
88        self.node.push_child(list(vec![sym(name), value]));
89    }
90
91    /// Remove the `(name value)` child, taking attached leading trivia.
92    pub fn remove_field(&mut self, name: &str) -> bool {
93        let Some(idx) = self.node.position_of(|n| n.head_symbol() == Some(name)) else {
94            return false;
95        };
96        self.node.remove_child(idx, true).is_some()
97    }
98}
99
100fn field_value<'n>(node: &'n Node, name: &str) -> Option<&'n Node> {
101    node.list_nodes()
102        .skip(1)
103        .find(|n| n.head_symbol() == Some(name))?
104        .data_child(1)
105}