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}