use crate::query::queryable::Queryable;
use crate::query::QueryPath;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, PartialEq)]
pub struct State<'a, T: Queryable> {
pub data: Data<'a, T>,
pub root: &'a T,
}
impl<'a, T: Queryable> Display for State<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.data)
}
}
impl<'a, T: Queryable> From<&'a T> for State<'a, T> {
fn from(root: &'a T) -> Self {
State::root(root)
}
}
impl<'a, T: Queryable> State<'a, T> {
pub fn bool(b: bool, root: &T) -> State<T> {
State::data(root, Data::Value(b.into()))
}
pub fn i64(i: i64, root: &T) -> State<T> {
State::data(root, Data::Value(i.into()))
}
pub fn str(v: &str, root: &'a T) -> State<'a, T> {
State::data(root, Data::Value(v.into()))
}
pub fn shift_to_root(self) -> State<'a, T> {
State::root(self.root)
}
pub fn root(root: &'a T) -> Self {
State {
root,
data: Data::new_ref(Pointer::new(root, "$".to_string())),
}
}
pub fn nothing(root: &'a T) -> Self {
State {
root,
data: Data::Nothing,
}
}
pub fn data(root: &'a T, data: Data<'a, T>) -> Self {
State { root, data }
}
pub fn ok_ref(self) -> Option<Vec<Pointer<'a, T>>> {
self.data.ok_ref()
}
pub fn ok_val(self) -> Option<T> {
match self.data {
Data::Value(v) => Some(v),
_ => None,
}
}
pub fn is_nothing(&self) -> bool {
matches!(&self.data, Data::Nothing)
}
pub fn reduce(self, other: State<'a, T>) -> State<'a, T> {
State {
root: self.root,
data: self.data.reduce(other.data),
}
}
pub fn flat_map<F>(self, f: F) -> State<'a, T>
where
F: Fn(Pointer<'a, T>) -> Data<'a, T>,
{
State {
root: self.root,
data: self.data.flat_map(f),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Data<'a, T: Queryable> {
Ref(Pointer<'a, T>),
Refs(Vec<Pointer<'a, T>>),
Value(T),
Nothing,
}
impl<'a, T: Queryable> Display for Data<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Data::Ref(p) => write!(f, "&{}", p),
Data::Refs(p) => write!(
f,
"{}",
p.iter()
.map(|ptr| ptr.to_string())
.collect::<Vec<_>>()
.join("; ")
),
Data::Value(v) => write!(f, "{:?}", v),
Data::Nothing => write!(f, "Nothing"),
}
}
}
impl<'a, T: Queryable> Default for Data<'a, T> {
fn default() -> Self {
Data::Nothing
}
}
impl<'a, T: Queryable> Data<'a, T> {
pub fn reduce(self, other: Data<'a, T>) -> Data<'a, T> {
match (self, other) {
(Data::Ref(data), Data::Ref(data2)) => Data::Refs(vec![data, data2]),
(Data::Ref(data), Data::Refs(data_vec)) => {
Data::Refs(vec![data].into_iter().chain(data_vec).collect())
}
(Data::Refs(data_vec), Data::Ref(data)) => {
Data::Refs(data_vec.into_iter().chain(vec![data]).collect())
}
(Data::Refs(data_vec), Data::Refs(data_vec2)) => {
Data::Refs(data_vec.into_iter().chain(data_vec2).collect())
}
(d @ (Data::Ref(_) | Data::Refs(..)), Data::Nothing) => d,
(Data::Nothing, d @ (Data::Ref(_) | Data::Refs(..))) => d,
_ => Data::Nothing,
}
}
pub fn flat_map<F>(self, f: F) -> Data<'a, T>
where
F: Fn(Pointer<'a, T>) -> Data<'a, T>,
{
match self {
Data::Ref(data) => f(data),
Data::Refs(data_vec) => Data::Refs(
data_vec
.into_iter()
.flat_map(|data| match f(data) {
Data::Ref(data) => vec![data],
Data::Refs(data_vec) => data_vec,
_ => vec![],
})
.collect::<Vec<_>>(),
),
_ => Data::Nothing,
}
}
pub fn ok_ref(self) -> Option<Vec<Pointer<'a, T>>> {
match self {
Data::Ref(data) => Some(vec![data]),
Data::Refs(data) => Some(data),
_ => None,
}
}
pub fn ok_val(self) -> Option<T> {
match self {
Data::Value(v) => Some(v),
_ => None,
}
}
pub fn new_ref(data: Pointer<'a, T>) -> Data<'a, T> {
Data::Ref(data)
}
pub fn new_refs(data: Vec<Pointer<'a, T>>) -> Data<'a, T> {
Data::Refs(data)
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Pointer<'a, T: Queryable> {
pub inner: &'a T,
pub path: QueryPath,
}
impl<'a, T: Queryable> Display for Pointer<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}='{}'", self.inner, self.path)
}
}
impl<'a, T: Queryable> Pointer<'a, T> {
pub fn new(inner: &'a T, path: QueryPath) -> Self {
Pointer { inner, path }
}
pub fn key(inner: &'a T, path: QueryPath, key: &str) -> Self {
let path = if key.starts_with("'") && key.ends_with("'") {
format!("{}[{}]", path, key)
} else {
format!("{}['{}']", path, key)
};
Pointer { inner, path }
}
pub fn idx(inner: &'a T, path: QueryPath, index: usize) -> Self {
Pointer {
inner,
path: format!("{}[{}]", path, index),
}
}
pub fn empty(inner: &'a T) -> Self {
Pointer {
inner,
path: String::new(),
}
}
pub fn is_internal(&self) -> bool {
self.path.is_empty()
}
}