use std::ops;
use crate::{Cons, Value};
pub trait Index: private::Sealed {
#[doc(hidden)]
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;
}
mod private {
pub trait Sealed {}
impl Sealed for usize {}
impl Sealed for str {}
impl Sealed for String {}
impl<'a, T: ?Sized> Sealed for &'a T where T: Sealed {}
impl Sealed for super::Value {}
}
impl Index for usize {
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
match v {
Value::Vector(elements) => elements.get(*self),
Value::Cons(cons) => {
let mut cursor = cons;
for _ in 0..*self {
match cursor.cdr() {
Value::Cons(next) => cursor = next,
_ => return None,
}
}
Some(cursor.car())
}
_ => None,
}
}
}
fn match_pair_name<'a>(name: &str, pair: &'a Cons) -> Option<&'a Value> {
match pair.car() {
Value::Cons(inner) if inner.car().as_name() == Some(name) => Some(inner.cdr()),
_ => None,
}
}
impl Index for str {
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
match v {
Value::Cons(pair) => pair.iter().find_map(|e| match_pair_name(self, e)),
_ => None,
}
}
}
impl Index for String {
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
self[..].index_into(v)
}
}
impl<'a, T: ?Sized> Index for &'a T
where
T: Index,
{
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
(**self).index_into(v)
}
}
fn match_pair_key<'a>(value: &Value, pair: &'a Cons) -> Option<&'a Value> {
match pair.car() {
Value::Cons(inner) if inner.car() == value => Some(inner.cdr()),
_ => None,
}
}
impl Index for Value {
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
match v {
Value::Cons(pair) => pair.iter().find_map(|e| match_pair_key(self, e)),
_ => None,
}
}
}
impl<I> ops::Index<I> for Value
where
I: Index,
{
type Output = Value;
fn index(&self, index: I) -> &Value {
static NIL: Value = Value::Nil;
index.index_into(self).unwrap_or(&NIL)
}
}