mysql_common/
lib.rs

1// Copyright (c) 2017 Anatoly Ikorsky
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9//! This crate is an implementation of basic MySql protocol primitives.
10//!
11//! This crate:
12//! * defines basic MySql constants;
13//! * implements necessary functionality for MySql `cached_sha2_password`,
14//!   `mysql_native_password` and legacy authentication plugins;
15//! * implements helper traits for MySql protocol IO;
16//! * implements support of named parameters for prepared statements;
17//! * implements parsers for a subset of MySql protocol packets (including binlog packets);
18//! * defines rust representation of MySql protocol values and rows;
19//! * implements conversion between MySql values and rust types, between MySql rows and tuples
20//!   of rust types.
21//! * implements [FromRow and FromValue derive macros][2]
22//!
23//! ## Supported rust types
24//!
25//! Crate offers conversion from/to MySql values for following types (please see MySql documentation
26//! on supported ranges for numeric types). Following table refers to MySql protocol types
27//! (see `Value` struct) and not to MySql column types. Please see [MySql documentation][1] for
28//! column and protocol type correspondence:
29//!
30//! | Type                                 | Notes                                                     |
31//! | ------------------------------------ | -------------------------------------------------------   |
32//! | `{i,u}8..{i,u}128`, `{i,u}size`      | MySql int/uint will be converted, bytes will be parsed.<br>⚠️ Note that range of `{i,u}128` is greater than supported by MySql integer types but it'll be serialized anyway (as decimal bytes string). |
33//! | `f32`                                | MySql float will be converted to `f32`, bytes will be parsed as `f32`.<br>⚠️ MySql double won't be converted to `f32` to avoid precision loss (see #17) |
34//! | `f64`                                | MySql float and double will be converted to `f64`, bytes will be parsed as `f64`. |
35//! | `bool`                               | MySql int {`0`, `1`} or bytes {`"0x30"`, `"0x31"`}        |
36//! | `Vec<u8>`                            | MySql bytes                                               |
37//! | `String`                             | MySql bytes parsed as utf8                                |
38//! | `Duration` (`std` and `time`)        | MySql time or bytes parsed as MySql time string           |
39//! | [`time::PrimitiveDateTime`] (v0.2.x) | MySql date time or bytes parsed as MySql date time string (⚠️ lossy! microseconds are ignored)           |
40//! | [`time::Date`] (v0.2.x)              | MySql date or bytes parsed as MySql date string (⚠️ lossy! microseconds are ignored)           |
41//! | [`time::Time`] (v0.2.x)              | MySql time or bytes parsed as MySql time string (⚠️ lossy! microseconds are ignored)           |
42//! | [`time::Duration`] (v0.2.x)          | MySql time or bytes parsed as MySql time string           |
43//! | [`time::PrimitiveDateTime`] (v0.3.x) | MySql date time or bytes parsed as MySql date time string (⚠️ lossy! microseconds are ignored)           |
44//! | [`time::Date`] (v0.3.x)              | MySql date or bytes parsed as MySql date string (⚠️ lossy! microseconds are ignored)           |
45//! | [`time::Time`] (v0.3.x)              | MySql time or bytes parsed as MySql time string (⚠️ lossy! microseconds are ignored)           |
46//! | [`time::Duration`] (v0.3.x)          | MySql time or bytes parsed as MySql time string           |
47//! | [`chrono::NaiveTime`]                | MySql date or bytes parsed as MySql date string           |
48//! | [`chrono::NaiveDate`]                | MySql date or bytes parsed as MySql date string           |
49//! | [`chrono::NaiveDateTime`]            | MySql date or bytes parsed as MySql date string           |
50//! | [`uuid::Uuid`]                       | MySql bytes parsed using `Uuid::from_slice`               |
51//! | [`serde_json::Value`]                | MySql bytes parsed using `serde_json::from_str`           |
52//! | `mysql_common::Deserialized<T : DeserializeOwned>` | MySql bytes parsed using `serde_json::from_str` |
53//! | `Option<T: FromValue>`               | Must be used for nullable columns to avoid errors         |
54//! | [`decimal::Decimal`]                 | MySql int, uint or bytes parsed using `Decimal::from_str`.<br>⚠️ Note that this type doesn't support full range of MySql `DECIMAL` type. |
55//! | [`bigdecimal::BigDecimal`]           | MySql int, uint, floats or bytes parsed using `BigDecimal::parse_bytes`.<br>⚠️ Note that range of this type is greater than supported by MySql `DECIMAL` type but it'll be serialized anyway. |
56//! | `num_bigint::{BigInt, BigUint}`      | MySql int, uint or bytes parsed using `_::parse_bytes`.<br>⚠️ Note that range of this type is greater than supported by MySql integer types but it'll be serialized anyway (as decimal bytes string). |
57//!
58//! Also crate provides from-row convertion for the following list of types (see `FromRow` trait):
59//!
60//! | Type                                            | Notes                                             |
61//! | ----------------------------------------------- | ------------------------------------------------- |
62//! | `Row`                                           | Trivial conversion for `Row` itself.              |
63//! | `T: FromValue`                                  | For rows with a single column.                    |
64//! | `(T1: FromValue [, ..., T12: FromValue])`       | Row to a tuple of arity 1-12.                     |
65//! | [`frunk::hlist::HList`] types                   | Usefull to overcome tuple arity limitation        |
66//!
67//! ## Crate features
68//!
69//! | Feature        | Description                                          | Default |
70//! | -------------- | ---------------------------------------------------- | ------- |
71//! | `bigdecimal`   | Enables `bigdecimal` >=0.3.x, <0.5.x types support   | 🔴      |
72//! | `chrono`       | Enables `chrono` types support                       | 🔴      |
73//! | `rust_decimal` | Enables `rust_decimal` types support                 | 🔴      |
74//! | `time`         | Enables `time` v0.3.x types support                  | 🔴      |
75//! | `frunk`        | Enables `FromRow` for `frunk::Hlist!` types          | 🔴      |
76//! | `derive`       | Enables [`FromValue` and `FromRow` derive macros][2] | 🟢      |
77//! | `binlog`       | Binlog-related functionality                         | 🔴      |
78//!
79//! # Derive Macros
80//!
81//! ## `FromValue` Derive
82//!
83//! Supported derivations:
84//!
85//! *   for enum – you should carefully read the [corresponding section of MySql documentation][4].
86//! *   for newtypes (see [New Type Idiom][3]) – given that the wrapped type itself satisfies
87//!     `FromValue`.
88//!
89//! ### Enums
90//!
91//! #### Container attributes:
92//!
93//! *  `#[mysql(crate_name = "some_name")]` – overrides an attempt to guess a crate that provides
94//!    required traits
95//! *  `#[mysql(rename_all = ...)]` – rename all the variants according to the given case
96//!    convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase",
97//!    "snake_case", "SCREAMING_SNAKE_CASE", "kebab-case", "SCREAMING-KEBAB-CASE"
98//! *  `#[mysql(is_integer)]` – tells derive macro that the value is an integer rather than MySql
99//!    ENUM. Macro won't warn if variants are sparse or greater than u16 and will not try to parse
100//!    textual representation.
101//! *  `#[mysql(is_string)]` – tells derive macro that the value is a string rather than MySql
102//!    ENUM. Macro won't warn if variants are sparse or greater than u16 and will not try to parse
103//!    integer representation.
104//!
105//! #### Example
106//!
107//! Given `ENUM('x-small', 'small', 'medium', 'large', 'x-large')` on MySql side:
108//!
109//! ```no_run
110//! # use mysql_common_derive::FromValue;
111//! # use mysql_common::{row::Row, row::convert::from_row};
112//!
113//! fn main() {
114//!
115//! /// Note: the `crate_name` attribute should not be necessary.
116//! #[derive(FromValue)]
117//! #[mysql(rename_all = "kebab-case", crate_name = "mysql_common")]
118//! #[repr(u8)]
119//! enum Size {
120//!     XSmall = 1,
121//!     Small,
122//!     Medium,
123//!     Large,
124//!     XLarge,
125//! }
126//!
127//! fn assert_from_row_works(x: Row) -> Size {
128//!     from_row(x)
129//! }
130//!
131//! }
132//! ```
133//!
134//! ### Newtypes
135//!
136//! It is expected, that wrapper value satisfies `FromValue` or `deserialize_with` is given.
137//! Also note, that to support `FromRow` the wrapped value must satisfy `Into<Value>` or
138//! `serialize_with` must be given.
139//!
140//! #### Container attributes:
141//!
142//! *  `#[mysql(crate_name = "some_name")]` – overrides an attempt to guess a crate to import types from
143//! *  `#[mysql(bound = "Foo: Bar, Baz: Quux")]` – use the following additional bounds
144//! *  `#[mysql(deserialize_with = "some::path")]` – use the following function to deserialize
145//!    the wrapped value. Expected signature is `fn (Value) -> Result<Wrapped, FromValueError>`.
146//! *  `#[mysql(serialize_with = "some::path")]` – use the following function to serialize
147//!    the wrapped value. Expected signature is `fn (Wrapped) -> Value`.
148//!
149//! #### Example
150//!
151//! ```no_run
152//! # use mysql_common::{row::Row, row::convert::from_row, prelude::FromValue, value::Value, value::convert::{from_value, FromValueError}};
153//! # use std::convert::TryFrom;
154//!
155//! /// Trivial example
156//! #[derive(FromValue)]
157//! # #[mysql(crate_name = "mysql_common")]
158//! struct Inch(i32);
159//!
160//! /// Example of a {serialize|deserialize}_with.
161//! #[derive(FromValue)]
162//! # #[mysql(crate_name = "mysql_common")]
163//! #[mysql(deserialize_with = "neg_de", serialize_with = "neg_ser")]
164//! struct Neg(i64);
165//!
166//! /// Wrapped generic. Bounds are inferred.
167//! #[derive(FromValue)]
168//! # #[mysql(crate_name = "mysql_common")]
169//! struct Foo<T>(Option<T>);
170//!
171//! /// Example of additional bounds.
172//! #[derive(FromValue)]
173//! # #[mysql(crate_name = "mysql_common")]
174//! #[mysql(bound = "'b: 'a, T: 'a, U: From<String>, V: From<u64>")]
175//! struct Bar<'a, 'b, const N: usize, T, U, V>(ComplexTypeToWrap<'a, 'b, N, T, U, V>);
176//!
177//! fn assert_from_row_works<'a, 'b, const N: usize, T, U, V>(x: Row) -> (Inch, Neg, Foo<u8>, Bar<'a, 'b, N, T, U, V>)
178//! where 'b: 'a, T: 'a, U: From<String>, V: From<u64>,
179//! {
180//!     from_row(x)
181//! }
182//!
183//!
184//! // test boilerplate..
185//!
186//!
187//! /// Dummy complex type with additional bounds on FromValue impl.
188//! struct ComplexTypeToWrap<'a, 'b, const N: usize, T, U, V>([(&'a T, &'b U, V); N]);
189//!
190//! struct FakeIr;
191//!
192//! impl TryFrom<Value> for FakeIr {
193//!     // ...
194//! #    type Error = FromValueError;
195//! #    fn try_from(v: Value) -> Result<Self, Self::Error> {
196//! #        unimplemented!();
197//! #    }
198//! }
199//!
200//! impl<'a, 'b: 'a, const N: usize, T: 'a, U: From<String>, V: From<u64>> From<FakeIr> for ComplexTypeToWrap<'a, 'b, N, T, U, V> {
201//!     // ...
202//! #    fn from(x: FakeIr) -> Self {
203//! #        unimplemented!();
204//! #    }
205//! }
206//!
207//! impl From<FakeIr> for Value {
208//!     // ...
209//! #    fn from(x: FakeIr) -> Self {
210//! #        unimplemented!();
211//! #    }
212//! }
213//!
214//! impl<'a, 'b: 'a, const N: usize, T: 'a, U: From<String>, V: From<u64>> FromValue for ComplexTypeToWrap<'a, 'b, N, T, U, V> {
215//!     type Intermediate = FakeIr;
216//! }
217//!
218//! fn neg_de(v: Value) -> Result<i64, FromValueError> {
219//!     match v {
220//!         Value::Int(x) => Ok(-x),
221//!         Value::UInt(x) => Ok(-(x as i64)),
222//!         x => Err(FromValueError(x)),
223//!     }
224//! }
225//!
226//! fn neg_ser(x: i64) -> Value {
227//!     Value::Int(-x)
228//! }
229//!
230//! # fn main() {}
231//! ```
232//!
233//! ## `FromRow` Derive
234//!
235//! Also defines some constants on the struct:
236//!
237//! *  `const TABLE_NAME: &str` – if `table_name` is given
238//! *  `const {}_FIELD: &str` – for each struct field (`{}` is a SCREAMING_SNAKE_CASE representation
239//!    of a struct field name (not a column name))
240//!
241//! Supported derivations:
242//!
243//! * for a struct with named fields – field name will be used as a column name to search for a value
244//!
245//! ### Container attributes:
246//!
247//! *  `#[mysql(crate_name = "some_name")]` – overrides an attempt to guess a crate that provides
248//!    required traits
249//! *  `#[mysql(rename_all = ...)]` – rename all column names according to the given case
250//!    convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase",
251//!    "snake_case", "SCREAMING_SNAKE_CASE", "kebab-case", "SCREAMING-KEBAB-CASE"
252//! *  `#[mysql(table_name = "some_name")]` – defines `pub const TABLE_NAME: &str` on the struct
253//!
254//! ### Field attributes:
255//!
256//! *  `#[mysql(rename = "some_name")]` – overrides column name of a field
257//! *  `#[mysql(json)]` - column will be interpreted as a JSON string containing
258//!    a value of a field type
259//! *  `#[mysql(deserialize_with = "some::path")]` – the following function
260//!    will be used to deserialize the field (instead of `FromValue`). Expected signature is
261//!    `fn (Value) -> Result<T, FromValueError>`.
262//! *  `#[mysql(serialize_with = "some::path")]` – the following function
263//!    will be used to serialize the field (instead of `Into<Value>`). Expected signature is
264//!    `fn (T) -> Value`.
265//!
266//! ### Example
267//!
268//! ```
269//! # use mysql_common_derive::FromRow;
270//! # use mysql_common::{
271//! #     constants::ColumnType,
272//! #     packets::Column,
273//! #     prelude::FromValue,
274//! #     row::{Row, new_row},
275//! #     row::convert::from_row,
276//! #     value::Value,
277//! #     value::convert::from_value,
278//! #     FromValueError,
279//! # };
280//! use time::{
281//!     macros::{datetime, offset}, OffsetDateTime, PrimitiveDateTime, UtcOffset,
282//! };
283//!
284//! /// Note: the `crate_name` attribute should not be necessary.
285//! #[derive(Debug, PartialEq, Eq, FromRow)]
286//! #[mysql(table_name = "Foos", crate_name = "mysql_common")]
287//! struct Foo {
288//!     id: u64,
289//!     #[mysql(
290//!         serialize_with = "datetime_to_value",
291//!         deserialize_with = "value_to_datetime",
292//!     )]
293//!     ctime: OffsetDateTime,
294//!     #[mysql(json, rename = "def")]
295//!     definition: Bar,
296//!     child: Option<u64>,
297//! }
298//!
299//! fn value_to_datetime(value: Value) -> Result<OffsetDateTime, FromValueError> {
300//!     // assume mysql session timezone has been properly set up
301//!     const OFFSET: UtcOffset = offset!(+3);
302//!
303//!     let primitive = PrimitiveDateTime::from_value_opt(value)?;
304//!     Ok(primitive.assume_offset(OFFSET))
305//! }
306//!
307//! fn datetime_to_value(datetime: OffsetDateTime) -> Value {
308//!     // assume mysql session timezone has been properly set up
309//!     PrimitiveDateTime::new(datetime.date(), datetime.time()).into()
310//! }
311//!
312//! #[derive(Debug, serde::Deserialize, PartialEq, Eq)]
313//! enum Bar {
314//!     Left,
315//!     Right,
316//! }
317//!
318//! /// Returns the following row:
319//! ///
320//! /// ```
321//! /// +----+-----------+-------+------------------------+
322//! /// | id | def       | child | ctime                  |
323//! /// +----+-----------+-------+------------------------+
324//! /// | 42 | '"Right"' | NULL  | '2015-05-15 12:00:00'  |
325//! /// +----+-----------+-------+------------------------+
326//! /// ```
327//! fn get_row() -> Row {
328//!     // ...
329//! #   let values = vec![
330//! #       Value::Int(42),
331//! #       Value::Bytes(b"\"Right\"".as_slice().into()),
332//! #       Value::NULL,
333//! #       Value::Date(2015, 5, 15, 12, 0, 0, 0),
334//! #   ];
335//! #   let columns = vec![
336//! #       Column::new(ColumnType::MYSQL_TYPE_LONG).with_name(b"id"),
337//! #       Column::new(ColumnType::MYSQL_TYPE_BLOB).with_name(b"def"),
338//! #       Column::new(ColumnType::MYSQL_TYPE_NULL).with_name(b"child"),
339//! #       Column::new(ColumnType::MYSQL_TYPE_STRING).with_name(b"ctime"),
340//! #   ];
341//! #   new_row(values, columns.into_boxed_slice().into())
342//! }
343//!
344//! # fn main() {
345//! assert_eq!(Foo::TABLE_NAME, "Foos");
346//! assert_eq!(Foo::ID_FIELD, "id");
347//! assert_eq!(Foo::DEFINITION_FIELD, "def");
348//! assert_eq!(Foo::CHILD_FIELD, "child");
349//!
350//! let foo = from_row::<Foo>(get_row());
351//! assert_eq!(
352//!     foo,
353//!     Foo {
354//!         id: 42,
355//!         definition: Bar::Right,
356//!         child: None,
357//!         ctime: datetime!(2015-05-15 12:00 +3),
358//!     }
359//! );
360//! # }
361//! ```
362//!
363//! [1]: https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
364//! [2]: #derive-macros
365//! [3]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
366//! [4]: https://dev.mysql.com/doc/refman/8.0/en/enum.html
367#![cfg_attr(feature = "nightly", feature(test))]
368#![cfg_attr(docsrs, feature(doc_cfg))]
369
370// The `test` feature is required to compile tests.
371// It'll bind test binaries to an official C++ impl of MySql decimals (see build.rs)
372// The idea is to test our rust impl against C++ impl.
373#[cfg(all(not(feature = "test"), test))]
374compile_error!("Please invoke `cargo test` with `--features test` flags");
375
376#[cfg(feature = "nightly")]
377extern crate test;
378
379#[macro_use]
380pub mod bitflags_ext;
381
382#[cfg(feature = "bigdecimal")]
383pub use bigdecimal;
384
385#[cfg(feature = "chrono")]
386pub use chrono;
387
388#[cfg(feature = "frunk")]
389pub use frunk;
390
391#[cfg(feature = "rust_decimal")]
392pub use rust_decimal;
393
394#[cfg(feature = "time")]
395pub use time;
396
397pub use uuid;
398
399#[cfg(feature = "derive")]
400#[allow(unused_imports)]
401#[macro_use]
402extern crate mysql_common_derive;
403
404pub use num_bigint;
405pub use serde;
406pub use serde_json;
407
408pub use value::convert::FromValueError;
409pub use value::Value;
410
411pub use row::convert::FromRowError;
412pub use row::Row;
413
414pub use value::json::{Deserialized, Serialized};
415
416pub mod prelude {
417    #[cfg(feature = "derive")]
418    #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
419    #[doc(inline)]
420    pub use mysql_common_derive::FromValue;
421
422    #[cfg(feature = "derive")]
423    #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
424    #[doc(inline)]
425    pub use mysql_common_derive::FromRow;
426
427    pub use crate::row::{convert::FromRow, ColumnIndex};
428    pub use crate::value::convert::{FromValue, ToValue};
429}
430
431/// This macro is a convenient way to pass named parameters to a statement.
432///
433/// ```ignore
434/// let foo = 42;
435/// conn.prep_exec("SELECT :foo, :foo2x", params! {
436///     foo,
437///     "foo2x" => foo * 2,
438/// });
439/// ```
440#[macro_export]
441macro_rules! params {
442    () => {};
443    (@to_pair $map:expr, $name:expr => $value:expr) => (
444        let entry = $map.entry(std::vec::Vec::<u8>::from($name));
445        if let std::collections::hash_map::Entry::Occupied(_) = entry {
446            panic!("Redefinition of named parameter `{}'", std::string::String::from_utf8_lossy(entry.key()));
447        } else {
448            entry.or_insert($crate::value::Value::from($value));
449        }
450    );
451    (@to_pair $map:expr, $name:ident) => (
452        let entry = $map.entry(stringify!($name).as_bytes().to_vec());
453        if let std::collections::hash_map::Entry::Occupied(_) = entry {
454            panic!("Redefinition of named parameter `{}'", std::string::String::from_utf8_lossy(entry.key()));
455        } else {
456            entry.or_insert($crate::value::Value::from($name));
457        }
458    );
459    (@expand $map:expr;) => {};
460    (@expand $map:expr; $name:expr => $value:expr, $($tail:tt)*) => {
461        params!(@to_pair $map, $name => $value);
462        params!(@expand $map; $($tail)*);
463    };
464    (@expand $map:expr; $name:expr => $value:expr $(, $tail:tt)*) => {
465        params!(@to_pair $map, $name => $value);
466        params!(@expand $map; $($tail)*);
467    };
468    (@expand $map:expr; $name:ident, $($tail:tt)*) => {
469        params!(@to_pair $map, $name);
470        params!(@expand $map; $($tail)*);
471    };
472    (@expand $map:expr; $name:ident $(, $tail:tt)*) => {
473        params!(@to_pair $map, $name);
474        params!(@expand $map; $($tail)*);
475    };
476    ($i:ident, $($tail:tt)*) => {
477        {
478            let mut map: std::collections::HashMap<std::vec::Vec<u8>, $crate::value::Value, _> = std::default::Default::default();
479            params!(@expand (&mut map); $i, $($tail)*);
480            $crate::params::Params::Named(map)
481        }
482    };
483    ($i:expr => $($tail:tt)*) => {
484        {
485            let mut map: std::collections::HashMap<std::vec::Vec<u8>, $crate::value::Value, _> = std::default::Default::default();
486            params!(@expand (&mut map); $i => $($tail)*);
487            $crate::params::Params::Named(map)
488        }
489    };
490    ($i:ident) => {
491        {
492            let mut map: std::collections::HashMap<std::vec::Vec<u8>, $crate::value::Value, _> = std::default::Default::default();
493            params!(@expand (&mut map); $i);
494            $crate::params::Params::Named(map)
495        }
496    }
497}
498
499pub mod collations;
500pub mod constants;
501pub mod crypto;
502pub mod io;
503pub mod misc;
504pub mod named_params;
505#[macro_use]
506pub mod packets;
507pub mod params;
508pub mod proto;
509pub mod row;
510pub mod scramble;
511pub mod value;
512
513#[cfg(feature = "binlog")]
514#[cfg_attr(docsrs, doc(cfg(feature = "binlog")))]
515pub mod binlog;
516
517#[cfg(test)]
518#[test]
519fn params_macro_test() {
520    use crate::{params::Params, value::Value};
521
522    let foo = 42;
523    let bar = "bar";
524
525    assert_eq!(
526        Params::from(vec![(String::from("foo"), Value::Int(42))]),
527        params! { foo }
528    );
529    assert_eq!(
530        Params::from(vec![(String::from("foo"), Value::Int(42))]),
531        params! { foo, }
532    );
533    assert_eq!(
534        Params::from(vec![
535            (String::from("foo"), Value::Int(42)),
536            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
537        ]),
538        params! { foo, bar }
539    );
540    assert_eq!(
541        Params::from(vec![
542            (String::from("foo"), Value::Int(42)),
543            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
544        ]),
545        params! { foo, bar, }
546    );
547    assert_eq!(
548        Params::from(vec![
549            (String::from("foo"), Value::Int(42)),
550            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
551        ]),
552        params! { "foo" => foo, "bar" => bar }
553    );
554    assert_eq!(
555        Params::from(vec![
556            (String::from("foo"), Value::Int(42)),
557            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
558        ]),
559        params! { "foo" => foo, "bar" => bar, }
560    );
561    assert_eq!(
562        Params::from(vec![
563            (String::from("foo"), Value::Int(42)),
564            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
565        ]),
566        params! { foo, "bar" => bar }
567    );
568    assert_eq!(
569        Params::from(vec![
570            (String::from("foo"), Value::Int(42)),
571            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
572        ]),
573        params! { "foo" => foo, bar }
574    );
575    assert_eq!(
576        Params::from(vec![
577            (String::from("foo"), Value::Int(42)),
578            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
579        ]),
580        params! { foo, "bar" => bar, }
581    );
582    assert_eq!(
583        Params::from(vec![
584            (String::from("foo"), Value::Int(42)),
585            (String::from("bar"), Value::Bytes((&b"bar"[..]).into())),
586        ]),
587        params! { "foo" => foo, bar, }
588    );
589}
590
591#[test]
592#[should_panic(expected = "Redefinition of named parameter `a'")]
593fn params_macro_should_panic_on_named_param_redefinition() {
594    params! {"a" => 1, "b" => 2, "a" => 3};
595}
596
597#[test]
598fn issue_88() {
599    use crate::{prelude::FromValue, Value};
600    #[derive(FromValue, Debug, Eq, PartialEq)]
601    #[mysql(is_integer)]
602    #[repr(u8)]
603    enum SomeType {
604        A,
605        B = 42,
606        C,
607    }
608
609    let value = Value::Int(42);
610    assert_eq!(SomeType::B, SomeType::from_value(value));
611
612    let value = Value::Int(0);
613    assert_eq!(SomeType::A, SomeType::from_value(value));
614}
615
616#[test]
617fn from_value_is_string() {
618    use crate::{prelude::FromValue, Value};
619    #[derive(FromValue, Debug, Eq, PartialEq)]
620    #[mysql(is_string, rename_all = "snake_case")]
621    enum SomeTypeIsString {
622        FirstVariant = 0,
623        SecondVariant = 2,
624        ThirdVariant = 3,
625    }
626
627    let value = Value::Bytes(b"first_variant".to_vec());
628    assert_eq!(
629        SomeTypeIsString::FirstVariant,
630        SomeTypeIsString::from_value(value)
631    );
632
633    let value = Value::Bytes(b"third_variant".to_vec());
634    assert_eq!(
635        SomeTypeIsString::ThirdVariant,
636        SomeTypeIsString::from_value(value)
637    );
638
639    assert_eq!(
640        Value::from(SomeTypeIsString::FirstVariant),
641        Value::Bytes(b"first_variant".to_vec())
642    );
643    assert_eq!(
644        Value::from(SomeTypeIsString::SecondVariant),
645        Value::Bytes(b"second_variant".to_vec())
646    );
647    assert_eq!(
648        Value::from(SomeTypeIsString::ThirdVariant),
649        Value::Bytes(b"third_variant".to_vec())
650    );
651}
652
653#[test]
654fn from_value_is_integer() {
655    use crate::{prelude::FromValue, Value};
656    #[derive(FromValue, Debug, Eq, PartialEq)]
657    #[mysql(is_integer, rename_all = "snake_case")]
658    #[repr(i8)]
659    enum SomeTypeIsInteger {
660        FirstVariant = -1_i8,
661        SecondVariant = 2,
662        ThirdVariant = 3,
663    }
664
665    let value = Value::Int(-1);
666    assert_eq!(
667        SomeTypeIsInteger::FirstVariant,
668        SomeTypeIsInteger::from_value(value)
669    );
670
671    let value = Value::Int(3);
672    assert_eq!(
673        SomeTypeIsInteger::ThirdVariant,
674        SomeTypeIsInteger::from_value(value)
675    );
676
677    assert_eq!(Value::from(SomeTypeIsInteger::FirstVariant), Value::Int(-1));
678    assert_eq!(Value::from(SomeTypeIsInteger::SecondVariant), Value::Int(2));
679    assert_eq!(Value::from(SomeTypeIsInteger::ThirdVariant), Value::Int(3));
680}
681
682#[cfg(test)]
683mod tests {
684    use crate::{
685        constants::ColumnType,
686        packets::Column,
687        row::{convert::FromRow, new_row},
688        value::{convert::from_value, Value},
689        FromValueError,
690    };
691    use unic_langid::LanguageIdentifier;
692
693    #[derive(FromValue)]
694    #[mysql(serialize_with = "from_langid", deserialize_with = "to_langid")]
695    struct LangId(LanguageIdentifier);
696
697    impl std::ops::Deref for LangId {
698        type Target = LanguageIdentifier;
699
700        fn deref(&self) -> &Self::Target {
701            &self.0
702        }
703    }
704
705    fn to_langid(v: Value) -> Result<LanguageIdentifier, FromValueError> {
706        match v {
707            Value::Bytes(ref b) => match LanguageIdentifier::from_bytes(b) {
708                Ok(ident) => Ok(ident),
709                Err(_) => Err(FromValueError(v)),
710            },
711            _ => Err(FromValueError(v)),
712        }
713    }
714
715    fn from_langid(land_id: LanguageIdentifier) -> Value {
716        Value::Bytes(land_id.to_string().into())
717    }
718
719    #[test]
720    fn newtype_with() {
721        let mut value = Value::Bytes(b"en-US".into());
722
723        let ident = from_value::<LangId>(value);
724
725        assert_eq!(ident.language.to_string().as_str(), "en");
726        assert_eq!(ident.to_string().as_str(), "en-US");
727
728        value = ident.into();
729
730        assert_eq!(value, Value::Bytes(b"en-US".into()));
731    }
732
733    #[test]
734    fn from_row_derive() {
735        #[derive(FromRow)]
736        #[mysql(table_name = "Foos", rename_all = "camelCase")]
737        struct Foo {
738            id: u64,
739            text_data: String,
740            #[mysql(json)]
741            json_data: serde_json::Value,
742            #[mysql(deserialize_with = "from_literal", rename = "custom")]
743            custom_bool: bool,
744        }
745
746        fn from_literal(value: crate::Value) -> Result<bool, crate::FromValueError> {
747            match value {
748                crate::Value::Bytes(x) if x == b"true" => Ok(true),
749                crate::Value::Bytes(x) if x == b"false" => Ok(false),
750                x => Err(crate::FromValueError(x)),
751            }
752        }
753
754        assert_eq!(Foo::TABLE_NAME, "Foos");
755        assert_eq!(Foo::ID_FIELD, "id");
756        assert_eq!(Foo::TEXT_DATA_FIELD, "textData");
757        assert_eq!(Foo::JSON_DATA_FIELD, "jsonData");
758        assert_eq!(Foo::CUSTOM_BOOL_FIELD, "custom");
759
760        let columns = vec![
761            Column::new(ColumnType::MYSQL_TYPE_LONGLONG)
762                .with_name(b"id")
763                .with_org_name(b"id")
764                .with_table(b"Foos")
765                .with_org_table(b"Foos"),
766            Column::new(ColumnType::MYSQL_TYPE_VARCHAR)
767                .with_name(b"textData")
768                .with_org_name(b"textData")
769                .with_table(b"Foos")
770                .with_org_table(b"Foos"),
771            Column::new(ColumnType::MYSQL_TYPE_JSON)
772                .with_name(b"jsonData")
773                .with_org_name(b"jsonData")
774                .with_table(b"Foos")
775                .with_org_table(b"Foos"),
776            Column::new(ColumnType::MYSQL_TYPE_VARCHAR)
777                .with_name(b"custom")
778                .with_org_name(b"custom")
779                .with_table(b"Foos")
780                .with_org_table(b"Foos"),
781        ];
782
783        let row = new_row(
784            vec![
785                crate::Value::Int(10),
786                crate::Value::Bytes(b"bytes".into()),
787                crate::Value::Bytes(b"[true,false,\"not found\"]".into()),
788                crate::Value::Bytes(b"true".into()),
789            ],
790            columns.into(),
791        );
792
793        let deserialized = Foo::from_row(row);
794
795        assert_eq!(deserialized.id, 10);
796        assert_eq!(deserialized.text_data, "bytes");
797        assert_eq!(
798            deserialized.json_data.to_string(),
799            "[true,false,\"not found\"]"
800        );
801        assert!(deserialized.custom_bool);
802    }
803}