From 82871e70c3f3dc9961ad81dc2b7b98d529e83342 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Thu, 28 Mar 2024 19:11:05 +0500 Subject: [PATCH] feat(named-args): use root shape to encode HashMap --- edgedb-protocol/src/query_arg.rs | 147 ++++++++++++++++--------------- 1 file changed, 78 insertions(+), 69 deletions(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index ea5ecca0..39919efa 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -15,7 +15,6 @@ use edgedb_errors::{ClientEncodingError, DescriptorMismatch, ProtocolError}; use edgedb_errors::{Error, ErrorKind, InvalidReferenceError}; use crate::codec::{self, build_codec, Codec, ObjectShape, ShapeElement}; -use crate::common::Cardinality; use crate::descriptors::TypePos; use crate::descriptors::{Descriptor, EnumerationTypeDescriptor}; use crate::errors; @@ -528,84 +527,94 @@ implement_tuple! {10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, } implement_tuple! {11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, } implement_tuple! {12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } +// This supertype allows user to provide both +// Into, Option>, Vec>, Option>> +// types in HashMap +pub struct UserValue(Option); - -pub struct ValueWithCardinality(Option, Cardinality); - -impl> From for ValueWithCardinality { - fn from(value: V) -> Self { - ValueWithCardinality(Some(value.into()), Cardinality::One) - } -} - -impl> From> for ValueWithCardinality - where Value: From -{ - fn from(value: Option) -> Self { - ValueWithCardinality(value.map(Value::from), Cardinality::AtMostOne) - } +impl> From for UserValue { + fn from(value: V) -> Self { + UserValue(Some(value.into())) + } } - -impl> From> for ValueWithCardinality - where Value: From +impl> From> for UserValue +where + Value: From { - fn from(value: Vec) -> Self { - ValueWithCardinality( - Some(Value::Array(value.into_iter().map(Value::from).collect())), - Cardinality::One - ) - } + fn from(value: Option) -> Self { + UserValue(value.map(Value::from)) + } } - -impl> From>> for ValueWithCardinality - where Value: From +impl> From> for UserValue +where + Value: From { - fn from(value: Option>) -> Self { - let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect())); - ValueWithCardinality(mapped, Cardinality::AtMostOne) - } + fn from(value: Vec) -> Self { + UserValue(Some(Value::Array(value.into_iter().map(Value::from).collect()))) + } } - -pub fn value_from_pairs(pairs: &HashMap) -> Value +impl> From>> for UserValue where - K: ToString, + Value: From { - let mut elements = Vec::new(); - let mut fields: Vec> = Vec::new(); - for (key, arg) in pairs.iter() { - let ValueWithCardinality(value, cd) = arg; - - elements.push(ShapeElement { - name: key.to_string(), - cardinality: Some(*cd), - flag_link: false, - flag_link_property: false, - flag_implicit: false - }); - fields.push(value.clone()); - } - - Value::Object { - shape: ObjectShape::new(elements), - fields - } + fn from(value: Option>) -> Self { + let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect())); + UserValue(mapped) + } } - -use std::collections::HashMap; -impl From<&HashMap> for Value -{ - fn from(value: &HashMap) -> Self { - value_from_pairs(value) - } +impl From for Option { + fn from(value: UserValue) -> Self { + value.0 + } } -impl QueryArgs for HashMap -where - K: ToString + Send + Sync -{ - fn encode(&self, encoder: &mut Encoder) -> Result<(), Error> { - Value::from(self).encode(encoder) - } +use std::collections::HashMap; +impl QueryArgs for HashMap<&str, UserValue> { + fn encode(&self, encoder: &mut Encoder) -> Result<(), Error> { + let target_shape = { + let root_pos = encoder.ctx.root_pos.ok_or_else(|| { + let msg = format!( + "provided {} positional arguments, but no arguments expected by the server", + self.len() + ); + ClientEncodingError::with_message(msg) + })?; + match encoder.ctx.get(root_pos)? { + Descriptor::ObjectShape(shape) => shape, + _ => return Err(ClientEncodingError::with_message("query didn't expect named arguments")) + } + }; + + let mut mapped_shapes: Vec = Vec::new(); + let mut field_values: Vec> = Vec::new(); + + for target_shape in target_shape.elements.iter() { + let user_value = self.get(target_shape.name.as_str()); + + if let Some(value) = user_value { + // these structs are actually from different crates + mapped_shapes.push(ShapeElement { + name: target_shape.name.clone(), + cardinality: target_shape.cardinality, + flag_implicit: target_shape.flag_implicit, + flag_link: target_shape.flag_link, + flag_link_property: target_shape.flag_link_property + }); + + field_values.push(value.0.clone()); + continue; + } + + let error_message = format!("argument for {} missing", target_shape.name); + return Err(ClientEncodingError::with_message(error_message)); + } + + Value::Object { + shape: ObjectShape::new(mapped_shapes), + fields: field_values + } + .encode(encoder) + } } #[cfg(feature = "macros")] @@ -617,7 +626,7 @@ macro_rules! eargs { const CAP: usize = <[()]>::len(&[$({ stringify!($key); }),*]); let mut map = std::collections::HashMap::with_capacity(CAP); $( - map.insert($key, $crate::query_arg::ValueWithCardinality::from($value)); + map.insert($key, $crate::query_arg::UserValue::from($value)); )* map }