1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
//! # tokio-pg-mapper //! //! `tokio-pg-mapper` is a proc-macro designed to make mapping from postgresql //! tables to structs simple. //! //! ### Why? //! //! It can be frustrating to write a lot of boilerplate and, ultimately, duplicated //! code for mapping from postgres Rows into structs. //! //! For example, this might be what someone would normally write: //! //! ```rust //! use postgres::row::Row; //! //! pub struct User { //! pub id: i64, //! pub name: String, //! pub email: Option<String>, //! } //! //! impl From<Row> for User { //! fn from(row: Row) -> Self { //! Self { //! id: row.get("id"), //! name: row.get("name"), //! email: row.get("email"), //! } //! } //! } //! //! // code to execute a query here and get back a row //! let user = User::from_row(row); // returns Result<User, tokio_pg_mapper::Error> //! ``` //! //! //! ### The two crates //! //! This repository contains two crates: `tokio-pg-mapper` which contains an `Error` //! enum and traits for converting from `tokio-postgres` `Row` //! without panicking, and `pg-mapper-derive` which contains the proc-macro. //! //! `pg-mapper-derive` has 3 features that can be enabled (where T is the //! struct being derived with the provided `TokioPostgresMapper` proc-macro): //! //! `impl FromTokioPostgresRow<::tokio_postgres::row::Row> for T` and //! `impl FromTokioPostgresRow<&::tokio_postgres::row::Row> for T` implementations //! - `pg-mapper` which, for each of the above features, implements //! `pg-mapper`'s `FromTokioPostgresRow` trait //! //! //! This will derive implementations for converting from owned and referenced //! `tokio-postgres::row::Row`s, as well as implementing `pg-mapper`'s //! `FromTokioPostgresRow` trait for non-panicking conversions. #[cfg(feature = "derive")] #[allow(unused_imports)] pub extern crate tokio_pg_mapper_derive; #[cfg(feature = "derive")] #[doc(hidden)] pub use tokio_pg_mapper_derive::*; use tokio_postgres; use tokio_postgres::row::Row as TokioRow; use std::error::Error as StdError; use std::fmt::{Display, Formatter, Result as FmtResult}; /// Trait containing various methods for converting from a `tokio-postgres` Row /// to a mapped type. /// /// When using the `pg_mapper_derive` crate's `TokioPostgresMapper` proc-macro, /// this will automatically be implemented on types. /// /// The [`from_row`] method exists for consuming a `Row` - useful /// for iterator mapping - while [`from_row_ref`] exists for borrowing /// a `Row`. pub trait FromTokioPostgresRow: Sized { /// Converts from a `tokio-postgres` `Row` into a mapped type, consuming the /// given `Row`. /// /// # Errors /// /// Returns [`Error::ColumnNotFound`] if the column in a mapping was not /// found. /// /// Returns [`Error::Conversion`] if there was an error converting the row /// column to the requested type. /// /// [`Error::ColumnNotFound`]: enum.Error.html#variant.ColumnNotFound /// [`Error::Conversion`]: enum.Error.html#variant.Conversion fn from_row(row: TokioRow) -> Result<Self, Error>; /// Converts from a `tokio-postgres` `Row` into a mapped type, borrowing the /// given `Row`. /// /// # Errors /// /// Returns [`Error::ColumnNotFound`] if the column in a mapping was not /// found. /// /// Returns [`Error::Conversion`] if there was an error converting the row /// column into the requested type. /// /// [`Error::ColumnNotFound`]: enum.Error.html#variant.ColumnNotFound /// [`Error::Conversion`]: enum.Error.html#variant.Conversion fn from_row_ref(row: &TokioRow) -> Result<Self, Error>; /// Get the name of the annotated sql table name. /// /// Example: /// /// The following will return the String " user ". /// Note the extra spaces on either side to avoid incorrect formatting. /// /// ``` /// #[derive(PostgresMapper)] /// #[pg_mapper(table = "user")] /// pub struct User { /// pub id: i64, /// pub email: Option<String>, /// } /// ``` fn sql_table() -> String; /// Get a list of the field names, excluding table name prefix. /// /// Example: /// /// The following will return the String " id, email ". /// Note the extra spaces on either side to avoid incorrect formatting. /// /// ``` /// #[derive(PostgresMapper)] /// #[pg_mapper(table = "user")] /// pub struct User { /// pub id: i64, /// pub email: Option<String>, /// } /// ``` /// fn sql_fields() -> String; /// Get a list of the field names, including table name prefix. /// /// We also expect an attribute tag #[pg_mapper(table = "foo")] /// so that a scoped list of fields can be generated. /// /// Example: /// /// The following will return the String " user.id, user.email ". /// Note the extra spaces on either side to avoid incorrect formatting. /// /// ``` /// #[derive(PostgresMapper)] /// #[pg_mapper(table = "user")] /// pub struct User { /// pub id: i64, /// pub email: Option<String>, /// } /// ``` /// fn sql_table_fields() -> String; } /// General error type returned throughout the library. #[derive(Debug)] pub enum Error { /// A column in a row was not found. ColumnNotFound, /// An error from the `tokio-postgres` crate while converting a type. Conversion(Box<dyn StdError + Send + Sync>), } impl From<tokio_postgres::Error> for Error { fn from(err: tokio_postgres::Error) -> Self { err.into_source().unwrap().into() } } impl From<Box<dyn StdError + Send + Sync>> for Error { fn from(err: Box<dyn StdError + Send + Sync>) -> Self { Error::Conversion(err) } } impl Display for Error { fn fmt(&self, f: &mut Formatter) -> FmtResult { f.write_str(self.description()) } } impl StdError for Error { fn description(&self) -> &str { match *self { Error::ColumnNotFound => "Column in row not found", Error::Conversion(ref inner) => inner.description(), } } }