1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt::{self, Write as _};
4use std::hash::{Hash, Hasher};
5use std::sync::{Arc, OnceLock};
6
7pub use smallvec::SmallVec;
14pub use smol_str::SmolStr;
15
16pub const SEPARATOR: char = '.';
22
23pub(crate) const ESCAPE: char = '\\';
24
25#[derive(Clone)]
43pub struct StorePath {
44 held: Held,
45}
46
47#[derive(Clone)]
48enum Held {
49 Written {
51 levels: &'static [&'static str],
52 joined: &'static str,
53 },
54
55 Levels(Arc<Levels>),
67
68 Joined { joined: SmolStr },
76
77 Prefix { of: Arc<Levels>, levels: usize },
90}
91
92struct Levels {
101 names: Box<[SmolStr]>,
102 joined: OnceLock<SmolStr>,
103}
104
105fn end_of_level(joined: &str, levels: usize) -> usize {
110 if levels == 0 {
111 return 0;
112 }
113
114 let mut seen = 0;
115 let mut escaped = false;
116
117 for (at, ch) in joined.char_indices() {
118 match ch {
119 _ if escaped => escaped = false,
120 ESCAPE => escaped = true,
121 SEPARATOR => {
122 seen += 1;
123 if seen == levels {
124 return at;
125 }
126 }
127 _ => {}
128 }
129 }
130
131 joined.len()
132}
133
134fn count_levels(joined: &str) -> usize {
136 if joined.is_empty() {
137 return 0;
138 }
139
140 let mut levels = 1;
141 let mut escaped = false;
142 for ch in joined.chars() {
143 match ch {
144 _ if escaped => escaped = false,
145 ESCAPE => escaped = true,
146 SEPARATOR => levels += 1,
147 _ => {}
148 }
149 }
150 levels
151}
152
153fn level_at(joined: &str, index: usize) -> Option<Cow<'_, str>> {
155 let mut level = 0;
156 let mut start = 0;
157 let mut escaped = false;
158
159 for (at, ch) in joined.char_indices() {
160 match ch {
161 _ if escaped => escaped = false,
162 ESCAPE => escaped = true,
163 SEPARATOR => {
164 if level == index {
165 return Some(unescape(&joined[start..at]));
166 }
167 level += 1;
168 start = at + ch.len_utf8();
169 }
170 _ => {}
171 }
172 }
173
174 (level == index && !joined.is_empty()).then(|| unescape(&joined[start..]))
175}
176
177impl StorePath {
178 pub const fn root() -> Self {
180 Self {
181 held: Held::Written {
182 levels: &[],
183 joined: "",
184 },
185 }
186 }
187
188 pub const fn from_static(segments: &'static [&'static str], joined: &'static str) -> Self {
208 check_static(segments, joined);
209
210 Self {
211 held: Held::Written {
212 levels: segments,
213 joined,
214 },
215 }
216 }
217
218 pub fn segment(name: impl AsRef<str>) -> Self {
227 Self::from_checked(vec![SmolStr::new(name.as_ref())])
228 }
229
230 #[track_caller]
241 pub fn as_one_level(&self) -> Self {
242 Self::segment(PathRef::from(self).as_str())
243 }
244
245 #[track_caller]
255 pub fn from_segments<I, S>(segments: I) -> Self
256 where
257 I: IntoIterator<Item = S>,
258 S: AsRef<str>,
259 {
260 match Self::try_from_segments(segments) {
261 Ok(path) => path,
262 Err(StorePathError::EmptyPath) => Self::root(),
263 Err(other) => panic!("{other}"),
264 }
265 }
266
267 pub fn try_from_segments<I, S>(segments: I) -> Result<Self, StorePathError>
275 where
276 I: IntoIterator<Item = S>,
277 S: AsRef<str>,
278 {
279 let segments = segments.into_iter();
280 let mut collected: Vec<SmolStr> = Vec::with_capacity(segments.size_hint().0);
281
282 for segment in segments {
283 collected.push(SmolStr::new(segment.as_ref()));
284 }
285
286 if collected.is_empty() {
287 return Err(StorePathError::EmptyPath);
288 }
289
290 Ok(Self::from_checked(collected))
291 }
292
293 fn from_checked(segments: Vec<SmolStr>) -> Self {
294 Self {
295 held: Held::Levels(Arc::new(Levels {
296 names: segments.into_boxed_slice(),
297 joined: OnceLock::new(),
298 })),
299 }
300 }
301
302 pub fn push(&self, name: impl AsRef<str>) -> Self {
304 self.push_shared(SmolStr::new(name.as_ref()))
305 }
306
307 pub fn push_shared(&self, name: SmolStr) -> Self {
313 let mut segments = self.own_levels();
314 segments.push(name);
315 Self::from_checked(segments)
316 }
317
318 pub fn join(&self, other: &StorePath) -> Self {
320 let mut segments = self.own_levels();
321 segments.extend(other.own_levels());
322 Self::from_checked(segments)
323 }
324
325 pub fn segments(&self) -> impl ExactSizeIterator<Item = Level<'_>> + '_ {
330 (0..self.len()).map(|i| self.segment_at(i).expect("counted"))
331 }
332
333 pub fn segment_at(&self, index: usize) -> Option<Level<'_>> {
335 match &self.held {
336 Held::Written { levels, .. } => levels.get(index).copied().map(Level::named),
337 Held::Levels(held) => held.names.get(index).map(|level| Level::named(level)),
338 Held::Joined { joined } => level_at(joined, index).map(Level::from_cow),
339 Held::Prefix { of, levels } => of
340 .names
341 .get(index)
342 .filter(|_| index < *levels)
343 .map(|level| Level::named(level)),
344 }
345 }
346
347 fn as_str(&self) -> &str {
361 match &self.held {
362 Held::Written { joined, .. } => joined,
363 Held::Levels(held) => held.joined.get_or_init(|| join(&held.names)),
364 Held::Joined { joined } => joined,
365
366 Held::Prefix { of, levels } => {
372 let whole = of.joined.get_or_init(|| join(&of.names));
373 &whole[..end_of_level(whole, *levels)]
374 }
375 }
376 }
377
378 fn own_levels(&self) -> Vec<SmolStr> {
381 match &self.held {
382 Held::Written { levels, .. } => levels
383 .iter()
384 .map(|level| SmolStr::new_static(level))
385 .collect(),
386 Held::Levels(held) => held.names.to_vec(),
387 Held::Joined { joined } => (0..count_levels(joined))
388 .map(|at| SmolStr::new(&*level_at(joined, at).expect("counted")))
389 .collect(),
390 Held::Prefix { of, levels } => of.names[..*levels].to_vec(),
391 }
392 }
393
394 pub fn is_root(&self) -> bool {
401 match &self.held {
402 Held::Joined { joined } => joined.is_empty(),
403 Held::Written { levels, .. } => levels.is_empty(),
404 Held::Levels(held) => held.names.is_empty(),
405 Held::Prefix { levels, .. } => *levels == 0,
406 }
407 }
408
409 #[allow(clippy::len_without_is_empty)]
417 pub fn len(&self) -> usize {
418 match &self.held {
419 Held::Written { levels, .. } => levels.len(),
420 Held::Levels(held) => held.names.len(),
421 Held::Joined { joined } => count_levels(joined),
422 Held::Prefix { levels, .. } => *levels,
423 }
424 }
425
426 pub fn starts_with(&self, prefix: &StorePath) -> bool {
431 prefix.len() <= self.len() && prefix.segments().zip(self.segments()).all(|(a, b)| a == b)
432 }
433
434 pub fn strip_prefix(&self, prefix: &StorePath) -> Option<StorePath> {
437 self.starts_with(prefix)
438 .then(|| StorePath::from_checked(self.own_levels()[prefix.len()..].to_vec()))
439 }
440
441 pub fn overlaps(&self, other: &StorePath) -> bool {
447 self.starts_with(other) || other.starts_with(self)
448 }
449
450 fn shared_levels(&self) -> Arc<Levels> {
453 match &self.held {
454 Held::Levels(held) => held.clone(),
455 Held::Prefix { of, .. } => of.clone(),
456 _ => Arc::new(Levels {
457 names: self.own_levels().into_boxed_slice(),
458 joined: OnceLock::new(),
459 }),
460 }
461 }
462
463 pub fn parent(&self) -> Option<StorePath> {
469 let levels = self.len().checked_sub(1)?;
470
471 Some(StorePath {
472 held: Held::Prefix {
473 of: self.shared_levels(),
474 levels,
475 },
476 })
477 }
478
479 pub fn upwards(&self) -> impl Iterator<Item = StorePath> + '_ {
487 let of = self.shared_levels();
488
489 (0..=self.len()).rev().map(move |levels| StorePath {
490 held: Held::Prefix {
491 of: of.clone(),
492 levels,
493 },
494 })
495 }
496
497 pub fn name(&self) -> Option<Level<'_>> {
499 self.segment_at(self.len().checked_sub(1)?)
500 }
501
502 pub fn level_under(&self, prefix: &StorePath) -> Under<'_> {
512 level_below(self.as_str(), prefix.as_str(), prefix.is_root())
513 }
514
515 pub fn name_under(&self, prefix: &StorePath) -> Option<Level<'_>> {
527 match level_below(self.as_str(), prefix.as_str(), prefix.is_root()) {
528 Under::Entry(name) | Under::Deeper(name) => Some(name),
529 Under::Prefix | Under::Outside => None,
530 }
531 }
532
533 pub fn entry_name<'k>(&self, key: &'k StorePath) -> Option<Level<'k>> {
540 if !key.starts_with(self) || key.len() == self.len() {
541 return None;
542 }
543
544 key.name()
545 }
546
547 pub fn parse_joined(joined: &str) -> Result<Self, StorePathError> {
554 validate_joined(joined)?;
555
556 Ok(Self {
557 held: Held::Joined {
558 joined: SmolStr::new(joined),
559 },
560 })
561 }
562}
563
564#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
577pub struct PathRef<'a> {
578 joined: &'a str,
579}
580
581impl<'a> PathRef<'a> {
582 pub fn parse(joined: &'a str) -> Result<Self, StorePathError> {
587 validate_joined(joined)?;
588
589 Ok(Self { joined })
590 }
591
592 pub fn as_str(&self) -> &'a str {
594 self.joined
595 }
596
597 pub fn is_root(&self) -> bool {
598 self.joined.is_empty()
599 }
600
601 pub fn level_under(&self, prefix: &StorePath) -> Under<'a> {
605 level_below(self.joined, prefix.as_str(), prefix.is_root())
606 }
607
608 pub fn name_under(&self, prefix: &StorePath) -> Option<Level<'a>> {
611 match self.level_under(prefix) {
612 Under::Entry(name) | Under::Deeper(name) => Some(name),
613 Under::Prefix | Under::Outside => None,
614 }
615 }
616
617 pub fn to_path(&self) -> StorePath {
622 StorePath::parse_joined(self.joined).expect("a borrowed path was checked when it was made")
623 }
624}
625
626impl<'a> From<&'a StorePath> for PathRef<'a> {
629 fn from(path: &'a StorePath) -> Self {
630 Self {
631 joined: path.as_str(),
632 }
633 }
634}
635
636impl fmt::Debug for PathRef<'_> {
637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638 write!(f, "PathRef({:?})", self.joined)
639 }
640}
641
642impl fmt::Display for PathRef<'_> {
643 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644 f.write_str(self.joined)
645 }
646}
647
648impl PartialEq<StorePath> for PathRef<'_> {
649 fn eq(&self, other: &StorePath) -> bool {
650 self.joined == other.as_str()
651 }
652}
653
654#[derive(Clone)]
667pub struct Level<'a>(Name<'a>);
668
669#[derive(Clone)]
670enum Name<'a> {
671 Borrowed(&'a str),
672 Held(SmolStr),
673}
674
675impl<'a> Level<'a> {
676 pub fn named(name: &'a str) -> Self {
684 Self(Name::Borrowed(name))
685 }
686
687 pub fn as_str(&self) -> &str {
689 match &self.0 {
690 Name::Borrowed(name) => name,
691 Name::Held(name) => name,
692 }
693 }
694
695 pub fn into_owned(self) -> Level<'static> {
697 Level(match self.0 {
698 Name::Borrowed(name) => Name::Held(SmolStr::new(name)),
699 Name::Held(name) => Name::Held(name),
700 })
701 }
702
703 fn from_cow(name: Cow<'a, str>) -> Self {
704 Self(match name {
705 Cow::Borrowed(name) => Name::Borrowed(name),
706 Cow::Owned(name) => Name::Held(SmolStr::new(name)),
707 })
708 }
709}
710
711impl Level<'static> {
712 pub fn held(name: SmolStr) -> Self {
719 Self(Name::Held(name))
720 }
721}
722
723impl PartialEq for Level<'_> {
726 fn eq(&self, other: &Self) -> bool {
727 self.as_str() == other.as_str()
728 }
729}
730
731impl Eq for Level<'_> {}
732
733impl Ord for Level<'_> {
734 fn cmp(&self, other: &Self) -> Ordering {
735 self.as_str().cmp(other.as_str())
736 }
737}
738
739impl PartialOrd for Level<'_> {
740 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
741 Some(self.cmp(other))
742 }
743}
744
745impl Hash for Level<'_> {
746 fn hash<H: Hasher>(&self, state: &mut H) {
747 self.as_str().hash(state);
748 }
749}
750
751impl std::borrow::Borrow<str> for Level<'_> {
754 fn borrow(&self) -> &str {
755 self.as_str()
756 }
757}
758
759impl fmt::Debug for Level<'_> {
760 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
761 write!(f, "Level({:?})", self.as_str())
762 }
763}
764
765impl fmt::Display for Level<'_> {
766 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767 f.write_str(self.as_str())
768 }
769}
770
771impl AsRef<str> for Level<'_> {
773 fn as_ref(&self) -> &str {
774 self.as_str()
775 }
776}
777
778impl PartialEq<str> for Level<'_> {
779 fn eq(&self, other: &str) -> bool {
780 self.as_str() == other
781 }
782}
783
784impl PartialEq<&str> for Level<'_> {
785 fn eq(&self, other: &&str) -> bool {
786 self.as_str() == *other
787 }
788}
789
790#[derive(Debug, Clone, Copy, PartialEq, Eq)]
800pub struct Stored<'a>(&'a str);
801
802impl<'a> Stored<'a> {
803 pub fn level(name: &'a Level<'_>) -> Self {
805 Self(name.as_str())
806 }
807
808 pub fn whole(path: PathRef<'a>) -> Self {
810 Self(path.as_str())
811 }
812
813 pub fn read(name: &'a str) -> Self {
818 Self(name)
819 }
820
821 pub fn as_str(&self) -> &'a str {
823 self.0
824 }
825}
826
827impl fmt::Display for Stored<'_> {
828 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
829 f.write_str(self.0)
830 }
831}
832
833#[derive(Debug, Clone, PartialEq, Eq)]
836pub enum Under<'a> {
837 Prefix,
839
840 Entry(Level<'a>),
842
843 Deeper(Level<'a>),
846
847 Outside,
849}
850
851pub fn level_under<'a>(key: &'a str, prefix: &StorePath) -> Result<Under<'a>, StorePathError> {
865 validate_joined(key)?;
866 Ok(level_below(key, prefix.as_str(), prefix.is_root()))
867}
868
869fn level_below<'a>(whole: &'a str, head: &str, head_is_root: bool) -> Under<'a> {
871 let rest = if head_is_root {
876 match whole.is_empty() {
877 true => return Under::Prefix,
878 false => whole,
879 }
880 } else if whole == head {
881 return Under::Prefix;
882 } else {
883 match whole
884 .strip_prefix(head)
885 .and_then(|rest| rest.strip_prefix(SEPARATOR))
886 {
887 Some(rest) => rest,
888 None => return Under::Outside,
889 }
890 };
891
892 let mut escaped = false;
893 for (at, ch) in rest.char_indices() {
894 match ch {
895 _ if escaped => escaped = false,
896 ESCAPE => escaped = true,
897 SEPARATOR => return Under::Deeper(Level::from_cow(unescape(&rest[..at]))),
898 _ => {}
899 }
900 }
901
902 Under::Entry(Level::from_cow(unescape(rest)))
903}
904
905fn validate_joined(joined: &str) -> Result<(), StorePathError> {
913 let mut escaped = false;
914
915 for ch in joined.chars() {
916 match ch {
917 _ if escaped => {
918 if ch != SEPARATOR && ch != ESCAPE {
919 return Err(StorePathError::DanglingEscape);
920 }
921 escaped = false;
922 }
923 ESCAPE => escaped = true,
924 _ => {}
925 }
926 }
927
928 match escaped {
929 true => Err(StorePathError::DanglingEscape),
930 false => Ok(()),
931 }
932}
933
934fn unescape(level: &str) -> Cow<'_, str> {
936 if !level.contains(ESCAPE) {
937 return Cow::Borrowed(level);
938 }
939
940 let mut out = String::with_capacity(level.len());
941 let mut escaped = false;
942 for ch in level.chars() {
943 match ch {
944 _ if escaped => {
945 out.push(ch);
946 escaped = false;
947 }
948 ESCAPE => escaped = true,
949 _ => out.push(ch),
950 }
951 }
952 Cow::Owned(out)
953}
954
955#[derive(Debug, Clone, PartialEq, Eq)]
957pub enum StorePathError {
958 DanglingEscape,
961
962 EmptyPath,
971}
972
973impl fmt::Display for StorePathError {
974 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
975 match self {
976 StorePathError::DanglingEscape => {
977 f.write_str("an escape must be followed by a separator or another escape")
978 }
979 StorePathError::EmptyPath => f.write_str(
980 "a path of no levels names the whole store - say `StorePath::root()` to mean it",
981 ),
982 }
983 }
984}
985
986impl std::error::Error for StorePathError {}
987
988#[diagnostic::on_unimplemented(
995 message = "a path is a list of levels, and `{Self}` is not one",
996 label = "expected something like `[\"ui\", \"width\"]`",
997 note = "a string is a name, not a path: `\"ui.width\"` would be one level \
998 whose name holds a separator, which is somewhere else entirely. \
999 Spell the nesting out, or use `StorePath::parse_joined` to read a \
1000 path out of a string on purpose",
1001 note = "addressing one name rather than a path is what `Kv` is for, and it \
1002 does take `&str`"
1003)]
1004pub trait IntoStorePath {
1005 fn into_store_path(self) -> Result<StorePath, StorePathError>;
1006}
1007
1008impl IntoStorePath for StorePath {
1009 fn into_store_path(self) -> Result<StorePath, StorePathError> {
1010 Ok(self)
1011 }
1012}
1013
1014impl IntoStorePath for &StorePath {
1015 fn into_store_path(self) -> Result<StorePath, StorePathError> {
1016 Ok(self.clone())
1017 }
1018}
1019
1020impl<S: AsRef<str>, const N: usize> IntoStorePath for [S; N] {
1021 fn into_store_path(self) -> Result<StorePath, StorePathError> {
1022 StorePath::try_from_segments(self)
1023 }
1024}
1025
1026impl<S: AsRef<str>> IntoStorePath for &[S] {
1027 fn into_store_path(self) -> Result<StorePath, StorePathError> {
1028 StorePath::try_from_segments(self)
1029 }
1030}
1031
1032impl<S: AsRef<str>> IntoStorePath for Vec<S> {
1033 fn into_store_path(self) -> Result<StorePath, StorePathError> {
1034 StorePath::try_from_segments(self)
1035 }
1036}
1037
1038const SEPARATOR_BYTE: u8 = SEPARATOR as u8;
1039const ESCAPE_BYTE: u8 = ESCAPE as u8;
1040
1041const ENDS_A_LEVEL: u8 = 0x00;
1048
1049const STANDS_IN: u8 = 0x01;
1056
1057pub type KeyBytes = SmallVec<[u8; 64]>;
1074
1075#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1076pub struct Key(KeyBytes);
1077
1078impl StorePath {
1079 pub fn key(&self) -> Key {
1081 let mut out = KeyBytes::new();
1082 self.write_key(&mut out);
1083 Key(out)
1084 }
1085
1086 pub fn write_key(&self, out: &mut KeyBytes) {
1088 for level in self.segments() {
1089 for &byte in level.as_str().as_bytes() {
1090 match byte {
1091 ENDS_A_LEVEL => out.extend_from_slice(&[STANDS_IN, STANDS_IN]),
1092 STANDS_IN => out.extend_from_slice(&[STANDS_IN, STANDS_IN + 1]),
1093 byte => out.push(byte),
1094 }
1095 }
1096 out.push(ENDS_A_LEVEL);
1097 }
1098 }
1099}
1100
1101impl Key {
1102 pub fn root() -> Self {
1104 Key(KeyBytes::new())
1105 }
1106
1107 pub fn from_bytes(bytes: &[u8]) -> Self {
1113 Key(KeyBytes::from_slice(bytes))
1114 }
1115
1116 pub fn as_bytes(&self) -> &[u8] {
1117 &self.0
1118 }
1119
1120 pub fn is_root(&self) -> bool {
1121 self.0.is_empty()
1122 }
1123
1124 pub fn path(&self) -> Result<StorePath, StorePathError> {
1130 let mut names: Vec<SmolStr> = Vec::new();
1131 let mut level = KeyBytes::new();
1132 let mut at = 0;
1133
1134 while at < self.0.len() {
1135 let byte = self.0[at];
1136 at += 1;
1137
1138 match byte {
1139 ENDS_A_LEVEL => {
1140 let Ok(name) = std::str::from_utf8(&level) else {
1141 return Err(not_a_key_this_wrote());
1142 };
1143
1144 names.push(SmolStr::new(name));
1145 level.clear();
1146 }
1147 STANDS_IN => {
1148 let held = self.0.get(at).copied();
1149 at += 1;
1150
1151 match held {
1152 Some(STANDS_IN) => level.push(ENDS_A_LEVEL),
1153 Some(byte) if byte == STANDS_IN + 1 => level.push(STANDS_IN),
1154 _ => return Err(not_a_key_this_wrote()),
1155 }
1156 }
1157 byte => level.push(byte),
1158 }
1159 }
1160
1161 if !level.is_empty() {
1162 return Err(not_a_key_this_wrote());
1163 }
1164
1165 match names.is_empty() {
1166 true => Ok(StorePath::root()),
1167 false => Ok(StorePath::from_checked(names)),
1168 }
1169 }
1170
1171 pub fn under(&self, prefix: &Key) -> bool {
1177 self.0.starts_with(&prefix.0)
1178 }
1179
1180 pub fn subtree(&self) -> (&[u8], Option<KeyBytes>) {
1188 let mut top = self.0.clone();
1189
1190 while let Some(last) = top.pop() {
1191 if last < u8::MAX {
1192 top.push(last + 1);
1193 return (&self.0, Some(top));
1194 }
1195 }
1196
1197 (&[], None)
1198 }
1199}
1200
1201#[cold]
1208#[inline(never)]
1209fn not_a_key_this_wrote() -> StorePathError {
1210 StorePathError::DanglingEscape
1211}
1212
1213impl fmt::Debug for Key {
1214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1215 match self.path() {
1216 Ok(path) => write!(f, "Key({path})"),
1217 Err(_) => write!(f, "Key({:?})", &self.0),
1218 }
1219 }
1220}
1221
1222impl From<&StorePath> for Key {
1223 fn from(path: &StorePath) -> Self {
1224 path.key()
1225 }
1226}
1227
1228const fn check_static(segments: &[&str], joined: &str) {
1229 if has_empty_segment(segments) {
1230 panic!("a path segment cannot be empty");
1231 }
1232
1233 if !joins_to(segments, joined) {
1234 panic!(
1235 "the joined form of a static path must be what StorePath::as_str writes for its segments"
1236 );
1237 }
1238}
1239
1240const fn has_empty_segment(segments: &[&str]) -> bool {
1241 let mut at = 0;
1242
1243 while at < segments.len() {
1244 if segments[at].is_empty() {
1245 return true;
1246 }
1247 at += 1;
1248 }
1249
1250 false
1251}
1252
1253const fn joins_to(segments: &[&str], joined: &str) -> bool {
1254 let key = joined.as_bytes();
1255 let mut at = 0;
1256 let mut i = 0;
1257
1258 while at < segments.len() {
1259 if at > 0 {
1260 if i >= key.len() || key[i] != SEPARATOR_BYTE {
1261 return false;
1262 }
1263 i += 1;
1264 }
1265
1266 let segment = segments[at].as_bytes();
1267 let mut j = 0;
1268
1269 while j < segment.len() {
1270 let byte = segment[j];
1271
1272 if byte == SEPARATOR_BYTE || byte == ESCAPE_BYTE {
1273 if i >= key.len() || key[i] != ESCAPE_BYTE {
1274 return false;
1275 }
1276 i += 1;
1277 }
1278
1279 if i >= key.len() || key[i] != byte {
1280 return false;
1281 }
1282 i += 1;
1283 j += 1;
1284 }
1285
1286 at += 1;
1287 }
1288
1289 i == key.len()
1290}
1291
1292fn join(segments: &[SmolStr]) -> SmolStr {
1293 let mut out = String::new();
1294
1295 for (i, segment) in segments.iter().enumerate() {
1296 if i > 0 {
1297 out.push(SEPARATOR);
1298 }
1299 for ch in segment.chars() {
1300 if ch == SEPARATOR || ch == ESCAPE {
1301 out.push(ESCAPE);
1302 }
1303 out.push(ch);
1304 }
1305 }
1306
1307 SmolStr::new(out)
1308}
1309
1310impl PartialEq for StorePath {
1314 fn eq(&self, other: &Self) -> bool {
1315 self.len() == other.len() && self.segments().eq(other.segments())
1316 }
1317}
1318
1319impl Eq for StorePath {}
1320
1321impl Ord for StorePath {
1332 fn cmp(&self, other: &Self) -> Ordering {
1333 self.segments().cmp(other.segments())
1334 }
1335}
1336
1337impl PartialOrd for StorePath {
1338 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1339 Some(self.cmp(other))
1340 }
1341}
1342
1343impl Hash for StorePath {
1349 fn hash<H: Hasher>(&self, state: &mut H) {
1350 state.write_usize(self.len());
1351 for level in self.segments() {
1352 level.as_str().hash(state);
1353 }
1354 }
1355}
1356
1357impl fmt::Debug for StorePath {
1358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1359 write!(f, "StorePath({})", self)
1360 }
1361}
1362
1363impl fmt::Display for StorePath {
1367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1368 for (at, level) in self.segments().enumerate() {
1369 if at > 0 {
1370 f.write_char(SEPARATOR)?;
1371 }
1372
1373 for ch in level.as_str().chars() {
1374 if ch == SEPARATOR || ch == ESCAPE {
1375 f.write_char(ESCAPE)?;
1376 }
1377 f.write_char(ch)?;
1378 }
1379 }
1380
1381 Ok(())
1382 }
1383}
1384
1385#[derive(Clone, Copy, PartialEq, Eq)]
1398pub struct StaticPath {
1399 segments: &'static [&'static str],
1400 joined: &'static str,
1401}
1402
1403impl StaticPath {
1404 pub const fn new(segments: &'static [&'static str], joined: &'static str) -> Self {
1410 check_static(segments, joined);
1411
1412 Self { segments, joined }
1413 }
1414
1415 pub const fn root() -> Self {
1417 Self {
1418 segments: &[],
1419 joined: "",
1420 }
1421 }
1422
1423 pub const fn as_str(&self) -> &'static str {
1425 self.joined
1426 }
1427
1428 pub const fn is_root(&self) -> bool {
1429 self.segments.is_empty()
1430 }
1431
1432 pub fn path(&self) -> StorePath {
1435 StorePath::from_static(self.segments, self.joined)
1436 }
1437}
1438
1439impl From<StaticPath> for StorePath {
1440 fn from(at: StaticPath) -> Self {
1441 at.path()
1442 }
1443}
1444
1445impl PartialEq<str> for StaticPath {
1446 fn eq(&self, other: &str) -> bool {
1447 self.joined == other
1448 }
1449}
1450
1451impl AsRef<str> for StaticPath {
1452 fn as_ref(&self) -> &str {
1453 self.joined
1454 }
1455}
1456
1457impl fmt::Debug for StaticPath {
1458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1459 write!(f, "StaticPath({:?})", self.joined)
1460 }
1461}
1462
1463impl fmt::Display for StaticPath {
1464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1465 f.write_str(self.joined)
1466 }
1467}
1468
1469impl serde::Serialize for StorePath {
1471 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1472 serializer.serialize_str(self.as_str())
1473 }
1474}
1475
1476impl<'de> serde::Deserialize<'de> for StorePath {
1481 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1482 let joined = <Cow<'de, str>>::deserialize(deserializer)?;
1483 StorePath::parse_joined(&joined).map_err(serde::de::Error::custom)
1484 }
1485}
1486
1487#[cfg(test)]
1488mod tests {
1489 use super::*;
1490 use proptest::prelude::*;
1491
1492 fn shares_a_key_by_levels(a: &StorePath, b: &StorePath) -> bool {
1493 let a: Vec<_> = a.segments().collect();
1494 let b: Vec<_> = b.segments().collect();
1495 let common = a.len().min(b.len());
1496 a[..common] == b[..common]
1497 }
1498
1499 #[test]
1500 fn two_subtrees_overlap_exactly_when_one_holds_the_other() {
1501 let cases: &[(&[&str], &[&str], bool, &str)] = &[
1502 (&["ui"], &["ui"], true, "the same path"),
1503 (&["ui"], &["ui", "theme"], true, "a child"),
1504 (
1505 &["ui"],
1506 &["ui", "a", "b"],
1507 true,
1508 "a grandchild - a subtree is not one level",
1509 ),
1510 (&["ui", "theme"], &["ui", "width"], false, "siblings"),
1511 (&["ui"], &["net"], false, "strangers"),
1512 (
1513 &["ui"],
1514 &["uix"],
1515 false,
1516 "a string prefix is not a level prefix",
1517 ),
1518 (
1519 &["ui"],
1520 &["uix", "width"],
1521 false,
1522 "and neither is its subtree",
1523 ),
1524 (
1525 &["ui"],
1526 &["ui!x"],
1527 false,
1528 "`!` sorts below the separator, and is still not under `ui`",
1529 ),
1530 (
1531 &["ui.theme"],
1532 &["ui"],
1533 false,
1534 "one level literally named `ui.theme`, escaped as `ui\\.theme`",
1535 ),
1536 (
1537 &["ui.theme"],
1538 &["ui", "theme"],
1539 false,
1540 "which is a different place from two levels",
1541 ),
1542 (&[], &["ui", "theme"], true, "the root holds everything"),
1543 ];
1544
1545 for (a, b, want, why) in cases {
1546 let (a, b) = (StorePath::from_segments(*a), StorePath::from_segments(*b));
1547 assert_eq!(a.overlaps(&b), *want, "{why}: {a} vs {b}");
1548 assert_eq!(
1549 b.overlaps(&a),
1550 *want,
1551 "{why}, the other way round: {b} vs {a}"
1552 );
1553 }
1554 }
1555
1556 use crate::strategies::{key as key_strategy, name_holding_the_separator, segment};
1557
1558 fn segment_strategy() -> impl Strategy<Value = String> {
1559 segment()
1560 }
1561
1562 fn dotted_segment() -> impl Strategy<Value = String> {
1563 name_holding_the_separator().prop_map(|(_, _, joined)| joined)
1564 }
1565
1566 fn path_strategy() -> impl Strategy<Value = Vec<String>> {
1567 prop::collection::vec(segment_strategy(), 1..5)
1568 }
1569
1570 fn hash_of(value: &impl Hash) -> u64 {
1571 let mut hasher = std::collections::hash_map::DefaultHasher::new();
1572 value.hash(&mut hasher);
1573 hasher.finish()
1574 }
1575
1576 proptest! {
1577 #[test]
1578 fn two_subtrees_meet_exactly_when_one_starts_the_other(
1579 a in path_strategy(),
1580 b in path_strategy(),
1581 ) {
1582 let (a, b) = (StorePath::from_segments(&a), StorePath::from_segments(&b));
1583
1584 prop_assert_eq!(
1585 a.overlaps(&b),
1586 shares_a_key_by_levels(&a, &b),
1587 "the joined form and the levels disagree: {} vs {}", a, b
1588 );
1589 prop_assert_eq!(a.overlaps(&b), b.overlaps(&a), "{} vs {}", a, b);
1590
1591 if a.overlaps(&b) {
1593 let deeper = if a.len() >= b.len() { &a } else { &b };
1594 prop_assert!(deeper.starts_with(&a));
1595 prop_assert!(deeper.starts_with(&b));
1596 }
1597 }
1598
1599 #[test]
1600 fn a_path_meets_everything_grown_from_it(
1601 head in path_strategy(),
1602 tail in prop::collection::vec(segment_strategy(), 0..4),
1603 ) {
1604 let a = StorePath::from_segments(&head);
1605 let b = StorePath::from_segments(head.iter().chain(&tail));
1606
1607 prop_assert!(a.overlaps(&b), "{} does not hold {}", a, b);
1608 prop_assert!(b.starts_with(&a), "{} vs {}", a, b);
1609 }
1610
1611 #[test]
1612 fn two_branches_of_one_level_never_meet(
1613 head in path_strategy(),
1614 left in segment_strategy(),
1615 right in segment_strategy(),
1616 left_tail in prop::collection::vec(segment_strategy(), 0..3),
1617 right_tail in prop::collection::vec(segment_strategy(), 0..3),
1618 ) {
1619 prop_assume!(left != right);
1620
1621 let a = StorePath::from_segments(head.iter().chain([&left]).chain(&left_tail));
1622 let b = StorePath::from_segments(head.iter().chain([&right]).chain(&right_tail));
1623
1624 prop_assert!(!a.overlaps(&b), "siblings met: {} vs {}", a, b);
1625 }
1626
1627 #[test]
1628 fn a_separator_inside_a_name_is_never_a_level(
1629 segments in prop::collection::vec(dotted_segment(), 1..16)
1630 ) {
1631 let path = StorePath::from_segments(&segments);
1632
1633 prop_assert_eq!(path.len(), segments.len());
1634 prop_assert_eq!(
1635 path.segments().map(|s| s.to_string()).collect::<Vec<_>>(),
1636 segments.clone()
1637 );
1638
1639 let split: Vec<&str> = segments.iter().flat_map(|s| s.split('.')).collect();
1640 if let Ok(taken_apart) = StorePath::try_from_segments(&split) {
1641 prop_assert_ne!(taken_apart, path);
1642 }
1643 }
1644 #[test]
1645 fn the_joined_form_round_trips(segments in path_strategy()) {
1646 let path = StorePath::from_segments(&segments);
1647 prop_assert_eq!(StorePath::parse_joined(path.as_str()).unwrap(), path);
1648 }
1649
1650 #[test]
1651 fn different_levels_never_join_to_one_key(a in path_strategy(), b in path_strategy()) {
1652 let pa = StorePath::from_segments(&a);
1653 let pb = StorePath::from_segments(&b);
1654
1655 prop_assert_eq!(a == b, pa.as_str() == pb.as_str());
1656 }
1657
1658 #[test]
1659 fn a_prefix_is_stripped_back_off(head in path_strategy(), tail in path_strategy()) {
1660 let prefix = StorePath::from_segments(&head);
1661 let full = prefix.join(&StorePath::from_segments(&tail));
1662
1663 prop_assert!(full.starts_with(&prefix));
1664 prop_assert_eq!(
1665 full.strip_prefix(&prefix).unwrap(),
1666 StorePath::from_segments(&tail)
1667 );
1668 }
1669
1670 #[test]
1671 fn a_path_does_not_remember_how_it_was_built(segments in path_strategy()) {
1672 let all_at_once = StorePath::from_segments(&segments);
1673
1674 let mut one_at_a_time = StorePath::root();
1675 for segment in &segments {
1676 one_at_a_time = one_at_a_time.push(segment);
1677 }
1678
1679 let by_joining = segments.iter().fold(StorePath::root(), |acc, segment| {
1680 acc.join(&StorePath::segment(segment))
1681 });
1682
1683 prop_assert_eq!(&one_at_a_time, &all_at_once);
1684 prop_assert_eq!(&by_joining, &all_at_once);
1685 prop_assert_eq!(one_at_a_time.as_str(), all_at_once.as_str());
1686 }
1687
1688 #[test]
1689 fn any_spelling_of_the_levels_gives_the_same_path(segments in path_strategy()) {
1690 let built = StorePath::from_segments(&segments);
1691 let refs: Vec<&str> = segments.iter().map(String::as_str).collect();
1692
1693 prop_assert_eq!(refs.as_slice().into_store_path().unwrap(), built.clone());
1694 prop_assert_eq!(segments.clone().into_store_path().unwrap(), built.clone());
1695 prop_assert_eq!((&built).into_store_path().unwrap(), built.clone());
1696 prop_assert_eq!(built.clone().into_store_path().unwrap(), built);
1697 }
1698
1699 #[test]
1700 fn every_path_is_under_the_root(segments in path_strategy()) {
1701 let path = StorePath::from_segments(&segments);
1702 let root = StorePath::root();
1703
1704 prop_assert!(path.starts_with(&root));
1705 prop_assert_eq!(path.strip_prefix(&root).unwrap(), path.clone());
1706 prop_assert!(!root.starts_with(&path));
1707 }
1708
1709 #[test]
1710 fn a_level_with_no_name_is_a_level_like_any_other(
1711 segments in path_strategy(),
1712 at in 0usize..8
1713 ) {
1714 let mut with_a_hole = segments.clone();
1715 let at = at % (with_a_hole.len() + 1);
1716 with_a_hole.insert(at, String::new());
1717
1718 let path = StorePath::try_from_segments(&with_a_hole).unwrap();
1719 let held = path.segment_at(at).unwrap();
1720
1721 prop_assert_eq!(path.len(), with_a_hole.len());
1722 prop_assert_eq!(held.as_str(), "");
1723
1724 let pushed = StorePath::from_segments(&segments).push("");
1725 let last = pushed.segment_at(segments.len()).unwrap();
1726
1727 prop_assert_eq!(pushed.len(), segments.len() + 1);
1728 prop_assert_eq!(last.as_str(), "");
1729 }
1730
1731 #[test]
1732 fn every_separator_inside_a_name_is_escaped(segments in path_strategy()) {
1733 let path = StorePath::from_segments(&segments);
1734
1735 let mut unescaped = 0usize;
1736 let mut escaped = false;
1737 for ch in path.as_str().chars() {
1738 match ch {
1739 _ if escaped => escaped = false,
1740 ESCAPE => escaped = true,
1741 SEPARATOR => unescaped += 1,
1742 _ => {}
1743 }
1744 }
1745
1746 prop_assert_eq!(unescaped, segments.len() - 1);
1747 }
1748
1749 #[test]
1750 fn growing_a_name_never_makes_it_a_prefix(
1751 head in path_strategy(),
1752 extra in segment_strategy()
1753 ) {
1754 let base = StorePath::from_segments(&head);
1755
1756 let mut grown = head.clone();
1757 grown.last_mut().unwrap().push_str(&extra);
1758 let longer = StorePath::from_segments(&grown);
1759
1760 prop_assert!(longer.as_str().starts_with(base.as_str()));
1761
1762 prop_assert_ne!(&longer, &base);
1763 prop_assert!(!longer.starts_with(&base));
1764 prop_assert!(!base.starts_with(&longer));
1765 }
1766
1767 #[test]
1768 fn stripping_succeeds_exactly_when_the_prefix_matches(
1769 a in path_strategy(),
1770 b in path_strategy()
1771 ) {
1772 let path = StorePath::from_segments(&a);
1773 let candidate = StorePath::from_segments(&b);
1774
1775 prop_assert_eq!(
1776 path.strip_prefix(&candidate).is_some(),
1777 path.starts_with(&candidate)
1778 );
1779 }
1780
1781 #[test]
1782 fn a_key_that_parses_joins_back_to_itself(key in key_strategy()) {
1783 if let Ok(path) = StorePath::parse_joined(&key) {
1784 prop_assert_eq!(path.as_str(), key.as_str());
1785 }
1786 }
1787
1788 #[test]
1789 fn a_key_with_a_nameless_level_reads_that_level_as_nameless(
1790 head in path_strategy(),
1791 tail in path_strategy(),
1792 at in 0usize..3
1793 ) {
1794 let head = StorePath::from_segments(&head);
1795 let tail = StorePath::from_segments(&tail);
1796
1797 let (key, hole, levels) = match at % 3 {
1798 0 => (format!("{SEPARATOR}{tail}"), 0, 1 + tail.len()),
1799 1 => (format!("{head}{SEPARATOR}"), head.len(), head.len() + 1),
1800 _ => (
1801 format!("{head}{SEPARATOR}{SEPARATOR}{tail}"),
1802 head.len(),
1803 head.len() + 1 + tail.len(),
1804 ),
1805 };
1806
1807 let read = StorePath::parse_joined(&key).unwrap();
1808 let empty = read.segment_at(hole).unwrap();
1809
1810 prop_assert_eq!(read.len(), levels, "key: {:?}", key);
1811 prop_assert_eq!(empty.as_str(), "", "key: {:?}", key);
1812 }
1813
1814
1815 #[test]
1816 fn a_borrowed_path_answers_like_the_owned_one(
1817 key in path_strategy(),
1818 prefix in path_strategy()
1819 ) {
1820 let owned = StorePath::from_segments(&key);
1821 let under = StorePath::from_segments(&prefix);
1822 let borrowed = PathRef::from(&owned);
1823
1824 prop_assert_eq!(borrowed.as_str(), owned.as_str());
1825 prop_assert_eq!(borrowed.is_root(), owned.is_root());
1826 prop_assert_eq!(borrowed.level_under(&under), owned.level_under(&under));
1827 prop_assert_eq!(borrowed.name_under(&under), owned.name_under(&under));
1828 prop_assert_eq!(borrowed.to_path(), owned);
1829 }
1830
1831 #[test]
1832 fn a_key_borrows_exactly_when_it_parses(key in key_strategy()) {
1833 prop_assert_eq!(
1834 PathRef::parse(&key).is_ok(),
1835 StorePath::parse_joined(&key).is_ok(),
1836 "key: {:?}", key
1837 );
1838
1839 if let Ok(borrowed) = PathRef::parse(&key) {
1840 let owned = borrowed.to_path();
1841
1842 prop_assert_eq!(borrowed.as_str(), key.as_str());
1843 prop_assert_eq!(owned.as_str(), key.as_str());
1844 }
1845 }
1846
1847 #[test]
1848 fn the_range_a_key_gives_holds_exactly_the_subtree(
1849 head in path_strategy(),
1850 other in path_strategy()
1851 ) {
1852 let prefix = StorePath::from_segments(&head);
1853 let key = StorePath::from_segments(&other);
1854
1855 let under = prefix.key();
1856 let (low, high) = under.subtree();
1857 let bytes = key.key();
1858 let bytes = bytes.as_bytes();
1859
1860 let in_range = bytes >= low && high.as_ref().is_none_or(|h| bytes < h.as_slice());
1861
1862 prop_assert_eq!(
1863 in_range,
1864 key.starts_with(&prefix),
1865 "{} against the subtree of {}", key, prefix
1866 );
1867 }
1868
1869 #[test]
1870 fn everything_grown_from_a_path_is_in_its_range(
1871 head in path_strategy(),
1872 tail in prop::collection::vec(segment_strategy(), 0..4)
1873 ) {
1874 let prefix = StorePath::from_segments(&head);
1875 let under = prefix.join(&StorePath::from_segments(&tail));
1876
1877 prop_assert!(
1878 under.key().under(&prefix.key()),
1879 "{} is under {} and the range left it out", under, prefix
1880 );
1881 }
1882
1883 #[test]
1884 fn a_key_is_under_a_path_exactly_when_it_starts_with_it_and_a_separator(
1885 head in path_strategy(),
1886 tail in path_strategy(),
1887 other in path_strategy()
1888 ) {
1889 let prefix = StorePath::from_segments(&head);
1890 let boundary = format!("{}{}", prefix.as_str(), SEPARATOR);
1891
1892 let candidates = [
1893 prefix.join(&StorePath::from_segments(&tail)),
1894 StorePath::from_segments(&other),
1895 prefix.clone(),
1896 StorePath::from_segments(&tail).join(&prefix),
1897 ];
1898
1899 for candidate in candidates {
1900 let under = candidate.starts_with(&prefix) && candidate != prefix;
1901 prop_assert_eq!(under, candidate.as_str().starts_with(&boundary));
1902 }
1903 }
1904
1905 #[test]
1906 fn no_call_makes_a_path_from_segments_would_refuse(
1907 a in path_strategy(),
1908 b in path_strategy()
1909 ) {
1910 let pa = StorePath::from_segments(&a);
1911 let pb = StorePath::from_segments(&b);
1912
1913 let mut derived = vec![
1914 pa.join(&pb),
1915 pa.push("x"),
1916 StorePath::parse_joined(pa.as_str()).unwrap(),
1917 pa.join(&StorePath::root()),
1918 ];
1919 derived.extend(pa.parent());
1920 derived.extend(pa.join(&pb).strip_prefix(&pa));
1921
1922 for path in derived {
1923 let rebuilt = match StorePath::try_from_segments(path.segments()) {
1924 Ok(rebuilt) => rebuilt,
1925 Err(StorePathError::EmptyPath) => {
1926 prop_assert!(path.is_root());
1927 continue;
1928 }
1929 Err(other) => {
1930 return Err(TestCaseError::fail(format!(
1931 "a derived path holds an empty level: {other}"
1932 )));
1933 }
1934 };
1935 prop_assert_eq!(&rebuilt, &path);
1936 prop_assert_eq!(rebuilt.as_str(), path.as_str());
1937 }
1938 }
1939
1940 #[test]
1941 fn comparing_names_answers_what_comparing_their_keys_answers(
1942 a in segment_strategy(),
1943 b in segment_strategy()
1944 ) {
1945 let under = StorePath::segment("m");
1946 let ka = under.push(&a);
1947 let kb = under.push(&b);
1948
1949 prop_assert_eq!(
1950 a.cmp(&b),
1951 ka.key().as_bytes().cmp(kb.key().as_bytes()),
1952 "names {:?} and {:?} became keys {:?} and {:?}",
1953 a, b, ka, kb
1954 );
1955 }
1956
1957 #[test]
1958 fn equality_hashing_and_the_key_agree(a in path_strategy(), b in path_strategy()) {
1959 let pa = StorePath::from_segments(&a);
1960 let pb = StorePath::from_segments(&b);
1961
1962 let left = [pa.clone(), pa.parent().unwrap_or_else(StorePath::root), StorePath::root()];
1963 let right = [pb.clone(), pa.join(&pb).strip_prefix(&pa).unwrap(), StorePath::root()];
1964
1965 for x in &left {
1966 for y in &right {
1967 prop_assert_eq!(x == y, x.as_str() == y.as_str());
1968 if x == y {
1969 prop_assert_eq!(hash_of(x), hash_of(y));
1970 }
1971 }
1972 }
1973 }
1974
1975 #[test]
1976 fn the_const_check_accepts_what_the_join_writes(segments in path_strategy()) {
1977 let path = StorePath::from_segments(&segments);
1978 let levels: Vec<&str> = segments.iter().map(String::as_str).collect();
1979
1980 prop_assert!(
1981 joins_to(&levels, path.as_str()),
1982 "refused its own join: {:?}", path.as_str()
1983 );
1984
1985 let joined = path.as_str();
1986 let perturbed = [
1987 format!("{joined}{SEPARATOR}"),
1988 format!("{SEPARATOR}{joined}"),
1989 format!("{joined}{ESCAPE}"),
1990 format!("{joined}x"),
1991 ];
1992
1993 for candidate in perturbed {
1994 prop_assert!(
1995 !joins_to(&levels, &candidate),
1996 "accepted {:?} as the join of {:?}", candidate, levels
1997 );
1998 }
1999
2000 if levels.len() > 1 {
2001 let shorter = StorePath::from_segments(&segments[..segments.len() - 1]);
2002 prop_assert!(!joins_to(&levels, shorter.as_str()));
2003 }
2004 }
2005 }
2006
2007 #[test]
2008 fn the_encoding_is_a_backslash_before_the_separator() {
2009 assert_eq!(StorePath::segment("dark.mode").as_str(), "dark\\.mode");
2010 assert_eq!(
2011 StorePath::from_segments(["dark", "mode"]).as_str(),
2012 "dark.mode"
2013 );
2014 }
2015
2016 #[test]
2017 fn a_key_that_is_not_a_path_is_refused_at_both_doors() {
2018 let refused = [
2019 ("a\\", StorePathError::DanglingEscape),
2020 ("a\\x", StorePathError::DanglingEscape),
2021 ];
2022
2023 for (key, why) in refused {
2024 assert_eq!(
2025 StorePath::parse_joined(key).unwrap_err(),
2026 why,
2027 "parse_joined took {key:?}"
2028 );
2029 assert!(
2030 PathRef::parse(key).is_err(),
2031 "PathRef::parse took {key:?}, and the two doors must agree"
2032 );
2033 }
2034 }
2035
2036 #[test]
2037 fn a_subtree_is_one_run_with_nothing_of_anyone_elses_in_it() {
2038 let pot = StorePath::segment("pot");
2039 let luck = StorePath::segment("pot!luck");
2040 let ato = StorePath::from_segments(["pot", "ato"]);
2041 let potato = StorePath::segment("potato");
2042
2043 let mut sorted = [&potato, &luck, &ato, &pot];
2044 sorted.sort();
2045
2046 assert_eq!(
2047 sorted,
2048 [&pot, &ato, &luck, &potato],
2049 "everything under `pot` has to come directly after it"
2050 );
2051
2052 assert!(ato.starts_with(&pot));
2053 assert!(!luck.starts_with(&pot));
2054 assert!(!potato.starts_with(&pot));
2055 }
2056
2057 #[test]
2058 fn a_shared_prefix_answers_as_the_path_it_spells() {
2059 let full = StorePath::from_segments(["ui", "panels", "left", "width"]);
2060
2061 for (up, spelled) in [(1, "ui.panels.left"), (2, "ui.panels"), (3, "ui"), (4, "")] {
2062 let mut at = full.clone();
2063 for _ in 0..up {
2064 at = at.parent().expect("there is a level to drop");
2065 }
2066
2067 let built = StorePath::parse_joined(spelled).expect("the spelling parses");
2068
2069 assert_eq!(at.as_str(), spelled);
2070 assert_eq!(at, built);
2071 assert_eq!(at.len(), built.len());
2072 assert_eq!(at.is_root(), built.is_root());
2073 assert_eq!(at.key().as_bytes(), built.key().as_bytes());
2074 assert_eq!(
2075 at.segments().collect::<Vec<_>>(),
2076 built.segments().collect::<Vec<_>>()
2077 );
2078 }
2079 }
2080
2081 #[test]
2082 fn a_prefix_of_a_name_holding_the_separator_cuts_where_the_level_does() {
2083 let full = StorePath::from_segments(["dark.mode", "width"]);
2084 let up = full.parent().expect("there is a level to drop");
2085
2086 assert_eq!(up.len(), 1);
2087 assert_eq!(up.name().expect("one level").as_str(), "dark.mode");
2088 assert_eq!(up.as_str(), "dark\\.mode");
2089 assert_eq!(up, StorePath::segment("dark.mode"));
2090 }
2091
2092 #[test]
2093 fn walking_upwards_ends_at_the_root_and_holds_the_path_itself() {
2094 let full = StorePath::from_segments(["a", "b", "c"]);
2095
2096 let seen: Vec<String> = full.upwards().map(|at| at.as_str().to_string()).collect();
2097
2098 assert_eq!(seen, ["a.b.c", "a.b", "a", ""]);
2099 assert_eq!(StorePath::root().upwards().count(), 1);
2100 }
2101
2102 #[test]
2103 fn the_root_is_empty_and_stays_empty() {
2104 let root = StorePath::root();
2105
2106 assert!(root.is_root());
2107 assert_eq!(root.as_str(), "");
2108 assert_eq!(StorePath::parse_joined("").unwrap(), root);
2109 assert_eq!(root.parent(), None);
2110 assert_eq!(root.name(), None);
2111 }
2112
2113 #[test]
2114 fn a_key_no_join_could_have_written_is_not_a_second_name_for_one() {
2115 assert_ne!(
2116 StorePath::parse_joined("a\\b").ok(),
2117 StorePath::parse_joined("ab").ok()
2118 );
2119 assert_ne!(
2120 StorePath::parse_joined("a\\").ok(),
2121 StorePath::parse_joined("a").ok()
2122 );
2123 }
2124
2125 #[test]
2126 fn the_root_has_no_separator_boundary() {
2127 let root = StorePath::root();
2128 let child = StorePath::from_segments(["ui", "width"]);
2129
2130 assert!(child.starts_with(&root));
2131 assert_eq!(format!("{}{}", root.as_str(), SEPARATOR), ".");
2132 assert!(!child.as_str().starts_with(SEPARATOR));
2133 assert!(
2134 !StorePath::segment(".hidden")
2135 .as_str()
2136 .starts_with(SEPARATOR)
2137 );
2138 }
2139
2140 #[test]
2141 fn a_subtree_boundary_does_not_cover_the_node_itself() {
2142 let node = StorePath::segment("ui");
2143 let boundary = format!("{}{}", node.as_str(), SEPARATOR);
2144
2145 assert!(node.push("width").as_str().starts_with(&boundary));
2146 assert!(!node.as_str().starts_with(&boundary));
2147 assert!(node.starts_with(&node));
2148 }
2149
2150 #[test]
2151 fn a_key_carries_what_a_glob_pattern_reads() {
2152 let path = StorePath::segment("a*b[c]?\u{0}d");
2153
2154 assert_eq!(path.as_str(), "a*b[c]?\u{0}d");
2155 }
2156
2157 #[test]
2158 fn a_path_hashes_by_its_levels_however_it_arrived() {
2159 let built = StorePath::from_segments(["ui", "dark.mode"]);
2160 let parsed = StorePath::parse_joined("ui.dark\\.mode").expect("the spelling parses");
2161
2162 assert_eq!(built, parsed);
2163 assert_eq!(hash_of(&built), hash_of(&parsed));
2164 }
2165
2166 #[test]
2167 fn two_levels_do_not_hash_as_the_one_they_spell() {
2168 let two = StorePath::from_segments(["a", "b"]);
2169 let one = StorePath::segment("ab");
2170
2171 assert_ne!(two, one);
2172 assert_ne!(hash_of(&two), hash_of(&one));
2173 }
2174
2175 #[test]
2176 fn a_path_flattened_to_one_level_is_named_by_what_it_spells() {
2177 let flat = StorePath::from_segments(["ui", "theme"]).as_one_level();
2178
2179 assert_eq!(flat.len(), 1);
2180 assert_eq!(flat.name().expect("one level has a name"), "ui.theme");
2181 }
2182
2183 #[test]
2184 fn a_level_holding_a_separator_flattens_apart_from_the_two_it_reads_as() {
2185 let one = StorePath::segment("ui.theme").as_one_level();
2186 let two = StorePath::from_segments(["ui", "theme"]).as_one_level();
2187
2188 assert_ne!(one, two);
2189 assert_eq!(
2190 StorePath::parse_joined(one.name().expect("one level has a name").as_str())
2191 .expect("the spelling parses"),
2192 StorePath::segment("ui.theme")
2193 );
2194 }
2195
2196 #[test]
2197 fn two_spellings_of_one_name_are_two_paths() {
2198 let precomposed = StorePath::segment("caf\u{e9}");
2199 let decomposed = StorePath::segment("cafe\u{301}");
2200
2201 assert_ne!(precomposed, decomposed);
2202 assert_ne!(precomposed.as_str(), decomposed.as_str());
2203 assert_eq!(precomposed.to_string().chars().count(), 4);
2204 assert_eq!(decomposed.to_string().chars().count(), 5);
2205 }
2206
2207 #[test]
2208 fn walking_a_document_borrows_each_level() {
2209 let path = StorePath::from_segments(["ui", "window", "width"]);
2210
2211 assert_eq!(
2212 path.segments()
2213 .map(|l| l.as_str().to_string())
2214 .collect::<Vec<_>>(),
2215 ["ui", "window", "width"]
2216 );
2217 assert_eq!(
2218 path.segment_at(1).as_ref().map(Level::as_str),
2219 Some("window")
2220 );
2221 assert_eq!(path.segment_at(3), None);
2222 }
2223
2224 #[test]
2225 fn a_static_path_is_the_same_path() {
2226 static UI_WIDTH: StorePath = StorePath::from_static(&["ui", "width"], "ui.width");
2227
2228 assert_eq!(UI_WIDTH, StorePath::from_segments(["ui", "width"]));
2229 assert_eq!(UI_WIDTH.as_str(), "ui.width");
2230 assert_eq!(UI_WIDTH.name().as_ref().map(Level::as_str), Some("width"));
2231 assert!(UI_WIDTH.starts_with(&StorePath::from_static(&["ui"], "ui")));
2232 }
2233
2234 #[test]
2235 fn a_static_path_carries_what_a_name_holds() {
2236 static ODD: StorePath =
2237 StorePath::from_static(&["dark.mode", "a\\b"], "dark\\.mode.a\\\\b");
2238 static NOTHING: StorePath = StorePath::from_static(&[], "");
2239
2240 assert_eq!(ODD, StorePath::from_segments(["dark.mode", "a\\b"]));
2241 assert_eq!(
2242 ODD.as_str(),
2243 StorePath::from_segments(["dark.mode", "a\\b"]).as_str()
2244 );
2245 assert_eq!(NOTHING, StorePath::root());
2246 }
2247}