Refactor - consolidate Field enum to reduce redundancy. (#2104)

This commit is contained in:
Finn Bear 2023-06-10 13:23:22 -07:00 committed by GitHub
parent 98c5bafd37
commit 5ebf4ee963
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 271 additions and 325 deletions

View file

@ -14,6 +14,7 @@ use crate::sql::table::Table;
use crate::sql::thing::Thing; use crate::sql::thing::Thing;
use crate::sql::value::Value; use crate::sql::value::Value;
use async_recursion::async_recursion; use async_recursion::async_recursion;
use std::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::mem; use std::mem;
@ -219,36 +220,31 @@ impl Iterator {
let vals = Value::from(vals); let vals = Value::from(vals);
// Loop over each group clause // Loop over each group clause
for field in fields.other() { for field in fields.other() {
// Process it if it is a normal field // Process the field
if let Field::Alone(v) = field { if let Field::Single {
match v { 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() => { Value::Function(f) if f.is_aggregate() => {
let x = vals let x =
.all() vals.all().get(ctx, opt, txn, None, idiom.as_ref()).await?;
.get(ctx, opt, txn, None, v.to_idiom().as_ref())
.await?;
let x = f.aggregate(x).compute(ctx, opt, txn, None).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 = vals.first();
let x = v.compute(ctx, opt, txn, Some(&x)).await?; let x = if let Some(alias) = alias {
obj.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?; alias.compute(ctx, opt, txn, Some(&x)).await?
} } else {
} expr.compute(ctx, opt, txn, Some(&x)).await?
} };
// Process it if it is a aliased field obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
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?;
} }
} }
} }

View file

@ -261,67 +261,40 @@ impl<'a> Document<'a> {
}; };
// //
for field in exp.other() { for field in exp.other() {
// Process it if it is a normal field // Process the field
if let Field::Alone(v) = field { if let Field::Single {
match v { 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() { Value::Function(f) if f.is_rolling() => match f.name() {
"count" => { "count" => {
let val = f.compute(ctx, opt, txn, doc).await?; 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" => { "math::sum" => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?; 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" => { "math::min" => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?; 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" => { "math::max" => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?; 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" => { "math::mean" => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?; 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!(), _ => unreachable!(),
}, },
_ => { _ => {
let val = v.compute(ctx, opt, txn, doc).await?; let val = expr.compute(ctx, opt, txn, doc).await?;
self.set(&mut ops, v.to_idiom(), val); self.set(&mut ops, 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);
} }
} }
} }

View file

@ -14,6 +14,7 @@ use nom::branch::alt;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
use nom::multi::separated_list1; use nom::multi::separated_list1;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::{self, Display, Formatter, Write}; use std::fmt::{self, Display, Formatter, Write};
use std::ops::Deref; use std::ops::Deref;
@ -91,122 +92,79 @@ impl Fields {
for v in self.other() { for v in self.other() {
match v { match v {
Field::All => (), Field::All => (),
Field::Alone(v) => match v { Field::Single {
// This expression is a grouped aggregate function expr,
Value::Function(f) if group && f.is_aggregate() => { alias,
let x = match f.args().len() { } => {
// If no function arguments, then compute the result let idiom = alias
0 => f.compute(ctx, opt, txn, Some(doc)).await?, .as_ref()
// If arguments, then pass the first value through .map(Cow::Borrowed)
_ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?, .unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
}; match expr {
// Check if this is a single VALUE field expression // This expression is a grouped aggregate function
match self.single().is_some() { Value::Function(f) if group && f.is_aggregate() => {
false => out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?, let x = match f.args().len() {
true => out = x, // 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
// This expression is a multi-output graph traversal _ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?,
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 // Check if this is a single VALUE field expression
let x = x match self.single().is_some() {
.get(ctx, opt, txn, Some(doc), v) false => out.set(ctx, opt, txn, idiom.as_ref(), x).await?,
.await? true => out = x,
.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 multi-output graph traversal
// This expression is a normal field expression Value::Idiom(v) if v.is_multi_yield() => {
_ => { // Store the different output yields here
let x = v.compute(ctx, opt, txn, Some(doc)).await?; let mut res: Vec<(&[Part], Value)> = Vec::new();
// Check if this is a single VALUE field expression // Split the expression by each output alias
match self.single().is_some() { for v in v.split_inclusive(Idiom::split_multi_yield) {
false => out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?, // Use the last fetched value for each fetch
true => out = x, let x = match res.last() {
} Some((_, r)) => r,
} None => doc,
}, };
Field::Alias(v, i) => match v { // Continue fetching the next idiom part
// This expression is a grouped aggregate function let x = x
Value::Function(f) if group && f.is_aggregate() => { .get(ctx, opt, txn, Some(doc), v)
let x = match f.args().len() { .await?
// If no function arguments, then compute the result .compute(ctx, opt, txn, Some(doc))
0 => f.compute(ctx, opt, txn, Some(doc)).await?, .await?
// If arguments, then pass the first value through .flatten();
_ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?, // Add the result to the temporary store
}; res.push((v, x));
// Check if this is a single VALUE field expression }
match self.single().is_some() { // Assign each fetched yield to the output
false => out.set(ctx, opt, txn, i, x).await?, for (p, x) in res {
true => out = x, match p.last().unwrap().alias() {
} // This is an alias expression part
} Some(a) => {
// This expression is a multi-output graph traversal if let Some(i) = alias {
Value::Idiom(v) if v.is_multi_yield() => { out.set(ctx, opt, txn, i, x.clone()).await?;
// Store the different output yields here }
let mut res: Vec<(&[Part], Value)> = Vec::new(); out.set(ctx, opt, txn, a, x).await?;
// Split the expression by each output alias }
for v in v.split_inclusive(Idiom::split_multi_yield) { // This is the end of the expression
// Use the last fetched value for each fetch None => {
let x = match res.last() { out.set(ctx, opt, txn, alias.as_ref().unwrap_or(v), x)
Some((_, r)) => r, .await?
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) => {
let v = x.clone();
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?, }
// This expression is a normal field expression
_ => {
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, idiom.as_ref(), x).await?,
true => out = x,
} }
} }
} }
// 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, i, x).await?,
true => out = x,
}
}
},
} }
} }
Ok(out) Ok(out)
@ -230,25 +188,35 @@ fn field_many(i: &str) -> IResult<&str, Fields> {
Ok((i, Fields(v, false))) 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 { pub enum Field {
/// The `*` in `SELECT * FROM ...`
#[default]
All, All,
Alone(Value), /// The 'rating' in `SELECT rating FROM ...`
Alias(Value, Idiom), Single {
} expr: Value,
/// The `quality` in `SELECT rating AS quality FROM ...`
impl Default for Field { alias: Option<Idiom>,
fn default() -> Self { },
Self::All
}
} }
impl Display for Field { impl Display for Field {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::All => f.write_char('*'), Self::All => f.write_char('*'),
Self::Alone(e) => Display::fmt(e, f), Self::Single {
Self::Alias(e, a) => write!(f, "{e} AS {a}"), 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> { pub fn alone(i: &str) -> IResult<&str, Field> {
let (i, f) = value(i)?; let (i, expr) = value(i)?;
Ok((i, Field::Alone(f))) Ok((
i,
Field::Single {
expr,
alias: None,
},
))
} }
pub fn alias(i: &str) -> IResult<&str, Field> { pub fn alias(i: &str) -> IResult<&str, Field> {
let (i, f) = value(i)?; let (i, expr) = value(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("AS")(i)?; let (i, _) = tag_no_case("AS")(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, a) = idiom(i)?; let (i, alias) = idiom(i)?;
Ok((i, Field::Alias(f, a))) Ok((
i,
Field::Single {
expr,
alias: Some(alias),
},
))
} }
#[cfg(test)] #[cfg(test)]

View file

@ -4,43 +4,51 @@ use crate::sql::group::Groups;
use crate::sql::order::Orders; use crate::sql::order::Orders;
use crate::sql::split::Splits; use crate::sql::split::Splits;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::Idiom;
use nom::Err; use nom::Err;
use nom::Err::Failure; 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>( pub fn check_split_on_fields<'a>(
i: &'a str, i: &'a str,
fields: &Fields, fields: &Fields,
splits: &Option<Splits>, splits: &Option<Splits>,
) -> Result<(), Err<Error<&'a str>>> { ) -> 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 { if let Some(splits) = splits {
// Loop over each of the expressions in the SPLIT ON clause // Loop over each of the expressions in the SPLIT ON clause
'outer: for split in splits.iter() { for split in splits.iter() {
// Loop over each of the expressions in the SELECT clause if !contains_idiom(fields, &split.0) {
for field in fields.iter() { // If the expression isn't specified in the SELECT clause, then error
// Check to see whether the expression is in the SELECT clause return Err(Failure(Error::Split(i, split.to_string())));
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
_ => (),
}
} }
// 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 // This query is ok to run
@ -55,32 +63,11 @@ pub fn check_order_by_fields<'a>(
// Check to see if a ORDER BY clause has been defined // Check to see if a ORDER BY clause has been defined
if let Some(orders) = orders { if let Some(orders) = orders {
// Loop over each of the expressions in the ORDER BY clause // Loop over each of the expressions in the ORDER BY clause
'outer: for order in orders.iter() { for order in orders.iter() {
// Loop over each of the expressions in the SELECT clause if !contains_idiom(fields, &*order) {
for field in fields.iter() { // If the expression isn't specified in the SELECT clause, then error
// Check to see whether the expression is in the SELECT clause return Err(Failure(Error::Order(i, order.to_string())));
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
_ => (),
}
} }
// 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 // This query is ok to run
@ -95,54 +82,43 @@ pub fn check_group_by_fields<'a>(
// Check to see if a GROUP BY clause has been defined // Check to see if a GROUP BY clause has been defined
if let Some(groups) = groups { if let Some(groups) = groups {
// Loop over each of the expressions in the GROUP BY clause // Loop over each of the expressions in the GROUP BY clause
'outer: for group in groups.iter() { for group in groups.iter() {
// Loop over each of the expressions in the SELECT clause if !contains_idiom(fields, &group.0) {
for field in fields.iter() { // If the expression isn't specified in the SELECT clause, then error
// Check to see whether the expression is in the SELECT clause return Err(Failure(Error::Group(i, group.to_string())));
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
_ => (),
}
} }
// 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 // 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 // Loop over each of the expressions in the SELECT clause
'outer: for field in fields.iter() { 'outer: for field in fields.iter() {
// Loop over each of the expressions in the GROUP BY clause // Loop over each of the expressions in the GROUP BY clause
for group in groups.iter() { 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 { match field {
// This field is aliased, so check the alias name Field::Single {
Field::Alias(_, i) if i.as_ref() == group.as_ref() => continue 'outer, expr,
// Otherwise, check the type of the field value alias,
Field::Alias(v, _) | Field::Alone(v) => match v { } => {
// If the expression in the SELECT clause is a field, check to see if it exists in the GROUP BY if alias.as_ref().map(|i| i.as_ref() == group.as_ref()).unwrap_or(false)
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 // This field is aliased, and the alias name matched
Value::Function(f) if f.is_aggregate() => continue 'outer, continue 'outer;
// Otherwise check if the expression itself exists in the GROUP BY clause } else {
v if v.to_idiom() == group.0 => continue 'outer, match expr {
// Check if this is a static value which can be used in the GROUP BY clause // If the expression in the SELECT clause is a field, check to see if it exists in the GROUP BY
v if v.is_static() => continue 'outer, Value::Idiom(i) if i == &group.0 => continue 'outer,
// If not, then this query should fail // If the expression in the SELECT clause is a function, check to see if it is an aggregate function
_ => (), Value::Function(f) if f.is_aggregate() => continue 'outer,
}, // Otherwise check if the expression itself exists in the GROUP BY clause
v if v.to_idiom() == group.0 => continue 'outer,
// Check if this is a static value which can be used in the GROUP BY clause
v if v.is_static() => continue 'outer,
// If not, then this query should fail
_ => (),
}
}
}
_ => (), _ => (),
} }
} }

View file

@ -50,8 +50,10 @@ impl SelectStatement {
pub(crate) fn writeable(&self) -> bool { pub(crate) fn writeable(&self) -> bool {
if self.expr.iter().any(|v| match v { if self.expr.iter().any(|v| match v {
Field::All => false, Field::All => false,
Field::Alone(v) => v.writeable(), Field::Single {
Field::Alias(v, _) => v.writeable(), expr,
..
} => expr.writeable(),
}) { }) {
return true; return true;
} }

View file

@ -19,10 +19,10 @@ impl ser::Serializer for Serializer {
type SerializeSeq = Impossible<Field, Error>; type SerializeSeq = Impossible<Field, Error>;
type SerializeTuple = Impossible<Field, Error>; type SerializeTuple = Impossible<Field, Error>;
type SerializeTupleStruct = Impossible<Field, Error>; type SerializeTupleStruct = Impossible<Field, Error>;
type SerializeTupleVariant = SerializeValueIdiomTuple; type SerializeTupleVariant = Impossible<Field, Error>;
type SerializeMap = Impossible<Field, Error>; type SerializeMap = Impossible<Field, Error>;
type SerializeStruct = Impossible<Field, Error>; type SerializeStruct = Impossible<Field, Error>;
type SerializeStructVariant = Impossible<Field, Error>; type SerializeStructVariant = SerializeValueIdiomTuple;
const EXPECTED: &'static str = "an enum `Field`"; const EXPECTED: &'static str = "an enum `Field`";
@ -39,77 +39,89 @@ impl ser::Serializer for Serializer {
} }
} }
#[inline] fn serialize_struct_variant(
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(
self, self,
name: &'static str, name: &'static str,
_variant_index: u32, _variant_index: u32,
variant: &'static str, variant: &'static str,
_len: usize, _len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> { ) -> Result<Self::SerializeStructVariant, Self::Error> {
match variant { match variant {
"Alias" => Ok(SerializeValueIdiomTuple::default()), "Single" => Ok(SerializeValueIdiomTuple::default()),
variant => Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`"))), variant => Err(Error::custom(format!("unexpected struct variant `{name}::{variant}`"))),
} }
} }
} }
#[derive(Default)] #[derive(Default)]
pub(super) struct SerializeValueIdiomTuple { pub(super) struct SerializeValueIdiomTuple {
index: usize,
value: Option<Value>, 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 Ok = Field;
type Error = Error; 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 where
T: Serialize + ?Sized, T: Serialize + ?Sized,
{ {
match self.index { match key {
0 => { "expr" => {
self.value = Some(value.serialize(ser::value::Serializer.wrap())?); self.value = Some(value.serialize(ser::value::Serializer.wrap())?);
} }
1 => { "alias" => {
self.idiom = Some(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?)); self.idiom = Some(value.serialize(SerializeOptionIdiom.wrap())?);
} }
index => { key => {
return Err(Error::custom(format!("unexpected `Field::Alias` index `{index}`"))); return Err(Error::custom(format!("unexpected `Field::Single` field `{key}`")));
} }
} }
self.index += 1;
Ok(()) Ok(())
} }
fn end(self) -> Result<Self::Ok, Self::Error> { fn end(self) -> Result<Self::Ok, Self::Error> {
match (self.value, self.idiom) { match (self.value, self.idiom) {
(Some(value), Some(idiom)) => Ok(Field::Alias(value, idiom)), (Some(expr), Some(alias)) => Ok(Field::Single {
_ => Err(Error::custom("`Field::Alias` missing required value(s)")), 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -124,14 +136,20 @@ mod tests {
#[test] #[test]
fn alone() { fn alone() {
let field = Field::Alone(Default::default()); let field = Field::Single {
expr: Default::default(),
alias: None,
};
let serialized = field.serialize(Serializer.wrap()).unwrap(); let serialized = field.serialize(Serializer.wrap()).unwrap();
assert_eq!(field, serialized); assert_eq!(field, serialized);
} }
#[test] #[test]
fn alias() { 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(); let serialized = field.serialize(Serializer.wrap()).unwrap();
assert_eq!(field, serialized); assert_eq!(field, serialized);
} }

View file

@ -2225,6 +2225,7 @@ impl Value {
Value::None => true, Value::None => true,
Value::Null => true, Value::Null => true,
Value::Bool(_) => true, Value::Bool(_) => true,
Value::Bytes(_) => true,
Value::Uuid(_) => true, Value::Uuid(_) => true,
Value::Number(_) => true, Value::Number(_) => true,
Value::Strand(_) => true, Value::Strand(_) => true,