Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support named arguments #304

Merged
merged 16 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion edgedb-protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ bitflags = "2.4.0"
serde = {version="1.0.190", optional=true}

[features]
default = []
default = ["macros"]
with-num-bigint = ["num-bigint", "num-traits"]
with-bigdecimal = ["bigdecimal", "num-bigint", "num-traits"]
with-chrono = ["chrono"]
all-types = ["with-num-bigint", "with-bigdecimal", "with-chrono"]
with-serde = ["serde"]
macros = []
MrFoxPro marked this conversation as resolved.
Show resolved Hide resolved

[dev-dependencies]
rand = "0.8"
Expand Down
2 changes: 1 addition & 1 deletion edgedb-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ pub mod descriptors;
pub mod value;
pub mod codec;
pub mod queryable;
#[macro_use]
pub mod query_arg;
pub mod model;


pub use query_result::QueryResult;
108 changes: 107 additions & 1 deletion edgedb-protocol/src/query_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use edgedb_errors::ParameterTypeMismatchError;
use edgedb_errors::{ClientEncodingError, DescriptorMismatch, ProtocolError};
use edgedb_errors::{Error, ErrorKind, InvalidReferenceError};

use crate::codec::{self, build_codec, Codec};
use crate::codec::{self, build_codec, Codec, ObjectShape, ShapeElement};
use crate::descriptors::TypePos;
use crate::descriptors::{Descriptor, EnumerationTypeDescriptor};
use crate::errors;
Expand Down Expand Up @@ -526,3 +526,109 @@ implement_tuple! {9, T0, T1, T2, T3, T4, T5, T6, T7, T8, }
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
aljazerzen marked this conversation as resolved.
Show resolved Hide resolved
// Into<Value>, Option<Into<Value>>, Vec<Into<Value>>, Option<Vec<Into<Value>>>
// types in HashMap
MrFoxPro marked this conversation as resolved.
Show resolved Hide resolved
pub struct UserValue(Option<Value>);
aljazerzen marked this conversation as resolved.
Show resolved Hide resolved

impl<V: Into<Value>> From<V> for UserValue {
fn from(value: V) -> Self {
UserValue(Some(value.into()))
}
}
MrFoxPro marked this conversation as resolved.
Show resolved Hide resolved
impl<V: Into<Value>> From<Option<V>> for UserValue
where
Value: From<V>
{
fn from(value: Option<V>) -> Self {
UserValue(value.map(Value::from))
}
}
impl<V: Into<Value>> From<Vec<V>> for UserValue
where
Value: From<V>
{
fn from(value: Vec<V>) -> Self {
UserValue(Some(Value::Array(value.into_iter().map(Value::from).collect())))
}
}
impl<V: Into<Value>> From<Option<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()));
UserValue(mapped)
}
}
impl From<UserValue> for Option<Value> {
fn from(value: UserValue) -> Self {
value.0
}
}

use std::collections::HashMap;
impl QueryArgs for HashMap<&str, UserValue> {
fn encode(&self, encoder: &mut Encoder) -> Result<(), Error> {
let target_shape = {
MrFoxPro marked this conversation as resolved.
Show resolved Hide resolved
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")]
#[macro_export]
macro_rules! eargs {
aljazerzen marked this conversation as resolved.
Show resolved Hide resolved
($($key:expr => $value:expr,)+) => { $crate::eargs!($($key => $value),+) };
($($key:expr => $value:expr),*) => {
{
const CAP: usize = <[()]>::len(&[$({ stringify!($key); }),*]);
let mut map = std::collections::HashMap::with_capacity(CAP);
$(
map.insert($key, $crate::query_arg::UserValue::from($value));
)*
map
}
};
}