use crate::sql::{fmt::Fmt, strand::no_nul_bytes, Graph, Ident, Idiom, Number, Value};
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Write;
use std::str;
use super::fmt::{is_pretty, pretty_indent};
#[revisioned(revision = 2)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub enum Part {
All,
Flatten,
Last,
First,
Field(Ident),
Index(Number),
Where(Value),
Graph(Graph),
Value(Value),
Start(Value),
Method(#[serde(with = "no_nul_bytes")] String, Vec<Value>),
#[revision(start = 2)]
Destructure(Vec<DestructurePart>),
Optional,
}
impl From<i32> for Part {
fn from(v: i32) -> Self {
Self::Index(v.into())
}
}
impl From<isize> for Part {
fn from(v: isize) -> Self {
Self::Index(v.into())
}
}
impl From<usize> for Part {
fn from(v: usize) -> Self {
Self::Index(v.into())
}
}
impl From<String> for Part {
fn from(v: String) -> Self {
Self::Field(v.into())
}
}
impl From<Number> for Part {
fn from(v: Number) -> Self {
Self::Index(v)
}
}
impl From<Ident> for Part {
fn from(v: Ident) -> Self {
Self::Field(v)
}
}
impl From<Graph> for Part {
fn from(v: Graph) -> Self {
Self::Graph(v)
}
}
impl From<&str> for Part {
fn from(v: &str) -> Self {
match v.parse::<isize>() {
Ok(v) => Self::from(v),
_ => Self::from(v.to_owned()),
}
}
}
impl Part {
pub(crate) fn writeable(&self) -> bool {
match self {
Part::Start(v) => v.writeable(),
Part::Where(v) => v.writeable(),
Part::Value(v) => v.writeable(),
Part::Method(_, v) => v.iter().any(Value::writeable),
_ => false,
}
}
pub(crate) fn alias(&self) -> Option<&Idiom> {
match self {
Part::Graph(v) => v.alias.as_ref(),
_ => None,
}
}
}
impl fmt::Display for Part {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Part::All => f.write_str("[*]"),
Part::Last => f.write_str("[$]"),
Part::First => f.write_str("[0]"),
Part::Start(v) => write!(f, "{v}"),
Part::Field(v) => write!(f, ".{v}"),
Part::Flatten => f.write_str("…"),
Part::Index(v) => write!(f, "[{v}]"),
Part::Where(v) => write!(f, "[WHERE {v}]"),
Part::Graph(v) => write!(f, "{v}"),
Part::Value(v) => write!(f, "[{v}]"),
Part::Method(v, a) => write!(f, ".{v}({})", Fmt::comma_separated(a)),
Part::Destructure(v) => {
f.write_str(".{")?;
if !is_pretty() {
f.write_char(' ')?;
}
if !v.is_empty() {
let indent = pretty_indent();
write!(f, "{}", Fmt::pretty_comma_separated(v))?;
drop(indent);
}
if is_pretty() {
f.write_char('}')
} else {
f.write_str(" }")
}
}
Part::Optional => write!(f, "?"),
}
}
}
pub trait Next<'a> {
fn next(&'a self) -> &[Part];
}
impl<'a> Next<'a> for &'a [Part] {
fn next(&'a self) -> &'a [Part] {
match self.len() {
0 => &[],
_ => &self[1..],
}
}
}
pub trait NextMethod<'a> {
fn next_method(&'a self) -> &[Part];
}
impl<'a> NextMethod<'a> for &'a [Part] {
fn next_method(&'a self) -> &'a [Part] {
match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
None => &[],
Some(i) => &self[i..],
}
}
}
impl<'a> NextMethod<'a> for &'a Idiom {
fn next_method(&'a self) -> &'a [Part] {
match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
None => &[],
Some(i) => &self[i..],
}
}
}
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub enum DestructurePart {
All(Ident),
Field(Ident),
Aliased(Ident, Idiom),
Destructure(Ident, Vec<DestructurePart>),
}
impl DestructurePart {
pub fn field(&self) -> &Ident {
match self {
DestructurePart::All(v) => v,
DestructurePart::Field(v) => v,
DestructurePart::Aliased(v, _) => v,
DestructurePart::Destructure(v, _) => v,
}
}
pub fn path(&self) -> Vec<Part> {
match self {
DestructurePart::All(v) => vec![Part::Field(v.clone()), Part::All],
DestructurePart::Field(v) => vec![Part::Field(v.clone())],
DestructurePart::Aliased(_, v) => v.0.clone(),
DestructurePart::Destructure(f, d) => {
vec![Part::Field(f.clone()), Part::Destructure(d.clone())]
}
}
}
}
impl fmt::Display for DestructurePart {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DestructurePart::All(fd) => write!(f, "{fd}.*"),
DestructurePart::Field(fd) => write!(f, "{fd}"),
DestructurePart::Aliased(fd, v) => write!(f, "{fd}: {v}"),
DestructurePart::Destructure(fd, d) => {
write!(f, "{fd}{}", Part::Destructure(d.clone()))
}
}
}
}