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