Skip to content

Commit

Permalink
edgedb-query-derive: (#19)
Browse files Browse the repository at this point in the history
- add detached statement when needed for filter statements
  - add #[query(value="")] macro attribute
  • Loading branch information
imagineDevit committed Apr 24, 2023
1 parent ece7af9 commit 9fd7a56
Show file tree
Hide file tree
Showing 26 changed files with 524 additions and 250 deletions.
2 changes: 1 addition & 1 deletion edgedb-query-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "edgedb-query-derive"
version = "0.2.2"
version = "0.2.3"
description = "Crate that provide a bunch of attribute macros that help to build EdgeDB query using edgedb-tokio crate"
authors = ["hsedjame <sedhjodev@gmail.com>"]
repository = "https://github.com/imagineDevit/edgedb/tree/main/edgedb-query-derive"
Expand Down
3 changes: 2 additions & 1 deletion edgedb-query-derive/src/builders/impl_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ impl QueryImplBuilder {

let const_check_quote = self.static_const_check_statements.clone();

let table_name = self.table_name.clone().unwrap_or(String::new());
let struct_quote = self.build_struct();

let edge_ql_impl_quote = self.build_to_edgeql_impl();
Expand All @@ -167,7 +168,7 @@ impl QueryImplBuilder {

impl edgedb_query::ToEdgeScalar for #struct_name {
fn scalar() -> String {
String::default()
format!("<{}>", #table_name)
}
}

Expand Down
4 changes: 4 additions & 0 deletions edgedb-query-derive/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@

// region other
pub const EMPTY : &str = "";
pub const SPACE : &str = " ";
pub const SCALAR_TYPE: &str = "$scalar_type$";
pub const EDGEQL: &str = "$edgeql$";
pub const INF_SIGN: &str = "<";
pub const SUP_SIGN: &str = ">";
pub const AT: &str = "@";
pub const DOLLAR: &str = "$";
pub const ID: &str= "id";
// endregion other

Expand Down Expand Up @@ -111,6 +113,7 @@ pub const EXPECT_NON_EMPTY_LIT: &str= "Expected a non-empty string literal";
pub const EXPECT_LIT: &str = "Expected a string literal";
pub const EXPECT_TABLE: &str = "Expected a table name";
pub const EXPECT_SRC: &str = "Expected a src value";
pub const EXPECT_VALUE: &str = "Expected a query value";
pub const EXPECT_OPERATOR: &str = "Expected filter operator attribute `#[filter(operator = \"...\")]`";
pub const UNSUPPORTED_ATTRIBUTE: &str = "Unsupported attribute";
pub const FIRST_FILTER_EXPECTED: &str = "Expected first filter attribute `#[filter(...)]`";
Expand Down Expand Up @@ -214,6 +217,7 @@ pub const EITHER_ONE_FILTERS_OR_FILTER_TAG_EXPECTED: &str = "SelectQuery can onl
pub const EITHER_ONE_SETS_OR_SET_TAG_EXPECTED: &str = "UpdateQuery can only have either one `sets` or one or more `set` or `nested_query` fields";
pub const EXPECTED_AT_LEAST_ONE_SET_FIELD: &str = "UpdateQuery must have at least one field with #[set] attribute or with no attribute";
pub const EXPECTED_ID_FIELD: &str = "Query result struct must have an id field of type uuid::Uuid";
pub const AT_LEAST_ONE_FIELD_ATTRIBUTE_EXPECTED: &str = "At least one field attribute is expected: 'column_name', 'param' or 'scalar'";
// endregion messages

// region types
Expand Down
11 changes: 6 additions & 5 deletions edgedb-query-derive/src/delete_query.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use edgedb_query::QueryType;
use syn::{Ident, ItemStruct};
use syn::parse::{Parse, ParseStream};
use edgedb_query::QueryType;

use crate::constants::*;
use crate::{queries::Query, meta_data::{TableInfo, try_get_meta}};
use crate::{meta_data::{TableInfo, try_get_meta}, queries::Query};
use crate::builders::impl_builder::QueryImplBuilder;
use crate::statements::filters::{FilterRequiredQuery, filters_from_fields, FilterStatement};
use crate::constants::*;
use crate::statements::filters::{FilterRequiredQuery, filters_from_fields, FilterStatement, set_table_name};

#[derive(Debug, Clone)]
pub struct DeleteQuery {
Expand All @@ -24,7 +24,8 @@ impl DeleteQuery {
}

pub fn with_meta(&mut self, meta: TableInfo) -> &mut Self {
self.meta = Some(meta);
self.meta = Some(meta.clone());
set_table_name(&mut self.filter_statement, meta.table_name());
self
}
}
Expand Down
1 change: 1 addition & 0 deletions edgedb-query-derive/src/edgedb_filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl EdgedbFilters {

impl edgedb_query::queries::filter::Filter for #struct_name {
fn to_edgeql(&self, table_name: &str) -> String {
use edgedb_query::{ToEdgeQl, EdgeQl};
let mut query = String::new();
#(#filters)*
query
Expand Down
33 changes: 14 additions & 19 deletions edgedb-query-derive/src/file_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ use regex::Regex;
use syn::{Field, Ident, ItemStruct};
use syn::parse::{Parse, ParseStream};
use edgedb_query::QueryType;
use crate::constants::{PARAM, PARAM_PATTERN};
use crate::constants::{DOLLAR, EMPTY, PARAM, PARAM_PATTERN};
use crate::builders::impl_builder::{FieldCat, QueryImplBuilder, ImplBuilderField};
use crate::meta_data::{SrcFile, try_get_meta};
use crate::meta_data::{SrcQuery, try_get_meta};
use crate::queries::{Query, QueryField};
use crate::tags::{build_tags_from_field, Tagged};
use crate::tags::param_tag::{ParamTag, ParamTagBuilder};
use crate::tags::TagBuilders::ParamBuilder;

#[derive(Debug, Clone)]
pub struct FileQuery {
pub struct FileQuery<T: SrcQuery> {
pub ident: Ident,
pub meta: Option<SrcFile>,
pub meta: Option<T>,
pub params: Vec<ParamField>
}

impl FileQuery {
impl <T: SrcQuery + Clone> FileQuery<T> {
pub fn new(ident: Ident) -> Self {
Self {
ident,
Expand All @@ -28,7 +28,7 @@ impl FileQuery {
}
}

pub fn with_meta(&mut self, meta: SrcFile) -> &mut Self {
pub fn with_meta(&mut self, meta: T) -> &mut Self {
self.meta = Some(meta);
self
}
Expand All @@ -48,35 +48,30 @@ impl FileQuery {
.map(|f| f.param())
.collect::<Vec<String>>();


let param_matches = param_matches.iter()
.map(|s| s.replace("$", ""))
.map(|s| s.replace(DOLLAR, EMPTY))
.collect::<Vec<String>>();

let struct_params_not_query = params_values.clone().into_iter()
.filter(|s| !param_matches.contains(s))
.collect::<Vec<String>>();

let query_params_not_struct = param_matches.clone().into_iter()
.filter(|s| !params_values.contains(&s.replace("$", "")))
.filter(|s| !params_values.contains(&s.replace(DOLLAR, EMPTY)))
.collect::<Vec<String>>();

if struct_params_not_query.len() > 0 {
if !struct_params_not_query.is_empty() {
return Err(
syn::Error::new_spanned(
self.ident.clone(),
format!(r"
Following struct attributes do not appear as query parameters : {:#?}
",struct_params_not_query),
format!("Following struct attributes do not appear as query parameters : {struct_params_not_query:#?}"),
)
)
} else if query_params_not_struct.len() > 0 {
} else if !query_params_not_struct.is_empty() {
return Err(
syn::Error::new_spanned(
self.ident.clone(),
format!(r"
Following query parameters do not appear as struct attribute : {:#?}
",query_params_not_struct),
format!("Following query parameters do not appear as struct attribute : {query_params_not_struct:#?}"),
)
)
} else if param_matches != params_values {
Expand All @@ -91,7 +86,7 @@ impl FileQuery {
}
}

impl Query for FileQuery {
impl <T: SrcQuery + Clone> Query for FileQuery<T> {
fn get_param_labels(&self) -> Vec<(Ident, String)> {
self.params.iter()
.map(|f| (f.field.ident.clone(), f.param()))
Expand Down Expand Up @@ -126,7 +121,7 @@ impl Query for FileQuery {
}
}

impl Parse for FileQuery {
impl <T: SrcQuery + Clone> Parse for FileQuery<T> {
fn parse(input: ParseStream) -> syn::Result<Self> {
let strukt = input.parse::<ItemStruct>()?;

Expand Down
65 changes: 63 additions & 2 deletions edgedb-query-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::edgedb_enum::EdgedbEnum;
use crate::edgedb_filters::EdgedbFilters;
use crate::edgedb_sets::EdgedbSets;
use crate::file_query::FileQuery;
use crate::meta_data::SrcFile;
use crate::meta_data::{SrcFile, SrcValue};
use crate::query_result::QueryResult;
use crate::select_query::SelectQuery;
use crate::update_query::UpdateQuery;
Expand Down Expand Up @@ -321,6 +321,7 @@ pub fn delete_query(attr: TokenStream, item: TokenStream) -> TokenStream {
///
/// let add_user = AddUser {
/// name: "Joe".to_string(),
/// age: 18,
/// friend: "Henri".to_string(),
/// };
///
Expand All @@ -337,7 +338,67 @@ pub fn file_query(attr: TokenStream, item: TokenStream) -> TokenStream {

let meta = parse_macro_input!(attr as SrcFile);

parse_macro_input!(item as FileQuery)
parse_macro_input!(item as FileQuery<SrcFile>)
.with_meta(meta)
.validate()
.and_then(|q| q.to_token_stream())
.unwrap_or_else(|e| e.to_compile_error().into())
}

/// Create an edgeDB query based on a source file
///
/// ## Usage

/// ```rust
/// use edgedb_query_derive::{query};
/// use edgedb_query::BasicResult;
/// use edgedb_query::models::edge_query::ToEdgeQuery;
///
/// #[query(value=r#"
/// insert users::User {
/// name := <str>$user_name,
/// age := <int16>$age,
/// friend := (
/// select users::User {
/// name,
/// age,
/// }
/// filter .name = <str>$friend_name
/// )
/// }"#
/// )]
/// pub struct AddUser {
/// #[param("user_name")]
/// pub name: String,
/// pub age: i8,
/// #[param("friend_name")]
/// pub friend: String,
/// }
///
/// async fn main() {
///
/// let client = edgedb_tokio::create_client().await.unwrap();
///
/// let add_user = AddUser {
/// name: "Joe".to_string(),
/// age: 18,
/// friend: "Henri".to_string(),
/// };
///
/// let query = add_user.to_edge_query();
///
/// let result = client
/// .query_single::<BasicResult, _>(query.query.as_str(), &query.args.unwrap())
/// .await
/// .unwrap();
/// }
/// ```
#[proc_macro_attribute]
pub fn query(attr: TokenStream, item: TokenStream) -> TokenStream {

let meta = parse_macro_input!(attr as SrcValue);

parse_macro_input!(item as FileQuery<SrcValue>)
.with_meta(meta)
.validate()
.and_then(|q| q.to_token_stream())
Expand Down

0 comments on commit 9fd7a56

Please sign in to comment.