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 const fn segments(&self) -> &'static [&'static str] {
1434 self.segments
1435 }
1436
1437 pub fn path(&self) -> StorePath {
1440 StorePath::from_static(self.segments, self.joined)
1441 }
1442}
1443
1444impl From<StaticPath> for StorePath {
1445 fn from(at: StaticPath) -> Self {
1446 at.path()
1447 }
1448}
1449
1450impl PartialEq<str> for StaticPath {
1451 fn eq(&self, other: &str) -> bool {
1452 self.joined == other
1453 }
1454}
1455
1456impl AsRef<str> for StaticPath {
1457 fn as_ref(&self) -> &str {
1458 self.joined
1459 }
1460}
1461
1462impl fmt::Debug for StaticPath {
1463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1464 write!(f, "StaticPath({:?})", self.joined)
1465 }
1466}
1467
1468impl fmt::Display for StaticPath {
1469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1470 f.write_str(self.joined)
1471 }
1472}
1473
1474impl serde::Serialize for StorePath {
1476 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1477 serializer.serialize_str(self.as_str())
1478 }
1479}
1480
1481impl<'de> serde::Deserialize<'de> for StorePath {
1486 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1487 let joined = <Cow<'de, str>>::deserialize(deserializer)?;
1488 StorePath::parse_joined(&joined).map_err(serde::de::Error::custom)
1489 }
1490}
1491
1492#[cfg(test)]
1493mod tests {
1494 use super::*;
1495 use proptest::prelude::*;
1496
1497 fn shares_a_key_by_levels(a: &StorePath, b: &StorePath) -> bool {
1498 let a: Vec<_> = a.segments().collect();
1499 let b: Vec<_> = b.segments().collect();
1500 let common = a.len().min(b.len());
1501 a[..common] == b[..common]
1502 }
1503
1504 #[test]
1505 fn two_subtrees_overlap_exactly_when_one_holds_the_other() {
1506 let cases: &[(&[&str], &[&str], bool, &str)] = &[
1507 (&["ui"], &["ui"], true, "the same path"),
1508 (&["ui"], &["ui", "theme"], true, "a child"),
1509 (
1510 &["ui"],
1511 &["ui", "a", "b"],
1512 true,
1513 "a grandchild - a subtree is not one level",
1514 ),
1515 (&["ui", "theme"], &["ui", "width"], false, "siblings"),
1516 (&["ui"], &["net"], false, "strangers"),
1517 (
1518 &["ui"],
1519 &["uix"],
1520 false,
1521 "a string prefix is not a level prefix",
1522 ),
1523 (
1524 &["ui"],
1525 &["uix", "width"],
1526 false,
1527 "and neither is its subtree",
1528 ),
1529 (
1530 &["ui"],
1531 &["ui!x"],
1532 false,
1533 "`!` sorts below the separator, and is still not under `ui`",
1534 ),
1535 (
1536 &["ui.theme"],
1537 &["ui"],
1538 false,
1539 "one level literally named `ui.theme`, escaped as `ui\\.theme`",
1540 ),
1541 (
1542 &["ui.theme"],
1543 &["ui", "theme"],
1544 false,
1545 "which is a different place from two levels",
1546 ),
1547 (&[], &["ui", "theme"], true, "the root holds everything"),
1548 ];
1549
1550 for (a, b, want, why) in cases {
1551 let (a, b) = (StorePath::from_segments(*a), StorePath::from_segments(*b));
1552 assert_eq!(a.overlaps(&b), *want, "{why}: {a} vs {b}");
1553 assert_eq!(
1554 b.overlaps(&a),
1555 *want,
1556 "{why}, the other way round: {b} vs {a}"
1557 );
1558 }
1559 }
1560
1561 use crate::strategies::{key as key_strategy, name_holding_the_separator, segment};
1562
1563 fn segment_strategy() -> impl Strategy<Value = String> {
1564 segment()
1565 }
1566
1567 fn dotted_segment() -> impl Strategy<Value = String> {
1568 name_holding_the_separator().prop_map(|(_, _, joined)| joined)
1569 }
1570
1571 fn path_strategy() -> impl Strategy<Value = Vec<String>> {
1572 prop::collection::vec(segment_strategy(), 1..5)
1573 }
1574
1575 fn hash_of(value: &impl Hash) -> u64 {
1576 let mut hasher = std::collections::hash_map::DefaultHasher::new();
1577 value.hash(&mut hasher);
1578 hasher.finish()
1579 }
1580
1581 proptest! {
1582 #[test]
1583 fn two_subtrees_meet_exactly_when_one_starts_the_other(
1584 a in path_strategy(),
1585 b in path_strategy(),
1586 ) {
1587 let (a, b) = (StorePath::from_segments(&a), StorePath::from_segments(&b));
1588
1589 prop_assert_eq!(
1590 a.overlaps(&b),
1591 shares_a_key_by_levels(&a, &b),
1592 "the joined form and the levels disagree: {} vs {}", a, b
1593 );
1594 prop_assert_eq!(a.overlaps(&b), b.overlaps(&a), "{} vs {}", a, b);
1595
1596 if a.overlaps(&b) {
1598 let deeper = if a.len() >= b.len() { &a } else { &b };
1599 prop_assert!(deeper.starts_with(&a));
1600 prop_assert!(deeper.starts_with(&b));
1601 }
1602 }
1603
1604 #[test]
1605 fn a_path_meets_everything_grown_from_it(
1606 head in path_strategy(),
1607 tail in prop::collection::vec(segment_strategy(), 0..4),
1608 ) {
1609 let a = StorePath::from_segments(&head);
1610 let b = StorePath::from_segments(head.iter().chain(&tail));
1611
1612 prop_assert!(a.overlaps(&b), "{} does not hold {}", a, b);
1613 prop_assert!(b.starts_with(&a), "{} vs {}", a, b);
1614 }
1615
1616 #[test]
1617 fn two_branches_of_one_level_never_meet(
1618 head in path_strategy(),
1619 left in segment_strategy(),
1620 right in segment_strategy(),
1621 left_tail in prop::collection::vec(segment_strategy(), 0..3),
1622 right_tail in prop::collection::vec(segment_strategy(), 0..3),
1623 ) {
1624 prop_assume!(left != right);
1625
1626 let a = StorePath::from_segments(head.iter().chain([&left]).chain(&left_tail));
1627 let b = StorePath::from_segments(head.iter().chain([&right]).chain(&right_tail));
1628
1629 prop_assert!(!a.overlaps(&b), "siblings met: {} vs {}", a, b);
1630 }
1631
1632 #[test]
1633 fn a_separator_inside_a_name_is_never_a_level(
1634 segments in prop::collection::vec(dotted_segment(), 1..16)
1635 ) {
1636 let path = StorePath::from_segments(&segments);
1637
1638 prop_assert_eq!(path.len(), segments.len());
1639 prop_assert_eq!(
1640 path.segments().map(|s| s.to_string()).collect::<Vec<_>>(),
1641 segments.clone()
1642 );
1643
1644 let split: Vec<&str> = segments.iter().flat_map(|s| s.split('.')).collect();
1645 if let Ok(taken_apart) = StorePath::try_from_segments(&split) {
1646 prop_assert_ne!(taken_apart, path);
1647 }
1648 }
1649 #[test]
1650 fn the_joined_form_round_trips(segments in path_strategy()) {
1651 let path = StorePath::from_segments(&segments);
1652 prop_assert_eq!(StorePath::parse_joined(path.as_str()).unwrap(), path);
1653 }
1654
1655 #[test]
1656 fn different_levels_never_join_to_one_key(a in path_strategy(), b in path_strategy()) {
1657 let pa = StorePath::from_segments(&a);
1658 let pb = StorePath::from_segments(&b);
1659
1660 prop_assert_eq!(a == b, pa.as_str() == pb.as_str());
1661 }
1662
1663 #[test]
1664 fn a_prefix_is_stripped_back_off(head in path_strategy(), tail in path_strategy()) {
1665 let prefix = StorePath::from_segments(&head);
1666 let full = prefix.join(&StorePath::from_segments(&tail));
1667
1668 prop_assert!(full.starts_with(&prefix));
1669 prop_assert_eq!(
1670 full.strip_prefix(&prefix).unwrap(),
1671 StorePath::from_segments(&tail)
1672 );
1673 }
1674
1675 #[test]
1676 fn a_path_does_not_remember_how_it_was_built(segments in path_strategy()) {
1677 let all_at_once = StorePath::from_segments(&segments);
1678
1679 let mut one_at_a_time = StorePath::root();
1680 for segment in &segments {
1681 one_at_a_time = one_at_a_time.push(segment);
1682 }
1683
1684 let by_joining = segments.iter().fold(StorePath::root(), |acc, segment| {
1685 acc.join(&StorePath::segment(segment))
1686 });
1687
1688 prop_assert_eq!(&one_at_a_time, &all_at_once);
1689 prop_assert_eq!(&by_joining, &all_at_once);
1690 prop_assert_eq!(one_at_a_time.as_str(), all_at_once.as_str());
1691 }
1692
1693 #[test]
1694 fn any_spelling_of_the_levels_gives_the_same_path(segments in path_strategy()) {
1695 let built = StorePath::from_segments(&segments);
1696 let refs: Vec<&str> = segments.iter().map(String::as_str).collect();
1697
1698 prop_assert_eq!(refs.as_slice().into_store_path().unwrap(), built.clone());
1699 prop_assert_eq!(segments.clone().into_store_path().unwrap(), built.clone());
1700 prop_assert_eq!((&built).into_store_path().unwrap(), built.clone());
1701 prop_assert_eq!(built.clone().into_store_path().unwrap(), built);
1702 }
1703
1704 #[test]
1705 fn every_path_is_under_the_root(segments in path_strategy()) {
1706 let path = StorePath::from_segments(&segments);
1707 let root = StorePath::root();
1708
1709 prop_assert!(path.starts_with(&root));
1710 prop_assert_eq!(path.strip_prefix(&root).unwrap(), path.clone());
1711 prop_assert!(!root.starts_with(&path));
1712 }
1713
1714 #[test]
1715 fn a_level_with_no_name_is_a_level_like_any_other(
1716 segments in path_strategy(),
1717 at in 0usize..8
1718 ) {
1719 let mut with_a_hole = segments.clone();
1720 let at = at % (with_a_hole.len() + 1);
1721 with_a_hole.insert(at, String::new());
1722
1723 let path = StorePath::try_from_segments(&with_a_hole).unwrap();
1724 let held = path.segment_at(at).unwrap();
1725
1726 prop_assert_eq!(path.len(), with_a_hole.len());
1727 prop_assert_eq!(held.as_str(), "");
1728
1729 let pushed = StorePath::from_segments(&segments).push("");
1730 let last = pushed.segment_at(segments.len()).unwrap();
1731
1732 prop_assert_eq!(pushed.len(), segments.len() + 1);
1733 prop_assert_eq!(last.as_str(), "");
1734 }
1735
1736 #[test]
1737 fn every_separator_inside_a_name_is_escaped(segments in path_strategy()) {
1738 let path = StorePath::from_segments(&segments);
1739
1740 let mut unescaped = 0usize;
1741 let mut escaped = false;
1742 for ch in path.as_str().chars() {
1743 match ch {
1744 _ if escaped => escaped = false,
1745 ESCAPE => escaped = true,
1746 SEPARATOR => unescaped += 1,
1747 _ => {}
1748 }
1749 }
1750
1751 prop_assert_eq!(unescaped, segments.len() - 1);
1752 }
1753
1754 #[test]
1755 fn growing_a_name_never_makes_it_a_prefix(
1756 head in path_strategy(),
1757 extra in segment_strategy()
1758 ) {
1759 let base = StorePath::from_segments(&head);
1760
1761 let mut grown = head.clone();
1762 grown.last_mut().unwrap().push_str(&extra);
1763 let longer = StorePath::from_segments(&grown);
1764
1765 prop_assert!(longer.as_str().starts_with(base.as_str()));
1766
1767 prop_assert_ne!(&longer, &base);
1768 prop_assert!(!longer.starts_with(&base));
1769 prop_assert!(!base.starts_with(&longer));
1770 }
1771
1772 #[test]
1773 fn stripping_succeeds_exactly_when_the_prefix_matches(
1774 a in path_strategy(),
1775 b in path_strategy()
1776 ) {
1777 let path = StorePath::from_segments(&a);
1778 let candidate = StorePath::from_segments(&b);
1779
1780 prop_assert_eq!(
1781 path.strip_prefix(&candidate).is_some(),
1782 path.starts_with(&candidate)
1783 );
1784 }
1785
1786 #[test]
1787 fn a_key_that_parses_joins_back_to_itself(key in key_strategy()) {
1788 if let Ok(path) = StorePath::parse_joined(&key) {
1789 prop_assert_eq!(path.as_str(), key.as_str());
1790 }
1791 }
1792
1793 #[test]
1794 fn a_key_with_a_nameless_level_reads_that_level_as_nameless(
1795 head in path_strategy(),
1796 tail in path_strategy(),
1797 at in 0usize..3
1798 ) {
1799 let head = StorePath::from_segments(&head);
1800 let tail = StorePath::from_segments(&tail);
1801
1802 let (key, hole, levels) = match at % 3 {
1803 0 => (format!("{SEPARATOR}{tail}"), 0, 1 + tail.len()),
1804 1 => (format!("{head}{SEPARATOR}"), head.len(), head.len() + 1),
1805 _ => (
1806 format!("{head}{SEPARATOR}{SEPARATOR}{tail}"),
1807 head.len(),
1808 head.len() + 1 + tail.len(),
1809 ),
1810 };
1811
1812 let read = StorePath::parse_joined(&key).unwrap();
1813 let empty = read.segment_at(hole).unwrap();
1814
1815 prop_assert_eq!(read.len(), levels, "key: {:?}", key);
1816 prop_assert_eq!(empty.as_str(), "", "key: {:?}", key);
1817 }
1818
1819
1820 #[test]
1821 fn a_borrowed_path_answers_like_the_owned_one(
1822 key in path_strategy(),
1823 prefix in path_strategy()
1824 ) {
1825 let owned = StorePath::from_segments(&key);
1826 let under = StorePath::from_segments(&prefix);
1827 let borrowed = PathRef::from(&owned);
1828
1829 prop_assert_eq!(borrowed.as_str(), owned.as_str());
1830 prop_assert_eq!(borrowed.is_root(), owned.is_root());
1831 prop_assert_eq!(borrowed.level_under(&under), owned.level_under(&under));
1832 prop_assert_eq!(borrowed.name_under(&under), owned.name_under(&under));
1833 prop_assert_eq!(borrowed.to_path(), owned);
1834 }
1835
1836 #[test]
1837 fn a_key_borrows_exactly_when_it_parses(key in key_strategy()) {
1838 prop_assert_eq!(
1839 PathRef::parse(&key).is_ok(),
1840 StorePath::parse_joined(&key).is_ok(),
1841 "key: {:?}", key
1842 );
1843
1844 if let Ok(borrowed) = PathRef::parse(&key) {
1845 let owned = borrowed.to_path();
1846
1847 prop_assert_eq!(borrowed.as_str(), key.as_str());
1848 prop_assert_eq!(owned.as_str(), key.as_str());
1849 }
1850 }
1851
1852 #[test]
1853 fn the_range_a_key_gives_holds_exactly_the_subtree(
1854 head in path_strategy(),
1855 other in path_strategy()
1856 ) {
1857 let prefix = StorePath::from_segments(&head);
1858 let key = StorePath::from_segments(&other);
1859
1860 let under = prefix.key();
1861 let (low, high) = under.subtree();
1862 let bytes = key.key();
1863 let bytes = bytes.as_bytes();
1864
1865 let in_range = bytes >= low && high.as_ref().is_none_or(|h| bytes < h.as_slice());
1866
1867 prop_assert_eq!(
1868 in_range,
1869 key.starts_with(&prefix),
1870 "{} against the subtree of {}", key, prefix
1871 );
1872 }
1873
1874 #[test]
1875 fn everything_grown_from_a_path_is_in_its_range(
1876 head in path_strategy(),
1877 tail in prop::collection::vec(segment_strategy(), 0..4)
1878 ) {
1879 let prefix = StorePath::from_segments(&head);
1880 let under = prefix.join(&StorePath::from_segments(&tail));
1881
1882 prop_assert!(
1883 under.key().under(&prefix.key()),
1884 "{} is under {} and the range left it out", under, prefix
1885 );
1886 }
1887
1888 #[test]
1889 fn a_key_is_under_a_path_exactly_when_it_starts_with_it_and_a_separator(
1890 head in path_strategy(),
1891 tail in path_strategy(),
1892 other in path_strategy()
1893 ) {
1894 let prefix = StorePath::from_segments(&head);
1895 let boundary = format!("{}{}", prefix.as_str(), SEPARATOR);
1896
1897 let candidates = [
1898 prefix.join(&StorePath::from_segments(&tail)),
1899 StorePath::from_segments(&other),
1900 prefix.clone(),
1901 StorePath::from_segments(&tail).join(&prefix),
1902 ];
1903
1904 for candidate in candidates {
1905 let under = candidate.starts_with(&prefix) && candidate != prefix;
1906 prop_assert_eq!(under, candidate.as_str().starts_with(&boundary));
1907 }
1908 }
1909
1910 #[test]
1911 fn no_call_makes_a_path_from_segments_would_refuse(
1912 a in path_strategy(),
1913 b in path_strategy()
1914 ) {
1915 let pa = StorePath::from_segments(&a);
1916 let pb = StorePath::from_segments(&b);
1917
1918 let mut derived = vec![
1919 pa.join(&pb),
1920 pa.push("x"),
1921 StorePath::parse_joined(pa.as_str()).unwrap(),
1922 pa.join(&StorePath::root()),
1923 ];
1924 derived.extend(pa.parent());
1925 derived.extend(pa.join(&pb).strip_prefix(&pa));
1926
1927 for path in derived {
1928 let rebuilt = match StorePath::try_from_segments(path.segments()) {
1929 Ok(rebuilt) => rebuilt,
1930 Err(StorePathError::EmptyPath) => {
1931 prop_assert!(path.is_root());
1932 continue;
1933 }
1934 Err(other) => {
1935 return Err(TestCaseError::fail(format!(
1936 "a derived path holds an empty level: {other}"
1937 )));
1938 }
1939 };
1940 prop_assert_eq!(&rebuilt, &path);
1941 prop_assert_eq!(rebuilt.as_str(), path.as_str());
1942 }
1943 }
1944
1945 #[test]
1946 fn comparing_names_answers_what_comparing_their_keys_answers(
1947 a in segment_strategy(),
1948 b in segment_strategy()
1949 ) {
1950 let under = StorePath::segment("m");
1951 let ka = under.push(&a);
1952 let kb = under.push(&b);
1953
1954 prop_assert_eq!(
1955 a.cmp(&b),
1956 ka.key().as_bytes().cmp(kb.key().as_bytes()),
1957 "names {:?} and {:?} became keys {:?} and {:?}",
1958 a, b, ka, kb
1959 );
1960 }
1961
1962 #[test]
1963 fn equality_hashing_and_the_key_agree(a in path_strategy(), b in path_strategy()) {
1964 let pa = StorePath::from_segments(&a);
1965 let pb = StorePath::from_segments(&b);
1966
1967 let left = [pa.clone(), pa.parent().unwrap_or_else(StorePath::root), StorePath::root()];
1968 let right = [pb.clone(), pa.join(&pb).strip_prefix(&pa).unwrap(), StorePath::root()];
1969
1970 for x in &left {
1971 for y in &right {
1972 prop_assert_eq!(x == y, x.as_str() == y.as_str());
1973 if x == y {
1974 prop_assert_eq!(hash_of(x), hash_of(y));
1975 }
1976 }
1977 }
1978 }
1979
1980 #[test]
1981 fn the_const_check_accepts_what_the_join_writes(segments in path_strategy()) {
1982 let path = StorePath::from_segments(&segments);
1983 let levels: Vec<&str> = segments.iter().map(String::as_str).collect();
1984
1985 prop_assert!(
1986 joins_to(&levels, path.as_str()),
1987 "refused its own join: {:?}", path.as_str()
1988 );
1989
1990 let joined = path.as_str();
1991 let perturbed = [
1992 format!("{joined}{SEPARATOR}"),
1993 format!("{SEPARATOR}{joined}"),
1994 format!("{joined}{ESCAPE}"),
1995 format!("{joined}x"),
1996 ];
1997
1998 for candidate in perturbed {
1999 prop_assert!(
2000 !joins_to(&levels, &candidate),
2001 "accepted {:?} as the join of {:?}", candidate, levels
2002 );
2003 }
2004
2005 if levels.len() > 1 {
2006 let shorter = StorePath::from_segments(&segments[..segments.len() - 1]);
2007 prop_assert!(!joins_to(&levels, shorter.as_str()));
2008 }
2009 }
2010 }
2011
2012 #[test]
2013 fn the_encoding_is_a_backslash_before_the_separator() {
2014 assert_eq!(StorePath::segment("dark.mode").as_str(), "dark\\.mode");
2015 assert_eq!(
2016 StorePath::from_segments(["dark", "mode"]).as_str(),
2017 "dark.mode"
2018 );
2019 }
2020
2021 #[test]
2022 fn a_key_that_is_not_a_path_is_refused_at_both_doors() {
2023 let refused = [
2024 ("a\\", StorePathError::DanglingEscape),
2025 ("a\\x", StorePathError::DanglingEscape),
2026 ];
2027
2028 for (key, why) in refused {
2029 assert_eq!(
2030 StorePath::parse_joined(key).unwrap_err(),
2031 why,
2032 "parse_joined took {key:?}"
2033 );
2034 assert!(
2035 PathRef::parse(key).is_err(),
2036 "PathRef::parse took {key:?}, and the two doors must agree"
2037 );
2038 }
2039 }
2040
2041 #[test]
2042 fn a_subtree_is_one_run_with_nothing_of_anyone_elses_in_it() {
2043 let pot = StorePath::segment("pot");
2044 let luck = StorePath::segment("pot!luck");
2045 let ato = StorePath::from_segments(["pot", "ato"]);
2046 let potato = StorePath::segment("potato");
2047
2048 let mut sorted = [&potato, &luck, &ato, &pot];
2049 sorted.sort();
2050
2051 assert_eq!(
2052 sorted,
2053 [&pot, &ato, &luck, &potato],
2054 "everything under `pot` has to come directly after it"
2055 );
2056
2057 assert!(ato.starts_with(&pot));
2058 assert!(!luck.starts_with(&pot));
2059 assert!(!potato.starts_with(&pot));
2060 }
2061
2062 #[test]
2063 fn a_shared_prefix_answers_as_the_path_it_spells() {
2064 let full = StorePath::from_segments(["ui", "panels", "left", "width"]);
2065
2066 for (up, spelled) in [(1, "ui.panels.left"), (2, "ui.panels"), (3, "ui"), (4, "")] {
2067 let mut at = full.clone();
2068 for _ in 0..up {
2069 at = at.parent().expect("there is a level to drop");
2070 }
2071
2072 let built = StorePath::parse_joined(spelled).expect("the spelling parses");
2073
2074 assert_eq!(at.as_str(), spelled);
2075 assert_eq!(at, built);
2076 assert_eq!(at.len(), built.len());
2077 assert_eq!(at.is_root(), built.is_root());
2078 assert_eq!(at.key().as_bytes(), built.key().as_bytes());
2079 assert_eq!(
2080 at.segments().collect::<Vec<_>>(),
2081 built.segments().collect::<Vec<_>>()
2082 );
2083 }
2084 }
2085
2086 #[test]
2087 fn a_prefix_of_a_name_holding_the_separator_cuts_where_the_level_does() {
2088 let full = StorePath::from_segments(["dark.mode", "width"]);
2089 let up = full.parent().expect("there is a level to drop");
2090
2091 assert_eq!(up.len(), 1);
2092 assert_eq!(up.name().expect("one level").as_str(), "dark.mode");
2093 assert_eq!(up.as_str(), "dark\\.mode");
2094 assert_eq!(up, StorePath::segment("dark.mode"));
2095 }
2096
2097 #[test]
2098 fn walking_upwards_ends_at_the_root_and_holds_the_path_itself() {
2099 let full = StorePath::from_segments(["a", "b", "c"]);
2100
2101 let seen: Vec<String> = full.upwards().map(|at| at.as_str().to_string()).collect();
2102
2103 assert_eq!(seen, ["a.b.c", "a.b", "a", ""]);
2104 assert_eq!(StorePath::root().upwards().count(), 1);
2105 }
2106
2107 #[test]
2108 fn the_root_is_empty_and_stays_empty() {
2109 let root = StorePath::root();
2110
2111 assert!(root.is_root());
2112 assert_eq!(root.as_str(), "");
2113 assert_eq!(StorePath::parse_joined("").unwrap(), root);
2114 assert_eq!(root.parent(), None);
2115 assert_eq!(root.name(), None);
2116 }
2117
2118 #[test]
2119 fn a_key_no_join_could_have_written_is_not_a_second_name_for_one() {
2120 assert_ne!(
2121 StorePath::parse_joined("a\\b").ok(),
2122 StorePath::parse_joined("ab").ok()
2123 );
2124 assert_ne!(
2125 StorePath::parse_joined("a\\").ok(),
2126 StorePath::parse_joined("a").ok()
2127 );
2128 }
2129
2130 #[test]
2131 fn the_root_has_no_separator_boundary() {
2132 let root = StorePath::root();
2133 let child = StorePath::from_segments(["ui", "width"]);
2134
2135 assert!(child.starts_with(&root));
2136 assert_eq!(format!("{}{}", root.as_str(), SEPARATOR), ".");
2137 assert!(!child.as_str().starts_with(SEPARATOR));
2138 assert!(
2139 !StorePath::segment(".hidden")
2140 .as_str()
2141 .starts_with(SEPARATOR)
2142 );
2143 }
2144
2145 #[test]
2146 fn a_subtree_boundary_does_not_cover_the_node_itself() {
2147 let node = StorePath::segment("ui");
2148 let boundary = format!("{}{}", node.as_str(), SEPARATOR);
2149
2150 assert!(node.push("width").as_str().starts_with(&boundary));
2151 assert!(!node.as_str().starts_with(&boundary));
2152 assert!(node.starts_with(&node));
2153 }
2154
2155 #[test]
2156 fn a_key_carries_what_a_glob_pattern_reads() {
2157 let path = StorePath::segment("a*b[c]?\u{0}d");
2158
2159 assert_eq!(path.as_str(), "a*b[c]?\u{0}d");
2160 }
2161
2162 #[test]
2163 fn a_path_hashes_by_its_levels_however_it_arrived() {
2164 let built = StorePath::from_segments(["ui", "dark.mode"]);
2165 let parsed = StorePath::parse_joined("ui.dark\\.mode").expect("the spelling parses");
2166
2167 assert_eq!(built, parsed);
2168 assert_eq!(hash_of(&built), hash_of(&parsed));
2169 }
2170
2171 #[test]
2172 fn two_levels_do_not_hash_as_the_one_they_spell() {
2173 let two = StorePath::from_segments(["a", "b"]);
2174 let one = StorePath::segment("ab");
2175
2176 assert_ne!(two, one);
2177 assert_ne!(hash_of(&two), hash_of(&one));
2178 }
2179
2180 #[test]
2181 fn a_path_flattened_to_one_level_is_named_by_what_it_spells() {
2182 let flat = StorePath::from_segments(["ui", "theme"]).as_one_level();
2183
2184 assert_eq!(flat.len(), 1);
2185 assert_eq!(flat.name().expect("one level has a name"), "ui.theme");
2186 }
2187
2188 #[test]
2189 fn a_level_holding_a_separator_flattens_apart_from_the_two_it_reads_as() {
2190 let one = StorePath::segment("ui.theme").as_one_level();
2191 let two = StorePath::from_segments(["ui", "theme"]).as_one_level();
2192
2193 assert_ne!(one, two);
2194 assert_eq!(
2195 StorePath::parse_joined(one.name().expect("one level has a name").as_str())
2196 .expect("the spelling parses"),
2197 StorePath::segment("ui.theme")
2198 );
2199 }
2200
2201 #[test]
2202 fn two_spellings_of_one_name_are_two_paths() {
2203 let precomposed = StorePath::segment("caf\u{e9}");
2204 let decomposed = StorePath::segment("cafe\u{301}");
2205
2206 assert_ne!(precomposed, decomposed);
2207 assert_ne!(precomposed.as_str(), decomposed.as_str());
2208 assert_eq!(precomposed.to_string().chars().count(), 4);
2209 assert_eq!(decomposed.to_string().chars().count(), 5);
2210 }
2211
2212 #[test]
2213 fn walking_a_document_borrows_each_level() {
2214 let path = StorePath::from_segments(["ui", "window", "width"]);
2215
2216 assert_eq!(
2217 path.segments()
2218 .map(|l| l.as_str().to_string())
2219 .collect::<Vec<_>>(),
2220 ["ui", "window", "width"]
2221 );
2222 assert_eq!(
2223 path.segment_at(1).as_ref().map(Level::as_str),
2224 Some("window")
2225 );
2226 assert_eq!(path.segment_at(3), None);
2227 }
2228
2229 #[test]
2230 fn a_static_path_is_the_same_path() {
2231 static UI_WIDTH: StorePath = StorePath::from_static(&["ui", "width"], "ui.width");
2232
2233 assert_eq!(UI_WIDTH, StorePath::from_segments(["ui", "width"]));
2234 assert_eq!(UI_WIDTH.as_str(), "ui.width");
2235 assert_eq!(UI_WIDTH.name().as_ref().map(Level::as_str), Some("width"));
2236 assert!(UI_WIDTH.starts_with(&StorePath::from_static(&["ui"], "ui")));
2237 }
2238
2239 #[test]
2240 fn a_static_path_carries_what_a_name_holds() {
2241 static ODD: StorePath =
2242 StorePath::from_static(&["dark.mode", "a\\b"], "dark\\.mode.a\\\\b");
2243 static NOTHING: StorePath = StorePath::from_static(&[], "");
2244
2245 assert_eq!(ODD, StorePath::from_segments(["dark.mode", "a\\b"]));
2246 assert_eq!(
2247 ODD.as_str(),
2248 StorePath::from_segments(["dark.mode", "a\\b"]).as_str()
2249 );
2250 assert_eq!(NOTHING, StorePath::root());
2251 }
2252}