config/path/
mod.rs

1use std::str::FromStr;
2
3use crate::error::{ConfigError, Result};
4use crate::map::Map;
5use crate::value::{Value, ValueKind};
6
7mod parser;
8
9#[derive(Debug, Eq, PartialEq, Clone, Hash)]
10pub(crate) struct Expression {
11    root: String,
12    postfix: Vec<Postfix>,
13}
14
15impl Expression {
16    pub(crate) fn root(root: String) -> Self {
17        Self {
18            root,
19            postfix: Vec::new(),
20        }
21    }
22}
23
24impl FromStr for Expression {
25    type Err = ConfigError;
26
27    fn from_str(s: &str) -> Result<Self> {
28        parser::from_str(s).map_err(|e| ConfigError::PathParse {
29            cause: Box::new(ParseError::new(e)),
30        })
31    }
32}
33
34#[derive(Debug, Eq, PartialEq, Clone, Hash)]
35enum Postfix {
36    Key(String),
37    Index(isize),
38}
39
40#[derive(Debug)]
41struct ParseError(String);
42
43impl ParseError {
44    fn new(inner: winnow::error::ParseError<&str, winnow::error::ContextError>) -> Self {
45        Self(inner.to_string())
46    }
47}
48
49impl std::fmt::Display for ParseError {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        self.0.fmt(f)
52    }
53}
54
55impl std::error::Error for ParseError {}
56
57/// Convert a relative index into an absolute index
58fn abs_index(index: isize, len: usize) -> Result<usize, usize> {
59    if index >= 0 {
60        Ok(index as usize)
61    } else if let Some(index) = len.checked_sub(index.unsigned_abs()) {
62        Ok(index)
63    } else {
64        Err((len as isize + index).unsigned_abs())
65    }
66}
67
68impl Expression {
69    pub(crate) fn get(self, root: &Value) -> Option<&Value> {
70        let ValueKind::Table(map) = &root.kind else {
71            return None;
72        };
73        let mut child = map.get(&self.root)?;
74        for postfix in &self.postfix {
75            match postfix {
76                Postfix::Key(key) => {
77                    let ValueKind::Table(map) = &child.kind else {
78                        return None;
79                    };
80                    child = map.get(key)?;
81                }
82                Postfix::Index(rel_index) => {
83                    let ValueKind::Array(array) = &child.kind else {
84                        return None;
85                    };
86                    let index = abs_index(*rel_index, array.len()).ok()?;
87                    child = array.get(index)?;
88                }
89            }
90        }
91        Some(child)
92    }
93
94    pub(crate) fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> &'a mut Value {
95        if !matches!(root.kind, ValueKind::Table(_)) {
96            *root = Map::<String, Value>::new().into();
97        }
98        let ValueKind::Table(map) = &mut root.kind else {
99            unreachable!()
100        };
101        let mut child = map
102            .entry(self.root.clone())
103            .or_insert_with(|| Value::new(None, ValueKind::Nil));
104        for postfix in &self.postfix {
105            match postfix {
106                Postfix::Key(key) => {
107                    if !matches!(child.kind, ValueKind::Table(_)) {
108                        *child = Map::<String, Value>::new().into();
109                    }
110                    let ValueKind::Table(ref mut map) = child.kind else {
111                        unreachable!()
112                    };
113
114                    child = map
115                        .entry(key.clone())
116                        .or_insert_with(|| Value::new(None, ValueKind::Nil));
117                }
118                Postfix::Index(rel_index) => {
119                    if !matches!(child.kind, ValueKind::Array(_)) {
120                        *child = Vec::<Value>::new().into();
121                    }
122                    let ValueKind::Array(ref mut array) = child.kind else {
123                        unreachable!()
124                    };
125
126                    let uindex = match abs_index(*rel_index, array.len()) {
127                        Ok(uindex) => {
128                            if uindex >= array.len() {
129                                array.resize(uindex + 1, Value::new(None, ValueKind::Nil));
130                            }
131                            uindex
132                        }
133                        Err(insertion) => {
134                            array.splice(
135                                0..0,
136                                (0..insertion).map(|_| Value::new(None, ValueKind::Nil)),
137                            );
138                            0
139                        }
140                    };
141
142                    child = &mut array[uindex];
143                }
144            }
145        }
146        child
147    }
148
149    pub(crate) fn set(&self, root: &mut Value, value: Value) {
150        let parent = self.get_mut_forcibly(root);
151        match value.kind {
152            ValueKind::Table(ref incoming_map) => {
153                // If the parent is not a table, overwrite it, treating it as a
154                // table
155                if !matches!(parent.kind, ValueKind::Table(_)) {
156                    *parent = Map::<String, Value>::new().into();
157                }
158
159                // Continue the deep merge
160                for (key, val) in incoming_map {
161                    Self::root(key.clone()).set(parent, val.clone());
162                }
163            }
164            _ => {
165                *parent = value;
166            }
167        }
168    }
169}