use serde::{Deserialize, Serialize};
use std::{fmt::Display, marker::PhantomData};
use crate::{IndexNavigable, Navigable};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum VariantTagType {
External,
Internal,
Adjacent,
Untagged,
}
#[derive(Debug, Serialize, PartialEq)]
#[serde(transparent)]
pub struct KeyPath<Root, Value> {
pub path: Vec<KeyPathElement>,
#[serde(skip)]
root: PhantomData<Root>,
#[serde(skip)]
value: PhantomData<Value>,
}
impl<Root, Value> Clone for KeyPath<Root, Value> {
fn clone(&self) -> Self {
Self {
path: self.path.clone(),
root: self.root,
value: self.value,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum KeyPathElement {
Field { key: &'static str },
Variant {
key: &'static str,
tag: VariantTagType,
},
Index { key: usize },
StringKey { key: String },
}
impl Display for KeyPathElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
KeyPathElement::Field { key } => write!(f, "{}", key),
KeyPathElement::Variant { key, .. } => write!(f, "{}", key),
KeyPathElement::Index { key } => write!(f, "[{}]", key),
KeyPathElement::StringKey { key } => write!(f, "[\"{}\"]", key),
}
}
}
impl<Root, Value> KeyPath<Root, Value> {
pub fn field(name: &'static str) -> Self {
Self {
path: vec![KeyPathElement::Field { key: name }],
root: PhantomData::<Root>,
value: PhantomData::<Value>,
}
}
pub fn variant(key: &'static str, tag: VariantTagType) -> Self {
Self {
path: vec![KeyPathElement::Variant { key, tag }],
root: PhantomData::<Root>,
value: PhantomData::<Value>,
}
}
pub fn tuple_variant(key: &'static str, index: &'static str, tag: VariantTagType) -> Self {
Self {
path: vec![
KeyPathElement::Variant { key, tag },
KeyPathElement::Field { key: index },
],
root: PhantomData::<Root>,
value: PhantomData::<Value>,
}
}
pub fn index(index: usize) -> Self {
Self {
path: vec![KeyPathElement::Index { key: index }],
root: PhantomData::<Root>,
value: PhantomData::<Value>,
}
}
pub fn string_key<K: Display>(key: K) -> Self {
Self {
path: vec![KeyPathElement::StringKey {
key: format!("{key}"),
}],
root: PhantomData::<Root>,
value: PhantomData::<Value>,
}
}
pub fn unit() -> Self {
KeyPath {
path: vec![],
root: PhantomData::<Root>,
value: PhantomData::<Value>,
}
}
pub fn appending<T>(&self, next: &KeyPath<Value, T>) -> KeyPath<Root, T> {
let mut path = self.path.clone();
path.extend(next.path.clone());
KeyPath {
path,
root: PhantomData::<Root>,
value: PhantomData::<T>,
}
}
fn prepending<T>(&self, previous: &KeyPath<T, Root>) -> KeyPath<T, Value> {
previous.appending(self)
}
pub fn dangerously_construct_from_path(path: Vec<KeyPathElement>) -> Self {
Self {
path,
root: PhantomData::<Root>,
value: PhantomData::<Value>,
}
}
pub fn fields(&self) -> Value::Reflection<Root>
where
Value: Navigable,
{
Value::append_to_keypath(self)
}
pub fn at<K, V>(&self, index: K) -> KeyPath<Root, V>
where
Value: IndexNavigable<K, V>,
{
Value::index_keypath_segment(index).prepending(self)
}
}
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct KeyPathFrom<Root> {
pub path: Vec<KeyPathElement>,
root: PhantomData<Root>,
}
impl<T> Display for KeyPathFrom<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, ".")?;
for (ix, p) in self.path.iter().enumerate() {
write!(f, "{}", p)?;
if ix + 1 != self.path.len()
&& matches!(
p,
KeyPathElement::Field { .. } | KeyPathElement::Variant { .. }
)
{
write!(f, ".")?;
}
}
Ok(())
}
}
impl<Root> KeyPathFrom<Root> {
pub fn prepending<Base>(&self, keypath: &KeyPath<Base, Root>) -> KeyPathFrom<Base> {
let mut path = keypath.path.clone();
path.extend(self.path.clone());
KeyPathFrom {
path,
root: PhantomData::<Base>,
}
}
pub fn is_subpath_of(&self, other: &Self) -> bool {
if other.path.len() <= self.path.len() {
return false;
}
for (own_element, other_element) in self.path.iter().zip(other.path.iter()) {
if own_element != other_element {
return false;
}
}
true
}
pub fn downcast<T>(&self) -> KeyPath<Root, T> {
KeyPath {
path: self.path.clone(),
root: PhantomData::<Root>,
value: PhantomData::<T>,
}
}
}
impl<Root, T> From<KeyPath<Root, T>> for KeyPathFrom<Root> {
fn from(value: KeyPath<Root, T>) -> Self {
KeyPathFrom {
path: value.path,
root: PhantomData::<Root>,
}
}
}
impl<Root, T> PartialEq<KeyPath<Root, T>> for KeyPathFrom<Root> {
fn eq(&self, other: &KeyPath<Root, T>) -> bool {
self.path == other.path
}
}