inquerest/
lib.rs

1#![deny(warnings)]
2//! Inquerest can parse complex url query into a SQL abstract syntax tree.
3//!
4//! Example this url:
5//! ```no_run,ignore
6//! /person?age=lt.42&(student=eq.true|gender=eq.'M')&group_by=sum(age),grade,gender&having=min(age)=gt.42&order_by=age.desc,height.asc&page=20&page_size=100
7//! ```
8//! will be parsed into:
9//!
10//! ```rust,ignore
11//! Select {
12//!         from_table: FromTable {
13//!             from: Table {
14//!                 name: "person",
15//!             },
16//!             join: None,
17//!         },
18//!         filter: Some(
19//!             BinaryOperation(
20//!                 BinaryOperation {
21//!                     left: BinaryOperation(
22//!                         BinaryOperation {
23//!                             left: Column(
24//!                                 Column {
25//!                                     name: "age",
26//!                                 },
27//!                             ),
28//!                             operator: Lt,
29//!                             right: Value(
30//!                                 Number(
31//!                                     42.0,
32//!                                 ),
33//!                             ),
34//!                         },
35//!                     ),
36//!                     operator: And,
37//!                     right: Nested(
38//!                         BinaryOperation(
39//!                             BinaryOperation {
40//!                                 left: BinaryOperation(
41//!                                     BinaryOperation {
42//!                                         left: Column(
43//!                                             Column {
44//!                                                 name: "student",
45//!                                             },
46//!                                         ),
47//!                                         operator: Eq,
48//!                                         right: Value(
49//!                                             Bool(
50//!                                                 true,
51//!                                             ),
52//!                                         ),
53//!                                     },
54//!                                 ),
55//!                                 operator: Or,
56//!                                 right: BinaryOperation(
57//!                                     BinaryOperation {
58//!                                         left: Column(
59//!                                             Column {
60//!                                                 name: "gender",
61//!                                             },
62//!                                         ),
63//!                                         operator: Eq,
64//!                                         right: Value(
65//!                                             String(
66//!                                                 "M",
67//!                                             ),
68//!                                         ),
69//!                                     },
70//!                                 ),
71//!                             },
72//!                         ),
73//!                     ),
74//!                 },
75//!             ),
76//!         ),
77//!         group_by: Some(
78//!             [
79//!                 Function(
80//!                     Function {
81//!                         name: "sum",
82//!                         params: [
83//!                             Column(
84//!                                 Column {
85//!                                     name: "age",
86//!                                 },
87//!                             ),
88//!                         ],
89//!                     },
90//!                 ),
91//!                 Column(
92//!                     Column {
93//!                         name: "grade",
94//!                     },
95//!                 ),
96//!                 Column(
97//!                     Column {
98//!                         name: "gender",
99//!                     },
100//!                 ),
101//!             ],
102//!         ),
103//!         having: Some(
104//!             BinaryOperation(
105//!                 BinaryOperation {
106//!                     left: Function(
107//!                         Function {
108//!                             name: "min",
109//!                             params: [
110//!                                 Column(
111//!                                     Column {
112//!                                         name: "age",
113//!                                     },
114//!                                 ),
115//!                             ],
116//!                         },
117//!                     ),
118//!                     operator: Gt,
119//!                     right: Value(
120//!                         Number(
121//!                             42.0,
122//!                         ),
123//!                     ),
124//!                 },
125//!             ),
126//!         ),
127//!         projection: None,
128//!         order_by: Some(
129//!             [
130//!                 Order {
131//!                     expr: Column(
132//!                         Column {
133//!                             name: "age",
134//!                         },
135//!                     ),
136//!                     direction: Some(
137//!                         Desc,
138//!                     ),
139//!                 },
140//!                 Order {
141//!                     expr: Column(
142//!                         Column {
143//!                             name: "height",
144//!                         },
145//!                     ),
146//!                     direction: Some(
147//!                         Asc,
148//!                     ),
149//!                 },
150//!             ],
151//!         ),
152//!         range: Some(
153//!             Page(
154//!                 Page {
155//!                     page: 20,
156//!                     page_size: 100,
157//!                 },
158//!             ),
159//!         ),
160//!     }
161//! ```
162//! Which translate to the sql statement:
163//! ```sql
164//! SELECT * FROM person WHERE age < 42 AND (student = true OR gender = 'M') GROUP BY sum(age), grade, gender HAVING min(age) > 42 ORDER BY age DESC, height ASC LIMIT 100 OFFSET 1900 ROWS
165//! ```
166//! Note: However, you don't want to convert to the sql statement directly to avoid sql injection
167//! attack. You need to validate the tables and columns if it is allowed to be accessed by the
168//! user. You also need to extract the values yourself and supply it as a parameterized value into
169//! your ORM.
170//!
171//! #### Please support this project:
172//! [![Become a patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/ivanceras)
173pub use restq;
174
175pub use restq::{
176    ast::{Expr, Select},
177    parser::filter_expr,
178    to_chars, Error,
179};
180
181/// Parse a path and query in a url to a Select AST
182/// Example:
183/// ```rust
184///     use inquerest::*;
185///
186///     let url = "/person?age=lt.42&(student=eq.true|gender=eq.'M')&group_by=sum(age),grade,gender&having=min(age)=gt.42&order_by=age.desc,height.asc&page=20&page_size=100";
187///     let query = inquerest::parse_query(url);
188///     println!("query: {:#?}", query);
189///     println!(
190///         "sql query: {}",
191///         query.unwrap().into_sql_statement(None).unwrap().to_string()
192///     );
193/// ```
194pub fn parse_query(input: &str) -> Result<Select, Error> {
195    let input_chars = to_chars(input);
196    restq::parse_select_chars(&input_chars)
197}
198
199/// Parse the query in a url to an Expression
200///
201/// Example:
202/// ```rust
203///     use inquerest::*;
204///
205///     let filter = "age=lt.42&(student=eq.true|gender=eq.'M')&group_by=sum(age),grade,gender&having=min(age)=gt.42&order_by=age.desc,height.asc&page=20&page_size=100";
206///     let result = parse_filter(filter);
207///     println!("filter_only: {:#?}", result);
208/// ```
209pub fn parse_filter(input: &str) -> Result<Expr, Error> {
210    let input_chars = to_chars(input);
211    parse_filter_chars(&input_chars)
212}
213
214fn parse_filter_chars(input: &[char]) -> Result<Expr, Error> {
215    Ok(filter_expr().parse(input)?)
216}