From 05c793d816cb23f5e73b383badb5bcfa00ac3add Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Wed, 27 Mar 2024 18:04:30 +0500 Subject: [PATCH 01/14] feat(named-args): initial commit --- edgedb-protocol/Cargo.toml | 2 + edgedb-protocol/src/lib.rs | 2 +- edgedb-protocol/src/query_arg.rs | 94 +++++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/edgedb-protocol/Cargo.toml b/edgedb-protocol/Cargo.toml index 936dbeb0..180df699 100644 --- a/edgedb-protocol/Cargo.toml +++ b/edgedb-protocol/Cargo.toml @@ -22,6 +22,7 @@ chrono = {version="0.4.23", optional=true, features=["std"], default-features=fa edgedb-errors = {path = "../edgedb-errors", version = "0.4.0" } bitflags = "2.4.0" serde = {version="1.0.190", optional=true} +indexmap = {version = "2.2", optional=true} [features] default = [] @@ -30,6 +31,7 @@ with-bigdecimal = ["bigdecimal", "num-bigint", "num-traits"] with-chrono = ["chrono"] all-types = ["with-num-bigint", "with-bigdecimal", "with-chrono"] with-serde = ["serde"] +macros = ["indexmap"] [dev-dependencies] rand = "0.8" diff --git a/edgedb-protocol/src/lib.rs b/edgedb-protocol/src/lib.rs index e3e0c0f6..0159eaf6 100644 --- a/edgedb-protocol/src/lib.rs +++ b/edgedb-protocol/src/lib.rs @@ -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; diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 2cf317c5..1eb9f654 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -14,7 +14,8 @@ 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::common::Cardinality; use crate::descriptors::TypePos; use crate::descriptors::{Descriptor, EnumerationTypeDescriptor}; use crate::errors; @@ -526,3 +527,94 @@ 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, } + + + +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 ValueWithCardinality + where Value: From +{ + fn from(value: Vec) -> Self { + ValueWithCardinality(Some(Value::Array(value.into_iter().map(Value::from).collect())), Cardinality::One) + } +} + +impl> From>> for ValueWithCardinality + 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) + } +} + +pub fn object_from_pairs(iter: impl IntoIterator) -> Value +where + K: ToString, + V: Into +{ + let mut elements = Vec::new(); + let mut fields: Vec> = Vec::new(); + for (key, arg) in iter.into_iter() { + let ValueWithCardinality(value, cd) = arg.into(); + + elements.push(ShapeElement { + name: key.to_string(), + cardinality: Some(cd), + flag_link: false, + flag_link_property: false, + flag_implicit: false + }); + fields.push(value); + } + + Value::Object { + shape: ObjectShape::new(elements), + fields + } +} + +#[cfg(feature = "macros")] +pub mod macros { + use crate::query_arg::{Value, ValueWithCardinality, object_from_pairs}; + + pub struct EdgedbArgsIndexMap<'i>(pub indexmap::IndexMap<&'i str, ValueWithCardinality>); + impl EdgedbArgsIndexMap<'_> { + pub fn to_value(self) -> Value { + Value::from(self) + } + } + + impl From> for Value { + fn from(value: EdgedbArgsIndexMap) -> Self { + object_from_pairs(value.0) + } + } + + #[macro_export] + macro_rules! eargs { + ($($key:expr => $value:expr),*) => { + edgedb_protocol::query_arg::macros::EdgedbArgsIndexMap(indexmap::indexmap! { + $($key => edgedb_protocol::query_arg::ValueWithCardinality::from($value)),* + }) + }; + } +} \ No newline at end of file From ab1db254a3e23cb7726c62f9bad6a44edb1666fb Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Wed, 27 Mar 2024 18:17:57 +0500 Subject: [PATCH 02/14] chore(named-args): fix identation --- edgedb-protocol/src/query_arg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 1eb9f654..fefd842f 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -574,7 +574,7 @@ where let mut elements = Vec::new(); let mut fields: Vec> = Vec::new(); for (key, arg) in iter.into_iter() { - let ValueWithCardinality(value, cd) = arg.into(); + let ValueWithCardinality(value, cd) = arg.into(); elements.push(ShapeElement { name: key.to_string(), From cb5ad3e18d59e68e3a80da868021ce38e20affab Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Wed, 27 Mar 2024 23:42:53 +0500 Subject: [PATCH 03/14] feat(named-args): use std::collections::HashMap instead of `indexmap` crate --- edgedb-protocol/Cargo.toml | 5 ++--- edgedb-protocol/src/query_arg.rs | 38 ++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/edgedb-protocol/Cargo.toml b/edgedb-protocol/Cargo.toml index 180df699..00d40cb4 100644 --- a/edgedb-protocol/Cargo.toml +++ b/edgedb-protocol/Cargo.toml @@ -22,16 +22,15 @@ chrono = {version="0.4.23", optional=true, features=["std"], default-features=fa edgedb-errors = {path = "../edgedb-errors", version = "0.4.0" } bitflags = "2.4.0" serde = {version="1.0.190", optional=true} -indexmap = {version = "2.2", 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 = ["indexmap"] +macros = [] [dev-dependencies] rand = "0.8" diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index fefd842f..0a57341c 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -594,27 +594,33 @@ where #[cfg(feature = "macros")] pub mod macros { - use crate::query_arg::{Value, ValueWithCardinality, object_from_pairs}; - - pub struct EdgedbArgsIndexMap<'i>(pub indexmap::IndexMap<&'i str, ValueWithCardinality>); - impl EdgedbArgsIndexMap<'_> { - pub fn to_value(self) -> Value { - Value::from(self) - } - } + use crate::query_arg::{object_from_pairs, Value, ValueWithCardinality}; - impl From> for Value { - fn from(value: EdgedbArgsIndexMap) -> Self { - object_from_pairs(value.0) - } - } + pub struct EdgedbArgsIndexMap<'i>(pub std::collections::HashMap<&'i str, ValueWithCardinality>); + impl EdgedbArgsIndexMap<'_> { + pub fn to_value(self) -> Value { + Value::from(self) + } + } + + impl From> for Value { + fn from(value: EdgedbArgsIndexMap) -> Self { + object_from_pairs(value.0) + } + } #[macro_export] macro_rules! eargs { + ($($key:expr => $value:expr,)+) => { $crate::eargs!($($key => $value),+) }; ($($key:expr => $value:expr),*) => { - edgedb_protocol::query_arg::macros::EdgedbArgsIndexMap(indexmap::indexmap! { - $($key => edgedb_protocol::query_arg::ValueWithCardinality::from($value)),* - }) + { + const CAP: usize = <[()]>::len(&[$({ stringify!($key); }),*]); + let mut map = std::collections::HashMap::with_capacity(CAP); + $( + map.insert($key, $crate::query_arg::ValueWithCardinality::from($value)); + )* + $crate::query_arg::macros::EdgedbArgsIndexMap(map) + } }; } } \ No newline at end of file From 659964d5250a7ed58b42478705d25d978a9d8958 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Thu, 28 Mar 2024 00:58:03 +0500 Subject: [PATCH 04/14] feat(named-args): refactor, implement QueryArgs for HashMap --- edgedb-protocol/src/query_arg.rs | 101 ++++++++++++++++--------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 0a57341c..37793e8f 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -532,58 +532,57 @@ implement_tuple! {12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } pub struct ValueWithCardinality(Option, Cardinality); -impl> From for ValueWithCardinality -{ - fn from(value: V) -> Self { - ValueWithCardinality(Some(value.into()), Cardinality::One) - } +impl> From for ValueWithCardinality { + fn from(value: V) -> Self { + ValueWithCardinality(Some(value.into()), Cardinality::One) + } } -impl> From> for ValueWithCardinality - where Value: From +impl> From> for ValueWithCardinality + where Value: From { - fn from(value: Option) -> Self { - ValueWithCardinality(value.map(Value::from), Cardinality::AtMostOne) - } + fn from(value: Option) -> Self { + ValueWithCardinality(value.map(Value::from), Cardinality::AtMostOne) + } } -impl> From> for ValueWithCardinality +impl> From> for ValueWithCardinality where Value: From { - fn from(value: Vec) -> Self { - ValueWithCardinality(Some(Value::Array(value.into_iter().map(Value::from).collect())), Cardinality::One) - } + fn from(value: Vec) -> Self { + ValueWithCardinality( + Some(Value::Array(value.into_iter().map(Value::from).collect())), + Cardinality::One + ) + } } -impl> From>> for ValueWithCardinality +impl> From>> for ValueWithCardinality 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: Option>) -> Self { + let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect())); + ValueWithCardinality(mapped, Cardinality::AtMostOne) + } } -pub fn object_from_pairs(iter: impl IntoIterator) -> Value +pub fn value_from_pairs(pairs: &HashMap) -> Value where K: ToString, - V: Into { let mut elements = Vec::new(); let mut fields: Vec> = Vec::new(); - for (key, arg) in iter.into_iter() { - let ValueWithCardinality(value, cd) = arg.into(); + 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); + name: key.to_string(), + cardinality: Some(*cd), + flag_link: false, + flag_link_property: false, + flag_implicit: false + }); + fields.push(value.clone()); } Value::Object { @@ -592,25 +591,27 @@ where } } -#[cfg(feature = "macros")] -pub mod macros { - use crate::query_arg::{object_from_pairs, Value, ValueWithCardinality}; - - pub struct EdgedbArgsIndexMap<'i>(pub std::collections::HashMap<&'i str, ValueWithCardinality>); - impl EdgedbArgsIndexMap<'_> { - pub fn to_value(self) -> Value { - Value::from(self) - } +use std::collections::HashMap; +impl From<&HashMap> for Value +{ + fn from(value: &HashMap) -> Self { + value_from_pairs(value) } +} - impl From> for Value { - fn from(value: EdgedbArgsIndexMap) -> Self { - object_from_pairs(value.0) - } - } +impl QueryArgs for HashMap +where + K: ToString + Send + Sync +{ + fn encode(&self, encoder: &mut Encoder) -> Result<(), Error> { + Value::from(self).encode(encoder) + } +} - #[macro_export] - macro_rules! eargs { +#[cfg(feature = "macros")] +pub mod macros { + #[macro_export] + macro_rules! eargs { ($($key:expr => $value:expr,)+) => { $crate::eargs!($($key => $value),+) }; ($($key:expr => $value:expr),*) => { { @@ -619,8 +620,8 @@ pub mod macros { $( map.insert($key, $crate::query_arg::ValueWithCardinality::from($value)); )* - $crate::query_arg::macros::EdgedbArgsIndexMap(map) + map } }; } -} \ No newline at end of file +} From bcb9a0909041c8f81ae54c7cf462d5ae160a33b4 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Thu, 28 Mar 2024 01:10:16 +0500 Subject: [PATCH 05/14] feat(named-args): remove `pub mod macros` --- edgedb-protocol/src/query_arg.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 37793e8f..1a16147e 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -609,19 +609,17 @@ where } #[cfg(feature = "macros")] -pub mod macros { - #[macro_export] - macro_rules! eargs { - ($($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::ValueWithCardinality::from($value)); - )* - map - } - }; - } +#[macro_export] +macro_rules! eargs { + ($($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::ValueWithCardinality::from($value)); + )* + map + } + }; } From 2ff429c2f7eae7ec0b7a559b62935013ac25f1a2 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Thu, 28 Mar 2024 01:56:02 +0500 Subject: [PATCH 06/14] feat(named-args): convert identation to spaces --- edgedb-protocol/src/query_arg.rs | 78 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 1a16147e..ea5ecca0 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -533,75 +533,75 @@ implement_tuple! {12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } pub struct ValueWithCardinality(Option, Cardinality); impl> From for ValueWithCardinality { - fn from(value: V) -> Self { - ValueWithCardinality(Some(value.into()), Cardinality::One) - } + 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) - } + fn from(value: Option) -> Self { + ValueWithCardinality(value.map(Value::from), Cardinality::AtMostOne) + } } impl> From> for ValueWithCardinality where Value: From { - fn from(value: Vec) -> Self { - ValueWithCardinality( - Some(Value::Array(value.into_iter().map(Value::from).collect())), - Cardinality::One - ) - } + fn from(value: Vec) -> Self { + ValueWithCardinality( + Some(Value::Array(value.into_iter().map(Value::from).collect())), + Cardinality::One + ) + } } impl> From>> for ValueWithCardinality 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: Option>) -> Self { + let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect())); + ValueWithCardinality(mapped, Cardinality::AtMostOne) + } } pub fn value_from_pairs(pairs: &HashMap) -> Value where - K: ToString, + K: ToString, { - 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 - } + 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 + } } use std::collections::HashMap; impl From<&HashMap> for Value { - fn from(value: &HashMap) -> Self { + fn from(value: &HashMap) -> Self { value_from_pairs(value) - } + } } impl QueryArgs for HashMap where - K: ToString + Send + Sync + K: ToString + Send + Sync { fn encode(&self, encoder: &mut Encoder) -> Result<(), Error> { Value::from(self).encode(encoder) From 82871e70c3f3dc9961ad81dc2b7b98d529e83342 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Thu, 28 Mar 2024 19:11:05 +0500 Subject: [PATCH 07/14] 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 } From 295db400fea3f589c2c1e1a21d8c526bd5e57230 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Tue, 9 Apr 2024 19:30:00 +0500 Subject: [PATCH 08/14] feat(named-args): expose macro without special feature --- edgedb-protocol/Cargo.toml | 3 +-- edgedb-protocol/src/query_arg.rs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/edgedb-protocol/Cargo.toml b/edgedb-protocol/Cargo.toml index 00d40cb4..936dbeb0 100644 --- a/edgedb-protocol/Cargo.toml +++ b/edgedb-protocol/Cargo.toml @@ -24,13 +24,12 @@ bitflags = "2.4.0" serde = {version="1.0.190", optional=true} [features] -default = ["macros"] +default = [] 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 = [] [dev-dependencies] rand = "0.8" diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 39919efa..4fb3fbcc 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -617,7 +617,6 @@ impl QueryArgs for HashMap<&str, UserValue> { } } -#[cfg(feature = "macros")] #[macro_export] macro_rules! eargs { ($($key:expr => $value:expr,)+) => { $crate::eargs!($($key => $value),+) }; From b1abcb9a8679a976bc5af80af5f0d39e972963d2 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Tue, 9 Apr 2024 19:34:25 +0500 Subject: [PATCH 09/14] feat(named-args): indent using spaces --- edgedb-protocol/src/query_arg.rs | 128 +++++++++++++++---------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 4fb3fbcc..c319ff9a 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -533,88 +533,88 @@ implement_tuple! {12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } pub struct UserValue(Option); impl> From for UserValue { - fn from(value: V) -> Self { - UserValue(Some(value.into())) - } + fn from(value: V) -> Self { + UserValue(Some(value.into())) + } } impl> From> for UserValue where - Value: From + Value: From { - fn from(value: Option) -> Self { - UserValue(value.map(Value::from)) - } + fn from(value: Option) -> Self { + UserValue(value.map(Value::from)) + } } impl> From> for UserValue where - Value: From + Value: From { - fn from(value: Vec) -> Self { - UserValue(Some(Value::Array(value.into_iter().map(Value::from).collect()))) - } + fn from(value: Vec) -> Self { + UserValue(Some(Value::Array(value.into_iter().map(Value::from).collect()))) + } } impl> From>> for UserValue where - Value: From + Value: From { - fn from(value: Option>) -> Self { - let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect())); - UserValue(mapped) - } + fn from(value: Option>) -> Self { + let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect())); + UserValue(mapped) + } } impl From for Option { - fn from(value: UserValue) -> Self { - value.0 - } + 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 = { - 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) - } + 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) + } } #[macro_export] From 4fc736b74913451b2ea6e0dabf2d660c1414ea9c Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Tue, 9 Apr 2024 19:35:42 +0500 Subject: [PATCH 10/14] feat(named-args): add doc comment suggested by @aljazerzen --- edgedb-protocol/src/query_arg.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index c319ff9a..9eb1a63d 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -527,9 +527,10 @@ 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 +/// An optional [Value] that can be constructed from `impl Into`, +/// `Option>`, `Vec>` or +/// `Option>>`. +/// Used by [eargs!] macro. pub struct UserValue(Option); impl> From for UserValue { From c85f5077a2815d6f8f3ad769c46b8a7014152840 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Tue, 9 Apr 2024 20:18:26 +0500 Subject: [PATCH 11/14] feat(named-args): handle empty hashmap --- edgedb-protocol/src/query_arg.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 9eb1a63d..30a971df 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -572,10 +572,14 @@ impl From for Option { use std::collections::HashMap; impl QueryArgs for HashMap<&str, UserValue> { fn encode(&self, encoder: &mut Encoder) -> Result<(), Error> { + if self.len() == 0 && encoder.ctx.root_pos.is_none() { + return Ok(()); + } + 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", + "provided {} positional arguments, but no arguments were expected by the server", self.len() ); ClientEncodingError::with_message(msg) From 8f63e880c551db17efcd5936f9f2516e3cde868d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Mur=20Er=C5=BEen?= Date: Tue, 9 Apr 2024 18:54:04 +0200 Subject: [PATCH 12/14] a test --- edgedb-tokio/tests/func/client.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/edgedb-tokio/tests/func/client.rs b/edgedb-tokio/tests/func/client.rs index 64df8524..5fd80e56 100644 --- a/edgedb-tokio/tests/func/client.rs +++ b/edgedb-tokio/tests/func/client.rs @@ -1,3 +1,4 @@ +use edgedb_protocol::{eargs, value::Value}; use edgedb_tokio::Client; use edgedb_errors::NoDataError; use futures_util::stream::{self, StreamExt}; @@ -42,6 +43,21 @@ async fn simple() -> anyhow::Result<()> { client.execute("SELECT 1+1", &()).await?; client.execute("START MIGRATION TO {}; ABORT MIGRATION", &()).await?; + let value = client.query_required_single::( + "select ( + std::array_join(>$msg1, ' ') + ++ ($question ?? ' the ultimate question of life') + ++ ': ' + ++ $answer + );", + &eargs! { + "msg1" => vec!["the".to_string(), "answer".to_string(), "to".to_string()], + "question" => None::, + "answer" => 42, + } + ).await.unwrap(); + assert_eq!(value.as_str(), "the answer to the ultimate question of life: 42"); + Ok(()) } From 088faeaebc1dd5c87b8ab7a02aa2ca74b33ba89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Mur=20Er=C5=BEen?= Date: Tue, 9 Apr 2024 19:04:30 +0200 Subject: [PATCH 13/14] actually make the test work --- edgedb-tokio/tests/func/client.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/edgedb-tokio/tests/func/client.rs b/edgedb-tokio/tests/func/client.rs index 5fd80e56..259ce4ca 100644 --- a/edgedb-tokio/tests/func/client.rs +++ b/edgedb-tokio/tests/func/client.rs @@ -1,4 +1,4 @@ -use edgedb_protocol::{eargs, value::Value}; +use edgedb_protocol::eargs; use edgedb_tokio::Client; use edgedb_errors::NoDataError; use futures_util::stream::{self, StreamExt}; @@ -48,12 +48,12 @@ async fn simple() -> anyhow::Result<()> { std::array_join(>$msg1, ' ') ++ ($question ?? ' the ultimate question of life') ++ ': ' - ++ $answer + ++ $answer );", &eargs! { "msg1" => vec!["the".to_string(), "answer".to_string(), "to".to_string()], "question" => None::, - "answer" => 42, + "answer" => 42 as i64, } ).await.unwrap(); assert_eq!(value.as_str(), "the answer to the ultimate question of life: 42"); From f2286e0d008f9427d71b2096f39d03dc35d353c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Mur=20Er=C5=BEen?= Date: Fri, 12 Apr 2024 14:55:26 +0200 Subject: [PATCH 14/14] organize --- edgedb-protocol/src/codec.rs | 40 +++++---- edgedb-protocol/src/lib.rs | 15 ++-- edgedb-protocol/src/query_arg.rs | 112 +------------------------ edgedb-protocol/src/query_result.rs | 2 +- edgedb-protocol/src/value_opt.rs | 125 ++++++++++++++++++++++++++++ edgedb-tokio/tests/func/client.rs | 6 +- 6 files changed, 160 insertions(+), 140 deletions(-) create mode 100644 edgedb-protocol/src/value_opt.rs diff --git a/edgedb-protocol/src/codec.rs b/edgedb-protocol/src/codec.rs index 685e417a..8e3bdad0 100644 --- a/edgedb-protocol/src/codec.rs +++ b/edgedb-protocol/src/codec.rs @@ -785,24 +785,28 @@ impl Codec for ArrayAdapter { impl<'a> From<&'a [descriptors::ShapeElement]> for ObjectShape { fn from(shape: &'a [descriptors::ShapeElement]) -> ObjectShape { ObjectShape(Arc::new(ObjectShapeInfo { - elements: shape.iter().map(|e| { - let descriptors::ShapeElement { - flag_implicit, - flag_link_property, - flag_link, - cardinality, - name, - type_pos: _, - } = e; - ShapeElement { - flag_implicit: *flag_implicit, - flag_link_property: *flag_link_property, - flag_link: *flag_link, - cardinality: *cardinality, - name: name.clone(), - } - }).collect(), - })) + elements: shape.iter().map(ShapeElement::from).collect(), + })) + } +} + +impl<'a> From<&'a descriptors::ShapeElement> for ShapeElement { + fn from(e: &'a descriptors::ShapeElement) -> ShapeElement { + let descriptors::ShapeElement { + flag_implicit, + flag_link_property, + flag_link, + cardinality, + name, + type_pos: _, + } = e; + ShapeElement { + flag_implicit: *flag_implicit, + flag_link_property: *flag_link_property, + flag_link: *flag_link, + cardinality: *cardinality, + name: name.clone(), + } } } diff --git a/edgedb-protocol/src/lib.rs b/edgedb-protocol/src/lib.rs index 0159eaf6..a50a05b6 100644 --- a/edgedb-protocol/src/lib.rs +++ b/edgedb-protocol/src/lib.rs @@ -59,19 +59,20 @@ pub enum Value { mod query_result; // sealed trait should remain non-public -pub mod encoding; +pub mod client_message; +pub mod codec; pub mod common; +pub mod descriptors; +pub mod encoding; +pub mod error_response; +pub mod errors; pub mod features; +pub mod queryable; pub mod serialization; -pub mod client_message; pub mod server_message; -pub mod errors; -pub mod error_response; -pub mod descriptors; pub mod value; -pub mod codec; -pub mod queryable; #[macro_use] +pub mod value_opt; pub mod query_arg; pub mod model; diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index bc4c4657..91eb4017 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -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, ObjectShape, ShapeElement}; +use crate::codec::{self, build_codec, Codec}; use crate::descriptors::TypePos; use crate::descriptors::{Descriptor, EnumerationTypeDescriptor}; use crate::errors; @@ -528,113 +528,3 @@ 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, } - -/// An optional [Value] that can be constructed from `impl Into`, -/// `Option>`, `Vec>` or -/// `Option>>`. -/// Used by [eargs!] macro. -pub struct UserValue(Option); - -impl> From for UserValue { - fn from(value: V) -> Self { - UserValue(Some(value.into())) - } -} -impl> From> for UserValue -where - Value: From -{ - fn from(value: Option) -> Self { - UserValue(value.map(Value::from)) - } -} -impl> From> for UserValue -where - Value: From -{ - fn from(value: Vec) -> Self { - UserValue(Some(Value::Array(value.into_iter().map(Value::from).collect()))) - } -} -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())); - UserValue(mapped) - } -} -impl From for Option { - 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> { - if self.len() == 0 && encoder.ctx.root_pos.is_none() { - return Ok(()); - } - - let target_shape = { - let root_pos = encoder.ctx.root_pos.ok_or_else(|| { - let msg = format!( - "provided {} positional arguments, but no arguments were 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) - } -} - -#[macro_export] -macro_rules! eargs { - ($($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 - } - }; -} diff --git a/edgedb-protocol/src/query_result.rs b/edgedb-protocol/src/query_result.rs index 91be7964..6fe8cf91 100644 --- a/edgedb-protocol/src/query_result.rs +++ b/edgedb-protocol/src/query_result.rs @@ -11,7 +11,7 @@ use edgedb_errors::{ProtocolEncodingError, DescriptorMismatch}; use crate::codec::Codec; use crate::queryable::{Queryable, Decoder, DescriptorContext}; -use crate::descriptors::{TypePos}; +use crate::descriptors::TypePos; use crate::value::Value; pub trait Sealed: Sized {} diff --git a/edgedb-protocol/src/value_opt.rs b/edgedb-protocol/src/value_opt.rs new file mode 100644 index 00000000..1df03cf9 --- /dev/null +++ b/edgedb-protocol/src/value_opt.rs @@ -0,0 +1,125 @@ +use std::collections::HashMap; + +use edgedb_errors::{ClientEncodingError, Error, ErrorKind}; + +use crate::codec::{ObjectShape, ShapeElement}; +use crate::descriptors::Descriptor; +use crate::query_arg::{Encoder, QueryArgs}; +use crate::value::Value; + +/// An optional [Value] that can be constructed from `impl Into`, +/// `Option>`, `Vec>` or +/// `Option>>`. +/// Used by [eargs!] macro. +pub struct ValueOpt(Option); + +impl> From for ValueOpt { + fn from(value: V) -> Self { + ValueOpt(Some(value.into())) + } +} +impl> From> for ValueOpt +where + Value: From, +{ + fn from(value: Option) -> Self { + ValueOpt(value.map(Value::from)) + } +} +impl> From> for ValueOpt +where + Value: From, +{ + fn from(value: Vec) -> Self { + ValueOpt(Some(Value::Array( + value.into_iter().map(Value::from).collect(), + ))) + } +} +impl> From>> for ValueOpt +where + Value: From, +{ + fn from(value: Option>) -> Self { + let mapped = value.map(|value| Value::Array(value.into_iter().map(Value::from).collect())); + ValueOpt(mapped) + } +} +impl From for Option { + fn from(value: ValueOpt) -> Self { + value.0 + } +} + +impl QueryArgs for HashMap<&str, ValueOpt> { + fn encode(&self, encoder: &mut Encoder) -> Result<(), Error> { + if self.len() == 0 && encoder.ctx.root_pos.is_none() { + return Ok(()); + } + + let root_pos = encoder.ctx.root_pos.ok_or_else(|| { + ClientEncodingError::with_message(format!( + "provided {} named arguments, but no arguments were expected by the server", + self.len() + )) + })?; + + let Descriptor::ObjectShape(target_shape) = encoder.ctx.get(root_pos)? else { + return Err(ClientEncodingError::with_message( + "query didn't expect named arguments", + )); + }; + + let mut shape_elements: Vec = Vec::new(); + let mut fields: Vec> = Vec::new(); + + for param_descriptor in target_shape.elements.iter() { + let value = self.get(param_descriptor.name.as_str()); + + let Some(value) = value else { + return Err(ClientEncodingError::with_message(format!( + "argument for ${} missing", + param_descriptor.name + ))); + }; + + shape_elements.push(ShapeElement::from(param_descriptor)); + fields.push(value.0.clone()); + } + + Value::Object { + shape: ObjectShape::new(shape_elements), + fields, + } + .encode(encoder) + } +} + +/// Constructs named query arguments that implement [QueryArgs] so they can be passed +/// into any query method. +/// ```no_run +/// use edgedb_protocol::value::Value; +/// +/// let query = "SELECT ($my_str, $my_int)"; +/// let args = edgedb_protocol::named_args! { +/// "my_str" => "Hello world!".to_string(), +/// "my_int" => Value::Int64(42), +/// }; +/// ``` +/// +/// The value side of an argument must be `impl Into`. +/// The type of the returned object is `HashMap<&str, ValueOpt>`. +#[macro_export] +macro_rules! named_args { + ($($key:expr => $value:expr,)+) => { $crate::named_args!($($key => $value),+) }; + ($($key:expr => $value:expr),*) => { + { + const CAP: usize = <[()]>::len(&[$({ stringify!($key); }),*]); + let mut map = ::std::collections::HashMap::<&str, $crate::value_opt::ValueOpt>::with_capacity(CAP); + $( + map.insert($key, $crate::value_opt::ValueOpt::from($value)); + )* + map + } + }; +} diff --git a/edgedb-tokio/tests/func/client.rs b/edgedb-tokio/tests/func/client.rs index 99d5429a..532f40ba 100644 --- a/edgedb-tokio/tests/func/client.rs +++ b/edgedb-tokio/tests/func/client.rs @@ -1,4 +1,4 @@ -use edgedb_protocol::eargs; +use edgedb_protocol::named_args; use edgedb_protocol::value::{EnumValue, Value}; use edgedb_tokio::Client; use edgedb_errors::NoDataError; @@ -71,7 +71,7 @@ async fn simple() -> anyhow::Result<()> { true ); - // params as macro + // named args let value = client.query_required_single::( "select ( std::array_join(>$msg1, ' ') @@ -79,7 +79,7 @@ async fn simple() -> anyhow::Result<()> { ++ ': ' ++ $answer );", - &eargs! { + &named_args! { "msg1" => vec!["the".to_string(), "answer".to_string(), "to".to_string()], "question" => None::, "answer" => 42 as i64,