├── .github ├── dependabot.yml └── workflows │ └── serde_rusqlite.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── release.toml ├── rustfmt.toml ├── src ├── de │ ├── iter.rs │ └── mod.rs ├── error.rs ├── lib.rs ├── ser │ ├── blob.rs │ ├── mod.rs │ ├── named.rs │ ├── positional.rs │ ├── slice.rs │ └── tosql.rs └── tests.rs └── tools └── pre-release.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/serde_rusqlite.yml: -------------------------------------------------------------------------------- 1 | name: serde_rusqlite 2 | on: 3 | push: 4 | branches: 5 | - "*" 6 | pull_request: 7 | branches: 8 | - "*" 9 | jobs: 10 | ci: 11 | runs-on: ubuntu-22.04 12 | env: 13 | SCCACHE_GHA_ENABLED: "true" 14 | RUSTC_WRAPPER: "sccache" 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: mozilla-actions/sccache-action@v0.0.8 18 | - uses: dtolnay/rust-toolchain@stable 19 | 20 | - name: Clippy 21 | run: cargo clippy --workspace --all-targets --all-features --tests -- -D warnings 22 | 23 | - name: Test 24 | run: cargo test --workspace --all-targets --all-features 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /Makefile 4 | /README.tpl 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_rusqlite" 3 | version = "0.39.0" 4 | edition = "2021" 5 | description = "Serialize/deserialize rusqlite rows" 6 | keywords = ["serde", "sqlite", "rusqlite", "serialization", "deserialization"] 7 | categories = ["database", "encoding"] 8 | authors = ["Pro "] 9 | license = "MIT OR Apache-2.0" 10 | documentation = "https://docs.rs/serde_rusqlite" 11 | repository = "https://github.com/twistedfall/serde_rusqlite" 12 | 13 | [badges] 14 | maintenance = { status = "passively-maintained" } 15 | 16 | [dependencies] 17 | rusqlite = "0.36" 18 | serde = "1" 19 | 20 | [dev-dependencies] 21 | serde_bytes = "0.11" 22 | serde_derive = "1" 23 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serde_rusqlite 2 | 3 | ## Documentation 4 | 5 | See [full documentation](https://docs.rs/serde_rusqlite) 6 | 7 | ## Usage 8 | 9 | Add this to your Cargo.toml: 10 | ``` 11 | [dependencies] 12 | serde_rusqlite = "0.39.0" 13 | ``` 14 | 15 | ![Maintenance](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen.svg) 16 | [![Build Status](https://github.com/twistedfall/serde_rusqlite/actions/workflows/serde_rusqlite.yml/badge.svg)](https://github.com/twistedfall/serde_rusqlite/actions/workflows/serde_rusqlite.yml) 17 | [![Documentation](https://docs.rs/serde_rusqlite/badge.svg)](https://docs.rs/serde_rusqlite) 18 | 19 | ## Serde Rusqlite 20 | 21 | This crate provides convenience functions to bridge serde and rusqlite. With their help 22 | you can "deserialize" rusqlite `Row`'s into serde `Deserialize` types and "serialize" types 23 | implementing `Serialize` into bound query arguments (positional or named) that rusqlite expects. 24 | 25 | Serialization of named bound arguments is only supported from `struct`s and `map`s because other 26 | serde types lack column name information. Likewise, serialization of positional bound arguments 27 | is only supported from `tuple`s, `sequence`s and primitive non-iterable types. In the latter case 28 | the result will be single-element vector. Each serialized field or element must implement 29 | `rusqlite::types::ToSql`. 30 | 31 | For deserialization you can use two families of functions: `from_*()` and `from_*_with_columns()`. 32 | The most used one is the former. The latter allows you to specify column names for types that need 33 | them, but don't supply them. This includes different `Map` types like `HashMap`. Specifying columns 34 | for deserialization into e.g. `struct` doesn't have any effect as the field list of the struct itself 35 | will be used in any case. 36 | 37 | SQLite only supports 5 types: `NULL` (`None`), `INTEGER` (`i64`), `REAL` (`f64`), `TEXT` (`String`) 38 | and `BLOB` (`Vec`). Corresponding rust types are inside brackets. 39 | 40 | Some types employ non-trivial handling, these are described below: 41 | 42 | * Serialization of `u64` will fail if it can't be represented by `i64` due to sqlite limitations. 43 | * Simple `enum`s will be serialized as strings so: 44 | 45 | ``` 46 | enum Gender { 47 | M, 48 | F, 49 | } 50 | ``` 51 | 52 | will have two possible `TEXT` options in the database "M" and "F". Deserialization into `enum` 53 | from `TEXT` is also supported. 54 | * `bool`s are serialized as `INTEGER`s 0 or 1, can be deserialized from `INTEGER` and `REAL` where 55 | 0 and 0.0 are `false`, anything else is `true`. 56 | * `f64` and `f32` values of `NaN` are serialized as `NULL`s. When deserializing such value `Option` 57 | will have value of `None` and `f64` will have value of `NaN`. The same applies to `f32`. 58 | * `Bytes`, `ByteBuf` from `serde_bytes` are supported as optimized way of handling `BLOB`s. 59 | * `unit` serializes to `NULL`. 60 | * Only `sequence`s of `u8` are serialized and deserialized, `BLOB` database type is used. It's 61 | more optimal though to use `Bytes` and `ByteBuf` from `serde_bytes` for such fields. 62 | * `unit_struct` serializes to `struct` name as `TEXT`, when deserializing the check is made to ensure 63 | that `struct` name coincides with the string in the database. 64 | 65 | ## Examples 66 | ```rust 67 | use serde_derive::{Deserialize, Serialize}; 68 | use serde_rusqlite::*; 69 | 70 | #[derive(Serialize, Deserialize, Debug, PartialEq)] 71 | struct Example { 72 | id: i64, 73 | name: String, 74 | } 75 | 76 | let connection = rusqlite::Connection::open_in_memory().unwrap(); 77 | connection.execute("CREATE TABLE example (id INT, name TEXT)", []).unwrap(); 78 | 79 | // using structure to generate named bound query arguments 80 | let row1 = Example { id: 1, name: "first name".into() }; 81 | connection.execute("INSERT INTO example (id, name) VALUES (:id, :name)", to_params_named(&row1).unwrap().to_slice().as_slice()).unwrap(); 82 | // and limiting the set of fields that are to be serialized 83 | let row2 = Example { id: 10, name: "second name".into() }; 84 | connection.execute("INSERT INTO example (id, name) VALUES (2, :name)", to_params_named_with_fields(&row2, &["name"]).unwrap().to_slice().as_slice()).unwrap(); 85 | 86 | // using tuple to generate positional bound query arguments 87 | let row2 = (3, "third name"); 88 | connection.execute("INSERT INTO example (id, name) VALUES (?, ?)", to_params(&row2).unwrap()).unwrap(); 89 | 90 | // deserializing using query() and from_rows(), the most efficient way 91 | let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 92 | let mut res = from_rows::(statement.query([]).unwrap()); 93 | assert_eq!(res.next().unwrap().unwrap(), row1); 94 | assert_eq!(res.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() }); 95 | 96 | // deserializing using query_and_then() and from_row(), incurs extra overhead in from_row() call 97 | let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 98 | let mut rows = statement.query_and_then([], from_row::).unwrap(); 99 | assert_eq!(rows.next().unwrap().unwrap(), row1); 100 | assert_eq!(rows.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() }); 101 | 102 | // deserializing using query_and_then() and from_row_with_columns(), better performance than from_row() 103 | let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 104 | let columns = columns_from_statement(&statement); 105 | let mut rows = statement.query_and_then([], |row| from_row_with_columns::(row, &columns)).unwrap(); 106 | assert_eq!(rows.next().unwrap().unwrap(), row1); 107 | assert_eq!(rows.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() }); 108 | 109 | // deserializing using query() and from_rows_ref() 110 | let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 111 | let mut rows = statement.query([]).unwrap(); 112 | { 113 | // only first record is deserialized here 114 | let mut res = from_rows_ref::(&mut rows); 115 | assert_eq!(res.next().unwrap().unwrap(), row1); 116 | } 117 | // the second record is deserialized using the original Rows iterator 118 | assert_eq!(from_row::(&rows.next().unwrap().unwrap()).unwrap(), Example { id: 2, name: "second name".into() }); 119 | ``` 120 | 121 | License: MIT OR Apache-2.0 122 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | allow-branch = ["master"] 2 | pre-release-hook = "tools/pre-release.sh" 3 | pre-release-commit-message = "v{{version}}" 4 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | fn_params_layout = "Tall" 3 | force_explicit_abi = true 4 | hard_tabs = true 5 | match_arm_leading_pipes = "Never" 6 | match_block_trailing_comma = false 7 | max_width = 130 8 | merge_derives = true 9 | newline_style = "Unix" 10 | remove_nested_parens = true 11 | reorder_imports = true 12 | reorder_modules = true 13 | single_line_if_else_max_width = 0 14 | short_array_element_width_threshold = 10 15 | tab_spaces = 3 16 | use_field_init_shorthand = true 17 | use_small_heuristics = "Default" 18 | use_try_shorthand = true 19 | -------------------------------------------------------------------------------- /src/de/iter.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use rusqlite::{Row, Rows}; 4 | use serde::de::DeserializeOwned; 5 | 6 | use crate::{Error, Result}; 7 | 8 | /// Iterator to automatically deserialize each row from owned `rusqlite::Rows` into `D: serde::Deserialize` 9 | pub struct DeserRows<'stmt, D> { 10 | rows: Rows<'stmt>, 11 | columns: Option>, 12 | d: PhantomData<*const D>, 13 | } 14 | 15 | impl<'stmt, D: DeserializeOwned> DeserRows<'stmt, D> { 16 | pub fn new(rows: Rows<'stmt>) -> Self { 17 | Self { 18 | columns: columns_from_rows(&rows), 19 | rows, 20 | d: PhantomData, 21 | } 22 | } 23 | } 24 | 25 | impl Iterator for DeserRows<'_, D> { 26 | type Item = Result; 27 | 28 | fn next(&mut self) -> Option { 29 | deser_row(self.rows.next(), &self.columns) 30 | } 31 | } 32 | 33 | /// Iterator to automatically deserialize each row from borrowed `rusqlite::Rows` into `D: serde::Deserialize` 34 | pub struct DeserRowsRef<'rows, 'stmt, D> { 35 | rows: &'rows mut Rows<'stmt>, 36 | columns: Option>, 37 | d: PhantomData<*const D>, 38 | } 39 | 40 | impl<'rows, 'stmt, D: DeserializeOwned> DeserRowsRef<'rows, 'stmt, D> { 41 | pub fn new(rows: &'rows mut Rows<'stmt>) -> Self { 42 | Self { 43 | columns: columns_from_rows(rows), 44 | rows, 45 | d: PhantomData, 46 | } 47 | } 48 | } 49 | 50 | impl Iterator for DeserRowsRef<'_, '_, D> { 51 | type Item = Result; 52 | 53 | fn next(&mut self) -> Option { 54 | deser_row(self.rows.next(), &self.columns) 55 | } 56 | } 57 | 58 | #[inline] 59 | fn deser_row(row: rusqlite::Result>, columns: &Option>) -> Option> { 60 | if let Some(columns) = columns { 61 | match row { 62 | Ok(Some(row)) => Some(crate::from_row_with_columns(row, columns)), 63 | Ok(None) => None, 64 | Err(e) => Some(Err(e.into())), 65 | } 66 | } else { 67 | Some(Err(Error::ColumnNamesNotAvailable)) 68 | } 69 | } 70 | 71 | fn columns_from_rows(rows: &rusqlite::Rows) -> Option> { 72 | rows.as_ref().map(|stmt| { 73 | let len = stmt.column_count(); 74 | let mut out = Vec::with_capacity(len); 75 | for i in 0..len { 76 | out.push(stmt.column_name(i).expect("Impossible, we checked the length").to_owned()) 77 | } 78 | out 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /src/de/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{f32, f64}; 2 | 3 | use rusqlite::types::{FromSql, Value}; 4 | use rusqlite::Row; 5 | use serde::de::{DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess, Visitor}; 6 | use serde::{forward_to_deserialize_any, Deserializer}; 7 | 8 | pub use iter::{DeserRows, DeserRowsRef}; 9 | 10 | use crate::{Error, Result}; 11 | 12 | mod iter; 13 | 14 | macro_rules! forward_to_row_value_deserializer { 15 | ($($fun:ident)*) => { 16 | $( 17 | fn $fun>(self, visitor: V) -> Result { 18 | self.row_value().$fun(visitor) 19 | } 20 | )* 21 | } 22 | } 23 | 24 | /// Deserializer for `rusqlite::Row` 25 | /// 26 | /// You shouldn't use it directly, but via the crate's `from_row()` function. Check the crate documentation for example. 27 | pub struct RowDeserializer<'row, 'stmt, 'cols> { 28 | row: &'row Row<'stmt>, 29 | columns: &'cols [String], 30 | } 31 | 32 | impl<'row, 'stmt, 'cols> RowDeserializer<'row, 'stmt, 'cols> { 33 | pub fn from_row_with_columns(row: &'row Row<'stmt>, columns: &'cols [String]) -> Self { 34 | Self { row, columns } 35 | } 36 | 37 | fn row_value(&self) -> RowValue<'row, 'stmt> { 38 | RowValue { row: self.row, idx: 0 } 39 | } 40 | } 41 | 42 | impl<'de> Deserializer<'de> for RowDeserializer<'de, '_, '_> { 43 | type Error = Error; 44 | 45 | fn deserialize_unit_struct>(self, name: &'static str, visitor: V) -> Result { 46 | self.row_value().deserialize_unit_struct(name, visitor) 47 | } 48 | 49 | fn deserialize_newtype_struct>(self, _name: &'static str, visitor: V) -> Result { 50 | visitor.visit_newtype_struct(self.row_value()) 51 | } 52 | 53 | fn deserialize_tuple>(self, _len: usize, visitor: V) -> Result { 54 | visitor.visit_seq(RowSeqAccess { idx: 0, de: self }) 55 | } 56 | 57 | fn deserialize_map>(self, visitor: V) -> Result { 58 | visitor.visit_map(RowMapAccess { idx: 0, de: self }) 59 | } 60 | 61 | fn deserialize_struct>( 62 | self, 63 | _name: &'static str, 64 | _fields: &'static [&'static str], 65 | visitor: V, 66 | ) -> Result { 67 | self.deserialize_map(visitor) 68 | } 69 | 70 | fn deserialize_enum>( 71 | self, 72 | name: &'static str, 73 | variants: &'static [&'static str], 74 | visitor: V, 75 | ) -> Result { 76 | self.row_value().deserialize_enum(name, variants, visitor) 77 | } 78 | 79 | forward_to_row_value_deserializer! { 80 | deserialize_bool 81 | deserialize_f32 82 | deserialize_f64 83 | deserialize_option 84 | deserialize_unit 85 | deserialize_any 86 | deserialize_byte_buf 87 | } 88 | 89 | forward_to_deserialize_any! { 90 | i8 i16 i32 i64 u8 u16 u32 u64 char str string bytes 91 | seq tuple_struct identifier ignored_any 92 | } 93 | } 94 | 95 | struct RowValue<'row, 'stmt> { 96 | idx: usize, 97 | row: &'row Row<'stmt>, 98 | } 99 | 100 | impl<'row> RowValue<'row, '_> { 101 | fn value(&self) -> Result { 102 | self.row.get(self.idx).map_err(Error::from) 103 | } 104 | 105 | fn deserialize_any_helper>(self, visitor: V, value: Value) -> Result { 106 | match value { 107 | Value::Null => visitor.visit_none(), 108 | Value::Integer(val) => visitor.visit_i64(val), 109 | Value::Real(val) => visitor.visit_f64(val), 110 | Value::Text(val) => visitor.visit_string(val), 111 | Value::Blob(val) => visitor.visit_seq(val.into_deserializer()), 112 | } 113 | } 114 | } 115 | 116 | impl<'de> Deserializer<'de> for RowValue<'de, '_> { 117 | type Error = Error; 118 | 119 | fn deserialize_any>(self, visitor: V) -> Result { 120 | let val = self.value()?; 121 | self.deserialize_any_helper(visitor, val) 122 | } 123 | 124 | fn deserialize_bool>(self, visitor: V) -> Result { 125 | match self.value()? { 126 | Value::Integer(val) => visitor.visit_bool(val != 0), 127 | Value::Real(val) => visitor.visit_bool(val != 0.), 128 | val => self.deserialize_any_helper(visitor, val), 129 | } 130 | } 131 | 132 | fn deserialize_f32>(self, visitor: V) -> Result { 133 | match self.value()? { 134 | Value::Null => visitor.visit_f32(f32::NAN), 135 | val => self.deserialize_any_helper(visitor, val), 136 | } 137 | } 138 | 139 | fn deserialize_f64>(self, visitor: V) -> Result { 140 | match self.value()? { 141 | Value::Null => visitor.visit_f64(f64::NAN), 142 | val => self.deserialize_any_helper(visitor, val), 143 | } 144 | } 145 | 146 | fn deserialize_byte_buf>(self, visitor: V) -> Result { 147 | visitor.visit_byte_buf(self.value()?) 148 | } 149 | 150 | fn deserialize_option>(self, visitor: V) -> Result { 151 | match self.value()? { 152 | Value::Null => visitor.visit_none(), 153 | _ => visitor.visit_some(self), 154 | } 155 | } 156 | 157 | fn deserialize_unit>(self, visitor: V) -> Result { 158 | match self.value()? { 159 | Value::Null => visitor.visit_unit(), 160 | val => self.deserialize_any_helper(visitor, val), 161 | } 162 | } 163 | 164 | fn deserialize_unit_struct>(self, name: &'static str, visitor: V) -> Result { 165 | match self.value()? { 166 | Value::Text(ref val) if val == name => visitor.visit_unit(), 167 | val => self.deserialize_any_helper(visitor, val), 168 | } 169 | } 170 | 171 | fn deserialize_enum>( 172 | self, 173 | _name: &'static str, 174 | _variants: &'static [&'static str], 175 | visitor: V, 176 | ) -> Result { 177 | visitor.visit_enum(RowEnumAccess(self.value()?)) 178 | } 179 | 180 | forward_to_deserialize_any! { 181 | i8 i16 i32 i64 u8 u16 u32 u64 char str string bytes 182 | newtype_struct seq tuple 183 | tuple_struct map struct identifier ignored_any 184 | } 185 | } 186 | 187 | struct RowMapAccess<'row, 'stmt, 'cols> { 188 | idx: usize, 189 | de: RowDeserializer<'row, 'stmt, 'cols>, 190 | } 191 | 192 | impl<'de> MapAccess<'de> for RowMapAccess<'de, '_, '_> { 193 | type Error = Error; 194 | 195 | fn next_key_seed>(&mut self, seed: K) -> Result> { 196 | if self.idx >= self.de.columns.len() { 197 | Ok(None) 198 | } else { 199 | let column = self.de.columns[self.idx].as_str(); 200 | seed 201 | .deserialize(column.into_deserializer()) 202 | .map(Some) 203 | .map_err(|e| add_field_to_error(e, column)) 204 | } 205 | } 206 | 207 | fn next_value_seed>(&mut self, seed: V) -> Result { 208 | let out = seed 209 | .deserialize(RowValue { 210 | idx: self.idx, 211 | row: self.de.row, 212 | }) 213 | .map_err(|e| add_field_to_error(e, &self.de.columns[self.idx])); 214 | self.idx += 1; 215 | out 216 | } 217 | } 218 | 219 | struct RowSeqAccess<'row, 'stmt, 'cols> { 220 | idx: usize, 221 | de: RowDeserializer<'row, 'stmt, 'cols>, 222 | } 223 | 224 | impl<'de> SeqAccess<'de> for RowSeqAccess<'de, '_, '_> { 225 | type Error = Error; 226 | 227 | fn next_element_seed>(&mut self, seed: T) -> Result> { 228 | let out = seed 229 | .deserialize(RowValue { 230 | idx: self.idx, 231 | row: self.de.row, 232 | }) 233 | .map(Some) 234 | .map_err(|e| add_field_to_error(e, &self.de.columns[self.idx])); 235 | self.idx += 1; 236 | out 237 | } 238 | } 239 | 240 | struct RowEnumAccess(String); 241 | 242 | impl<'de> EnumAccess<'de> for RowEnumAccess { 243 | type Error = Error; 244 | type Variant = RowVariantAccess; 245 | 246 | fn variant_seed>(self, seed: V) -> Result<(V::Value, Self::Variant)> { 247 | seed.deserialize(self.0.into_deserializer()).map(|v| (v, RowVariantAccess)) 248 | } 249 | } 250 | 251 | struct RowVariantAccess; 252 | 253 | impl<'de> VariantAccess<'de> for RowVariantAccess { 254 | type Error = Error; 255 | 256 | fn unit_variant(self) -> Result<()> { 257 | Ok(()) 258 | } 259 | 260 | fn newtype_variant_seed>(self, _seed: T) -> Result { 261 | Err(Error::de_unsupported("newtype_variant")) 262 | } 263 | fn tuple_variant>(self, _len: usize, _visitor: V) -> Result { 264 | Err(Error::de_unsupported("tuple_variant")) 265 | } 266 | fn struct_variant>(self, _fields: &'static [&'static str], _visitor: V) -> Result { 267 | Err(Error::de_unsupported("struct_variant")) 268 | } 269 | } 270 | 271 | fn add_field_to_error(mut error: Error, error_column: &str) -> Error { 272 | if let Error::Deserialization { column, .. } = &mut error { 273 | *column = Some(error_column.to_string()); 274 | } 275 | error 276 | } 277 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error as StdError, fmt, result}; 2 | 3 | use serde::{de, ser}; 4 | 5 | #[derive(Debug)] 6 | pub enum Error { 7 | /// This type of serialization or deserialization is not supported 8 | Unsupported(String), 9 | /// The value is too large, e.g. trying to serialize `u64` that is too large to fit in `i64` 10 | ValueTooLarge(String), 11 | /// General error during serialization 12 | Serialization(String), 13 | /// General error during deserialization 14 | Deserialization { column: Option, message: String }, 15 | /// Error originating from rusqlite 16 | Rusqlite(rusqlite::Error), 17 | /// No column name information available 18 | ColumnNamesNotAvailable, 19 | } 20 | 21 | pub type Result = result::Result; 22 | 23 | impl Error { 24 | /// Create the instance of `Unsupported` during serialization `Error` 25 | pub fn ser_unsupported(typ: &str) -> Self { 26 | Error::Unsupported(format!("Serialization is not supported from type: {}", typ)) 27 | } 28 | 29 | /// Create the instance of `Unsupported` during deserialization `Error` 30 | pub fn de_unsupported(typ: &str) -> Self { 31 | Error::Unsupported(format!("Deserialization is not supported into type: {}", typ)) 32 | } 33 | } 34 | 35 | impl fmt::Display for Error { 36 | fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 37 | match self { 38 | Error::Unsupported(s) | Error::ValueTooLarge(s) => write!(f, "{}", s), 39 | Error::Serialization(s) => write!(f, "Serialization error: {}", s), 40 | Error::Deserialization { 41 | column: Some(column), 42 | message, 43 | } => write!(f, "Deserialization failed for column: {} error: {}", column, message), 44 | Error::Deserialization { message, .. } => write!(f, "Deserialization error: {}", message), 45 | Error::Rusqlite(s) => write!(f, "Rusqlite error: {}", s), 46 | Error::ColumnNamesNotAvailable => write!(f, "Column names are not available"), 47 | } 48 | } 49 | } 50 | 51 | impl StdError for Error { 52 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 53 | match self { 54 | Error::Rusqlite(e) => Some(e), 55 | Error::Unsupported(_) 56 | | Error::ValueTooLarge(_) 57 | | Error::Serialization(_) 58 | | Error::Deserialization { .. } 59 | | Error::ColumnNamesNotAvailable => None, 60 | } 61 | } 62 | } 63 | 64 | impl de::Error for Error { 65 | fn custom(msg: T) -> Self { 66 | Error::Deserialization { 67 | column: None, 68 | message: msg.to_string(), 69 | } 70 | } 71 | } 72 | 73 | impl ser::Error for Error { 74 | fn custom(msg: T) -> Self { 75 | Error::Serialization(msg.to_string()) 76 | } 77 | } 78 | 79 | impl From for Error { 80 | fn from(e: rusqlite::Error) -> Self { 81 | Error::Rusqlite(e) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Serde Rusqlite 2 | //! 3 | //! This crate provides convenience functions to bridge serde and rusqlite. With their help 4 | //! you can "deserialize" rusqlite `Row`'s into serde `Deserialize` types and "serialize" types 5 | //! implementing `Serialize` into bound query arguments (positional or named) that rusqlite expects. 6 | //! 7 | //! Serialization of named bound arguments is only supported from `struct`s and `map`s because other 8 | //! serde types lack column name information. Likewise, serialization of positional bound arguments 9 | //! is only supported from `tuple`s, `sequence`s and primitive non-iterable types. In the latter case 10 | //! the result will be single-element vector. Each serialized field or element must implement 11 | //! `rusqlite::types::ToSql`. 12 | //! 13 | //! For deserialization you can use two families of functions: `from_*()` and `from_*_with_columns()`. 14 | //! The most used one is the former. The latter allows you to specify column names for types that need 15 | //! them, but don't supply them. This includes different `Map` types like `HashMap`. Specifying columns 16 | //! for deserialization into e.g. `struct` doesn't have any effect as the field list of the struct itself 17 | //! will be used in any case. 18 | //! 19 | //! SQLite only supports 5 types: `NULL` (`None`), `INTEGER` (`i64`), `REAL` (`f64`), `TEXT` (`String`) 20 | //! and `BLOB` (`Vec`). Corresponding rust types are inside brackets. 21 | //! 22 | //! Some types employ non-trivial handling, these are described below: 23 | //! 24 | //! * Serialization of `u64` will fail if it can't be represented by `i64` due to sqlite limitations. 25 | //! * Simple `enum`s will be serialized as strings so: 26 | //! 27 | //! ``` 28 | //! enum Gender { 29 | //! M, 30 | //! F, 31 | //! } 32 | //! ``` 33 | //! 34 | //! will have two possible `TEXT` options in the database "M" and "F". Deserialization into `enum` 35 | //! from `TEXT` is also supported. 36 | //! * `bool`s are serialized as `INTEGER`s 0 or 1, can be deserialized from `INTEGER` and `REAL` where 37 | //! 0 and 0.0 are `false`, anything else is `true`. 38 | //! * `f64` and `f32` values of `NaN` are serialized as `NULL`s. When deserializing such value `Option` 39 | //! will have value of `None` and `f64` will have value of `NaN`. The same applies to `f32`. 40 | //! * `Bytes`, `ByteBuf` from `serde_bytes` are supported as optimized way of handling `BLOB`s. 41 | //! * `unit` serializes to `NULL`. 42 | //! * Only `sequence`s of `u8` are serialized and deserialized, `BLOB` database type is used. It's 43 | //! more optimal though to use `Bytes` and `ByteBuf` from `serde_bytes` for such fields. 44 | //! * `unit_struct` serializes to `struct` name as `TEXT`, when deserializing the check is made to ensure 45 | //! that `struct` name coincides with the string in the database. 46 | //! 47 | //! # Examples 48 | //! ``` 49 | //! use serde_derive::{Deserialize, Serialize}; 50 | //! use serde_rusqlite::*; 51 | //! 52 | //! #[derive(Serialize, Deserialize, Debug, PartialEq)] 53 | //! struct Example { 54 | //! id: i64, 55 | //! name: String, 56 | //! } 57 | //! 58 | //! let connection = rusqlite::Connection::open_in_memory().unwrap(); 59 | //! connection.execute("CREATE TABLE example (id INT, name TEXT)", []).unwrap(); 60 | //! 61 | //! // using structure to generate named bound query arguments 62 | //! let row1 = Example { id: 1, name: "first name".into() }; 63 | //! connection.execute("INSERT INTO example (id, name) VALUES (:id, :name)", to_params_named(&row1).unwrap().to_slice().as_slice()).unwrap(); 64 | //! // and limiting the set of fields that are to be serialized 65 | //! let row2 = Example { id: 10, name: "second name".into() }; 66 | //! connection.execute("INSERT INTO example (id, name) VALUES (2, :name)", to_params_named_with_fields(&row2, &["name"]).unwrap().to_slice().as_slice()).unwrap(); 67 | //! 68 | //! // using tuple to generate positional bound query arguments 69 | //! let row2 = (3, "third name"); 70 | //! connection.execute("INSERT INTO example (id, name) VALUES (?, ?)", to_params(&row2).unwrap()).unwrap(); 71 | //! 72 | //! // deserializing using query() and from_rows(), the most efficient way 73 | //! let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 74 | //! let mut res = from_rows::(statement.query([]).unwrap()); 75 | //! assert_eq!(res.next().unwrap().unwrap(), row1); 76 | //! assert_eq!(res.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() }); 77 | //! 78 | //! // deserializing using query_and_then() and from_row(), incurs extra overhead in from_row() call 79 | //! let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 80 | //! let mut rows = statement.query_and_then([], from_row::).unwrap(); 81 | //! assert_eq!(rows.next().unwrap().unwrap(), row1); 82 | //! assert_eq!(rows.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() }); 83 | //! 84 | //! // deserializing using query_and_then() and from_row_with_columns(), better performance than from_row() 85 | //! let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 86 | //! let columns = columns_from_statement(&statement); 87 | //! let mut rows = statement.query_and_then([], |row| from_row_with_columns::(row, &columns)).unwrap(); 88 | //! assert_eq!(rows.next().unwrap().unwrap(), row1); 89 | //! assert_eq!(rows.next().unwrap().unwrap(), Example { id: 2, name: "second name".into() }); 90 | //! 91 | //! // deserializing using query() and from_rows_ref() 92 | //! let mut statement = connection.prepare("SELECT * FROM example").unwrap(); 93 | //! let mut rows = statement.query([]).unwrap(); 94 | //! { 95 | //! // only first record is deserialized here 96 | //! let mut res = from_rows_ref::(&mut rows); 97 | //! assert_eq!(res.next().unwrap().unwrap(), row1); 98 | //! } 99 | //! // the second record is deserialized using the original Rows iterator 100 | //! assert_eq!(from_row::(&rows.next().unwrap().unwrap()).unwrap(), Example { id: 2, name: "second name".into() }); 101 | //! ``` 102 | 103 | pub use rusqlite; 104 | use rusqlite::{params_from_iter, ParamsFromIter}; 105 | 106 | pub use de::{DeserRows, DeserRowsRef, RowDeserializer}; 107 | pub use error::{Error, Result}; 108 | pub use ser::{NamedParamSlice, NamedSliceSerializer, PositionalParams, PositionalSliceSerializer}; 109 | 110 | pub mod de; 111 | pub mod error; 112 | pub mod ser; 113 | #[cfg(test)] 114 | mod tests; 115 | 116 | /// Returns column names of the statement the way `from_row_with_columns()` method expects them 117 | /// 118 | /// This function is needed because by default `column_names()` returns `Vec<&str>` which 119 | /// ties it to the lifetime of the `rusqlite::Statement`. This way we won't be able to run for example 120 | /// `.query_map()` because it mutably borrows `rusqlite::Statement` and by that time it's already borrowed 121 | /// for columns. So this function owns all column names to detach them from the lifetime of `rusqlite::Statement`. 122 | #[inline] 123 | pub fn columns_from_statement(stmt: &rusqlite::Statement) -> Vec { 124 | stmt.column_names().into_iter().map(str::to_owned).collect() 125 | } 126 | 127 | /// Deserializes an instance of `D: serde::Deserialize` from `rusqlite::Row` 128 | /// 129 | /// Calling this function incurs allocation and processing overhead because we need to fetch column names from the row. 130 | /// So use with care when calling this function in a loop or check `from_row_with_columns()` to avoid that overhead. 131 | /// 132 | /// You should supply this function to `query_map()`. 133 | #[inline] 134 | pub fn from_row(row: &rusqlite::Row) -> Result { 135 | let columns = row.as_ref().column_names(); 136 | let columns_ref = columns.iter().map(|x| x.to_string()).collect::>(); 137 | from_row_with_columns(row, &columns_ref) 138 | } 139 | 140 | /// Deserializes any instance of `D: serde::Deserialize` from `rusqlite::Row` with specified columns 141 | /// 142 | /// Use this function over `from_row()` to avoid allocation and overhead for fetching column names. To get columns names 143 | /// you can use `columns_from_statement()`. 144 | /// 145 | /// You should use this function in the closure you supply to `query_map()`. 146 | /// 147 | /// Note: `columns` is a slice of owned `String`s to be type compatible with what `columns_from_statement()` 148 | /// returns. Most of the time the result of that function will be used as the argument so it makes little sense 149 | /// to accept something like `&[impl AsRef]` here. It will only make usage of the API less ergonomic. E.g. 150 | /// There will be 2 generic type arguments to the `from_row_with_columns()` instead of one. 151 | #[inline] 152 | pub fn from_row_with_columns(row: &rusqlite::Row, columns: &[String]) -> Result { 153 | D::deserialize(RowDeserializer::from_row_with_columns(row, columns)) 154 | } 155 | 156 | /// Returns iterator that owns `rusqlite::Rows` and deserializes all records from it into instances of `D: serde::Deserialize` 157 | /// 158 | /// Also see `from_row()` for some specific info. 159 | /// 160 | /// This function covers most of the use cases and is easier to use than the alternative `from_rows_ref()`. 161 | #[inline] 162 | pub fn from_rows(rows: rusqlite::Rows) -> DeserRows { 163 | DeserRows::new(rows) 164 | } 165 | 166 | /// Returns iterator that borrows `rusqlite::Rows` and deserializes records from it into instances of `D: serde::Deserialize` 167 | /// 168 | /// Use this function instead of `from_rows()` when you still need iterator with the remaining rows after deserializing some 169 | /// of them. 170 | #[inline] 171 | pub fn from_rows_ref<'rows, 'stmt, D: serde::de::DeserializeOwned>( 172 | rows: &'rows mut rusqlite::Rows<'stmt>, 173 | ) -> DeserRowsRef<'rows, 'stmt, D> { 174 | DeserRowsRef::new(rows) 175 | } 176 | 177 | /// Serializes an instance of `S: serde::Serialize` into structure for positional bound query arguments 178 | /// 179 | /// To get the slice suitable for supplying to `query()` or `execute()` call `to_slice()` on the `Ok` result and 180 | /// borrow it. 181 | #[inline] 182 | pub fn to_params(obj: S) -> Result> { 183 | obj.serialize(PositionalSliceSerializer::default()).map(params_from_iter) 184 | } 185 | 186 | /// Serializes an instance of `S: serde::Serialize` into structure for named bound query arguments 187 | /// 188 | /// To get the slice suitable for supplying to `query_named()` or `execute_named()` call `to_slice()` on the `Ok` result 189 | /// and borrow it. 190 | #[inline] 191 | pub fn to_params_named(obj: S) -> Result { 192 | obj.serialize(NamedSliceSerializer::default()) 193 | } 194 | 195 | /// Serializes only the specified `fields` of an instance of `S: serde::Serialize` into structure 196 | /// for named bound query arguments 197 | /// 198 | /// To get the slice suitable for supplying to `query_named()` or `execute_named()` call `to_slice()` on the `Ok` result 199 | /// and borrow it. 200 | #[inline] 201 | pub fn to_params_named_with_fields(obj: S, fields: &[&str]) -> Result { 202 | obj.serialize(NamedSliceSerializer::with_only_fields(fields)) 203 | } 204 | -------------------------------------------------------------------------------- /src/ser/blob.rs: -------------------------------------------------------------------------------- 1 | use serde::ser; 2 | 3 | use crate::{Error, Result}; 4 | 5 | pub struct BlobSerializer { 6 | pub buf: Vec, 7 | } 8 | 9 | impl ser::SerializeSeq for BlobSerializer { 10 | type Ok = Box; 11 | type Error = Error; 12 | 13 | fn serialize_element(&mut self, value: &T) -> Result<()> { 14 | self.buf.push(value.serialize(U8Serializer)?); 15 | Ok(()) 16 | } 17 | 18 | fn end(self) -> Result { 19 | Ok(Box::new(self.buf)) 20 | } 21 | } 22 | 23 | pub struct U8Serializer; 24 | 25 | impl ser::Serializer for U8Serializer { 26 | type Ok = u8; 27 | type Error = Error; 28 | type SerializeSeq = ser::Impossible; 29 | type SerializeTuple = ser::Impossible; 30 | type SerializeTupleStruct = ser::Impossible; 31 | type SerializeTupleVariant = ser::Impossible; 32 | type SerializeMap = ser::Impossible; 33 | type SerializeStruct = ser::Impossible; 34 | type SerializeStructVariant = ser::Impossible; 35 | 36 | fn serialize_u8(self, v: u8) -> Result { 37 | Ok(v) 38 | } 39 | 40 | ser_unimpl!(serialize_bool, bool); 41 | ser_unimpl!(serialize_i8, i8); 42 | ser_unimpl!(serialize_i16, i16); 43 | ser_unimpl!(serialize_i32, i32); 44 | ser_unimpl!(serialize_i64, i64); 45 | ser_unimpl!(serialize_u16, u16); 46 | ser_unimpl!(serialize_u32, u32); 47 | ser_unimpl!(serialize_u64, u64); 48 | ser_unimpl!(serialize_f32, f32); 49 | ser_unimpl!(serialize_f64, f64); 50 | ser_unimpl!(serialize_char, char); 51 | ser_unimpl!(serialize_str, &str); 52 | ser_unimpl!(serialize_bytes, &[u8]); 53 | 54 | fn serialize_none(self) -> Result { 55 | Err(Error::ser_unsupported("None")) 56 | } 57 | fn serialize_some(self, _value: &T) -> Result { 58 | Err(Error::ser_unsupported("Some")) 59 | } 60 | fn serialize_unit(self) -> Result { 61 | Err(Error::ser_unsupported("()")) 62 | } 63 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 64 | self.serialize_unit() 65 | } 66 | fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str) -> Result { 67 | self.serialize_unit() 68 | } 69 | fn serialize_newtype_struct(self, _name: &'static str, _value: &T) -> Result { 70 | Err(Error::ser_unsupported("newtype_struct")) 71 | } 72 | fn serialize_newtype_variant( 73 | self, 74 | _name: &'static str, 75 | _variant_index: u32, 76 | _variant: &'static str, 77 | _value: &T, 78 | ) -> Result { 79 | Err(Error::ser_unsupported("newtype_variant")) 80 | } 81 | fn serialize_seq(self, _len: Option) -> Result { 82 | Err(Error::ser_unsupported("seq")) 83 | } 84 | fn serialize_tuple(self, _len: usize) -> Result { 85 | Err(Error::ser_unsupported("tuple")) 86 | } 87 | fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result { 88 | Err(Error::ser_unsupported("tuple_struct")) 89 | } 90 | fn serialize_tuple_variant( 91 | self, 92 | _name: &'static str, 93 | _variant_index: u32, 94 | _variant: &'static str, 95 | _len: usize, 96 | ) -> Result { 97 | Err(Error::ser_unsupported("type_variant")) 98 | } 99 | fn serialize_map(self, _len: Option) -> Result { 100 | Err(Error::ser_unsupported("map")) 101 | } 102 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 103 | Err(Error::ser_unsupported("struct")) 104 | } 105 | fn serialize_struct_variant( 106 | self, 107 | _name: &'static str, 108 | _variant_index: u32, 109 | _variant: &'static str, 110 | _len: usize, 111 | ) -> Result { 112 | Err(Error::ser_unsupported("struct_variant")) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/ser/mod.rs: -------------------------------------------------------------------------------- 1 | pub use super::{Error, Result}; 2 | 3 | pub use self::named::NamedSliceSerializer; 4 | pub use self::positional::{PositionalParams, PositionalSliceSerializer}; 5 | pub use self::slice::NamedParamSlice; 6 | 7 | macro_rules! ser_unimpl { 8 | ($fun:ident, $type:ty) => { 9 | fn $fun(self, _v: $type) -> Result { 10 | Err(Error::ser_unsupported(stringify!($type))) 11 | } 12 | }; 13 | } 14 | 15 | mod blob; 16 | mod named; 17 | mod positional; 18 | mod slice; 19 | mod tosql; 20 | -------------------------------------------------------------------------------- /src/ser/named.rs: -------------------------------------------------------------------------------- 1 | use serde::ser; 2 | 3 | use crate::{Error, NamedParamSlice, Result}; 4 | 5 | use super::tosql::ToSqlSerializer; 6 | 7 | /// Serializer into `NamedParamSlice` 8 | /// 9 | /// You shouldn't use it directly, but via the crate's `to_params_named()` function. Check the crate documentation for example. 10 | #[derive(Default)] 11 | pub struct NamedSliceSerializer<'f> { 12 | pub result: NamedParamSlice, 13 | entry_key: Option, 14 | only_fields: &'f [&'f str], 15 | } 16 | 17 | impl<'f> NamedSliceSerializer<'f> { 18 | pub fn with_only_fields(only_fields: &'f [&'f str]) -> Self { 19 | Self { 20 | result: NamedParamSlice::default(), 21 | entry_key: None, 22 | only_fields, 23 | } 24 | } 25 | 26 | #[inline] 27 | fn add_entry(&mut self, key: &str, value: impl serde::Serialize) -> Result<()> { 28 | if self.only_fields.is_empty() || self.only_fields.contains(&key) { 29 | self.result.push((format!(":{}", key), value.serialize(ToSqlSerializer)?)); 30 | } 31 | Ok(()) 32 | } 33 | } 34 | 35 | impl ser::Serializer for NamedSliceSerializer<'_> { 36 | type Ok = NamedParamSlice; 37 | type Error = Error; 38 | type SerializeSeq = ser::Impossible; 39 | type SerializeTuple = ser::Impossible; 40 | type SerializeTupleStruct = ser::Impossible; 41 | type SerializeTupleVariant = ser::Impossible; 42 | type SerializeMap = Self; 43 | type SerializeStruct = Self; 44 | type SerializeStructVariant = Self; 45 | 46 | fn serialize_none(self) -> Result { 47 | Err(Error::ser_unsupported("None")) 48 | } 49 | 50 | fn serialize_some(self, value: &T) -> Result { 51 | value.serialize(self) 52 | } 53 | 54 | fn serialize_unit(self) -> Result { 55 | Err(Error::ser_unsupported("()")) 56 | } 57 | 58 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 59 | Err(Error::ser_unsupported("unit_struct")) 60 | } 61 | 62 | fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result { 63 | self.serialize_str(variant) 64 | } 65 | 66 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result { 67 | value.serialize(self) 68 | } 69 | 70 | fn serialize_newtype_variant( 71 | self, 72 | _name: &'static str, 73 | _variant_index: u32, 74 | _variant: &'static str, 75 | value: &T, 76 | ) -> Result { 77 | value.serialize(self) 78 | } 79 | 80 | ser_unimpl!(serialize_bool, bool); 81 | ser_unimpl!(serialize_i8, i8); 82 | ser_unimpl!(serialize_i16, i16); 83 | ser_unimpl!(serialize_i32, i32); 84 | ser_unimpl!(serialize_i64, i64); 85 | ser_unimpl!(serialize_u8, u8); 86 | ser_unimpl!(serialize_u16, u16); 87 | ser_unimpl!(serialize_u32, u32); 88 | ser_unimpl!(serialize_u64, u64); 89 | ser_unimpl!(serialize_f32, f32); 90 | ser_unimpl!(serialize_f64, f64); 91 | ser_unimpl!(serialize_str, &str); 92 | ser_unimpl!(serialize_char, char); 93 | ser_unimpl!(serialize_bytes, &[u8]); 94 | 95 | fn serialize_seq(self, _len: Option) -> Result { 96 | Err(Error::ser_unsupported("seq")) 97 | } 98 | fn serialize_tuple(self, _len: usize) -> Result { 99 | Err(Error::ser_unsupported("tuple")) 100 | } 101 | fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result { 102 | Err(Error::ser_unsupported("tuple_struct")) 103 | } 104 | fn serialize_tuple_variant( 105 | self, 106 | _name: &'static str, 107 | _variant_index: u32, 108 | _variant: &'static str, 109 | _len: usize, 110 | ) -> Result { 111 | Err(Error::ser_unsupported("tuple_variant")) 112 | } 113 | fn serialize_map(mut self, len: Option) -> Result { 114 | if let Some(len) = len { 115 | self.result.reserve_exact(len); 116 | } 117 | Ok(self) 118 | } 119 | fn serialize_struct(mut self, _name: &'static str, len: usize) -> Result { 120 | self.result.reserve_exact(len); 121 | Ok(self) 122 | } 123 | fn serialize_struct_variant( 124 | mut self, 125 | _name: &'static str, 126 | _variant_index: u32, 127 | _variant: &'static str, 128 | len: usize, 129 | ) -> Result { 130 | self.result.reserve_exact(len); 131 | Ok(self) 132 | } 133 | } 134 | 135 | impl ser::SerializeMap for NamedSliceSerializer<'_> { 136 | type Ok = NamedParamSlice; 137 | type Error = Error; 138 | 139 | fn serialize_key(&mut self, key: &T) -> Result<()> { 140 | self.entry_key = Some(key.serialize(ColumNameSerializer)?); 141 | Ok(()) 142 | } 143 | 144 | fn serialize_value(&mut self, value: &T) -> Result<()> { 145 | if let Some(column_name) = self.entry_key.take() { 146 | self.add_entry(&column_name, value)?; 147 | } 148 | Ok(()) 149 | } 150 | 151 | fn end(self) -> Result { 152 | Ok(self.result) 153 | } 154 | } 155 | 156 | impl ser::SerializeStruct for NamedSliceSerializer<'_> { 157 | type Ok = NamedParamSlice; 158 | type Error = Error; 159 | 160 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> { 161 | self.add_entry(key, value) 162 | } 163 | 164 | fn end(self) -> Result { 165 | Ok(self.result) 166 | } 167 | } 168 | 169 | impl ser::SerializeStructVariant for NamedSliceSerializer<'_> { 170 | type Ok = NamedParamSlice; 171 | type Error = Error; 172 | 173 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> { 174 | self.add_entry(key, value) 175 | } 176 | 177 | fn end(self) -> Result { 178 | Ok(self.result) 179 | } 180 | } 181 | 182 | struct ColumNameSerializer; 183 | 184 | impl ser::Serializer for ColumNameSerializer { 185 | type Ok = String; 186 | type Error = Error; 187 | type SerializeSeq = ser::Impossible; 188 | type SerializeTuple = ser::Impossible; 189 | type SerializeTupleStruct = ser::Impossible; 190 | type SerializeTupleVariant = ser::Impossible; 191 | type SerializeMap = ser::Impossible; 192 | type SerializeStruct = ser::Impossible; 193 | type SerializeStructVariant = ser::Impossible; 194 | 195 | fn serialize_char(self, v: char) -> Result { 196 | Ok(v.to_string()) 197 | } 198 | 199 | fn serialize_str(self, v: &str) -> Result { 200 | Ok(v.into()) 201 | } 202 | 203 | ser_unimpl!(serialize_bool, bool); 204 | ser_unimpl!(serialize_i8, i8); 205 | ser_unimpl!(serialize_i16, i16); 206 | ser_unimpl!(serialize_i32, i32); 207 | ser_unimpl!(serialize_i64, i64); 208 | ser_unimpl!(serialize_u8, u8); 209 | ser_unimpl!(serialize_u16, u16); 210 | ser_unimpl!(serialize_u32, u32); 211 | ser_unimpl!(serialize_u64, u64); 212 | ser_unimpl!(serialize_f32, f32); 213 | ser_unimpl!(serialize_f64, f64); 214 | ser_unimpl!(serialize_bytes, &[u8]); 215 | 216 | fn serialize_none(self) -> Result { 217 | Err(Error::ser_unsupported("None")) 218 | } 219 | fn serialize_some(self, _value: &T) -> Result { 220 | Err(Error::ser_unsupported("Some")) 221 | } 222 | fn serialize_unit(self) -> Result { 223 | Err(Error::ser_unsupported("()")) 224 | } 225 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 226 | Err(Error::ser_unsupported("unit_struct")) 227 | } 228 | fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str) -> Result { 229 | Err(Error::ser_unsupported("unit_variant")) 230 | } 231 | fn serialize_newtype_struct(self, _name: &'static str, _value: &T) -> Result { 232 | Err(Error::ser_unsupported("newtype_struct")) 233 | } 234 | fn serialize_newtype_variant( 235 | self, 236 | _name: &'static str, 237 | _variant_index: u32, 238 | _variant: &'static str, 239 | _value: &T, 240 | ) -> Result { 241 | Err(Error::ser_unsupported("newtype_variant")) 242 | } 243 | fn serialize_seq(self, _len: Option) -> Result { 244 | Err(Error::ser_unsupported("seq")) 245 | } 246 | fn serialize_tuple(self, _len: usize) -> Result { 247 | Err(Error::ser_unsupported("tuple")) 248 | } 249 | fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result { 250 | Err(Error::ser_unsupported("tuple_struct")) 251 | } 252 | fn serialize_tuple_variant( 253 | self, 254 | _name: &'static str, 255 | _variant_index: u32, 256 | _variant: &'static str, 257 | _len: usize, 258 | ) -> Result { 259 | Err(Error::ser_unsupported("type_variant")) 260 | } 261 | fn serialize_map(self, _len: Option) -> Result { 262 | Err(Error::ser_unsupported("map")) 263 | } 264 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 265 | Err(Error::ser_unsupported("struct")) 266 | } 267 | fn serialize_struct_variant( 268 | self, 269 | _name: &'static str, 270 | _variant_index: u32, 271 | _variant: &'static str, 272 | _len: usize, 273 | ) -> Result { 274 | Err(Error::ser_unsupported("struct_variant")) 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/ser/positional.rs: -------------------------------------------------------------------------------- 1 | use rusqlite::ToSql; 2 | use serde::ser; 3 | 4 | use crate::{Error, Result}; 5 | 6 | use super::tosql::ToSqlSerializer; 7 | 8 | macro_rules! forward_tosql { 9 | ($fun:ident, $type:ty) => { 10 | fn $fun(mut self, v: $type) -> Result { 11 | self.result.push(ToSqlSerializer.$fun(v)?); 12 | Ok(self.result) 13 | } 14 | }; 15 | ($fun:ident) => { 16 | fn $fun(mut self) -> Result { 17 | self.result.push(ToSqlSerializer.$fun()?); 18 | Ok(self.result) 19 | } 20 | }; 21 | } 22 | 23 | pub type PositionalParams = Vec>; 24 | 25 | /// Serializer into `PositionalParams` 26 | /// 27 | /// You shouldn't use it directly, but via the crate's `to_params()` function. Check the crate documentation for example. 28 | #[derive(Default)] 29 | pub struct PositionalSliceSerializer { 30 | pub result: PositionalParams, 31 | } 32 | 33 | impl ser::Serializer for PositionalSliceSerializer { 34 | type Ok = PositionalParams; 35 | type Error = Error; 36 | type SerializeSeq = Self; 37 | type SerializeTuple = Self; 38 | type SerializeTupleStruct = Self; 39 | type SerializeTupleVariant = Self; 40 | type SerializeMap = ser::Impossible; 41 | type SerializeStruct = ser::Impossible; 42 | type SerializeStructVariant = ser::Impossible; 43 | 44 | forward_tosql!(serialize_bool, bool); 45 | forward_tosql!(serialize_i8, i8); 46 | forward_tosql!(serialize_i16, i16); 47 | forward_tosql!(serialize_i32, i32); 48 | forward_tosql!(serialize_i64, i64); 49 | forward_tosql!(serialize_u8, u8); 50 | forward_tosql!(serialize_u16, u16); 51 | forward_tosql!(serialize_u32, u32); 52 | forward_tosql!(serialize_u64, u64); 53 | forward_tosql!(serialize_f32, f32); 54 | forward_tosql!(serialize_f64, f64); 55 | forward_tosql!(serialize_str, &str); 56 | forward_tosql!(serialize_char, char); 57 | forward_tosql!(serialize_bytes, &[u8]); 58 | forward_tosql!(serialize_none); 59 | forward_tosql!(serialize_unit); 60 | 61 | fn serialize_some(self, value: &T) -> Result { 62 | value.serialize(self) 63 | } 64 | 65 | fn serialize_unit_struct(mut self, name: &'static str) -> Result { 66 | self.result.push(ToSqlSerializer.serialize_unit_struct(name)?); 67 | Ok(self.result) 68 | } 69 | 70 | fn serialize_unit_variant(mut self, name: &'static str, variant_index: u32, variant: &'static str) -> Result { 71 | self 72 | .result 73 | .push(ToSqlSerializer.serialize_unit_variant(name, variant_index, variant)?); 74 | Ok(self.result) 75 | } 76 | 77 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result { 78 | value.serialize(self) 79 | } 80 | 81 | fn serialize_newtype_variant( 82 | self, 83 | _name: &'static str, 84 | _variant_index: u32, 85 | _variant: &'static str, 86 | value: &T, 87 | ) -> Result { 88 | value.serialize(self) 89 | } 90 | 91 | fn serialize_seq(mut self, len: Option) -> Result { 92 | if let Some(len) = len { 93 | self.result.reserve_exact(len); 94 | } 95 | Ok(self) 96 | } 97 | 98 | fn serialize_tuple(mut self, len: usize) -> Result { 99 | self.result.reserve_exact(len); 100 | Ok(self) 101 | } 102 | 103 | fn serialize_tuple_struct(mut self, _name: &'static str, len: usize) -> Result { 104 | self.result.reserve_exact(len); 105 | Ok(self) 106 | } 107 | 108 | fn serialize_tuple_variant( 109 | mut self, 110 | _name: &'static str, 111 | _variant_index: u32, 112 | _variant: &'static str, 113 | len: usize, 114 | ) -> Result { 115 | self.result.reserve_exact(len); 116 | Ok(self) 117 | } 118 | 119 | fn serialize_map(self, _len: Option) -> Result { 120 | Err(Error::ser_unsupported("map")) 121 | } 122 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 123 | Err(Error::ser_unsupported("struct")) 124 | } 125 | fn serialize_struct_variant( 126 | self, 127 | _name: &'static str, 128 | _variant_index: u32, 129 | _variant: &'static str, 130 | _len: usize, 131 | ) -> Result { 132 | Err(Error::ser_unsupported("struct_variant")) 133 | } 134 | } 135 | 136 | impl ser::SerializeSeq for PositionalSliceSerializer { 137 | type Ok = PositionalParams; 138 | type Error = Error; 139 | 140 | fn serialize_element(&mut self, value: &T) -> Result<()> { 141 | self.result.push(value.serialize(ToSqlSerializer)?); 142 | Ok(()) 143 | } 144 | 145 | fn end(self) -> Result { 146 | Ok(self.result) 147 | } 148 | } 149 | 150 | impl ser::SerializeTuple for PositionalSliceSerializer { 151 | type Ok = PositionalParams; 152 | type Error = Error; 153 | 154 | fn serialize_element(&mut self, value: &T) -> Result<()> { 155 | self.result.push(value.serialize(ToSqlSerializer)?); 156 | Ok(()) 157 | } 158 | 159 | fn end(self) -> Result { 160 | Ok(self.result) 161 | } 162 | } 163 | 164 | impl ser::SerializeTupleStruct for PositionalSliceSerializer { 165 | type Ok = PositionalParams; 166 | type Error = Error; 167 | 168 | fn serialize_field(&mut self, value: &T) -> Result<()> { 169 | self.result.push(value.serialize(ToSqlSerializer)?); 170 | Ok(()) 171 | } 172 | 173 | fn end(self) -> Result { 174 | Ok(self.result) 175 | } 176 | } 177 | 178 | impl ser::SerializeTupleVariant for PositionalSliceSerializer { 179 | type Ok = PositionalParams; 180 | type Error = Error; 181 | 182 | fn serialize_field(&mut self, value: &T) -> Result<()> { 183 | self.result.push(value.serialize(ToSqlSerializer)?); 184 | Ok(()) 185 | } 186 | 187 | fn end(self) -> Result { 188 | Ok(self.result) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/ser/slice.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::ops::{Deref, DerefMut}; 3 | 4 | /// Stores named bound query arguments 5 | /// 6 | /// This `struct` stores data for passing as argument slice to `*_named()` query functions of rusqlite. 7 | /// To get the instance call crate's `to_named_params()` function. 8 | #[derive(Default)] 9 | pub struct NamedParamSlice(Vec<(String, Box)>); 10 | 11 | impl NamedParamSlice { 12 | pub fn to_slice(&self) -> Vec<(&str, &dyn rusqlite::types::ToSql)> { 13 | self.0.iter().map(|x| (x.0.as_str(), x.1.borrow())).collect() 14 | } 15 | } 16 | 17 | impl From)>> for NamedParamSlice { 18 | fn from(src: Vec<(String, Box)>) -> Self { 19 | Self(src) 20 | } 21 | } 22 | 23 | impl Deref for NamedParamSlice { 24 | type Target = Vec<(String, Box)>; 25 | 26 | fn deref(&self) -> &Self::Target { 27 | &self.0 28 | } 29 | } 30 | 31 | impl DerefMut for NamedParamSlice { 32 | fn deref_mut(&mut self) -> &mut Self::Target { 33 | &mut self.0 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/ser/tosql.rs: -------------------------------------------------------------------------------- 1 | use rusqlite::types::{ToSql, Value}; 2 | use serde::ser; 3 | 4 | use crate::{Error, Result}; 5 | 6 | use super::blob::BlobSerializer; 7 | 8 | macro_rules! tosql_ser { 9 | ($fun:ident, &$type:ty) => { 10 | fn $fun(self, v: &$type) -> Result { 11 | Ok(Box::new(v.to_owned())) 12 | } 13 | }; 14 | ($fun:ident, $type:ty) => { 15 | fn $fun(self, v: $type) -> Result { 16 | Ok(Box::new(v)) 17 | } 18 | }; 19 | } 20 | 21 | pub struct ToSqlSerializer; 22 | 23 | impl ser::Serializer for ToSqlSerializer { 24 | type Ok = Box; 25 | type Error = Error; 26 | type SerializeSeq = BlobSerializer; 27 | type SerializeTuple = ser::Impossible; 28 | type SerializeTupleStruct = ser::Impossible; 29 | type SerializeTupleVariant = ser::Impossible; 30 | type SerializeMap = ser::Impossible; 31 | type SerializeStruct = ser::Impossible; 32 | type SerializeStructVariant = ser::Impossible; 33 | 34 | tosql_ser!(serialize_bool, bool); 35 | tosql_ser!(serialize_i8, i8); 36 | tosql_ser!(serialize_i16, i16); 37 | tosql_ser!(serialize_i32, i32); 38 | tosql_ser!(serialize_i64, i64); 39 | tosql_ser!(serialize_u8, u8); 40 | tosql_ser!(serialize_u16, u16); 41 | tosql_ser!(serialize_u32, u32); 42 | tosql_ser!(serialize_f64, f64); 43 | tosql_ser!(serialize_str, &str); 44 | tosql_ser!(serialize_bytes, &[u8]); 45 | 46 | fn serialize_u64(self, v: u64) -> Result { 47 | if v > i64::MAX as u64 { 48 | Err(Error::ValueTooLarge(format!("Value is too large to fit into i64: {}", v))) 49 | } else { 50 | self.serialize_i64(v as i64) 51 | } 52 | } 53 | 54 | fn serialize_f32(self, v: f32) -> Result { 55 | self.serialize_f64(f64::from(v)) 56 | } 57 | 58 | fn serialize_char(self, v: char) -> Result { 59 | let mut char_bytes = [0; 4]; 60 | self.serialize_str(v.encode_utf8(&mut char_bytes)) 61 | } 62 | 63 | fn serialize_none(self) -> Result { 64 | Ok(Box::new(Value::Null)) 65 | } 66 | 67 | fn serialize_some(self, value: &T) -> Result { 68 | value.serialize(self) 69 | } 70 | 71 | fn serialize_unit(self) -> Result { 72 | self.serialize_none() 73 | } 74 | 75 | fn serialize_unit_struct(self, name: &'static str) -> Result { 76 | self.serialize_str(name) 77 | } 78 | 79 | fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result { 80 | self.serialize_str(variant) 81 | } 82 | 83 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result { 84 | value.serialize(self) 85 | } 86 | 87 | fn serialize_newtype_variant( 88 | self, 89 | name: &'static str, 90 | _variant_index: u32, 91 | _variant: &'static str, 92 | value: &T, 93 | ) -> Result { 94 | self.serialize_newtype_struct(name, value) 95 | } 96 | 97 | fn serialize_seq(self, len: Option) -> Result { 98 | Ok(BlobSerializer { 99 | buf: Vec::with_capacity(len.unwrap_or(0)), 100 | }) 101 | } 102 | 103 | fn serialize_tuple(self, _len: usize) -> Result { 104 | Err(Error::ser_unsupported("tuple")) 105 | } 106 | fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result { 107 | Err(Error::ser_unsupported("tuple_struct")) 108 | } 109 | fn serialize_tuple_variant( 110 | self, 111 | _name: &'static str, 112 | _variant_index: u32, 113 | _variant: &'static str, 114 | _len: usize, 115 | ) -> Result { 116 | Err(Error::ser_unsupported("tuple_variant")) 117 | } 118 | fn serialize_map(self, _len: Option) -> Result { 119 | Err(Error::ser_unsupported("map")) 120 | } 121 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 122 | Err(Error::ser_unsupported("struct")) 123 | } 124 | fn serialize_struct_variant( 125 | self, 126 | _name: &'static str, 127 | _variant_index: u32, 128 | _variant: &'static str, 129 | _len: usize, 130 | ) -> Result { 131 | Err(Error::ser_unsupported("struct_variant")) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use std::{collections, fmt::Debug}; 2 | 3 | use rusqlite::types::{ToSqlOutput, Value, ValueRef}; 4 | use serde_derive::{Deserialize, Serialize}; 5 | 6 | use crate::Error; 7 | 8 | use super::to_params_named_with_fields; 9 | 10 | fn make_connection() -> rusqlite::Connection { 11 | make_connection_with_spec( 12 | " 13 | f_integer INT CHECK(typeof(f_integer) IN ('integer', 'null')), 14 | f_real REAL CHECK(typeof(f_real) IN ('real', 'null')), 15 | f_text TEXT CHECK(typeof(f_text) IN ('text', 'null')), 16 | f_blob BLOB CHECK(typeof(f_blob) IN ('blob', 'null')), 17 | f_null INT CHECK(typeof(f_integer) IN ('integer', 'null')) 18 | ", 19 | ) 20 | } 21 | 22 | fn make_connection_with_spec(table_spec: &str) -> rusqlite::Connection { 23 | let con = rusqlite::Connection::open_in_memory().unwrap(); 24 | con.execute(&format!("CREATE TABLE test({})", table_spec), []).unwrap(); 25 | con 26 | } 27 | 28 | fn test_value_same(db_type: &str, src: &T) { 29 | test_values(db_type, &src.clone(), src) 30 | } 31 | 32 | fn test_values( 33 | db_type: &str, 34 | value_ser: &impl serde::Serialize, 35 | value_de: &D, 36 | ) { 37 | test_values_with_cmp_fn::<_, _, &dyn Fn(&D, &D) -> bool>(db_type, value_ser, value_de, None) 38 | } 39 | 40 | fn test_ser_err bool>(value: &S, err_check_fn: F) { 41 | match super::to_params(value) { 42 | Err(e) => assert!(err_check_fn(&e), "Error raised was not of the correct type, got: {}", e), 43 | _ => panic!("Error was not raised"), 44 | } 45 | } 46 | 47 | fn test_values_with_cmp_fn(db_type: &str, value_ser: &S, value_de: &D, comparison_fn: Option) 48 | where 49 | S: serde::Serialize, 50 | D: serde::de::DeserializeOwned + PartialEq + Debug, 51 | F: Fn(&D, &D) -> bool, 52 | { 53 | let con = make_connection_with_spec(&format!("test_column {}", db_type)); 54 | // serialization 55 | con.execute( 56 | "INSERT INTO test(test_column) VALUES(?)", 57 | super::to_params(value_ser).unwrap(), 58 | ) 59 | .unwrap(); 60 | // deserialization 61 | let mut stmt = con.prepare("SELECT * FROM test").unwrap(); 62 | let res = stmt.query_and_then([], super::from_row::).unwrap(); 63 | for row in res { 64 | let row = row.unwrap(); 65 | match comparison_fn { 66 | None => assert_eq!(row, *value_de), 67 | Some(ref comparison_fn) => assert!( 68 | comparison_fn(&row, value_de), 69 | "value after deserialization is not the same as before" 70 | ), 71 | } 72 | } 73 | } 74 | 75 | #[test] 76 | fn test_bool() { 77 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &false); 78 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &true); 79 | } 80 | 81 | #[test] 82 | fn test_int() { 83 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &0_i8); 84 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &-9881_i16); 85 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &16526_i32); 86 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &-18968298731236_i64); 87 | } 88 | 89 | #[test] 90 | fn test_uint() { 91 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &112_u8); 92 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &7162u16); 93 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &98172983_u32); 94 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &98169812698712987_u64); 95 | test_ser_err(&u64::MAX, |err| matches!(*err, super::Error::ValueTooLarge(..))); 96 | } 97 | 98 | #[test] 99 | fn test_float() { 100 | test_value_same("REAL CHECK(typeof(test_column) == 'real')", &0.3_f32); 101 | test_value_same("REAL CHECK(typeof(test_column) == 'real')", &-54.7612_f64); 102 | test_value_same("REAL CHECK(typeof(test_column) == 'real')", &f64::NEG_INFINITY); 103 | test_value_same("REAL CHECK(typeof(test_column) == 'real')", &f64::INFINITY); 104 | test_value_same("REAL CHECK(typeof(test_column) == 'real')", &f32::NEG_INFINITY); 105 | test_value_same("REAL CHECK(typeof(test_column) == 'real')", &f32::INFINITY); 106 | // can't compare 2 NaN's directly, so using custom comparison function 107 | test_values_with_cmp_fn( 108 | "REAL CHECK(typeof(test_column) == 'null')", 109 | &f64::NAN, 110 | &f64::NAN, 111 | Some(|db: &f64, value: &f64| db.is_nan() && value.is_nan()), 112 | ); 113 | test_values_with_cmp_fn( 114 | "REAL CHECK(typeof(test_column) == 'null')", 115 | &f32::NAN, 116 | &f32::NAN, 117 | Some(|db: &f32, value: &f32| db.is_nan() && value.is_nan()), 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_string() { 123 | test_value_same("TEXT CHECK(typeof(test_column) == 'text')", &'a'); 124 | test_value_same("TEXT CHECK(typeof(test_column) == 'text')", &"test string".to_owned()); 125 | test_value_same("TEXT CHECK(typeof(test_column) == 'text')", &"Ünicódé".to_owned()); 126 | let val = "test string"; 127 | test_values("TEXT CHECK(typeof(test_column) == 'text')", &val, &val.to_string()); 128 | } 129 | 130 | #[test] 131 | fn test_bytes() { 132 | let val = b"123456"; 133 | test_values( 134 | "BLOB CHECK(typeof(test_column) == 'blob')", 135 | &serde_bytes::Bytes::new(val), 136 | &val.to_vec(), 137 | ); 138 | test_values( 139 | "BLOB CHECK(typeof(test_column) == 'blob')", 140 | &serde_bytes::Bytes::new(val), 141 | &serde_bytes::ByteBuf::from(val.to_vec()), 142 | ); 143 | } 144 | 145 | #[test] 146 | fn test_nullable() { 147 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &Some(18)); 148 | test_value_same::>("INT CHECK(typeof(test_column) == 'null')", &None); 149 | test_value_same("INT CHECK(typeof(test_column) == 'null')", &()); 150 | } 151 | 152 | #[test] 153 | fn test_enum() { 154 | { 155 | #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] 156 | enum Test { 157 | A, 158 | B, 159 | C, 160 | } 161 | test_value_same("TEXT CHECK(typeof(test_column) == 'text')", &Test::A); 162 | test_value_same("TEXT CHECK(typeof(test_column) == 'text')", &Test::B); 163 | test_value_same("TEXT CHECK(typeof(test_column) == 'text')", &Test::C); 164 | } 165 | } 166 | 167 | #[test] 168 | fn test_map() { 169 | { 170 | let con = make_connection_with_spec( 171 | " 172 | field_1 INT CHECK(typeof(field_1) == 'integer'), 173 | field_2 INT CHECK(typeof(field_2) == 'integer'), 174 | field_3 INT CHECK(typeof(field_3) == 'integer') 175 | ", 176 | ); 177 | // serialization 178 | let mut src = collections::HashMap::::new(); 179 | src.insert("field_2".into(), 2); 180 | src.insert("field_1".into(), 1); 181 | src.insert("field_3".into(), 3); 182 | con.execute( 183 | "INSERT INTO test VALUES(:field_1, :field_2, :field_3)", 184 | super::to_params_named(&src).unwrap().to_slice().as_slice(), 185 | ) 186 | .unwrap(); 187 | // deserialization with columns 188 | let mut stmt = con.prepare("SELECT * FROM test").unwrap(); 189 | { 190 | let columns = super::columns_from_statement(&stmt); 191 | let mut res = stmt 192 | .query_and_then([], |row| { 193 | super::from_row_with_columns::>(row, &columns) 194 | }) 195 | .unwrap(); 196 | assert_eq!(res.next().unwrap().unwrap(), src); 197 | } 198 | // deserialization without columns 199 | { 200 | let mut res = stmt 201 | .query_and_then([], super::from_row::>) 202 | .unwrap(); 203 | assert_eq!(res.next().unwrap().unwrap(), src); 204 | } 205 | } 206 | 207 | { 208 | let con = make_connection_with_spec( 209 | " 210 | a INT CHECK(typeof(a) == 'integer'), 211 | b INT CHECK(typeof(b) == 'integer'), 212 | c INT CHECK(typeof(c) == 'integer') 213 | ", 214 | ); 215 | // serialization 216 | let mut src = collections::HashMap::::new(); 217 | src.insert('a', 2); 218 | src.insert('b', 1); 219 | src.insert('c', 3); 220 | con.execute( 221 | "INSERT INTO test VALUES(:a, :b, :c)", 222 | super::to_params_named(&src).unwrap().to_slice().as_slice(), 223 | ) 224 | .unwrap(); 225 | let mut stmt = con.prepare("SELECT * FROM test").unwrap(); 226 | // deserialization with columns 227 | { 228 | let columns = super::columns_from_statement(&stmt); 229 | let mut res = stmt 230 | .query_and_then([], |row| { 231 | super::from_row_with_columns::>(row, &columns) 232 | }) 233 | .unwrap(); 234 | assert_eq!(res.next().unwrap().unwrap(), src); 235 | } 236 | // deserialization with custom columns 237 | { 238 | let columns = vec!["a".to_owned(), "b".to_owned()]; 239 | let mut res = stmt 240 | .query_and_then([], |row| { 241 | super::from_row_with_columns::>(row, &columns) 242 | }) 243 | .unwrap(); 244 | let mut src = src.clone(); 245 | src.remove(&'c'); 246 | assert_eq!(res.next().unwrap().unwrap(), src); 247 | } 248 | } 249 | } 250 | 251 | #[test] 252 | fn test_tuple() { 253 | let con = make_connection(); 254 | type Test = (i64, f64, String, Vec, Option); 255 | // serialization 256 | let src: Test = (34, 76.4, "the test".into(), vec![10, 20, 30], Some(9)); 257 | con.execute("INSERT INTO test VALUES(?, ?, ?, ?, ?)", super::to_params(&src).unwrap()) 258 | .unwrap(); 259 | let mut stmt = con.prepare("SELECT * FROM test").unwrap(); 260 | // deserialization with columns 261 | { 262 | let columns = super::columns_from_statement(&stmt); 263 | let mut res = stmt 264 | .query_and_then([], |row| super::from_row_with_columns::(row, &columns)) 265 | .unwrap(); 266 | assert_eq!(res.next().unwrap().unwrap(), src); 267 | } 268 | 269 | // deserialization without columns 270 | { 271 | let mut res = stmt.query_and_then([], super::from_row::).unwrap(); 272 | assert_eq!(res.next().unwrap().unwrap(), src); 273 | } 274 | } 275 | 276 | #[test] 277 | fn test_struct() { 278 | { 279 | #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] 280 | struct Test(i64); 281 | test_value_same("INT CHECK(typeof(test_column) == 'integer')", &Test(891287912)); 282 | } 283 | { 284 | #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] 285 | struct Test; 286 | test_value_same("TEXT CHECK(typeof(test_column) == 'text')", &Test); 287 | } 288 | { 289 | let con = make_connection(); 290 | #[derive(Deserialize, Serialize, Debug, PartialEq)] 291 | struct Test { 292 | f_integer: i64, 293 | f_real: f64, 294 | f_text: String, 295 | f_blob: Vec, 296 | f_null: Option, 297 | } 298 | #[derive(Serialize)] 299 | struct TestRef<'a> { 300 | f_integer: i64, 301 | f_real: f64, 302 | f_text: &'a str, 303 | f_blob: &'a [u8], 304 | f_null: Option, 305 | } 306 | // serialization 307 | let src = Test { 308 | f_integer: 10, 309 | f_real: 65.3, 310 | f_text: "the test".into(), 311 | f_blob: vec![0, 1, 2], 312 | f_null: None, 313 | }; 314 | let src_ref = TestRef { 315 | f_integer: src.f_integer, 316 | f_real: src.f_real, 317 | f_text: &src.f_text, 318 | f_blob: &src.f_blob, 319 | f_null: src.f_null, 320 | }; 321 | con.execute( 322 | "INSERT INTO test VALUES(:f_integer, :f_real, :f_text, :f_blob, :f_null)", 323 | super::to_params_named(src_ref).unwrap().to_slice().as_slice(), 324 | ) 325 | .unwrap(); 326 | // deserialization with columns 327 | let mut stmt = con.prepare("SELECT * FROM test").unwrap(); 328 | { 329 | let columns = super::columns_from_statement(&stmt); 330 | let mut res = stmt 331 | .query_and_then([], |row| super::from_row_with_columns::(row, &columns)) 332 | .unwrap(); 333 | assert_eq!(res.next().unwrap().unwrap(), src); 334 | } 335 | // deserialization without columns 336 | { 337 | let mut res = stmt.query_and_then([], super::from_row::).unwrap(); 338 | assert_eq!(res.next().unwrap().unwrap(), src); 339 | } 340 | } 341 | 342 | { 343 | let con = make_connection(); 344 | #[derive(Deserialize, Serialize, Debug, PartialEq)] 345 | struct Test { 346 | #[serde(with = "serde_bytes")] 347 | f_blob: Vec, 348 | f_integer: i64, 349 | f_text: String, 350 | f_null: Option, 351 | f_real: f64, 352 | } 353 | // serialization 354 | let src = Test { 355 | f_blob: vec![5, 10, 15], 356 | f_integer: 10, 357 | f_real: -65.3, 358 | f_text: "".into(), 359 | f_null: Some(43), 360 | }; 361 | con.execute( 362 | "INSERT INTO test VALUES(:f_integer, :f_real, :f_text, :f_blob, :f_null)", 363 | super::to_params_named(&src).unwrap().to_slice().as_slice(), 364 | ) 365 | .unwrap(); 366 | con.execute( 367 | "INSERT INTO test VALUES(:f_integer, :f_real, :f_text, :f_blob, :f_null)", 368 | super::to_params_named(&src).unwrap().to_slice().as_slice(), 369 | ) 370 | .unwrap(); 371 | // deserialization with columns 372 | let mut stmt = con.prepare("SELECT * FROM test").unwrap(); 373 | let mut rows = stmt.query([]).unwrap(); 374 | // deserialization 375 | let mut res = super::from_rows_ref::(&mut rows); 376 | assert_eq!(res.next().unwrap().unwrap(), src); 377 | assert_eq!(super::from_row::(rows.next().unwrap().unwrap()).unwrap(), src); 378 | } 379 | 380 | { 381 | let con = make_connection(); 382 | #[derive(Deserialize, Serialize, Debug, PartialEq)] 383 | struct Test { 384 | #[serde(with = "serde_bytes")] 385 | f_blob: Vec, 386 | f_integer: i64, 387 | f_text: String, 388 | f_null: Option, 389 | f_real: f64, 390 | } 391 | // serialization 392 | let src = Test { 393 | f_blob: vec![5, 10, 15], 394 | f_integer: 10, 395 | f_real: -65.3, 396 | f_text: "".into(), 397 | f_null: Some(43), 398 | }; 399 | con.execute( 400 | "INSERT INTO test VALUES(:f_integer, :f_real, :f_text, :f_blob, :f_null)", 401 | super::to_params_named(&src).unwrap().to_slice().as_slice(), 402 | ) 403 | .unwrap(); 404 | // deserialization 405 | let mut stmt = con.prepare("SELECT * FROM test").unwrap(); 406 | let mut res = super::from_rows::(stmt.query([]).unwrap()); 407 | assert_eq!(res.next().unwrap().unwrap(), src); 408 | } 409 | } 410 | 411 | #[test] 412 | fn test_attrs() { 413 | let con = make_connection(); 414 | #[derive(Serialize, Debug, PartialEq)] 415 | struct Test { 416 | #[serde(rename = "f_real")] 417 | custom_real: f64, 418 | f_integer: i64, 419 | f_blob: Vec, 420 | f_null: Option, 421 | f_text: String, 422 | } 423 | let src = Test { 424 | f_blob: vec![5, 10, 15], 425 | f_integer: 10, 426 | custom_real: -65.3, 427 | f_text: "test".into(), 428 | f_null: Some(43), 429 | }; 430 | con.execute( 431 | "INSERT INTO test VALUES(:f_integer, :f_real, :f_text, :f_blob, :f_null)", 432 | super::to_params_named(&src).unwrap().to_slice().as_slice(), 433 | ) 434 | .unwrap(); 435 | #[derive(Deserialize, Debug, PartialEq)] 436 | struct TestDeser { 437 | f_blob: Vec, 438 | #[serde(alias = "f_real")] 439 | custom_real: f64, 440 | #[serde(default = "default_string")] 441 | f_text: String, 442 | #[serde(default)] 443 | f_integer: i64, 444 | f_null: Option, 445 | } 446 | 447 | fn default_string() -> String { 448 | "default".into() 449 | } 450 | 451 | let mut stmt = con.prepare("SELECT f_real, f_blob, f_null FROM test").unwrap(); 452 | { 453 | let mut res = super::from_rows::(stmt.query([]).unwrap()); 454 | let row = res.next().unwrap().unwrap(); 455 | assert_eq!(row.f_integer, i64::default()); 456 | assert_eq!(row.custom_real, src.custom_real); 457 | assert_eq!(row.f_text, default_string()); 458 | assert_eq!(row.f_blob, src.f_blob); 459 | assert_eq!(row.f_null, src.f_null); 460 | } 461 | } 462 | 463 | #[test] 464 | fn test_deser_err() { 465 | let con = make_connection(); 466 | #[derive(Serialize, Debug, PartialEq)] 467 | struct Ser { 468 | f_real: f64, 469 | f_text: String, 470 | } 471 | let src = Ser { 472 | f_real: -65.3, 473 | f_text: "test".to_string(), 474 | }; 475 | con.execute( 476 | "INSERT INTO test(f_real, f_text) VALUES(:f_real, :f_text)", 477 | super::to_params_named(src).unwrap().to_slice().as_slice(), 478 | ) 479 | .unwrap(); 480 | #[derive(Deserialize, Debug, PartialEq)] 481 | struct Deser { 482 | f_real: f64, 483 | f_text: i64, 484 | } 485 | 486 | let mut stmt = con.prepare("SELECT f_real, f_text FROM test").unwrap(); 487 | { 488 | let mut res = super::from_rows::(stmt.query([]).unwrap()); 489 | let err = res.next().unwrap(); 490 | match err { 491 | Err(Error::Deserialization { column: Some(field), .. }) => { 492 | assert_eq!(field, "f_text") 493 | } 494 | _ => panic!("Unexpected result: {:?}", err), 495 | } 496 | } 497 | } 498 | 499 | #[test] 500 | fn pluck_named() { 501 | #[derive(Debug, Serialize, Deserialize)] 502 | struct Food { 503 | id: i32, 504 | name: String, 505 | flavor: String, 506 | color: String, 507 | expiration: String, 508 | } 509 | 510 | let item = Food { 511 | id: 15, 512 | name: "Snickers bar".to_string(), 513 | flavor: "choco and caramel".to_string(), 514 | color: "brown".to_string(), 515 | expiration: "2021-04-05".to_string(), 516 | }; 517 | 518 | let plucked = to_params_named_with_fields(item, &["flavor", "id"]).unwrap(); 519 | let sqlified = plucked 520 | .iter() 521 | .map(|(n, x)| (n.as_str(), x.to_sql().unwrap())) 522 | .collect::>(); 523 | 524 | assert_eq!( 525 | vec![ 526 | (":id", ToSqlOutput::Owned(Value::Integer(15))), 527 | ( 528 | ":flavor", 529 | ToSqlOutput::Borrowed(ValueRef::Text("choco and caramel".as_bytes())) 530 | ), 531 | ], 532 | sqlified 533 | ); 534 | } 535 | -------------------------------------------------------------------------------- /tools/pre-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | README_TPL="$WORKSPACE_ROOT/README.tpl" 6 | README="$CRATE_ROOT/README.md" 7 | 8 | if [[ "$DRY_RUN" == "false" ]]; then 9 | cargo readme --template="$README_TPL" --output="$README" 10 | else 11 | echo "Dry run, would generate $README from $README_TPL" 12 | fi 13 | --------------------------------------------------------------------------------