-
-
Notifications
You must be signed in to change notification settings - Fork 47
Add query macro docs #543
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
base: master
Are you sure you want to change the base?
Add query macro docs #543
Changes from all commits
306e873
85e9f3c
9f14d2e
7458e0b
05cdd28
23abe5c
377b744
5df92e2
8ccd578
565f16e
9e3cbf0
5c3d928
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -202,6 +202,163 @@ pub fn derive_model_helper(_item: TokenStream) -> TokenStream { | |
| TokenStream::new() | ||
| } | ||
|
|
||
| /// A convenient macro that allows you to write queries in a declarative | ||
| /// fashion. | ||
| /// | ||
| /// `query!` parses a query expression and lowers it into a [`Query`] builder. | ||
| /// The resulting query is still lazy: it is only executed when you call a | ||
| /// terminal query method such as [`Query::get`] or [`Query::all`]. | ||
| /// | ||
| /// The macro expands roughly to: | ||
| /// | ||
| /// ```ignore | ||
| /// <Model as cot::db::Model>::objects().filter(...) | ||
| /// ``` | ||
| /// | ||
| /// # Query syntax | ||
| /// | ||
| /// Query expressions can reference model fields with `$field_name`, combine | ||
| /// conditions with boolean operators, and use comparison and arithmetic | ||
| /// operators. | ||
| /// | ||
| /// ``` | ||
| /// use cot::db::{Database, model, query}; | ||
| /// | ||
| /// #[model] | ||
| /// #[derive(Debug, Clone)] | ||
| /// struct Customer { | ||
| /// #[model(primary_key)] | ||
| /// id: i32, | ||
| /// full_name: String, | ||
| /// status: String, | ||
| /// price: i32, | ||
| /// stock: i32, | ||
| /// quantity: i32, | ||
| /// is_active: bool | ||
| /// } | ||
| /// | ||
| /// # async fn run(db: Database) -> cot::Result<()> { | ||
| /// let customer = query!(Customer, $id == 5).get(&db).await?; | ||
| /// println!("Customer: {:?}", customer); | ||
| /// # Ok(()) | ||
| /// # } | ||
| /// ``` | ||
| /// | ||
| /// In the example above, `$id` and `$full_name` refer to fields on the | ||
| /// `Customer` model. | ||
| /// | ||
| /// | ||
| /// ## Field references | ||
| /// | ||
| /// Use `$name` to refer to a model field. | ||
| /// | ||
| /// ``` | ||
| /// use cot::db::{model, query}; | ||
| /// | ||
| /// # #[model] | ||
| /// # struct Customer { | ||
| /// # #[model(primary_key)] | ||
| /// # id: i32, | ||
| /// # full_name: String, | ||
| /// # } | ||
| /// let _ = query!(Customer, $id == 5); | ||
| /// ``` | ||
|
ElijahAhianyo marked this conversation as resolved.
|
||
| /// | ||
| /// ## Literal values | ||
| /// | ||
| /// Rust literals can be used directly in expressions. | ||
| /// | ||
| /// ``` | ||
| /// use cot::db::{model, query}; | ||
| /// | ||
| /// # #[model] | ||
| /// # struct Customer { | ||
| /// # #[model(primary_key)] | ||
| /// # id: i32, | ||
| /// # full_name: String, | ||
| /// # is_active: bool, | ||
| /// # } | ||
| /// let _ = query!(Customer, $id == 5); | ||
| /// let _ = query!(Customer, $full_name == "Jon Doe"); | ||
| /// let _ = query!(Customer, $is_active == true); | ||
| /// ``` | ||
| /// | ||
| /// ## Comparison operators | ||
| /// | ||
| /// ``` | ||
| /// use cot::db::{model, query}; | ||
| /// | ||
| /// # #[model] | ||
| /// # struct Customer { | ||
| /// # #[model(primary_key)] | ||
| /// # id: i32, | ||
| /// # } | ||
| /// let _ = query!(Customer, $id == 5); | ||
| /// let _ = query!(Customer, $id != 5); | ||
| /// let _ = query!(Customer, $id < 10); | ||
| /// let _ = query!(Customer, $id <= 10); | ||
| /// let _ = query!(Customer, $id > 5); | ||
| /// let _ = query!(Customer, $id >= 5); | ||
| /// ``` | ||
|
ElijahAhianyo marked this conversation as resolved.
|
||
| /// | ||
| /// ## Arithmetic operators | ||
| /// | ||
| /// Query expressions also support arithmetic over fields and values. | ||
| /// | ||
| /// ``` | ||
| /// use cot::db::{model, query}; | ||
| /// | ||
| /// # #[model] | ||
| /// # struct Customer { | ||
| /// # #[model(primary_key)] | ||
| /// # id: i32, | ||
| /// # price: i32, | ||
| /// # stock: i32, | ||
| /// # quantity: i32, | ||
| /// # } | ||
| /// let _ = query!(Customer, $price + 10 > 20); | ||
| /// let _ = query!(Customer, $stock - 1 == 20); | ||
| /// let _ = query!(Customer, $quantity * 2 < 100); | ||
| /// let _ = query!(Customer, $price / 2 != $quantity); | ||
| /// ``` | ||
| /// | ||
| /// ## Rust-side value expressions | ||
| /// | ||
| /// When an expression does not reference a database field, `query!` can treat | ||
| /// it as a Rust value expression. This includes member access, path access, and | ||
| /// function calls. | ||
| /// | ||
| /// ``` | ||
| /// use cot::db::{model, query}; | ||
| /// | ||
| /// # #[model] | ||
| /// # struct Customer { | ||
| /// # #[model(primary_key)] | ||
| /// # id: i32, | ||
| /// # status: String, | ||
| /// # } | ||
| /// struct User { | ||
| /// id: i32, | ||
| /// } | ||
| /// | ||
| /// mod constants { | ||
| /// pub const ACTIVE_STATUS: &str = "active"; | ||
| /// } | ||
| /// | ||
| /// fn next_customer_id() -> i32 { | ||
| /// 42 | ||
| /// } | ||
| /// | ||
| /// let user = User { id: 5 }; | ||
| /// | ||
| /// let _ = query!(Customer, $id == user.id); | ||
| /// let _ = query!(Customer, $status == constants::ACTIVE_STATUS); | ||
| /// let _ = query!(Customer, $id == next_customer_id()); | ||
| /// ``` | ||
| /// | ||
| /// [`Query`]: query/struct.Query.html | ||
| /// [`Query::get`]: query/Struct.Query.html#method.get | ||
| /// [`Query::all`]: query/Struct.Query.html#method.all | ||
|
Comment on lines
+359
to
+361
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can always move these docs into the main crate instead to avoid these hardcoded links. Does that make sense?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I get this. Where in the main crate will this be moved? |
||
| #[proc_macro] | ||
| pub fn query(input: TokenStream) -> TokenStream { | ||
| let query_input = parse_macro_input!(input as Query); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,10 +34,6 @@ pub(super) fn query_to_tokens(query: Query) -> TokenStream { | |
| } | ||
|
|
||
| pub(super) fn expr_to_tokens(model_name: &syn::Type, expr: Expr) -> TokenStream { | ||
| if let Some(tokens) = expr.as_tokens() { | ||
| return tokens; | ||
| } | ||
|
|
||
|
Comment on lines
-37
to
-40
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reasoning behind removing this? |
||
| let crate_name = cot_ident(); | ||
| match expr { | ||
| Expr::FieldRef { field_name, .. } => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.