Skip to content

Commit

Permalink
feat(named-args): use root shape to encode HashMap
Browse files Browse the repository at this point in the history
  • Loading branch information
MrFoxPro committed Mar 28, 2024
1 parent 2ff429c commit 0e39a7c
Showing 1 changed file with 79 additions and 69 deletions.
148 changes: 79 additions & 69 deletions edgedb-protocol/src/query_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -528,84 +527,95 @@ 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<Value>, Option<Into<Value>>, Vec<Into<Value>>, Option<Vec<Into<Value>>>
// types in HashMap
#[derive(Clone)]
pub struct UserValue(Option<Value>);


pub struct ValueWithCardinality(Option<Value>, Cardinality);

impl<V: Into<Value>> From<V> for ValueWithCardinality {
fn from(value: V) -> Self {
ValueWithCardinality(Some(value.into()), Cardinality::One)
}
}

impl<V: Into<Value>> From<Option<V>> for ValueWithCardinality
where Value: From<V>
{
fn from(value: Option<V>) -> Self {
ValueWithCardinality(value.map(Value::from), Cardinality::AtMostOne)
}
impl<V: Into<Value>> From<V> for UserValue {
fn from(value: V) -> Self {
UserValue(Some(value.into()))
}
}

impl<V: Into<Value>> From<Vec<V>> for ValueWithCardinality
where Value: From<V>
impl<V: Into<Value>> From<Option<V>> for UserValue
where
Value: From<V>
{
fn from(value: Vec<V>) -> Self {
ValueWithCardinality(
Some(Value::Array(value.into_iter().map(Value::from).collect())),
Cardinality::One
)
}
fn from(value: Option<V>) -> Self {
UserValue(value.map(Value::from))
}
}

impl<V: Into<Value>> From<Option<Vec<V>>> for ValueWithCardinality
where Value: From<V>
impl<V: Into<Value>> From<Vec<V>> for UserValue
where
Value: From<V>
{
fn from(value: Option<Vec<V>>) -> Self {
let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect()));
ValueWithCardinality(mapped, Cardinality::AtMostOne)
}
fn from(value: Vec<V>) -> Self {
UserValue(Some(Value::Array(value.into_iter().map(Value::from).collect())))
}
}

pub fn value_from_pairs<K>(pairs: &HashMap<K, ValueWithCardinality>) -> Value
impl<V: Into<Value>> From<Option<Vec<V>>> for UserValue
where
K: ToString,
Value: From<V>
{
let mut elements = Vec::new();
let mut fields: Vec<Option<Value>> = 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<Vec<V>>) -> Self {
let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect()));
UserValue(mapped)
}
}

use std::collections::HashMap;
impl<K: ToString> From<&HashMap<K, ValueWithCardinality>> for Value
{
fn from(value: &HashMap<K, ValueWithCardinality>) -> Self {
value_from_pairs(value)
}
impl From<UserValue> for Option<Value> {
fn from(value: UserValue) -> Self {
value.0
}
}

impl<K> QueryArgs for HashMap<K, ValueWithCardinality>
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<ShapeElement> = Vec::new();
let mut field_values: Vec<Option<Value>> = 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")]
Expand All @@ -617,7 +627,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
}
Expand Down

0 comments on commit 0e39a7c

Please sign in to comment.