use super::{Composite, Value, ValueDef, Variant};
use crate::prelude::*;
pub trait At<Ctx>: private::Sealed {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>>;
}
mod private {
use super::*;
pub trait Sealed {}
impl<Ctx> Sealed for Value<Ctx> {}
impl<Ctx> Sealed for Composite<Ctx> {}
impl<Ctx> Sealed for Variant<Ctx> {}
impl<T: Sealed> Sealed for Option<&T> {}
}
impl<Ctx> At<Ctx> for Composite<Ctx> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
match loc.as_location().inner {
LocationInner::Str(s) => match self {
Composite::Named(vals) => {
vals.iter().find_map(|(n, v)| if s == n { Some(v) } else { None })
}
_ => None,
},
LocationInner::Usize(n) => match self {
Composite::Named(vals) => {
let val = vals.get(n);
val.map(|v| &v.1)
}
Composite::Unnamed(vals) => vals.get(n),
},
}
}
}
impl<Ctx> At<Ctx> for Variant<Ctx> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
self.values.at(loc)
}
}
impl<Ctx> At<Ctx> for Value<Ctx> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
match &self.value {
ValueDef::Composite(c) => c.at(loc),
ValueDef::Variant(v) => v.at(loc),
_ => None,
}
}
}
impl<Ctx, T: At<Ctx>> At<Ctx> for Option<&T> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
self.as_ref().and_then(|v| v.at(loc))
}
}
pub trait AsLocation {
fn as_location(&self) -> Location<'_>;
}
impl AsLocation for usize {
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Usize(*self) }
}
}
impl AsLocation for &str {
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Str(self) }
}
}
impl AsLocation for String {
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Str(self) }
}
}
impl<T: AsLocation> AsLocation for &T {
fn as_location(&self) -> Location<'_> {
(*self).as_location()
}
}
#[derive(Copy, Clone)]
pub struct Location<'a> {
inner: LocationInner<'a>,
}
#[derive(Copy, Clone)]
enum LocationInner<'a> {
Usize(usize),
Str(&'a str),
}
#[cfg(test)]
mod test {
use crate::value;
use super::*;
#[test]
fn nested_accessing() {
let val = value!({hello: (1u32, true, { wibble: false, foo: {bar: 123u32}})});
assert_eq!(val.at("hello").at(0), Some(&Value::u128(1)));
assert_eq!(val.at("hello").at(1), Some(&Value::bool(true)));
assert_eq!(val.at("hello").at(2).at("wibble"), Some(&Value::bool(false)));
assert_eq!(val.at("hello").at(2).at("foo").at("bar"), Some(&Value::u128(123)));
assert_eq!(val.at("wibble").at(3), None);
assert_eq!(val.at("wibble").at("wobble").at("nope"), None);
assert_eq!(val.at("hello".to_string()).at(0), Some(&Value::u128(1)));
{
assert_eq!(val.at("hello").at(0), Some(&Value::u128(1)));
}
}
#[test]
fn accessing_variants() {
let val = value!(TheVariant { foo: 12345u32, bar: 'c' });
assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');
let val = value!(TheVariant(12345u32, 'c'));
assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
let val =
Variant::named_fields("TheVariant", [("foo", value!(12345u32)), ("bar", value!('c'))]);
assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');
let val = Variant::unnamed_fields("TheVariant", [value!(12345u32), value!('c')]);
assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
}
#[test]
fn accessing_composites() {
let val = Composite::named([("foo", value!(12345u32)), ("bar", value!('c'))]);
assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');
let val = Composite::unnamed([value!(12345u32), value!('c')]);
assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
}
}