1use super::error::{Error, Result};
5use crate::ConstString;
6use alloc::{borrow::ToOwned, string::String, vec::Vec};
7use core::ops::{Deref, DerefMut};
8
9#[must_use]
12fn is_valid_db_key(key: &str) -> bool {
13 !key.contains('"') && !key.contains('\'') && !key.contains(':') && !key.contains('{') && !key.contains('}')
14}
15
16#[must_use]
18pub fn is_const_assignment(key: &str) -> bool {
19 !key.starts_with('{') && !key.ends_with('}') || key.contains('"') || key.contains(':') || key.contains('\'')
20}
21
22#[must_use]
24pub fn is_board_pointer(key: &str) -> bool {
25 key.starts_with('{') && key.ends_with('}') && is_valid_db_key(&key[1..key.len() - 1])
26}
27
28#[must_use]
30pub fn strip_board_pointer(key: &str) -> Option<&str> {
31 if is_board_pointer(key) {
32 Some(&key[1..key.len() - 1])
33 } else {
34 None
35 }
36}
37
38pub fn check_board_pointer(key: &str) -> core::result::Result<&str, &str> {
42 if is_board_pointer(key) {
43 Ok(&key[1..key.len() - 1])
44 } else {
45 Err(key)
46 }
47}
48
49pub fn check_local_key(key: &str) -> core::result::Result<&str, &str> {
53 if key.starts_with('_') && is_valid_db_key(&key[1..]) {
54 Ok(&key[1..])
55 } else {
56 Err(key)
57 }
58}
59
60#[must_use]
62pub fn is_local_pointer(key: &str) -> bool {
63 key.starts_with("{_") && key.ends_with('}') && is_valid_db_key(&key[2..key.len() - 1])
64}
65
66#[must_use]
69pub fn strip_local_pointer(key: &str) -> Option<&str> {
70 if is_local_pointer(key) {
71 Some(&key[2..key.len() - 1])
72 } else {
73 None
74 }
75}
76
77pub fn check_local_pointer(key: &str) -> core::result::Result<&str, &str> {
81 if is_local_pointer(key) {
82 Ok(&key[2..key.len() - 1])
83 } else {
84 Err(key)
85 }
86}
87
88pub fn check_top_level_key(key: &str) -> core::result::Result<&str, &str> {
92 if key.starts_with('@') && is_valid_db_key(&key[1..]) {
93 Ok(&key[1..])
94 } else {
95 Err(key)
96 }
97}
98
99#[must_use]
101pub fn is_top_level_pointer(key: &str) -> bool {
102 key.starts_with("{@") && key.ends_with('}') && is_valid_db_key(&key[2..key.len() - 1])
103}
104
105#[must_use]
108pub fn strip_top_level_pointer(key: &str) -> Option<&str> {
109 if is_top_level_pointer(key) {
110 Some(&key[2..key.len() - 1])
111 } else {
112 None
113 }
114}
115
116pub fn check_top_level_pointer(key: &str) -> core::result::Result<&str, &str> {
120 if is_top_level_pointer(key) {
121 Ok(&key[2..key.len() - 1])
122 } else {
123 Err(key)
124 }
125}
126type RemappingEntry = (ConstString, ConstString);
131
132#[derive(Clone, Default)]
148#[repr(transparent)]
149pub struct Remappings(Vec<RemappingEntry>);
150
151impl core::fmt::Debug for Remappings {
152 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153 write!(f, "Remappings {{ ")?;
154 write!(f, "{:?}", &self.0)?;
155 write!(f, " }}")
156 }
157}
158
159impl Deref for Remappings {
160 type Target = Vec<RemappingEntry>;
161
162 fn deref(&self) -> &Self::Target {
163 &self.0
164 }
165}
166
167impl DerefMut for Remappings {
168 fn deref_mut(&mut self) -> &mut Self::Target {
169 &mut self.0
170 }
171}
172
173impl Remappings {
174 pub fn add(&mut self, key: impl Into<ConstString>, remap_to: impl Into<ConstString>) -> Result<()> {
178 let key = key.into();
179 for (original, remapped) in &self.0 {
180 if original == &key {
181 return Err(Error::AlreadyRemapped {
182 key,
183 remapped: remapped.to_owned(),
184 });
185 }
186 }
187 self.0.push((key, remap_to.into()));
188 Ok(())
189 }
190
191 pub fn overwrite(&mut self, key: &str, remapped: impl Into<ConstString>) {
194 for (original, old_value) in &mut self.0 {
195 if original.as_ref() == key {
196 *old_value = remapped.into();
198 return;
199 }
200 }
201 self.0.push((key.into(), remapped.into()));
203 }
204
205 #[must_use]
207 pub fn find(&self, key: &str) -> Option<ConstString> {
208 for (original, remapped) in &self.0 {
209 if original.as_ref() == key {
210 return if remapped.as_ref() == "{=}" {
212 Some((String::from("{") + key + "}").into())
213 } else {
214 Some(remapped.clone())
215 };
216 }
217 }
218 None
219 }
220
221 #[must_use]
223 pub fn remap(&self, name: &str) -> ConstString {
224 for (original, remapped) in &self.0 {
225 if original.as_ref() == name {
226 return if remapped.as_ref() == "{=}" {
228 name.into()
229 } else {
230 remapped.clone()
231 };
232 }
233 }
234 name.into()
235 }
236
237 pub fn shrink(&mut self) {
239 self.0.shrink_to_fit();
240 }
241}
242#[cfg(test)]
245mod tests {
246 use super::*;
247
248 const fn is_normal<T: Sized + Send + Sync>() {}
250
251 #[test]
252 const fn normal_types() {
253 is_normal::<Remappings>();
254 is_normal::<RemappingEntry>();
255 }
256}