Refactor - consolidate Field
enum to reduce redundancy. (#2104)
This commit is contained in:
parent
98c5bafd37
commit
5ebf4ee963
7 changed files with 271 additions and 325 deletions
|
@ -14,6 +14,7 @@ use crate::sql::table::Table;
|
|||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
use async_recursion::async_recursion;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
|
@ -219,36 +220,31 @@ impl Iterator {
|
|||
let vals = Value::from(vals);
|
||||
// Loop over each group clause
|
||||
for field in fields.other() {
|
||||
// Process it if it is a normal field
|
||||
if let Field::Alone(v) = field {
|
||||
match v {
|
||||
// Process the field
|
||||
if let Field::Single {
|
||||
expr,
|
||||
alias,
|
||||
} = field
|
||||
{
|
||||
let idiom = alias
|
||||
.as_ref()
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
|
||||
match expr {
|
||||
Value::Function(f) if f.is_aggregate() => {
|
||||
let x = vals
|
||||
.all()
|
||||
.get(ctx, opt, txn, None, v.to_idiom().as_ref())
|
||||
.await?;
|
||||
let x =
|
||||
vals.all().get(ctx, opt, txn, None, idiom.as_ref()).await?;
|
||||
let x = f.aggregate(x).compute(ctx, opt, txn, None).await?;
|
||||
obj.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
|
||||
obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
|
||||
}
|
||||
_ => {
|
||||
let x = vals.first();
|
||||
let x = v.compute(ctx, opt, txn, Some(&x)).await?;
|
||||
obj.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Process it if it is a aliased field
|
||||
if let Field::Alias(v, i) = field {
|
||||
match v {
|
||||
Value::Function(f) if f.is_aggregate() => {
|
||||
let x = vals.all().get(ctx, opt, txn, None, i).await?;
|
||||
let x = f.aggregate(x).compute(ctx, opt, txn, None).await?;
|
||||
obj.set(ctx, opt, txn, i, x).await?;
|
||||
}
|
||||
_ => {
|
||||
let x = vals.first();
|
||||
let x = i.compute(ctx, opt, txn, Some(&x)).await?;
|
||||
obj.set(ctx, opt, txn, i, x).await?;
|
||||
let x = if let Some(alias) = alias {
|
||||
alias.compute(ctx, opt, txn, Some(&x)).await?
|
||||
} else {
|
||||
expr.compute(ctx, opt, txn, Some(&x)).await?
|
||||
};
|
||||
obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,67 +261,40 @@ impl<'a> Document<'a> {
|
|||
};
|
||||
//
|
||||
for field in exp.other() {
|
||||
// Process it if it is a normal field
|
||||
if let Field::Alone(v) = field {
|
||||
match v {
|
||||
// Process the field
|
||||
if let Field::Single {
|
||||
expr,
|
||||
alias,
|
||||
} = field
|
||||
{
|
||||
let idiom = alias.clone().unwrap_or_else(|| expr.to_idiom());
|
||||
match expr {
|
||||
Value::Function(f) if f.is_rolling() => match f.name() {
|
||||
"count" => {
|
||||
let val = f.compute(ctx, opt, txn, doc).await?;
|
||||
self.chg(&mut ops, &act, v.to_idiom(), val);
|
||||
self.chg(&mut ops, &act, idiom, val);
|
||||
}
|
||||
"math::sum" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.chg(&mut ops, &act, v.to_idiom(), val);
|
||||
self.chg(&mut ops, &act, idiom, val);
|
||||
}
|
||||
"math::min" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.min(&mut ops, &act, v.to_idiom(), val);
|
||||
self.min(&mut ops, &act, idiom, val);
|
||||
}
|
||||
"math::max" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.max(&mut ops, &act, v.to_idiom(), val);
|
||||
self.max(&mut ops, &act, idiom, val);
|
||||
}
|
||||
"math::mean" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.mean(&mut ops, &act, v.to_idiom(), val);
|
||||
self.mean(&mut ops, &act, idiom, val);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
let val = v.compute(ctx, opt, txn, doc).await?;
|
||||
self.set(&mut ops, v.to_idiom(), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Process it if it is a aliased field
|
||||
if let Field::Alias(v, i) = field {
|
||||
match v {
|
||||
Value::Function(f) if f.is_rolling() => match f.name() {
|
||||
"count" => {
|
||||
let val = f.compute(ctx, opt, txn, doc).await?;
|
||||
self.chg(&mut ops, &act, i.to_owned(), val);
|
||||
}
|
||||
"math::sum" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.chg(&mut ops, &act, i.to_owned(), val);
|
||||
}
|
||||
"math::min" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.min(&mut ops, &act, i.to_owned(), val);
|
||||
}
|
||||
"math::max" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.max(&mut ops, &act, i.to_owned(), val);
|
||||
}
|
||||
"math::mean" => {
|
||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||
self.mean(&mut ops, &act, i.to_owned(), val);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
let val = v.compute(ctx, opt, txn, doc).await?;
|
||||
self.set(&mut ops, i.to_owned(), val);
|
||||
let val = expr.compute(ctx, opt, txn, doc).await?;
|
||||
self.set(&mut ops, idiom, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use nom::branch::alt;
|
|||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::multi::separated_list1;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Display, Formatter, Write};
|
||||
use std::ops::Deref;
|
||||
|
||||
|
@ -91,7 +92,15 @@ impl Fields {
|
|||
for v in self.other() {
|
||||
match v {
|
||||
Field::All => (),
|
||||
Field::Alone(v) => match v {
|
||||
Field::Single {
|
||||
expr,
|
||||
alias,
|
||||
} => {
|
||||
let idiom = alias
|
||||
.as_ref()
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
|
||||
match expr {
|
||||
// This expression is a grouped aggregate function
|
||||
Value::Function(f) if group && f.is_aggregate() => {
|
||||
let x = match f.args().len() {
|
||||
|
@ -102,63 +111,7 @@ impl Fields {
|
|||
};
|
||||
// Check if this is a single VALUE field expression
|
||||
match self.single().is_some() {
|
||||
false => out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?,
|
||||
true => out = x,
|
||||
}
|
||||
}
|
||||
// This expression is a multi-output graph traversal
|
||||
Value::Idiom(v) if v.is_multi_yield() => {
|
||||
// Store the different output yields here
|
||||
let mut res: Vec<(&[Part], Value)> = Vec::new();
|
||||
// Split the expression by each output alias
|
||||
for v in v.split_inclusive(Idiom::split_multi_yield) {
|
||||
// Use the last fetched value for each fetch
|
||||
let x = match res.last() {
|
||||
Some((_, r)) => r,
|
||||
None => doc,
|
||||
};
|
||||
// Continue fetching the next idiom part
|
||||
let x = x
|
||||
.get(ctx, opt, txn, Some(doc), v)
|
||||
.await?
|
||||
.compute(ctx, opt, txn, Some(doc))
|
||||
.await?
|
||||
.flatten();
|
||||
// Add the result to the temporary store
|
||||
res.push((v, x));
|
||||
}
|
||||
// Assign each fetched yield to the output
|
||||
for (p, x) in res {
|
||||
match p.last().unwrap().alias() {
|
||||
// This is an alias expression part
|
||||
Some(a) => out.set(ctx, opt, txn, a, x).await?,
|
||||
// This is the end of the expression
|
||||
None => out.set(ctx, opt, txn, v, x).await?,
|
||||
}
|
||||
}
|
||||
}
|
||||
// This expression is a normal field expression
|
||||
_ => {
|
||||
let x = v.compute(ctx, opt, txn, Some(doc)).await?;
|
||||
// Check if this is a single VALUE field expression
|
||||
match self.single().is_some() {
|
||||
false => out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?,
|
||||
true => out = x,
|
||||
}
|
||||
}
|
||||
},
|
||||
Field::Alias(v, i) => match v {
|
||||
// This expression is a grouped aggregate function
|
||||
Value::Function(f) if group && f.is_aggregate() => {
|
||||
let x = match f.args().len() {
|
||||
// If no function arguments, then compute the result
|
||||
0 => f.compute(ctx, opt, txn, Some(doc)).await?,
|
||||
// If arguments, then pass the first value through
|
||||
_ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?,
|
||||
};
|
||||
// Check if this is a single VALUE field expression
|
||||
match self.single().is_some() {
|
||||
false => out.set(ctx, opt, txn, i, x).await?,
|
||||
false => out.set(ctx, opt, txn, idiom.as_ref(), x).await?,
|
||||
true => out = x,
|
||||
}
|
||||
}
|
||||
|
@ -188,25 +141,30 @@ impl Fields {
|
|||
match p.last().unwrap().alias() {
|
||||
// This is an alias expression part
|
||||
Some(a) => {
|
||||
let v = x.clone();
|
||||
if let Some(i) = alias {
|
||||
out.set(ctx, opt, txn, i, x.clone()).await?;
|
||||
}
|
||||
out.set(ctx, opt, txn, a, x).await?;
|
||||
out.set(ctx, opt, txn, i, v).await?;
|
||||
}
|
||||
// This is the end of the expression
|
||||
None => out.set(ctx, opt, txn, i, x).await?,
|
||||
None => {
|
||||
out.set(ctx, opt, txn, alias.as_ref().unwrap_or(v), x)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// This expression is a normal field expression
|
||||
_ => {
|
||||
let x = v.compute(ctx, opt, txn, Some(doc)).await?;
|
||||
let x = expr.compute(ctx, opt, txn, Some(doc)).await?;
|
||||
// Check if this is a single VALUE field expression
|
||||
match self.single().is_some() {
|
||||
false => out.set(ctx, opt, txn, i, x).await?,
|
||||
false => out.set(ctx, opt, txn, idiom.as_ref(), x).await?,
|
||||
true => out = x,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
@ -230,25 +188,35 @@ fn field_many(i: &str) -> IResult<&str, Fields> {
|
|||
Ok((i, Fields(v, false)))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
pub enum Field {
|
||||
/// The `*` in `SELECT * FROM ...`
|
||||
#[default]
|
||||
All,
|
||||
Alone(Value),
|
||||
Alias(Value, Idiom),
|
||||
}
|
||||
|
||||
impl Default for Field {
|
||||
fn default() -> Self {
|
||||
Self::All
|
||||
}
|
||||
/// The 'rating' in `SELECT rating FROM ...`
|
||||
Single {
|
||||
expr: Value,
|
||||
/// The `quality` in `SELECT rating AS quality FROM ...`
|
||||
alias: Option<Idiom>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for Field {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::All => f.write_char('*'),
|
||||
Self::Alone(e) => Display::fmt(e, f),
|
||||
Self::Alias(e, a) => write!(f, "{e} AS {a}"),
|
||||
Self::Single {
|
||||
expr,
|
||||
alias,
|
||||
} => {
|
||||
Display::fmt(expr, f)?;
|
||||
if let Some(alias) = alias {
|
||||
f.write_str(" AS ")?;
|
||||
Display::fmt(alias, f)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,17 +231,29 @@ pub fn all(i: &str) -> IResult<&str, Field> {
|
|||
}
|
||||
|
||||
pub fn alone(i: &str) -> IResult<&str, Field> {
|
||||
let (i, f) = value(i)?;
|
||||
Ok((i, Field::Alone(f)))
|
||||
let (i, expr) = value(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Field::Single {
|
||||
expr,
|
||||
alias: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn alias(i: &str) -> IResult<&str, Field> {
|
||||
let (i, f) = value(i)?;
|
||||
let (i, expr) = value(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("AS")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, a) = idiom(i)?;
|
||||
Ok((i, Field::Alias(f, a)))
|
||||
let (i, alias) = idiom(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Field::Single {
|
||||
expr,
|
||||
alias: Some(alias),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -4,45 +4,53 @@ use crate::sql::group::Groups;
|
|||
use crate::sql::order::Orders;
|
||||
use crate::sql::split::Splits;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::Idiom;
|
||||
use nom::Err;
|
||||
use nom::Err::Failure;
|
||||
|
||||
/// Check to see whether the expression is in the SELECT clause
|
||||
fn contains_idiom(fields: &Fields, idiom: &Idiom) -> bool {
|
||||
fields.iter().any(|field| {
|
||||
match field {
|
||||
// There is a SELECT * expression, so presume everything is ok
|
||||
Field::All => true,
|
||||
// Check each field
|
||||
Field::Single {
|
||||
expr,
|
||||
alias,
|
||||
} => {
|
||||
if let Some(i) = alias {
|
||||
// This field is aliased, so check the alias name
|
||||
i.as_ref() == idiom.as_ref()
|
||||
} else {
|
||||
// This field is not aliased, so check the field value
|
||||
match expr {
|
||||
// Use raw idiom (TODO: should this use `simplify`?)
|
||||
Value::Idiom(i) => i.as_ref() == idiom.as_ref(),
|
||||
// Check the expression
|
||||
v => v.to_idiom().as_ref() == idiom.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_split_on_fields<'a>(
|
||||
i: &'a str,
|
||||
fields: &Fields,
|
||||
splits: &Option<Splits>,
|
||||
) -> Result<(), Err<Error<&'a str>>> {
|
||||
// Check to see if a ORDER BY clause has been defined
|
||||
// Check to see if a SPLIT ON clause has been defined
|
||||
if let Some(splits) = splits {
|
||||
// Loop over each of the expressions in the SPLIT ON clause
|
||||
'outer: for split in splits.iter() {
|
||||
// Loop over each of the expressions in the SELECT clause
|
||||
for field in fields.iter() {
|
||||
// Check to see whether the expression is in the SELECT clause
|
||||
match field {
|
||||
// There is a SELECT * expression, so presume everything is ok
|
||||
Field::All => break 'outer,
|
||||
// This field is aliased, so check the alias name
|
||||
Field::Alias(_, i) if i.as_ref() == split.as_ref() => continue 'outer,
|
||||
// This field is not aliased, so check the field value
|
||||
Field::Alone(v) => {
|
||||
match v {
|
||||
// If the expression in the SELECT clause is a field, check if it exists in the SPLIT ON clause
|
||||
Value::Idiom(i) if i.as_ref() == split.as_ref() => continue 'outer,
|
||||
// Otherwise check if the expression itself exists in the SPLIT ON clause
|
||||
v if v.to_idiom().as_ref() == split.as_ref() => continue 'outer,
|
||||
// If not, then this query should fail
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// If not, then this query should fail
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
for split in splits.iter() {
|
||||
if !contains_idiom(fields, &split.0) {
|
||||
// If the expression isn't specified in the SELECT clause, then error
|
||||
return Err(Failure(Error::Split(i, split.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
// This query is ok to run
|
||||
Ok(())
|
||||
}
|
||||
|
@ -55,34 +63,13 @@ pub fn check_order_by_fields<'a>(
|
|||
// Check to see if a ORDER BY clause has been defined
|
||||
if let Some(orders) = orders {
|
||||
// Loop over each of the expressions in the ORDER BY clause
|
||||
'outer: for order in orders.iter() {
|
||||
// Loop over each of the expressions in the SELECT clause
|
||||
for field in fields.iter() {
|
||||
// Check to see whether the expression is in the SELECT clause
|
||||
match field {
|
||||
// There is a SELECT * expression, so presume everything is ok
|
||||
Field::All => break 'outer,
|
||||
// This field is aliased, so check the alias name
|
||||
Field::Alias(_, i) if i.as_ref() == order.as_ref() => continue 'outer,
|
||||
// This field is not aliased, so check the field value
|
||||
Field::Alone(v) => {
|
||||
match v {
|
||||
// If the expression in the SELECT clause is a field, check if it exists in the ORDER BY clause
|
||||
Value::Idiom(i) if i.as_ref() == order.as_ref() => continue 'outer,
|
||||
// Otherwise check if the expression itself exists in the ORDER BY clause
|
||||
v if v.to_idiom().as_ref() == order.as_ref() => continue 'outer,
|
||||
// If not, then this query should fail
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// If not, then this query should fail
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
for order in orders.iter() {
|
||||
if !contains_idiom(fields, &*order) {
|
||||
// If the expression isn't specified in the SELECT clause, then error
|
||||
return Err(Failure(Error::Order(i, order.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
// This query is ok to run
|
||||
Ok(())
|
||||
}
|
||||
|
@ -95,43 +82,30 @@ pub fn check_group_by_fields<'a>(
|
|||
// Check to see if a GROUP BY clause has been defined
|
||||
if let Some(groups) = groups {
|
||||
// Loop over each of the expressions in the GROUP BY clause
|
||||
'outer: for group in groups.iter() {
|
||||
// Loop over each of the expressions in the SELECT clause
|
||||
for field in fields.iter() {
|
||||
// Check to see whether the expression is in the SELECT clause
|
||||
match field {
|
||||
// This field is aliased, so check the alias name
|
||||
Field::Alias(_, i) if i.as_ref() == group.as_ref() => continue 'outer,
|
||||
// This field is not aliased, so check the field value
|
||||
Field::Alone(v) => {
|
||||
match v {
|
||||
// If the expression in the SELECT clause is a field, check if it exists in the GROUP BY clause
|
||||
Value::Idiom(i) if i.as_ref() == group.as_ref() => continue 'outer,
|
||||
// Otherwise check if the expression itself exists in the GROUP BY clause
|
||||
v if v.to_idiom().as_ref() == group.as_ref() => continue 'outer,
|
||||
// If not, then this query should fail
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// If not, then this query should fail
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
for group in groups.iter() {
|
||||
if !contains_idiom(fields, &group.0) {
|
||||
// If the expression isn't specified in the SELECT clause, then error
|
||||
return Err(Failure(Error::Group(i, group.to_string())));
|
||||
}
|
||||
}
|
||||
// Check if this is a GROUP ALL clause or a GROUP BY clause
|
||||
if groups.len() > 0 {
|
||||
if !groups.is_empty() {
|
||||
// Loop over each of the expressions in the SELECT clause
|
||||
'outer: for field in fields.iter() {
|
||||
// Loop over each of the expressions in the GROUP BY clause
|
||||
for group in groups.iter() {
|
||||
// Check to see whether the expression is in the SELECT clause
|
||||
// Check to see whether the expression is in the GROUP BY clause or is an aggregate
|
||||
match field {
|
||||
// This field is aliased, so check the alias name
|
||||
Field::Alias(_, i) if i.as_ref() == group.as_ref() => continue 'outer,
|
||||
// Otherwise, check the type of the field value
|
||||
Field::Alias(v, _) | Field::Alone(v) => match v {
|
||||
Field::Single {
|
||||
expr,
|
||||
alias,
|
||||
} => {
|
||||
if alias.as_ref().map(|i| i.as_ref() == group.as_ref()).unwrap_or(false)
|
||||
{
|
||||
// This field is aliased, and the alias name matched
|
||||
continue 'outer;
|
||||
} else {
|
||||
match expr {
|
||||
// If the expression in the SELECT clause is a field, check to see if it exists in the GROUP BY
|
||||
Value::Idiom(i) if i == &group.0 => continue 'outer,
|
||||
// If the expression in the SELECT clause is a function, check to see if it is an aggregate function
|
||||
|
@ -142,7 +116,9 @@ pub fn check_group_by_fields<'a>(
|
|||
v if v.is_static() => continue 'outer,
|
||||
// If not, then this query should fail
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,8 +50,10 @@ impl SelectStatement {
|
|||
pub(crate) fn writeable(&self) -> bool {
|
||||
if self.expr.iter().any(|v| match v {
|
||||
Field::All => false,
|
||||
Field::Alone(v) => v.writeable(),
|
||||
Field::Alias(v, _) => v.writeable(),
|
||||
Field::Single {
|
||||
expr,
|
||||
..
|
||||
} => expr.writeable(),
|
||||
}) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ impl ser::Serializer for Serializer {
|
|||
type SerializeSeq = Impossible<Field, Error>;
|
||||
type SerializeTuple = Impossible<Field, Error>;
|
||||
type SerializeTupleStruct = Impossible<Field, Error>;
|
||||
type SerializeTupleVariant = SerializeValueIdiomTuple;
|
||||
type SerializeTupleVariant = Impossible<Field, Error>;
|
||||
type SerializeMap = Impossible<Field, Error>;
|
||||
type SerializeStruct = Impossible<Field, Error>;
|
||||
type SerializeStructVariant = Impossible<Field, Error>;
|
||||
type SerializeStructVariant = SerializeValueIdiomTuple;
|
||||
|
||||
const EXPECTED: &'static str = "an enum `Field`";
|
||||
|
||||
|
@ -39,77 +39,89 @@ impl ser::Serializer for Serializer {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
match variant {
|
||||
"Alone" => Ok(Field::Alone(value.serialize(ser::value::Serializer.wrap())?)),
|
||||
variant => {
|
||||
Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
match variant {
|
||||
"Alias" => Ok(SerializeValueIdiomTuple::default()),
|
||||
variant => Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`"))),
|
||||
"Single" => Ok(SerializeValueIdiomTuple::default()),
|
||||
variant => Err(Error::custom(format!("unexpected struct variant `{name}::{variant}`"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct SerializeValueIdiomTuple {
|
||||
index: usize,
|
||||
value: Option<Value>,
|
||||
idiom: Option<Idiom>,
|
||||
idiom: Option<Option<Idiom>>,
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleVariant for SerializeValueIdiomTuple {
|
||||
impl serde::ser::SerializeStructVariant for SerializeValueIdiomTuple {
|
||||
type Ok = Field;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
match self.index {
|
||||
0 => {
|
||||
match key {
|
||||
"expr" => {
|
||||
self.value = Some(value.serialize(ser::value::Serializer.wrap())?);
|
||||
}
|
||||
1 => {
|
||||
self.idiom = Some(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?));
|
||||
"alias" => {
|
||||
self.idiom = Some(value.serialize(SerializeOptionIdiom.wrap())?);
|
||||
}
|
||||
index => {
|
||||
return Err(Error::custom(format!("unexpected `Field::Alias` index `{index}`")));
|
||||
key => {
|
||||
return Err(Error::custom(format!("unexpected `Field::Single` field `{key}`")));
|
||||
}
|
||||
}
|
||||
self.index += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
match (self.value, self.idiom) {
|
||||
(Some(value), Some(idiom)) => Ok(Field::Alias(value, idiom)),
|
||||
_ => Err(Error::custom("`Field::Alias` missing required value(s)")),
|
||||
(Some(expr), Some(alias)) => Ok(Field::Single {
|
||||
expr,
|
||||
alias,
|
||||
}),
|
||||
_ => Err(Error::custom("`Field::Single` missing required value(s)")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SerializeOptionIdiom;
|
||||
|
||||
impl ser::Serializer for SerializeOptionIdiom {
|
||||
type Ok = Option<Idiom>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Self::Ok, Error>;
|
||||
type SerializeTuple = Impossible<Self::Ok, Error>;
|
||||
type SerializeTupleStruct = Impossible<Self::Ok, Error>;
|
||||
type SerializeTupleVariant = Impossible<Self::Ok, Error>;
|
||||
type SerializeMap = Impossible<Self::Ok, Error>;
|
||||
type SerializeStruct = Impossible<Self::Ok, Error>;
|
||||
type SerializeStructVariant = Impossible<Self::Ok, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an enum `Option<Idiom>`";
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let idiom = Idiom(value.serialize(ser::part::vec::Serializer.wrap())?);
|
||||
Ok(Some(idiom))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -124,14 +136,20 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn alone() {
|
||||
let field = Field::Alone(Default::default());
|
||||
let field = Field::Single {
|
||||
expr: Default::default(),
|
||||
alias: None,
|
||||
};
|
||||
let serialized = field.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(field, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias() {
|
||||
let field = Field::Alias(Default::default(), Default::default());
|
||||
let field = Field::Single {
|
||||
expr: Default::default(),
|
||||
alias: Some(Default::default()),
|
||||
};
|
||||
let serialized = field.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(field, serialized);
|
||||
}
|
||||
|
|
|
@ -2225,6 +2225,7 @@ impl Value {
|
|||
Value::None => true,
|
||||
Value::Null => true,
|
||||
Value::Bool(_) => true,
|
||||
Value::Bytes(_) => true,
|
||||
Value::Uuid(_) => true,
|
||||
Value::Number(_) => true,
|
||||
Value::Strand(_) => true,
|
||||
|
|
Loading…
Reference in a new issue