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, Debug, Default)]
148#[repr(transparent)]
149pub struct Remappings(Vec<RemappingEntry>);
150
151impl Deref for Remappings {
152 type Target = Vec<RemappingEntry>;
153
154 fn deref(&self) -> &Self::Target {
155 &self.0
156 }
157}
158
159impl DerefMut for Remappings {
160 fn deref_mut(&mut self) -> &mut Self::Target {
161 &mut self.0
162 }
163}
164
165impl Remappings {
166 pub fn add(&mut self, key: impl Into<ConstString>, remap_to: impl Into<ConstString>) -> Result<()> {
170 let key = key.into();
171 for (original, remapped) in &self.0 {
172 if original == &key {
173 return Err(Error::AlreadyRemapped {
174 key,
175 remapped: remapped.to_owned(),
176 });
177 }
178 }
179 self.0.push((key, remap_to.into()));
180 Ok(())
181 }
182
183 pub fn overwrite(&mut self, key: &str, remapped: impl Into<ConstString>) {
186 for (original, old_value) in &mut self.0 {
187 if original.as_ref() == key {
188 *old_value = remapped.into();
190 return;
191 }
192 }
193 self.0.push((key.into(), remapped.into()));
195 }
196
197 #[must_use]
199 pub fn find(&self, key: &str) -> Option<ConstString> {
200 for (original, remapped) in &self.0 {
201 if original.as_ref() == key {
202 return if remapped.as_ref() == "{=}" {
204 Some((String::from("{") + key + "}").into())
205 } else {
206 Some(remapped.clone())
207 };
208 }
209 }
210 None
211 }
212
213 #[must_use]
215 pub fn remap(&self, name: &str) -> ConstString {
216 for (original, remapped) in &self.0 {
217 if original.as_ref() == name {
218 return if remapped.as_ref() == "{=}" {
220 name.into()
221 } else {
222 remapped.clone()
223 };
224 }
225 }
226 name.into()
227 }
228
229 pub fn shrink(&mut self) {
231 self.0.shrink_to_fit();
232 }
233}
234#[cfg(test)]
237mod tests {
238 use super::*;
239
240 const fn is_normal<T: Sized + Send + Sync>() {}
242
243 #[test]
244 const fn normal_types() {
245 is_normal::<Remappings>();
246 is_normal::<RemappingEntry>();
247 }
248}