1use super::{Composite, Value, ValueDef, Variant};
22use crate::prelude::*;
23
24pub trait At<Ctx>: private::Sealed {
60 fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>>;
63}
64
65mod private {
67 use super::*;
68 pub trait Sealed {}
69 impl<Ctx> Sealed for Value<Ctx> {}
70 impl<Ctx> Sealed for Composite<Ctx> {}
71 impl<Ctx> Sealed for Variant<Ctx> {}
72 impl<T: Sealed> Sealed for Option<&T> {}
73}
74
75impl<Ctx> At<Ctx> for Composite<Ctx> {
76 fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
77 match loc.as_location().inner {
78 LocationInner::Str(s) => match self {
79 Composite::Named(vals) => {
80 vals.iter().find_map(|(n, v)| if s == n { Some(v) } else { None })
81 }
82 _ => None,
83 },
84 LocationInner::Usize(n) => match self {
85 Composite::Named(vals) => {
86 let val = vals.get(n);
87 val.map(|v| &v.1)
88 }
89 Composite::Unnamed(vals) => vals.get(n),
90 },
91 }
92 }
93}
94
95impl<Ctx> At<Ctx> for Variant<Ctx> {
96 fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
97 self.values.at(loc)
98 }
99}
100
101impl<Ctx> At<Ctx> for Value<Ctx> {
102 fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
103 match &self.value {
104 ValueDef::Composite(c) => c.at(loc),
105 ValueDef::Variant(v) => v.at(loc),
106 _ => None,
107 }
108 }
109}
110
111impl<Ctx, T: At<Ctx>> At<Ctx> for Option<&T> {
112 fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
113 self.as_ref().and_then(|v| v.at(loc))
114 }
115}
116
117pub trait AsLocation {
123 fn as_location(&self) -> Location<'_>;
124}
125
126impl AsLocation for usize {
127 fn as_location(&self) -> Location<'_> {
128 Location { inner: LocationInner::Usize(*self) }
129 }
130}
131
132impl AsLocation for &str {
133 fn as_location(&self) -> Location<'_> {
134 Location { inner: LocationInner::Str(self) }
135 }
136}
137
138impl AsLocation for String {
139 fn as_location(&self) -> Location<'_> {
140 Location { inner: LocationInner::Str(self) }
141 }
142}
143
144impl<T: AsLocation> AsLocation for &T {
145 fn as_location(&self) -> Location<'_> {
146 (*self).as_location()
147 }
148}
149
150#[derive(Copy, Clone)]
152pub struct Location<'a> {
153 inner: LocationInner<'a>,
154}
155
156#[derive(Copy, Clone)]
157enum LocationInner<'a> {
158 Usize(usize),
159 Str(&'a str),
160}
161
162#[cfg(test)]
163mod test {
164 use crate::value;
165
166 use super::*;
167
168 #[test]
170 fn nested_accessing() {
171 let val = value!({hello: (1u32, true, { wibble: false, foo: {bar: 123u32}})});
172 assert_eq!(val.at("hello").at(0), Some(&Value::u128(1)));
173 assert_eq!(val.at("hello").at(1), Some(&Value::bool(true)));
174 assert_eq!(val.at("hello").at(2).at("wibble"), Some(&Value::bool(false)));
175 assert_eq!(val.at("hello").at(2).at("foo").at("bar"), Some(&Value::u128(123)));
176
177 assert_eq!(val.at("wibble").at(3), None);
178 assert_eq!(val.at("wibble").at("wobble").at("nope"), None);
179
180 assert_eq!(val.at("hello".to_string()).at(0), Some(&Value::u128(1)));
182 {
184 assert_eq!(val.at("hello").at(0), Some(&Value::u128(1)));
185 }
186 }
187
188 #[test]
189 fn accessing_variants() {
190 let val = value!(TheVariant { foo: 12345u32, bar: 'c' });
191
192 assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
193 assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');
194
195 let val = value!(TheVariant(12345u32, 'c'));
196
197 assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
198 assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
199
200 let val =
203 Variant::named_fields("TheVariant", [("foo", value!(12345u32)), ("bar", value!('c'))]);
204
205 assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
206 assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');
207
208 let val = Variant::unnamed_fields("TheVariant", [value!(12345u32), value!('c')]);
209
210 assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
211 assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
212 }
213
214 #[test]
215 fn accessing_composites() {
216 let val = Composite::named([("foo", value!(12345u32)), ("bar", value!('c'))]);
220
221 assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
222 assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');
223
224 let val = Composite::unnamed([value!(12345u32), value!('c')]);
225
226 assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
227 assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
228 }
229}