1use mssql_types::ToSql;
4
5#[derive(Debug, Clone)]
9pub struct Query {
10 sql: String,
11 }
13
14impl Query {
15 #[must_use]
17 pub fn new(sql: impl Into<String>) -> Self {
18 Self { sql: sql.into() }
19 }
20
21 #[must_use]
23 pub fn sql(&self) -> &str {
24 &self.sql
25 }
26}
27
28pub trait QueryExt {
30 fn bind<T: ToSql>(self, value: &T) -> BoundQuery<'_>;
32}
33
34pub struct BoundQuery<'a> {
36 sql: &'a str,
37 params: Vec<&'a dyn ToSql>,
38}
39
40impl<'a> BoundQuery<'a> {
41 pub fn new(sql: &'a str) -> Self {
43 Self {
44 sql,
45 params: Vec::new(),
46 }
47 }
48
49 pub fn bind<T: ToSql>(mut self, value: &'a T) -> Self {
51 self.params.push(value);
52 self
53 }
54
55 #[must_use]
57 pub fn sql(&self) -> &str {
58 self.sql
59 }
60
61 #[must_use]
63 pub fn params(&self) -> &[&dyn ToSql] {
64 &self.params
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_query_new() {
74 let query = Query::new("SELECT * FROM users");
75 assert_eq!(query.sql(), "SELECT * FROM users");
76 }
77
78 #[test]
79 fn test_query_new_from_string() {
80 let sql = String::from("SELECT id FROM products");
81 let query = Query::new(sql);
82 assert_eq!(query.sql(), "SELECT id FROM products");
83 }
84
85 #[test]
86 fn test_query_clone() {
87 let query = Query::new("SELECT 1");
88 let cloned = query.clone();
89 assert_eq!(cloned.sql(), "SELECT 1");
90 }
91
92 #[test]
93 fn test_query_debug() {
94 let query = Query::new("SELECT 1");
95 let debug = format!("{:?}", query);
96 assert!(debug.contains("SELECT 1"));
97 }
98
99 #[test]
100 fn test_bound_query_new() {
101 let bound = BoundQuery::new("SELECT * FROM users WHERE id = @p1");
102 assert_eq!(bound.sql(), "SELECT * FROM users WHERE id = @p1");
103 assert!(bound.params().is_empty());
104 }
105
106 #[test]
107 fn test_bound_query_bind_single() {
108 let id = 42i32;
109 let bound = BoundQuery::new("SELECT * FROM users WHERE id = @p1").bind(&id);
110 assert_eq!(bound.sql(), "SELECT * FROM users WHERE id = @p1");
111 assert_eq!(bound.params().len(), 1);
112 }
113
114 #[test]
115 fn test_bound_query_bind_multiple() {
116 let id = 42i32;
117 let name = "Alice";
118 let bound = BoundQuery::new("SELECT * FROM users WHERE id = @p1 AND name = @p2")
119 .bind(&id)
120 .bind(&name);
121 assert_eq!(bound.params().len(), 2);
122 }
123
124 #[test]
125 fn test_bound_query_chained_binds() {
126 let a = 1i32;
127 let b = 2i32;
128 let c = 3i32;
129 let bound = BoundQuery::new("INSERT INTO t VALUES (@p1, @p2, @p3)")
130 .bind(&a)
131 .bind(&b)
132 .bind(&c);
133 assert_eq!(bound.params().len(), 3);
134 }
135}