Skip to main content

qusql_mysql_type/
lib.rs

1//! Proc macros to perform type typed mysql queries on top of qusql-mysql.
2//!
3//! The queries are typed based on a schema definition, that must be placed in "qusql-mysql-type-schema.sql"
4//! in the root of a using crate:
5//!
6//! ```sql
7//! DROP TABLE IF EXISTS `t1`;
8//! CREATE TABLE `t1` (
9//!     `id` int(11) NOT NULL,
10//!     `cbool` tinyint(1) NOT NULL DEFAULT false,
11//!     `cu8` tinyint UNSIGNED NOT NULL DEFAULT 0,
12//!     `cu16` smallint UNSIGNED NOT NULL DEFAULT 1,
13//!     `cu32` int UNSIGNED NOT NULL DEFAULT 2,
14//!     `cu64` bigint UNSIGNED NOT NULL DEFAULT 3,
15//!     `ci8` tinyint,
16//!     `ci16` smallint,
17//!     `ci32` int,
18//!     `ci64` bigint,
19//!     `ctext` varchar(100) NOT NULL,
20//!     `cbytes` blob,
21//!     `cf32` float,
22//!     `cf64` double
23//! ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
24//!
25//! ALTER TABLE `t1`
26//!     MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
27//! ```
28//! See [qusql_type::schema] for a detailed description.
29//!
30//! [qusql_type::schema]: https://docs.rs/qusql-type/latest/qusql_type/schema/index.html
31//!
32//! This schema can then be used to type queries:
33//!
34//! ```no_run
35//! use qusql_mysql::connection::{ConnectionOptions, ConnectionError, ExecutorExt};
36//! use qusql_mysql::pool::{Pool, PoolOptions};
37//! use qusql_mysql_type::{execute, fetch_one};
38//!
39//! async fn test() -> Result<(), ConnectionError> {
40//!     let pool = Pool::connect(
41//!         ConnectionOptions::from_url("mysql://user:pw@127.0.0.1:3307/db").unwrap(),
42//!         PoolOptions::new().max_connections(10)
43//!     ).await?;
44//!
45//!     let mut conn = pool.acquire().await?;
46//!
47//!     let id = execute!(&mut conn, "INSERT INTO `t1` (
48//!        `cbool`, `cu8`, `cu16`, `cu32`, `cu64`, `ctext`)
49//!         VALUES (?, ?, ?, ?, ?, ?)",
50//!         true, 8, 1243, 42, 42, "Hello world").await?.last_insert_id();
51//!
52//!     let row = fetch_one!(&mut conn,
53//!         "SELECT `cu16`, `ctext`, `ci32` FROM `t1` WHERE `id`=?", id).await?;
54//!
55//!     assert_eq!(row.cu16, 1234);
56//!     assert_eq!(row.ctext, "Hello would");
57//!     assert!(row.ci32.is_none());
58//!     Ok(())
59//! }
60//! ```
61#![forbid(unsafe_code)]
62#[allow(clippy::single_component_path_imports)]
63use qusql_mysql_type_macro;
64
65#[doc(hidden)]
66pub use qusql_mysql_type_macro::execute_impl;
67
68/// Statically checked execute
69///
70/// This expands into a [qusql_mysql::ExecutorExt::execute].
71///
72/// The type supplied arguments are checked against the query.
73///
74/// ```no_run
75/// use qusql_mysql_type::execute;
76///
77/// async fn run(conn: &mut qusql_mysql::Connection) {
78///     let res = execute!(
79///         conn,
80///         "INSERT INTO `t1` (`cu32`, `ctext`) VALUES (?,?)",
81///         42, "hello").await.unwrap();
82///     println!("{}", res.last_insert_id());
83/// }
84/// ```
85#[macro_export]
86macro_rules! execute {
87    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
88        qusql_mysql_type::execute_impl!(($executor), $stmt, $(($args),)*)
89    };
90}
91
92#[doc(hidden)]
93pub use qusql_mysql_type_macro::fetch_one_impl;
94
95/// Statically checked fetch_one with borrowed values
96///
97/// This expands into a [qusql_mysql::ExecutorExt::fetch_one_map], where a Row type is generated based the columns
98/// returned by the query. Text and blob fields are reference into the parsed mysql package.
99/// The type supplied arguments are checked against the query
100///
101/// ```no_run
102/// use qusql_mysql_type::fetch_one;
103///
104/// async fn run(conn: &mut qusql_mysql::Connection) {
105///     let row = fetch_one!(
106///         conn,
107///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
108///         42, "hello").await.unwrap();
109///     println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a &str
110/// }
111/// ```
112#[macro_export]
113macro_rules! fetch_one {
114    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)?) => {
115        qusql_mysql_type::fetch_one_impl!(($executor), $stmt, $($args,)*)
116    };
117}
118
119#[doc(hidden)]
120pub use qusql_mysql_type_macro::fetch_one_owned_impl;
121
122/// Statically checked fetch_one with owned values
123///
124/// This expands into a [qusql_mysql::ExecutorExt::fetch_one_map], where a Row type is generated based the columns
125/// returned by the query. Text and blob fields as represented as [String] and [`Vec<u8>`].
126/// The type supplied arguments are checked against the query
127///
128/// ```no_run
129/// use qusql_mysql_type::fetch_one_owned;
130///
131/// async fn run(conn: &mut qusql_mysql::Connection) {
132///     let row = fetch_one_owned!(
133///         conn,
134///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
135///         42, "hello").await.unwrap();
136///     println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a String
137/// }
138/// ```
139#[macro_export]
140macro_rules! fetch_one_owned {
141    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
142        qusql_mysql_type::fetch_one_owned_impl!(($executor), $stmt, $($args,)*)
143    };
144}
145
146#[doc(hidden)]
147pub use qusql_mysql_type_macro::fetch_one_as_impl;
148
149/// Statically checked fetch_one with borrowed values into custom type
150///
151/// This expands into a [qusql_mysql::ExecutorExt::fetch_one_map] into a give row type, with borrowed text and blob fields.
152/// The type supplied arguments are checked against the query
153///
154/// ```no_run
155/// use qusql_mysql_type::fetch_one_as;
156///
157/// async fn run(conn: &mut qusql_mysql::Connection) {
158///     struct Row<'a> {
159///         cu32: u32,
160///         ctext: &'a str
161///     }
162///     let row = fetch_one_as!(
163///         Row,
164///         conn,
165///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
166///         42, "hello").await.unwrap();
167///     println!("{} {}", row.cu32, row.ctext);
168/// }
169/// ```
170#[macro_export]
171macro_rules! fetch_one_as {
172    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
173        qusql_mysql_type::fetch_one_as_impl!($rt, ($executor), $stmt, $($args,)*)
174    };
175}
176
177#[doc(hidden)]
178pub use qusql_mysql_type_macro::fetch_one_as_owned_impl;
179
180/// Statically checked fetch_one with owned values into custom type
181///
182/// This expands into a [qusql_mysql::ExecutorExt::fetch_one_map] into a give row type, with owned text and blob fields.
183/// The type supplied arguments are checked against the query
184///
185/// ```no_run
186/// use qusql_mysql_type::fetch_one_as_owned;
187///
188/// async fn run(conn: &mut qusql_mysql::Connection) {
189///     struct Row {
190///         cu32: u32,
191///         ctext: String
192///     }
193///     let row = fetch_one_as_owned!(
194///         Row,
195///         conn,
196///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
197///         42, "hello").await.unwrap();
198///     println!("{} {}", row.cu32, row.ctext);
199/// }
200/// ```
201#[macro_export]
202macro_rules! fetch_one_as_owned {
203    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
204        qusql_mysql_type::fetch_one_as_owned_impl!($rt, ($executor), $stmt, $($args,)*)
205    };
206}
207
208#[doc(hidden)]
209pub use qusql_mysql_type_macro::fetch_optional_impl;
210
211/// Statically checked fetch_optional with borrowed values
212///
213/// This expands into a [qusql_mysql::ExecutorExt::fetch_optional_map], where a Row type is generated based the columns
214/// returned by the query. Text and blob fields are reference into the parsed mysql package.
215/// The type supplied arguments are checked against the query
216///
217/// ```no_run
218/// use qusql_mysql_type::fetch_optional;
219///
220/// async fn run(conn: &mut qusql_mysql::Connection) {
221///     let row = fetch_optional!(
222///         conn,
223///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
224///         42, "hello").await.unwrap();
225///     if let Some(row) = row {
226///         println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a &str
227///     }
228/// }
229/// ```
230#[macro_export]
231macro_rules! fetch_optional {
232    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)?) => {
233        qusql_mysql_type::fetch_optional_impl!(($executor), $stmt, $($args,)*)
234    };
235}
236
237#[doc(hidden)]
238pub use qusql_mysql_type_macro::fetch_optional_owned_impl;
239
240/// Statically checked fetch_optional with owned values
241///
242/// This expands into a [qusql_mysql::ExecutorExt::fetch_optional_map], where a Row type is generated based the columns
243/// returned by the query. Text and blob fields as represented as [String] and [`Vec<u8>`].
244/// The type supplied arguments are checked against the query
245///
246/// ```no_run
247/// use qusql_mysql_type::fetch_optional_owned;
248///
249/// async fn run(conn: &mut qusql_mysql::Connection) {
250///     let row = fetch_optional_owned!(
251///         conn,
252///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
253///         42, "hello").await.unwrap();
254///     if let Some(row) = row {
255///         println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a String
256///     }
257/// }
258/// ```
259#[macro_export]
260macro_rules! fetch_optional_owned {
261    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
262        qusql_mysql_type::fetch_optional_owned_impl!(($executor), $stmt, $($args,)*)
263    };
264}
265
266#[doc(hidden)]
267pub use qusql_mysql_type_macro::fetch_optional_as_impl;
268
269/// Statically checked fetch_optional with borrowed values into custom type
270///
271/// This expands into a [qusql_mysql::ExecutorExt::fetch_optional_map] into a give row type, with borrowed text and blob fields.
272/// The type supplied arguments are checked against the query
273///
274/// ```no_run
275/// use qusql_mysql_type::fetch_optional_as;
276///
277/// async fn run(conn: &mut qusql_mysql::Connection) {
278///     struct Row<'a> {
279///         cu32: u32,
280///         ctext: &'a str
281///     }
282///     let row = fetch_optional_as!(
283///         Row,
284///         conn,
285///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
286///         42, "hello").await.unwrap();
287///     if let Some(row) = row {
288///         println!("{} {}", row.cu32, row.ctext);
289///     }
290/// }
291/// ```
292#[macro_export]
293macro_rules! fetch_optional_as {
294    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
295        qusql_mysql_type::fetch_optional_as_impl!($rt, ($executor), $stmt, $($args,)*)
296    };
297}
298
299#[doc(hidden)]
300pub use qusql_mysql_type_macro::fetch_optional_as_owned_impl;
301
302/// Statically checked fetch_optional with owned values into custom type
303///
304/// This expands into a [qusql_mysql::ExecutorExt::fetch_optional_map] into a give row type, with owned text and blob fields.
305/// The type supplied arguments are checked against the query
306///
307/// ```no_run
308/// use qusql_mysql_type::fetch_optional_as_owned;
309///
310/// async fn run(conn: &mut qusql_mysql::Connection) {
311///     struct Row {
312///         cu32: u32,
313///         ctext: String
314///     }
315///     let row = fetch_optional_as_owned!(
316///         Row,
317///         conn,
318///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
319///         42, "hello").await.unwrap();
320///     if let Some(row) = row {
321///         println!("{} {}", row.cu32, row.ctext);
322///     }
323/// }
324/// ```
325#[macro_export]
326macro_rules! fetch_optional_as_owned {
327    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
328        qusql_mysql_type::fetch_optional_as_owned_impl!($rt, ($executor), $stmt, $($args,)*)
329    };
330}
331
332#[doc(hidden)]
333pub use qusql_mysql_type_macro::fetch_all_impl;
334
335/// Statically checked fetch_all with borrowed values
336///
337/// This expands into a [qusql_mysql::ExecutorExt::fetch_all_map], where a Row type is generated based the columns
338/// returned by the query. Text and blob fields are reference into the parsed mysql package.
339/// The type supplied arguments are checked against the query
340///
341/// ```no_run
342/// use qusql_mysql_type::fetch_all;
343///
344/// async fn run(conn: &mut qusql_mysql::Connection) {
345///     let rows = fetch_all!(
346///         conn,
347///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
348///         42, "hello").await.unwrap();
349///     for row in rows {
350///         println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a &str
351///     }
352/// }
353/// ```
354#[macro_export]
355macro_rules! fetch_all {
356    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
357        qusql_mysql_type::fetch_all_impl!(($executor), $stmt, $($args,)*)
358    };
359}
360
361#[doc(hidden)]
362pub use qusql_mysql_type_macro::fetch_all_owned_impl;
363
364/// Statically checked fetch_all with owned values
365///
366/// This expands into a [qusql_mysql::ExecutorExt::fetch_all_map], where a Row type is generated based the columns
367/// returned by the query. Text and blob fields as represented as [String] and [`Vec<u8>`].
368/// The type supplied arguments are checked against the query
369///
370/// ```no_run
371/// use qusql_mysql_type::fetch_all_owned;
372///
373/// async fn run(conn: &mut qusql_mysql::Connection) {
374///     let rows = fetch_all_owned!(
375///         conn,
376///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
377///         42, "hello").await.unwrap();
378///     for row in rows {
379///         println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a String
380///     }
381/// }
382/// ```
383#[macro_export]
384macro_rules! fetch_all_owned {
385    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
386        qusql_mysql_type::fetch_all_owned_impl!(($executor), $stmt, $($args,)*)
387    };
388}
389
390#[doc(hidden)]
391pub use qusql_mysql_type_macro::fetch_all_as_impl;
392
393/// Statically checked fetch_all with borrowed values into custom type
394///
395/// This expands into a [qusql_mysql::ExecutorExt::fetch_all_map] into a give row type, with borrowed text and blob fields.
396/// The type supplied arguments are checked against the query
397///
398/// ```no_run
399/// use qusql_mysql_type::fetch_all_as;
400///
401/// async fn run(conn: &mut qusql_mysql::Connection) {
402///     struct Row<'a> {
403///         cu32: u32,
404///         ctext: &'a str
405///     }
406///     let rows = fetch_all_as!(
407///         Row,
408///         conn,
409///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
410///         42, "hello").await.unwrap();
411///     for row in rows {
412///         println!("{} {}", row.r#cu32, row.r#ctext);
413///     }
414/// }
415/// ```
416#[macro_export]
417macro_rules! fetch_all_as {
418    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
419        qusql_mysql_type::fetch_all_as_impl!($rt, ($executor), $stmt, $($args,)*)
420    };
421}
422
423#[doc(hidden)]
424pub use qusql_mysql_type_macro::fetch_all_as_owned_impl;
425
426/// Statically checked fetch_all with owned values into custom type
427///
428/// This expands into a [qusql_mysql::ExecutorExt::fetch_all_map] into a give row type, with owned text and blob fields.
429/// The type supplied arguments are checked against the query
430///
431/// ```no_run
432/// use qusql_mysql_type::fetch_all_as_owned;
433///
434/// async fn run(conn: &mut qusql_mysql::Connection) {
435///     struct Row {
436///         cu32: u32,
437///         ctext: String
438///     }
439///     let rows = fetch_all_as_owned!(
440///         Row,
441///         conn,
442///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
443///         42, "hello").await.unwrap();
444///     for row in rows {
445///         println!("{} {}", row.cu32, row.ctext);
446///     }
447/// }
448/// ```
449#[macro_export]
450macro_rules! fetch_all_as_owned {
451    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)?) => {
452        qusql_mysql_type::fetch_all_as_owned_impl!($rt, ($executor), $stmt, $($args,)*)
453    };
454}
455
456#[doc(hidden)]
457pub use qusql_mysql_type_macro::fetch_impl;
458
459/// Statically checked streaming fetch with borrowed values
460///
461/// This expands into a [qusql_mysql::ExecutorExt::fetch_map], where a Row type is generated based the columns
462/// returned by the query. Text and blob fields are reference into the parsed mysql package.
463/// The type supplied arguments are checked against the query
464///
465/// ```no_run
466/// use qusql_mysql_type::fetch;
467///
468/// async fn run(conn: &mut qusql_mysql::Connection) {
469///     let mut row_iter = fetch!(
470///         conn,
471///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
472///         42, "hello").await.unwrap();
473///     while let Some(row) = row_iter.next().await.unwrap() {
474///         println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a &str
475///     }
476/// }
477/// ```
478#[macro_export]
479macro_rules! fetch {
480    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
481        qusql_mysql_type::fetch_impl!(($executor), $stmt, $($args,)*)
482    };
483}
484
485#[doc(hidden)]
486pub use qusql_mysql_type_macro::fetch_owned_impl;
487
488/// Statically checked streaming fetch with owned values
489///
490/// This expands into a [qusql_mysql::ExecutorExt::fetch_map], where a Row type is generated based the columns
491/// returned by the query. Text and blob fields as represented as [String] and [`Vec<u8>`].
492/// The type supplied arguments are checked against the query
493///
494/// ```no_run
495/// use qusql_mysql_type::fetch_owned;
496///
497/// async fn run(conn: &mut qusql_mysql::Connection) {
498///     let mut row_iter  = fetch_owned!(
499///         conn,
500///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
501///         42, "hello").await.unwrap();
502///     while let Some(row) = row_iter.next().await.unwrap() {
503///         println!("{} {}", row.cu32, row.ctext); // Here row.ctext is a String
504///     }
505/// }
506/// ```
507#[macro_export]
508macro_rules! fetch_owned {
509    ( $executor: expr, $stmt: literal $(, $args:expr )* $(,)?) => {
510        qusql_mysql_type::fetch_owned_impl!(($executor), $stmt, $($args,)*)
511    };
512}
513
514#[doc(hidden)]
515pub use qusql_mysql_type_macro::fetch_as_impl;
516
517/// Statically checked streaming fetch with borrowed values into custom type
518///
519/// This expands into a [qusql_mysql::ExecutorExt::fetch_map] into a give row type, with borrowed text and blob fields.
520/// The type supplied arguments are checked against the query
521///
522/// ```no_run
523/// use qusql_mysql_type::fetch_as;
524///
525/// async fn run(conn: &mut qusql_mysql::Connection) {
526///     struct Row<'a> {
527///         cu32: u32,
528///         ctext: &'a str
529///     }
530///     let mut row_iter  = fetch_as!(
531///         Row,
532///         conn,
533///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
534///         42, "hello").await.unwrap();
535///     while let Some(row) = row_iter.next().await.unwrap() {
536///         println!("{} {}", row.r#cu32, row.r#ctext);
537///     }
538/// }
539/// ```
540#[macro_export]
541macro_rules! fetch_as {
542    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
543        qusql_mysql_type::fetch_as_impl!($rt, ($executor), $stmt, $($args,)*)
544    };
545}
546
547#[doc(hidden)]
548pub use qusql_mysql_type_macro::fetch_as_owned_impl;
549
550/// Statically checked streaming fetch with owned values into custom type
551///
552/// This expands into a [qusql_mysql::ExecutorExt::fetch_map] into a give row type, with owned text and blob fields.
553/// The type supplied arguments are checked against the query
554///
555/// ```no_run
556/// use qusql_mysql_type::fetch_as_owned;
557///
558/// async fn run(conn: &mut qusql_mysql::Connection) {
559///     struct Row {
560///         cu32: u32,
561///         ctext: String
562///     }
563///     let mut row_iter = fetch_as_owned!(
564///         Row,
565///         conn,
566///         "SELECT `cu32`, `ctext` FROM `t1` WHERE `id` = ? AND `ctext` != ?",
567///         42, "hello").await.unwrap();
568///     while let Some(row) = row_iter.next().await.unwrap() {
569///         println!("{} {}", row.cu32, row.ctext);
570///     }
571/// }
572/// ```
573#[macro_export]
574macro_rules! fetch_as_owned {
575    ( $rt: ty, $executor: expr, $stmt: literal $(, $args:expr )* $(,)? ) => {
576        qusql_mysql_type::fetch_as_owned_impl!($rt, ($executor), $stmt, $($args,)*)
577    };
578}
579
580/// Tag type for integer input
581#[doc(hidden)]
582pub struct Integer;
583
584/// Tag type for float input
585#[doc(hidden)]
586pub struct Float;
587
588/// Tag type for timestamp input
589#[doc(hidden)]
590pub struct Timestamp;
591
592/// Tag type for datetime input
593#[doc(hidden)]
594pub struct DateTime;
595
596/// Tag type for date input
597#[doc(hidden)]
598pub struct Date;
599
600/// Tag type for time input
601#[doc(hidden)]
602pub struct Time;
603
604/// Tag type for time input
605#[doc(hidden)]
606pub struct Any;
607
608/// If `ArgIn<T>` is implemented for `J`, it means that `J` can be used as for arguments of type `T`
609pub trait ArgIn<T> {}
610
611/// If `ArgOut<N, T>` is implemented for `J`, it means that `J` can be construct from column of type `T`
612///
613/// `N` is used used with a struct named after the column to make figuring out what column has the
614/// wrong type feasible
615pub trait ArgOut<N, T> {}
616
617/// Implement [ArgIn] and [ArgOut] for the given types
618macro_rules! arg_io {
619    ( $dst: ty, $t: ty ) => {
620        impl ArgIn<$dst> for $t {}
621        impl ArgIn<$dst> for &$t {}
622        impl ArgIn<Option<$dst>> for $t {}
623        impl ArgIn<Option<$dst>> for &$t {}
624        impl ArgIn<Option<$dst>> for Option<$t> {}
625        impl ArgIn<Option<$dst>> for Option<&$t> {}
626        impl ArgIn<Option<$dst>> for &Option<$t> {}
627        impl ArgIn<Option<$dst>> for &Option<&$t> {}
628
629        impl<N> ArgOut<N, $dst> for $t {}
630        impl<N> ArgOut<N, Option<$dst>> for Option<$t> {}
631        impl<N> ArgOut<N, $dst> for Option<$t> {}
632    };
633}
634
635arg_io!(Any, u64);
636arg_io!(Any, i64);
637arg_io!(Any, u32);
638arg_io!(Any, i32);
639arg_io!(Any, u16);
640arg_io!(Any, i16);
641arg_io!(Any, u8);
642arg_io!(Any, i8);
643arg_io!(Any, String);
644arg_io!(Any, f64);
645arg_io!(Any, f32);
646arg_io!(Any, &str);
647
648arg_io!(Integer, u64);
649arg_io!(Integer, i64);
650arg_io!(Integer, u32);
651arg_io!(Integer, i32);
652arg_io!(Integer, u16);
653arg_io!(Integer, i16);
654arg_io!(Integer, u8);
655arg_io!(Integer, i8);
656
657arg_io!(String, String);
658
659arg_io!(Float, f64);
660arg_io!(Float, f32);
661
662arg_io!(u64, u64);
663arg_io!(i64, i64);
664arg_io!(u32, u32);
665arg_io!(i32, i32);
666arg_io!(u16, u16);
667arg_io!(i16, i16);
668arg_io!(u8, u8);
669arg_io!(i8, i8);
670arg_io!(bool, bool);
671arg_io!(f32, f32);
672arg_io!(f64, f64);
673
674arg_io!(&str, &str);
675arg_io!(&str, String);
676arg_io!(&str, std::borrow::Cow<'_, str>);
677
678arg_io!(&[u8], &[u8]);
679arg_io!(&[u8], Vec<u8>);
680arg_io!(Vec<u8>, Vec<u8>);
681
682#[cfg(feature = "chrono")]
683arg_io!(Timestamp, chrono::NaiveDateTime);
684#[cfg(feature = "chrono")]
685arg_io!(DateTime, chrono::NaiveDateTime);
686#[cfg(feature = "chrono")]
687arg_io!(chrono::DateTime<chrono::Utc>, chrono::DateTime<chrono::Utc>);
688#[cfg(feature = "chrono")]
689arg_io!(Timestamp, chrono::DateTime<chrono::Utc>);
690#[cfg(feature = "chrono")]
691arg_io!(Date, chrono::NaiveDate);
692
693#[doc(hidden)]
694pub fn check_arg<T, T2: ArgIn<T>>(_: &T2) {}
695
696#[doc(hidden)]
697pub fn check_arg_list_hack<T, T2: ArgIn<T>>(_: &qusql_mysql::List<'_, T2>) {}
698
699#[doc(hidden)]
700pub fn check_arg_out<N, T, T2: ArgOut<N, T>>(_: &T2) {}
701
702#[doc(hidden)]
703pub fn convert_list_query(query: &str, list_sizes: &[usize]) -> String {
704    let mut query_iter = query.split("_LIST_");
705    let mut query = query_iter.next().expect("None empty query").to_string();
706    for size in list_sizes {
707        if *size == 0 {
708            query.push_str("NULL");
709        } else {
710            for i in 0..*size {
711                if i == 0 {
712                    query.push('?');
713                } else {
714                    query.push_str(", ?");
715                }
716            }
717        }
718        query.push_str(query_iter.next().expect("More _LIST_ in query"));
719    }
720    if query_iter.next().is_some() {
721        panic!("Too many _LIST_ in query");
722    }
723    query
724}
725
726#[cfg(test)]
727mod tests {
728    use super::*;
729
730    #[test]
731    fn test_convert_list_query() {
732        // This assert would fire and test will fail.
733        // Please note, that private functions can be tested too!
734        assert_eq!(
735            &convert_list_query("FOO (_LIST_) X _LIST_ O _LIST_ BAR (_LIST_)", &[0, 1, 2, 3]),
736            "FOO (NULL) X ? O ?, ? BAR (?, ?, ?)"
737        );
738    }
739}