1use bstr::BStr;
2use bstr::ByteSlice;
3
4pub trait AsKey {
6 fn as_key(&self) -> KeyRef<'_>;
14
15 fn try_as_key(&self) -> Option<KeyRef<'_>>;
19}
20
21mod impls {
22 use bstr::{BStr, BString, ByteSlice};
23
24 use crate::key::{AsKey, KeyRef};
25
26 impl AsKey for String {
27 fn as_key(&self) -> KeyRef<'_> {
28 self.try_as_key()
29 .unwrap_or_else(|| panic!("'{self}' is not a valid configuration key"))
30 }
31
32 fn try_as_key(&self) -> Option<KeyRef<'_>> {
33 KeyRef::parse_unvalidated(self.as_str().into())
34 }
35 }
36
37 impl AsKey for &str {
38 fn as_key(&self) -> KeyRef<'_> {
39 self.try_as_key()
40 .unwrap_or_else(|| panic!("'{self}' is not a valid configuration key"))
41 }
42
43 fn try_as_key(&self) -> Option<KeyRef<'_>> {
44 KeyRef::parse_unvalidated((*self).into())
45 }
46 }
47
48 impl AsKey for BString {
49 fn as_key(&self) -> KeyRef<'_> {
50 self.try_as_key()
51 .unwrap_or_else(|| panic!("'{self}' is not a valid configuration key"))
52 }
53
54 fn try_as_key(&self) -> Option<KeyRef<'_>> {
55 KeyRef::parse_unvalidated(self.as_bstr())
56 }
57 }
58
59 impl AsKey for &BStr {
60 fn as_key(&self) -> KeyRef<'_> {
61 self.try_as_key()
62 .unwrap_or_else(|| panic!("'{self}' is not a valid configuration key"))
63 }
64
65 fn try_as_key(&self) -> Option<KeyRef<'_>> {
66 KeyRef::parse_unvalidated(self)
67 }
68 }
69
70 impl<T> AsKey for &T
71 where
72 T: AsKey,
73 {
74 fn as_key(&self) -> KeyRef<'_> {
75 (*self).as_key()
76 }
77
78 fn try_as_key(&self) -> Option<KeyRef<'_>> {
79 (*self).try_as_key()
80 }
81 }
82
83 impl AsKey for KeyRef<'_> {
84 fn as_key(&self) -> KeyRef<'_> {
85 *self
86 }
87
88 fn try_as_key(&self) -> Option<KeyRef<'_>> {
89 Some(*self)
90 }
91 }
92}
93
94#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Hash, Clone, Copy)]
96pub struct KeyRef<'a> {
97 pub section_name: &'a str,
99 pub subsection_name: Option<&'a BStr>,
101 pub value_name: &'a str,
103}
104
105impl KeyRef<'_> {
107 pub fn parse_unvalidated(input: &BStr) -> Option<KeyRef<'_>> {
111 let mut tokens = input.splitn(2, |b| *b == b'.');
112 let section_name = tokens.next()?;
113 let subsection_or_key = tokens.next()?;
114 let mut tokens = subsection_or_key.rsplitn(2, |b| *b == b'.');
115 let (subsection_name, value_name) = match (tokens.next(), tokens.next()) {
116 (Some(key), Some(subsection)) => (Some(subsection.into()), key),
117 (Some(key), None) => (None, key),
118 (None, Some(_)) => unreachable!("iterator can't restart producing items"),
119 (None, None) => return None,
120 };
121
122 Some(KeyRef {
123 section_name: section_name.to_str().ok()?,
124 subsection_name,
125 value_name: value_name.to_str().ok()?,
126 })
127 }
128}