edgedb_query_derive/
lib.rs

1//! Edgedb-query-derive provide a bunch procedural macros in order to facilitate writing of queries when using edgedb-rust crate.
2
3extern crate core;
4extern crate proc_macro;
5
6use proc_macro::TokenStream;
7use syn::parse_macro_input;
8use crate::insert_query::InsertQuery;
9use crate::{meta_data::{TableInfo, QueryMetaData, }, queries::Query};
10use crate::delete_query::DeleteQuery;
11use crate::edgedb_enum::EdgedbEnum;
12use crate::edgedb_filters::EdgedbFilters;
13use crate::edgedb_sets::EdgedbSets;
14use crate::file_query::FileQuery;
15use crate::meta_data::{SrcFile, SrcValue};
16use crate::query_result::QueryResult;
17use crate::select_query::SelectQuery;
18use crate::update_query::UpdateQuery;
19
20mod constants;
21mod utils;
22mod queries;
23mod tags;
24mod statements;
25mod insert_query;
26mod select_query;
27mod update_query;
28mod delete_query;
29mod builders;
30mod meta_data;
31mod file_query;
32mod query_result;
33mod edgedb_enum;
34mod edgedb_filters;
35mod edgedb_sets;
36
37/// Create an insert edgeDB query
38///
39/// ## Usage
40///
41/// ```rust
42///     use edgedb_query::{ToEdgeQuery, EdgeQuery};
43///     use edgedb_query::queries::conflict::{UnlessConflictElse, Conflict};
44///     use edgedb_query_derive::{
45///             insert_query,
46///             select_query,
47///             query_result,
48///             edgedb_enum
49///     };
50///
51///     #[insert_query(module ="users", table="User", result="UserResult")]
52///     pub struct InsertUser {
53///         #[field(param="first_name")]
54///         pub name: String,
55///         pub surname: Option<String>,
56///         pub age: i32,
57///         pub major: bool,
58///         pub vs: Vec<String>,
59///         #[field(scalar = "<users::Gender>")]
60///         pub gender: Sex,
61///         #[nested_query]
62///         pub wallet: Wallet,
63///         #[unless_conflict(on="username, surname")]
64///         pub find_user: UnlessConflictElse<FindUser>
65///     }
66///
67///     #[edgedb_enum]
68///     pub enum Sex {
69///         #[value("male")]
70///         Male,
71///         #[value("female")]
72///         _Female,
73///     }
74///
75///     #[insert_query(module = "users", table = "Wallet")]
76///     pub struct Wallet {
77///         pub money: i16,
78///     }
79///
80///     #[select_query(module = "users", table = "User")]
81///     pub struct FindUser {
82///         #[filter(operator="Is")]
83///         #[field(column_name="name")]
84///         pub user_name: String
85///     }
86///
87///     #[query_result]
88///     pub struct UserResult {
89///         pub id: uuid::Uuid,
90///         pub name: String,
91///     }
92///
93///     async fn main() {
94///         let insert_user = InsertUser {
95///             name: "Joe".to_string(),
96///             surname: Some("Henri".to_string()),
97///             age: 35,
98///             major: true,
99///             vs: vec!["vs1".to_string()],
100///             gender: Sex::Male,
101///             wallet: Wallet {
102///                 money: 0,
103///             },
104///             find_user: UnlessConflictElse {
105///                 else_query: FindUser{
106///                     user_name: "Joe".to_string(),
107///                 },
108///             }
109///         };
110///
111///         let query  = insert_user.to_edge_query();
112///
113///         let client = edgedb_tokio::create_client().await.unwrap();
114///
115///         let user: UserResult = client
116///                     .query_single::<UserResult, _>(query.query.as_str(), &query.args.unwrap())
117///                     .await
118///                     .unwrap();
119///
120///     }
121/// ```
122#[proc_macro_attribute]
123pub fn insert_query(attr: TokenStream, item: TokenStream) -> TokenStream {
124
125    let meta = parse_macro_input!(attr as QueryMetaData);
126
127    parse_macro_input!(item as InsertQuery)
128        .with_meta(meta)
129        .to_token_stream()
130        .unwrap_or_else(|e| e.to_compile_error().into())
131}
132
133/// Create a select edgeDB query
134///
135/// ## Usage
136///
137/// ```rust
138///     use edgedb_query_derive::{edgedb_filters, query_result, select_query};
139///     use edgedb_query::models::edge_query::{ToEdgeQuery, EdgeQuery};
140///     use edgedb_query::queries::select::{OrderDir, OrderOptions, SelectOptions};
141///
142///     #[select_query(module = "users", table = "User", result = "UserResult")]
143///     pub struct SelectQuery {
144///         #[filter(operator = "Is")]
145///         pub name: String,
146///
147///         #[and_filter(operator = "GreaterThan")]
148///         pub age: i8,
149///
150///         #[options]
151///         options: SelectOptions
152///     }
153///
154///     #[query_result]
155///     pub struct UserResult {
156///         pub id: uuid::Uuid,
157///         pub name: String,
158///         pub age: i8,
159///     }
160///
161///     async fn main() {
162///          let client = edgedb_tokio::create_client().await.unwrap();
163///
164///          let select_query =  SelectQuery {
165///             options: SelectOptions {
166///                 order_options: Some(OrderOptions {
167///                     order_by: "name".to_string(),
168///                     order_direction: Some(OrderDir::Desc)
169///                 }),
170///                 page_options: None
171///             },
172///             name:  "Joe".to_string(),
173///             age: 18
174///         };
175///
176///         let query = select_query.to_edge_query();
177///
178///         let user: UserResult = client
179///                 .query_single::<UserResult, _>(query.query.as_str(), &query.args.unwrap())
180///                 .await
181///                 .unwrap();
182///     }
183/// ```
184///
185#[proc_macro_attribute]
186pub fn select_query(attr: TokenStream, item: TokenStream) -> TokenStream {
187
188    let meta = parse_macro_input!(attr as QueryMetaData);
189
190    parse_macro_input!(item as SelectQuery)
191        .with_meta(meta)
192        .to_token_stream()
193        .unwrap_or_else(|e| e.to_compile_error().into())
194}
195
196/// Create an update edgeDB query
197///
198/// ## Usage
199///
200/// ```rust
201///     use edgedb_query_derive::{update_query};
202///     use edgedb_query::BasicResult;
203///     use edgedb_query::models::edge_query::ToEdgeQuery;
204///
205///     #[update_query(module = "users", table = "User")]
206///     pub struct UpdateUser {
207///          pub name: String,
208///
209///          #[filter(operator = "=", wrapper_fn = "str_lower")]
210///          #[field(column_name = "identity.first_name")]
211///          pub first_name: String,
212///
213///          #[and_filter(operator = ">=")]
214///          pub age: i8,
215///      }
216///
217///     async fn main() {
218///         let client = edgedb_tokio::create_client().await.unwrap();
219///
220///         let update_query = UpdateUser {
221///             name: "Joe".to_string(),
222///             first_name: "Henri".to_string(),
223///             age: 18,
224///         };
225///
226///         let query = update_query.to_edge_query();
227///
228///         let result: BasicResult = client
229///                 .query_single::<BasicResult, _>(query.query.as_str(), &query.args.unwrap())
230///                 .await
231///                 .unwrap();
232///     }
233/// ```
234#[proc_macro_attribute]
235pub fn update_query(attr: TokenStream, item: TokenStream) -> TokenStream {
236
237    let meta = parse_macro_input!(attr as QueryMetaData);
238
239    parse_macro_input!(item as UpdateQuery)
240        .with_meta(meta)
241        .to_token_stream()
242        .unwrap_or_else(|e| e.to_compile_error().into())
243}
244
245/// Create a delete edgeDB query
246///
247/// ## Usage
248///
249/// ```rust
250///     use edgedb_query_derive::{delete_query};
251///     use edgedb_query::models::edge_query::ToEdgeQuery;
252///
253///     #[delete_query(module ="users", table="User")]
254///     pub struct DeleteUsersByAge {
255///
256///         #[filter(operator="=")]
257///         pub age: i16
258///     }
259///
260///     async fn main() {
261///          let client = edgedb_tokio::create_client().await.unwrap();
262///          let delete_query = DeleteUsersByAge {
263///             age: 18,
264///         };
265///
266///         let query = delete_query.to_edge_query();
267///
268///         let _ = client
269///                 .query_single_json(query.query.as_str(), &query.args.unwrap())
270///                 .await
271///                 .unwrap();
272///     }
273/// ```
274#[proc_macro_attribute]
275pub fn delete_query(attr: TokenStream, item: TokenStream) -> TokenStream {
276
277    let meta = parse_macro_input!(attr as TableInfo);
278
279    parse_macro_input!(item as DeleteQuery)
280        .with_meta(meta)
281        .to_token_stream()
282        .unwrap_or_else(|e| e.to_compile_error().into())
283}
284
285/// Create an edgeDB query based on a source file
286///
287/// ## Usage
288///
289///
290/// _queries.edgeql_
291/// ``` sql
292///     insert users::User {
293///         name := <str>$user_name,
294///         age := <int16>$age,
295///         friend := (
296///             select users::User {
297///                 name,
298///                 age,
299///             }
300///             filter .name = <str>$friend_name
301///         )
302///     }
303/// ```
304/// ```rust
305///     use edgedb_query_derive::{file_query};
306///     use edgedb_query::BasicResult;
307///     use edgedb_query::models::edge_query::ToEdgeQuery;
308///
309///     #[file_query(src="queries.edgeql")]
310///     pub struct AddUser {
311///         #[param("user_name")]
312///         pub name: String,
313///         pub age: i8,
314///         #[param("friend_name")]
315///         pub friend: String,
316///     }
317///
318///     async fn main() {
319///
320///         let client = edgedb_tokio::create_client().await.unwrap();
321///
322///         let add_user = AddUser {
323///             name: "Joe".to_string(),
324///             age: 18,
325///             friend: "Henri".to_string(),
326///         };
327///
328///         let query = add_user.to_edge_query();
329///
330///         let result = client
331///                 .query_single::<BasicResult, _>(query.query.as_str(), &query.args.unwrap())
332///                 .await
333///                 .unwrap();
334///     }
335/// ```
336#[proc_macro_attribute]
337pub fn file_query(attr: TokenStream, item: TokenStream) -> TokenStream {
338
339    let meta = parse_macro_input!(attr as SrcFile);
340
341    parse_macro_input!(item as FileQuery<SrcFile>)
342        .with_meta(meta)
343        .validate()
344        .and_then(|q| q.to_token_stream())
345        .unwrap_or_else(|e| e.to_compile_error().into())
346}
347
348/// Create an edgeDB query based on a source file
349///
350/// ## Usage
351
352/// ```rust
353///     use edgedb_query_derive::{query};
354///     use edgedb_query::BasicResult;
355///     use edgedb_query::models::edge_query::ToEdgeQuery;
356///
357///     #[query(value=r#"
358///         insert users::User {
359///             name := <str>$user_name,
360///             age := <int16>$age,
361///             friend := (
362///                 select users::User {
363///                     name,
364///                     age,
365///                 }
366///             filter .name = <str>$friend_name
367///             )
368///         }"#
369///     )]
370///     pub struct AddUser {
371///         #[param("user_name")]
372///         pub name: String,
373///         pub age: i8,
374///         #[param("friend_name")]
375///         pub friend: String,
376///     }
377///
378///     async fn main() {
379///
380///         let client = edgedb_tokio::create_client().await.unwrap();
381///
382///         let add_user = AddUser {
383///             name: "Joe".to_string(),
384///             age: 18,
385///             friend: "Henri".to_string(),
386///         };
387///
388///         let query = add_user.to_edge_query();
389///
390///         let result = client
391///                 .query_single::<BasicResult, _>(query.query.as_str(), &query.args.unwrap())
392///                 .await
393///                 .unwrap();
394///     }
395/// ```
396#[proc_macro_attribute]
397pub fn query(attr: TokenStream, item: TokenStream) -> TokenStream {
398
399    let meta = parse_macro_input!(attr as SrcValue);
400
401    parse_macro_input!(item as FileQuery<SrcValue>)
402        .with_meta(meta)
403        .validate()
404        .and_then(|q| q.to_token_stream())
405        .unwrap_or_else(|e| e.to_compile_error().into())
406}
407
408/// Represents a query result
409///
410/// ## Usage
411///
412/// ```rust
413///     use edgedb_query_derive::query_result;
414///
415///     #[query_result]
416///     pub struct UserWithFriendAndFieldAndWrapperFn {
417///         pub id: uuid::Uuid,
418///         #[field(column_name="pseudo", wrapper_fn="str_upper", default_value="john")]
419///         pub login: String,
420///         pub identity: Identity,
421///         #[back_link(
422///             module="users",
423///             source_table="User",
424///             target_table="Friend",
425///             target_column="friend"
426///         )]
427///         pub friend: Friend,
428///     }
429/// ```
430#[proc_macro_attribute]
431pub fn query_result(_: TokenStream, item: TokenStream) -> TokenStream {
432    parse_macro_input!(item as QueryResult)
433        .to_token_stream()
434        .unwrap_or_else(|e| e.to_compile_error().into())
435}
436
437/// Represents an edgeDB enum type
438///
439/// ## Usage
440///
441/// ```rust
442///     use edgedb_query_derive::edgedb_enum;
443///
444///     #[edgedb_enum]
445///     pub enum Sex {
446///         #[value("man")]
447///         Male,
448///         #[value("woman")]
449///         Female,
450///     }
451/// ```
452#[proc_macro_attribute]
453pub fn edgedb_enum(_: TokenStream, item: TokenStream) -> TokenStream {
454    parse_macro_input!(item as EdgedbEnum)
455        .to_token_stream()
456        .unwrap_or_else(|e| e.to_compile_error().into())
457}
458
459/// Represents a list of edgeDB query filters
460///
461/// ## Usage
462///
463/// ```rust
464///     use edgedb_query_derive::edgedb_filters;
465///
466///     #[edgedb_filters]
467///     pub struct MyFilter {
468///         #[field(column_name="identity.first_name", param = "first_name")]
469///         #[filter(operator="=",  wrapper_fn="str_lower")]
470///         pub name: String,
471///         #[or_filter(operator=">=")]
472///         pub age: i8
473///     }
474/// ```
475#[proc_macro_attribute]
476pub fn edgedb_filters(_: TokenStream, item: TokenStream) -> TokenStream {
477    parse_macro_input!(item as EdgedbFilters)
478        .to_token_stream()
479        .unwrap_or_else(|e| e.to_compile_error().into())
480}
481
482/// Create an list of update edgeDB query sets
483///
484/// ## Usage
485///
486/// ```rust
487///     use edgedb_query_derive::edgedb_sets;
488///
489///
490///     #[edgedb_sets]
491///     pub struct MySet {
492///         #[field(column_name="first_name", param = "user_name", scalar="<str>")]
493///         #[set(option="Concat")]
494///         pub name: String,
495///         #[field(scalar="default::State")]
496///         pub status: Status,
497///         #[nested_query]
498///         pub users: FindUsers
499///     }
500/// ```
501#[proc_macro_attribute]
502pub fn edgedb_sets(_: TokenStream, item: TokenStream) -> TokenStream {
503    parse_macro_input!(item as EdgedbSets)
504        .to_token_stream()
505        .unwrap_or_else(|e| e.to_compile_error().into())
506}