lumus_sql_builder/sqlite/
select.rs

1use super::BuildableStatement;
2use crate::errors::SqlBuilderError;
3
4/// Represents the creation of a SELECT with specified table and options.
5#[derive(Debug)]
6pub struct Select {
7    table: String,
8    distinct: bool,
9    condition: Option<String>,
10    columns: Option<String>,
11    group: Option<String>,
12    order: Option<String>,
13    limit: Option<u32>,
14    offset: Option<u32>,
15    join: Option<Vec<String>>,
16}
17
18impl Select {
19    /// Creates a new `Select` instance with the specified table name.
20    /// # Example
21    /// ```
22    /// use lumus_sql_builder::sqlite::Select;
23    /// let select = Select::new("users").columns("name, age").build().unwrap();
24    /// assert_eq!(select, "SELECT name, age FROM users;")
25    /// ```
26    pub fn new(table: &str) -> Self {
27        Self {
28            table: table.to_string(),
29            distinct: false,
30            columns: None,
31            condition: None,
32            group: None,
33            order: None,
34            limit: None,
35            offset: None,
36            join: None,
37        }
38    }
39
40    /// Creates a new `Select` instance from a SQL query string.
41    /// The query string should be in the format "SELECT * FROM table_name [WHERE condition] [GROUP BY column] [ORDER BY column] [LIMIT limit] [OFFSET offset]".
42    /// # Example
43    /// ```
44    /// use lumus_sql_builder::sqlite::Select;
45    /// let query = "SELECT * FROM users WHERE age > 18 GROUP BY city ORDER BY name LIMIT 10 OFFSET 0";
46    /// Select::from(query);
47    /// ```
48    pub fn from(query: &str) -> Result<Select, SqlBuilderError> {
49        let mut parts = query.split_whitespace();
50        let select = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
51        if select.to_uppercase() != "SELECT" {
52            return Err(SqlBuilderError::InvalidQuery);
53        }
54
55        let _ = parts.next().ok_or(SqlBuilderError::InvalidQuery)?; // Skip the "*"
56
57        let from = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
58        if from.to_uppercase() != "FROM" {
59            return Err(SqlBuilderError::InvalidQuery);
60        }
61
62        let table = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
63
64        let mut select_builder = Select::new(table);
65
66        while let Some(part) = parts.next() {
67            match part.to_uppercase().as_str() {
68                "WHERE" => {
69                    let condition = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
70                    select_builder.condition(condition.to_string());
71                }
72                "GROUP" => {
73                    let by = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
74                    if by.to_uppercase() != "BY" {
75                        return Err(SqlBuilderError::InvalidQuery);
76                    }
77                    let group = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
78                    select_builder.group(group);
79                }
80                "ORDER" => {
81                    let by = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
82                    if by.to_uppercase() != "BY" {
83                        return Err(SqlBuilderError::InvalidQuery);
84                    }
85                    let order = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
86                    select_builder.order(order);
87                }
88                "LIMIT" => {
89                    let limit = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
90                    let limit = limit
91                        .parse::<u32>()
92                        .map_err(|_| SqlBuilderError::InvalidQuery)?;
93                    select_builder.limit(limit);
94                }
95                "OFFSET" => {
96                    let offset = parts.next().ok_or(SqlBuilderError::InvalidQuery)?;
97                    let offset = offset
98                        .parse::<u32>()
99                        .map_err(|_| SqlBuilderError::InvalidQuery)?;
100                    select_builder.offset(offset);
101                }
102                _ => return Err(SqlBuilderError::InvalidQuery),
103            }
104        }
105
106        Ok(select_builder)
107    }
108
109    /// Specifies that the select statement should return distinct rows.
110    pub fn distinct(&mut self) -> &mut Self {
111        self.distinct = true;
112        self
113    }
114
115    /// Specifies the columns to be selected in the query.
116    pub fn columns(&mut self, columns: &str) -> &mut Self {
117        self.columns = Some(columns.to_string());
118        self
119    }
120
121    /// Specifies the grouping for the query results.
122    pub fn group(&mut self, group: &str) -> &mut Self {
123        self.group = Some(group.to_string());
124        self
125    }
126
127    /// Specifies the ordering for the query results.
128    pub fn order(&mut self, order: &str) -> &mut Self {
129        self.order = Some(order.to_string());
130        self
131    }
132
133    /// Specifies where for `Select`.
134    pub fn condition(&mut self, condition: String) -> &mut Self {
135        self.condition = Some(condition);
136        self
137    }
138
139    /// Specifies the maximum number of rows to be returned by the query.
140    pub fn limit(&mut self, limit: u32) -> &mut Self {
141        self.limit = Some(limit);
142        self
143    }
144
145    /// Specifies the offset for the query results.
146    pub fn offset(&mut self, offset: u32) -> &mut Self {
147        self.offset = Some(offset);
148        self
149    }
150
151    /// Specifies a join.
152    pub fn join(&mut self, join: String) -> &mut Self {
153        match &mut self.join {
154            None => self.join = Some(vec![join]),
155            Some(j) => j.push(join),
156        }
157
158        self
159    }
160
161    /// Builds and returns the SQL statement for the select query.
162    pub fn build(&self) -> Result<String, SqlBuilderError> {
163        if self.table.is_empty() {
164            return Err(SqlBuilderError::EmptyTableName);
165        }
166
167        let mut statement = String::from("SELECT");
168
169        if self.distinct {
170            statement.push_str(" DISTINCT");
171        }
172
173        if let Some(columns) = &self.columns {
174            statement.push_str(&format!(" {}", columns));
175        } else {
176            statement.push_str(" *");
177        }
178
179        statement.push_str(&format!(" FROM {}", self.table));
180
181        if let Some(join) = &self.join {
182            for j in join {
183                statement.push_str(&format!(" {}", j))
184            }
185        }
186
187        if let Some(condition) = &self.condition {
188            statement.push_str(&format!(" WHERE {}", condition));
189        }
190
191        if let Some(group) = &self.group {
192            statement.push_str(&format!(" GROUP BY {}", group));
193        }
194
195        if let Some(order) = &self.order {
196            statement.push_str(&format!(" ORDER BY {}", order));
197        }
198
199        if let Some(limit) = &self.limit {
200            statement.push_str(&format!(" LIMIT {}", limit));
201        }
202
203        if let Some(offset) = &self.offset {
204            statement.push_str(&format!(" OFFSET {}", offset));
205        }
206
207        statement.push(';');
208        Ok(statement)
209    }
210}
211
212/// Implementation of the `BuildableStatement` trait for `Select`, allowing it to be printed.
213impl BuildableStatement for Select {
214    fn build(&self) -> String {
215        self.build().unwrap()
216    }
217}