use super::{Composite, Value, ValueDef, Variant};
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 super::*;
#[test]
fn nested_accessing() {
let val = Value::named_composite([(
"hello",
Value::unnamed_composite([
Value::u128(1),
Value::bool(true),
Value::named_composite([
("wibble", Value::bool(false)),
("foo", Value::named_composite([("bar", Value::u128(123))])),
]),
]),
)]);
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)));
#[allow(clippy::needless_borrow)]
{
assert_eq!(val.at(&"hello").at(&0), Some(&Value::u128(1)));
}
}
#[test]
fn accessing_variants() {
let val = Value::named_variant(
"TheVariant",
[("foo", Value::u128(12345)), ("bar", Value::char('c'))],
);
assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');
let val = Value::unnamed_variant("TheVariant", [Value::u128(12345), Value::char('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::u128(12345)), ("bar", Value::char('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::u128(12345), Value::char('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::u128(12345)), ("bar", Value::char('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::u128(12345), Value::char('c')]);
assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
}
}