1use error_context::prelude::*;
6use odbc::ffi::SqlDataType;
7use odbc::{ColumnDescriptor, DiagnosticRecord, OdbcType};
8use odbc::{SqlDate, SqlSsTime2, SqlTime, SqlTimestamp};
9use std::convert::TryFrom;
10use std::error::Error;
11use std::fmt;
12use std::string::FromUtf16Error;
13use std::convert::TryInto;
14
15#[cfg(feature = "rust_decimal")]
16use rust_decimal::Decimal;
17#[cfg(feature = "rust_decimal")]
18use std::str::FromStr;
19
20pub trait Configuration: Default + Clone + fmt::Debug {}
23
24#[derive(Debug, Default, Clone)]
26pub struct DefaultConfiguration;
27impl Configuration for DefaultConfiguration {}
28
29#[derive(Debug, Default)]
31pub struct Settings {
32 pub utf_16_strings: bool,
34}
35
36#[derive(Debug)]
39pub struct SqlDataTypeMismatch {
40 requested: &'static str,
41 queried: SqlDataType,
42}
43
44impl fmt::Display for SqlDataTypeMismatch {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 write!(
47 f,
48 "requested SQL column data type '{:?}' does not match queried data type '{:?}'",
49 self.requested, self.queried
50 )
51 }
52}
53
54impl Error for SqlDataTypeMismatch {}
55
56#[derive(Debug)]
58pub struct UnsupportedSqlDataType(SqlDataType);
59
60impl fmt::Display for UnsupportedSqlDataType {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 write!(f, "unsupported SQL data type: {:?}", self.0)
63 }
64}
65
66impl Error for UnsupportedSqlDataType {}
67
68#[derive(Debug)]
70#[allow(clippy::large_enum_variant)]
71pub enum DatumAccessError {
72 OdbcCursorError(DiagnosticRecord),
73 SqlDataTypeMismatch(SqlDataTypeMismatch),
74 FromUtf16Error(FromUtf16Error, &'static str),
75 #[cfg(feature = "serde_json")]
76 JsonError(serde_json::Error),
77}
78
79impl fmt::Display for DatumAccessError {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 match self {
82 DatumAccessError::OdbcCursorError(_) => {
83 write!(f, "failed to access data in ODBC cursor")
84 }
85 DatumAccessError::SqlDataTypeMismatch(_) => {
86 write!(f, "failed to handle data type conversion")
87 }
88 DatumAccessError::FromUtf16Error(_, context) => write!(
89 f,
90 "failed to create String from UTF-16 column data while {}",
91 context
92 ),
93 #[cfg(feature = "serde_json")]
94 DatumAccessError::JsonError(_) => write!(f, "failed to convert data to JSON Value"),
95 }
96 }
97}
98
99impl Error for DatumAccessError {
100 fn source(&self) -> Option<&(dyn Error + 'static)> {
101 match self {
102 DatumAccessError::OdbcCursorError(err) => Some(err),
103 DatumAccessError::SqlDataTypeMismatch(err) => Some(err),
104 DatumAccessError::FromUtf16Error(err, _) => Some(err),
105 #[cfg(feature = "serde_json")]
106 DatumAccessError::JsonError(err) => Some(err),
107 }
108 }
109}
110
111impl From<ErrorContext<FromUtf16Error, &'static str>> for DatumAccessError {
112 fn from(err: ErrorContext<FromUtf16Error, &'static str>) -> DatumAccessError {
113 DatumAccessError::FromUtf16Error(err.error, err.context)
114 }
115}
116
117#[cfg(feature = "serde_json")]
118impl From<serde_json::Error> for DatumAccessError {
119 fn from(err: serde_json::Error) -> DatumAccessError {
120 DatumAccessError::JsonError(err)
121 }
122}
123
124#[derive(Debug, Clone, PartialEq)]
126pub struct ColumnType {
127 pub datum_type: DatumType,
129 pub odbc_type: SqlDataType,
131 pub nullable: bool,
133 pub name: String,
135}
136
137#[derive(Debug, Clone, Copy, PartialEq)]
139pub enum DatumType {
140 Bit,
142 Tinyint,
144 Smallint,
146 Integer,
148 Bigint,
150 Float,
152 Double,
154 #[cfg(feature = "rust_decimal")]
155 Decimal,
157 String,
159 Timestamp,
161 Date,
163 Time,
165 #[cfg(feature = "serde_json")]
166 Json,
168}
169
170impl DatumType {
171 pub fn description(self) -> &'static str {
173 match self {
174 DatumType::Bit => "BIT",
175 DatumType::Tinyint => "TINYINT",
176 DatumType::Smallint => "SMALLINT",
177 DatumType::Integer => "INTEGER",
178 DatumType::Bigint => "BIGINT",
179 DatumType::Float => "FLOAT",
180 DatumType::Double => "DOUBLE",
181 #[cfg(feature = "rust_decimal")]
182 DatumType::Decimal => "DECIMAL",
183 DatumType::String => "STRING",
184 DatumType::Timestamp => "TIMESTAMP",
185 DatumType::Date => "DATE",
186 DatumType::Time => "TIME",
187 #[cfg(feature = "serde_json")]
188 DatumType::Json => "JSON",
189 }
190 }
191}
192
193impl TryFrom<ColumnDescriptor> for ColumnType {
194 type Error = UnsupportedSqlDataType;
195
196 fn try_from(column_descriptor: ColumnDescriptor) -> Result<ColumnType, UnsupportedSqlDataType> {
197 use SqlDataType::*;
198 let datum_type = match column_descriptor.data_type {
199 SQL_EXT_BIT => DatumType::Bit,
200 SQL_EXT_TINYINT => DatumType::Tinyint,
201 SQL_SMALLINT => DatumType::Smallint,
202 SQL_INTEGER => DatumType::Integer,
203 SQL_EXT_BIGINT => DatumType::Bigint,
204 SQL_FLOAT | SQL_REAL => DatumType::Float,
205 SQL_DOUBLE => DatumType::Double,
206 #[cfg(feature = "rust_decimal")]
207 SQL_DECIMAL | SQL_NUMERIC => DatumType::Decimal,
208 SQL_CHAR | SQL_VARCHAR | SQL_EXT_LONGVARCHAR | SQL_EXT_WCHAR | SQL_EXT_WVARCHAR
209 | SQL_EXT_WLONGVARCHAR => DatumType::String,
210 SQL_TIMESTAMP => DatumType::Timestamp,
211 SQL_DATE => DatumType::Date,
212 SQL_TIME | SQL_SS_TIME2 => DatumType::Time,
213 SQL_UNKNOWN_TYPE => {
214 #[cfg(feature = "serde_json")]
215 {
216 DatumType::Json
217 }
218 #[cfg(not(feature = "serde_json"))]
219 {
220 DatumType::String
221 }
222 }
223 _ => return Err(UnsupportedSqlDataType(column_descriptor.data_type)),
224 };
225
226 Ok(ColumnType {
227 datum_type,
228 odbc_type: column_descriptor.data_type,
229 nullable: column_descriptor.nullable.unwrap_or(true),
230 name: column_descriptor.name,
231 })
232 }
233}
234
235pub struct Column<'r, 's, 'c, S, C: Configuration> {
237 pub column_type: &'r ColumnType,
239 pub configuration: &'s C,
241 settings: &'r Settings,
243 cursor: &'r mut odbc::Cursor<'s, 'c, 'c, S>,
245 index: u16,
247}
248
249impl<'r, 's, 'c, S, C: Configuration> fmt::Debug for Column<'r, 's, 'c, S, C> {
250 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
251 f.debug_struct("Column")
252 .field("column_type", &self.column_type)
253 .field("settings", &self.settings)
254 .field("configuration", &self.configuration)
255 .field("index", &self.index)
256 .finish()
257 }
258}
259
260impl<'r, 's, 'c, S, C: Configuration> Column<'r, 's, 'c, S, C> {
261 fn into<T: OdbcType<'r>>(self) -> Result<Option<T>, DatumAccessError> {
262 self.cursor
263 .get_data::<T>(self.index + 1)
264 .map_err(DatumAccessError::OdbcCursorError)
265 }
266
267 pub fn into_bool(self) -> Result<Option<bool>, DatumAccessError> {
271 Ok(match self.column_type.odbc_type {
272 SqlDataType::SQL_EXT_BIT => self.into::<u8>()?.map(|byte| byte != 0),
273 queried => {
274 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
275 requested: "BIT",
276 queried,
277 }))
278 }
279 })
280 }
281
282 pub fn into_i8(self) -> Result<Option<i8>, DatumAccessError> {
284 Ok(match self.column_type.odbc_type {
285 SqlDataType::SQL_EXT_TINYINT => self.into::<i8>()?,
286 queried => {
287 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
288 requested: "TINYINT",
289 queried,
290 }))
291 }
292 })
293 }
294
295 pub fn into_i16(self) -> Result<Option<i16>, DatumAccessError> {
297 Ok(match self.column_type.odbc_type {
298 SqlDataType::SQL_SMALLINT => self.into::<i16>()?,
299 queried => {
300 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
301 requested: "SMALLINT",
302 queried,
303 }))
304 }
305 })
306 }
307
308 pub fn into_i32(self) -> Result<Option<i32>, DatumAccessError> {
310 Ok(match self.column_type.odbc_type {
311 SqlDataType::SQL_INTEGER => self.into::<i32>()?,
312 queried => {
313 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
314 requested: "INTEGER",
315 queried,
316 }))
317 }
318 })
319 }
320
321 pub fn into_i64(self) -> Result<Option<i64>, DatumAccessError> {
323 Ok(match self.column_type.odbc_type {
324 SqlDataType::SQL_EXT_BIGINT => self.into::<i64>()?,
325 queried => {
326 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
327 requested: "BIGINT",
328 queried,
329 }))
330 }
331 })
332 }
333
334 pub fn into_f32(self) -> Result<Option<f32>, DatumAccessError> {
336 Ok(match self.column_type.odbc_type {
337 SqlDataType::SQL_REAL | SqlDataType::SQL_FLOAT => self.into::<f32>()?,
338 queried => {
339 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
340 requested: "FLOAT",
341 queried,
342 }))
343 }
344 })
345 }
346
347 pub fn into_f64(self) -> Result<Option<f64>, DatumAccessError> {
349 Ok(match self.column_type.odbc_type {
350 SqlDataType::SQL_DOUBLE => self.into::<f64>()?,
351 queried => {
352 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
353 requested: "DOUBLE",
354 queried,
355 }))
356 }
357 })
358 }
359
360 #[cfg(feature = "rust_decimal")]
361 pub fn into_decimal(self) -> Result<Option<Decimal>, DatumAccessError> {
363 Ok(match self.column_type.odbc_type {
364 SqlDataType::SQL_DECIMAL | SqlDataType::SQL_NUMERIC => {
365 let decimal_as_string = &self.into::<String>()?;
367 match decimal_as_string {
368 Some(s) => Some(Decimal::from_str(&s).unwrap()),
369 None => None
370 }
371 },
372 queried => {
373 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
374 requested: "DECIMAL",
375 queried,
376 }))
377 }
378 })
379 }
380
381 pub fn into_string(self) -> Result<Option<String>, DatumAccessError> {
383 use SqlDataType::*;
384 Ok(match self.column_type.odbc_type {
385 SQL_CHAR | SQL_VARCHAR | SQL_EXT_LONGVARCHAR => self.into::<String>()?,
386 SQL_EXT_WCHAR | SQL_EXT_WVARCHAR | SQL_EXT_WLONGVARCHAR |
387 SQL_UNKNOWN_TYPE => {
388 if self.settings.utf_16_strings {
389 self.into::<&[u16]>()?
390 .map(|bytes| String::from_utf16(bytes).wrap_error_while("getting UTF-16 string (SQL_EXT_WCHAR | SQL_EXT_WVARCHAR | SQL_EXT_WLONGVARCHAR)"))
391 .transpose()?
392 } else {
393 self.into::<String>()?
394 }
395 }
396 queried => {
397 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
398 requested: "STRING",
399 queried,
400 }))
401 }
402 })
403 }
404
405 pub fn into_timestamp(self) -> Result<Option<SqlTimestamp>, DatumAccessError> {
407 Ok(match self.column_type.odbc_type {
408 SqlDataType::SQL_TIMESTAMP => self.into::<SqlTimestamp>()?,
409 queried => {
410 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
411 requested: "TIMESTAMP",
412 queried,
413 }))
414 }
415 })
416 }
417
418 pub fn into_date(self) -> Result<Option<SqlDate>, DatumAccessError> {
420 Ok(match self.column_type.odbc_type {
421 SqlDataType::SQL_DATE => self.into::<SqlDate>()?,
422 queried => {
423 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
424 requested: "DATE",
425 queried,
426 }))
427 }
428 })
429 }
430
431 pub fn into_time(self) -> Result<Option<SqlSsTime2>, DatumAccessError> {
433 Ok(match self.column_type.odbc_type {
434 SqlDataType::SQL_TIME => self.into::<SqlTime>()?.map(|ss| SqlSsTime2 {
435 hour: ss.hour,
436 minute: ss.minute,
437 second: ss.second,
438 fraction: 0,
439 }),
440 SqlDataType::SQL_SS_TIME2 => self.into::<SqlSsTime2>()?,
441 queried => {
442 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
443 requested: "TIME",
444 queried,
445 }))
446 }
447 })
448 }
449
450 #[cfg(feature = "serde_json")]
451 pub fn into_json(self) -> Result<Option<serde_json::Value>, DatumAccessError> {
453 Ok(match self.column_type.odbc_type {
454 queried @ SqlDataType::SQL_UNKNOWN_TYPE => {
455 self.into::<String>()?
456 .map(|data| {
457 if (data.starts_with("[") && data.ends_with("]"))
459 || (data.starts_with("{") && data.ends_with("}"))
460 {
461 serde_json::from_str(&data).map_err(Into::into)
462 } else {
463 return Err(DatumAccessError::SqlDataTypeMismatch(
465 SqlDataTypeMismatch {
466 requested: "JSON",
467 queried,
468 },
469 ));
470 }
471 })
472 .transpose()?
473 }
474 queried => {
475 return Err(DatumAccessError::SqlDataTypeMismatch(SqlDataTypeMismatch {
476 requested: "JSON",
477 queried,
478 }))
479 }
480 })
481 }
482
483 pub fn index(&self) -> u16 {
485 self.index
486 }
487}
488
489pub struct Row<'r, 's, 'c, S, C: Configuration> {
491 pub schema: &'r [ColumnType],
493 pub configuration: &'s C,
495 settings: &'r Settings,
497 cursor: odbc::Cursor<'s, 'c, 'c, S>,
499 index: u16,
501 columns: u16,
503}
504
505impl<'r, 's, 'c, S, C: Configuration> fmt::Debug for Row<'r, 's, 'c, S, C> {
506 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
507 f.debug_struct("Row")
508 .field("schema", &self.schema)
509 .field("settings", &self.settings)
510 .field("configuration", &self.configuration)
511 .field("index", &self.index)
512 .field("columns", &self.columns)
513 .finish()
514 }
515}
516
517impl<'r, 's, 'c, S, C: Configuration> Row<'r, 's, 'c, S, C> {
518 pub fn new(
519 cursor: odbc::Cursor<'s, 'c, 'c, S>,
520 schema: &'r [ColumnType],
521 settings: &'r Settings,
522 configuration: &'s C,
523 ) -> Row<'r, 's, 'c, S, C> {
524 Row {
525 schema,
526 configuration,
527 settings,
528 cursor,
529 index: 0,
530 columns: schema.len() as u16,
531 }
532 }
533
534 pub fn shift_column<'i>(&'i mut self) -> Option<Column<'i, 's, 'c, S, C>> {
535 self.schema
536 .get(self.index as usize)
537 .map(move |column_type| {
538 let column = Column {
539 column_type,
540 configuration: self.configuration,
541 settings: &self.settings,
542 cursor: &mut self.cursor,
543 index: self.index,
544 };
545
546 self.index += 1;
547 column
548 })
549 }
550
551 pub fn columns(&self) -> u16 {
553 self.columns
554 }
555}
556
557
558pub trait TryFromColumn<C: Configuration>: Sized {
562 type Error: Error + 'static;
563 fn try_from_column<'i, 's, 'c, S>(column: Column<'i, 's, 'c, S, C>) -> Result<Self, Self::Error>;
565}
566
567pub trait TryFromRow<C: Configuration>: Sized {
574 type Error: Error + 'static;
575 fn try_from_row<'r, 's, 'c, S>(row: Row<'r, 's, 'c, S, C>) -> Result<Self, Self::Error>;
577}
578
579#[derive(Debug)]
581pub enum ColumnConvertError {
582 UnexpectedNullValue(&'static str),
583 DatumAccessError(DatumAccessError),
584 ValueOutOfRange {
585 expected: &'static str,
586 },
587}
588
589impl From<DatumAccessError> for ColumnConvertError {
590 fn from(err: DatumAccessError) -> ColumnConvertError {
591 ColumnConvertError::DatumAccessError(err)
592 }
593}
594
595impl fmt::Display for ColumnConvertError {
596 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
597 match self {
598 ColumnConvertError::UnexpectedNullValue(t) => {
599 write!(f, "expecting value of type {} but got NULL", t)
600 }
601 ColumnConvertError::DatumAccessError(_) => {
602 write!(f, "problem accessing datum")
603 }
604 ColumnConvertError::ValueOutOfRange { expected } => {
605 write!(f, "value is out of range for type {}", expected)
606 }
607 }
608 }
609}
610
611impl Error for ColumnConvertError {
612 fn source(&self) -> Option<&(dyn Error + 'static)> {
613 match self {
614 ColumnConvertError::DatumAccessError(err) => Some(err),
615 ColumnConvertError::UnexpectedNullValue(_) |
616 ColumnConvertError::ValueOutOfRange { .. } => None,
617 }
618 }
619}
620
621macro_rules! try_from_row_not_null {
622 ($t:ty) => {
623 impl<C: Configuration> TryFromColumn<C> for $t {
624 type Error = ColumnConvertError;
625 fn try_from_column<'i, 's, 'c, S>(column: Column<'i, 's, 'c, S, C>) -> Result<Self, Self::Error> {
626 let value: Option<$t> = TryFromColumn::try_from_column(column)?;
627 value.ok_or_else(|| ColumnConvertError::UnexpectedNullValue(stringify!($t)))
628 }
629 }
630 }
631}
632
633macro_rules! try_from_row {
634 ($t:ty, $f:ident) => {
635 impl<C: Configuration> TryFromColumn<C> for Option<$t> {
636 type Error = ColumnConvertError;
637 fn try_from_column<'i, 's, 'c, S>(column: Column<'i, 's, 'c, S, C>) -> Result<Self, Self::Error> {
638 column.$f().map_err(Into::into)
639 }
640 }
641
642 try_from_row_not_null!($t);
643 };
644}
645
646macro_rules! try_from_row_unsigned {
647 ($it:ty, $t:ty) => {
648 impl<C: Configuration> TryFromColumn<C> for Option<$t> {
649 type Error = ColumnConvertError;
650 fn try_from_column<'i, 's, 'c, S>(column: Column<'i, 's, 'c, S, C>) -> Result<Self, Self::Error> {
651 let value: Option<$it> = TryFromColumn::try_from_column(column)?;
652 value.map(|value|
653 value
654 .try_into()
655 .map_err(|_| ColumnConvertError::ValueOutOfRange {
656 expected: stringify!($t),
657 })
658 ).transpose()
659 }
660 }
661
662 try_from_row_not_null!($t);
663 };
664}
665
666try_from_row![bool, into_bool];
667try_from_row![i8, into_i8];
668try_from_row_unsigned![i8, u8];
669try_from_row![i16, into_i16];
670try_from_row_unsigned![i16, u16];
671try_from_row![i32, into_i32];
672try_from_row_unsigned![i32, u32];
673try_from_row![i64, into_i64];
674try_from_row_unsigned![i64, u64];
675try_from_row![f32, into_f32];
676try_from_row![f64, into_f64];
677try_from_row![String, into_string];
678try_from_row![SqlTimestamp, into_timestamp];
679try_from_row![SqlDate, into_date];
680try_from_row![SqlSsTime2, into_time];
681#[cfg(feature = "serde_json")]
682try_from_row![serde_json::Value, into_json];
683
684#[cfg(feature = "chrono")]
685use chrono::{NaiveDateTime, NaiveDate, NaiveTime};
686
687#[cfg(feature = "chrono")]
688impl<C: Configuration> TryFromColumn<C> for Option<NaiveDateTime> {
689 type Error = ColumnConvertError;
690 fn try_from_column<'i, 's, 'c, S>(column: Column<'i, 's, 'c, S, C>) -> Result<Self, Self::Error> {
691 let value: Option<SqlTimestamp> = TryFromColumn::try_from_column(column)?;
692
693 Ok(value.map(|value| {
694 NaiveDate::from_ymd(
695 i32::from(value.year),
696 u32::from(value.month),
697 u32::from(value.day),
698 )
699 .and_hms_nano(
700 u32::from(value.hour),
701 u32::from(value.minute),
702 u32::from(value.second),
703 value.fraction,
704 )
705 }))
706 }
707}
708
709#[cfg(feature = "chrono")]
710try_from_row_not_null!(NaiveDateTime);
711
712#[cfg(feature = "chrono")]
713impl<C: Configuration> TryFromColumn<C> for Option<NaiveDate> {
714 type Error = ColumnConvertError;
715 fn try_from_column<'i, 's, 'c, S>(column: Column<'i, 's, 'c, S, C>) -> Result<Self, Self::Error> {
716 let value: Option<SqlDate> = TryFromColumn::try_from_column(column)?;
717
718 Ok(value.map(|value| {
719 NaiveDate::from_ymd(
720 i32::from(value.year),
721 u32::from(value.month),
722 u32::from(value.day),
723 )
724 }))
725 }
726}
727
728#[cfg(feature = "chrono")]
729try_from_row_not_null!(NaiveDate);
730
731#[cfg(feature = "chrono")]
732impl<C: Configuration> TryFromColumn<C> for Option<NaiveTime> {
733 type Error = ColumnConvertError;
734 fn try_from_column<'i, 's, 'c, S>(column: Column<'i, 's, 'c, S, C>) -> Result<Self, Self::Error> {
735 let value: Option<SqlSsTime2> = TryFromColumn::try_from_column(column)?;
736
737 Ok(value.map(|value| {
738 NaiveTime::from_hms_nano(
739 u32::from(value.hour),
740 u32::from(value.minute),
741 u32::from(value.second),
742 value.fraction,
743 )
744 }))
745 }
746}
747
748#[cfg(feature = "chrono")]
749try_from_row_not_null!(NaiveTime);
750
751#[derive(Debug)]
753pub enum RowConvertError {
754 UnexpectedNullValue(&'static str),
755 UnexpectedValue,
756 UnexpectedNumberOfColumns { expected: u16, got: u16 },
757 ColumnConvertError(Box<dyn Error>),
758}
759
760impl From<ColumnConvertError> for RowConvertError {
761 fn from(err: ColumnConvertError) -> RowConvertError {
762 RowConvertError::ColumnConvertError(Box::new(err))
763 }
764}
765
766impl fmt::Display for RowConvertError {
767 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
768 match self {
769 RowConvertError::UnexpectedNullValue(t) => {
770 write!(f, "expecting value of type {} but got NULL", t)
771 }
772 RowConvertError::UnexpectedValue => write!(f, "expecting no data (unit) but got a row"),
773 RowConvertError::UnexpectedNumberOfColumns { expected, got } => write!(
774 f,
775 "unexpected number of columns: expected {} but got {}",
776 expected, got
777 ),
778 RowConvertError::ColumnConvertError(_) => {
779 write!(f, "failed to convert column value to target type")
780 }
781 }
782 }
783}
784
785impl Error for RowConvertError {
786 fn source(&self) -> Option<&(dyn Error + 'static)> {
787 match self {
788 RowConvertError::UnexpectedNullValue(_)
789 | RowConvertError::UnexpectedValue
790 | RowConvertError::UnexpectedNumberOfColumns { .. } => None,
791 RowConvertError::ColumnConvertError(err) => Some(err.as_ref()),
792 }
793 }
794}
795
796impl TryFromRow<DefaultConfiguration> for () {
798 type Error = RowConvertError;
799 fn try_from_row<'r, 's, 'c, S>(_row: Row<'r, 's, 'c, S, DefaultConfiguration>) -> Result<Self, Self::Error> {
800 Err(RowConvertError::UnexpectedValue)
801 }
802}
803
804impl<T> TryFromRow<DefaultConfiguration> for T
806where
807 T: TryFromColumn<DefaultConfiguration>,
808{
809 type Error = RowConvertError;
810 fn try_from_row<'r, 's, 'c, S>(mut row: Row<'r, 's, 'c, S, DefaultConfiguration>) -> Result<Self, Self::Error> {
811 if row.columns() != 1 {
812 return Err(RowConvertError::UnexpectedNumberOfColumns {
813 expected: 1,
814 got: row.columns(),
815 });
816 }
817
818 let column = row.shift_column().unwrap();
819
820 TryFromColumn::try_from_column(column)
821 .map_err(|e| RowConvertError::ColumnConvertError(Box::new(e)))
822
823 }
824}
825
826#[derive(Debug)]
828pub enum RowConvertTupleError {
829 UnexpectedNumberOfColumns { expected: u16, tuple: &'static str },
830 ValueConvertError(Box<dyn Error>),
831}
832
833impl fmt::Display for RowConvertTupleError {
834 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
835 match self {
836 RowConvertTupleError::UnexpectedNumberOfColumns { expected, tuple } => write!(
837 f,
838 "failed to convert row with {} columns to tuple {}",
839 expected, tuple
840 ),
841 RowConvertTupleError::ValueConvertError(_) => {
842 write!(f, "failed to convert column value to target type")
843 }
844 }
845 }
846}
847
848impl Error for RowConvertTupleError {
849 fn source(&self) -> Option<&(dyn Error + 'static)> {
850 match self {
851 RowConvertTupleError::UnexpectedNumberOfColumns { .. } => None,
852 RowConvertTupleError::ValueConvertError(err) => Some(err.as_ref()),
853 }
854 }
855}
856
857macro_rules! count {
858 () => (0u16);
859 ( $x:tt $($xs:tt)* ) => (1u16 + count!($($xs)*));
860}
861
862macro_rules! try_from_tuple {
863 ($(
864 $Tuple:ident {
865 $(($idx:tt) -> $T:ident)+
866 }
867 )+) => {
868 $(
869 impl<C: Configuration, $($T: TryFromColumn<C>),+> TryFromRow<C> for ($($T,)+) {
870 type Error = RowConvertTupleError;
871 fn try_from_row<'r, 's, 'c, S>(mut row: Row<'r, 's, 'c, S, C>) -> Result<($($T,)+), Self::Error> {
872 if row.columns() != count!($($T)+) {
873 return Err(RowConvertTupleError::UnexpectedNumberOfColumns { expected: row.columns(), tuple: stringify![($($T,)+)] })
874 }
875 Ok(($({ let x: $T = $T::try_from_column(row.shift_column().unwrap()).map_err(|err| RowConvertTupleError::ValueConvertError(Box::new(err)))?; x},)+))
876 }
877 }
878 )+
879 }
880}
881
882try_from_tuple! {
883 Tuple1 {
884 (0) -> TA
885 }
886 Tuple2 {
887 (0) -> TA
888 (1) -> TB
889 }
890 Tuple3 {
891 (0) -> TA
892 (1) -> TB
893 (2) -> TC
894 }
895 Tuple4 {
896 (0) -> TA
897 (1) -> TB
898 (2) -> TC
899 (3) -> TD
900 }
901 Tuple5 {
902 (0) -> TA
903 (1) -> TB
904 (2) -> TC
905 (3) -> TD
906 (4) -> TE
907 }
908 Tuple6 {
909 (0) -> TA
910 (1) -> TB
911 (2) -> TC
912 (3) -> TD
913 (4) -> TE
914 (5) -> TF
915 }
916 Tuple7 {
917 (0) -> TA
918 (1) -> TB
919 (2) -> TC
920 (3) -> TD
921 (4) -> TE
922 (5) -> TF
923 (6) -> TG
924 }
925 Tuple8 {
926 (0) -> TA
927 (1) -> TB
928 (2) -> TC
929 (3) -> TD
930 (4) -> TE
931 (5) -> TF
932 (6) -> TG
933 (7) -> TH
934 }
935 Tuple9 {
936 (0) -> TA
937 (1) -> TB
938 (2) -> TC
939 (3) -> TD
940 (4) -> TE
941 (5) -> TF
942 (6) -> TG
943 (7) -> TH
944 (8) -> TI
945 }
946 Tuple10 {
947 (0) -> TA
948 (1) -> TB
949 (2) -> TC
950 (3) -> TD
951 (4) -> TE
952 (5) -> TF
953 (6) -> TG
954 (7) -> TH
955 (8) -> TI
956 (9) -> TJ
957 }
958 Tuple11 {
959 (0) -> TA
960 (1) -> TB
961 (2) -> TC
962 (3) -> TD
963 (4) -> TE
964 (5) -> TF
965 (6) -> TG
966 (7) -> TH
967 (8) -> TI
968 (9) -> TJ
969 (10) -> TK
970 }
971 Tuple12 {
972 (0) -> TA
973 (1) -> TB
974 (2) -> TC
975 (3) -> TD
976 (4) -> TE
977 (5) -> TF
978 (6) -> TG
979 (7) -> TH
980 (8) -> TI
981 (9) -> TJ
982 (10) -> TK
983 (11) -> TL
984 }
985 Tuple13 {
986 (0) -> TA
987 (1) -> TB
988 (2) -> TC
989 (3) -> TD
990 (4) -> TE
991 (5) -> TF
992 (6) -> TG
993 (7) -> TH
994 (8) -> TI
995 (9) -> TJ
996 (10) -> TK
997 (11) -> TL
998 (13) -> TM
999 }
1000 Tuple14 {
1001 (0) -> TA
1002 (1) -> TB
1003 (2) -> TC
1004 (3) -> TD
1005 (4) -> TE
1006 (5) -> TF
1007 (6) -> TG
1008 (7) -> TH
1009 (8) -> TI
1010 (9) -> TJ
1011 (10) -> TK
1012 (11) -> TL
1013 (13) -> TM
1014 (14) -> TN
1015 }
1016 Tuple15 {
1017 (0) -> TA
1018 (1) -> TB
1019 (2) -> TC
1020 (3) -> TD
1021 (4) -> TE
1022 (5) -> TF
1023 (6) -> TG
1024 (7) -> TH
1025 (8) -> TI
1026 (9) -> TJ
1027 (10) -> TK
1028 (11) -> TL
1029 (13) -> TM
1030 (14) -> TN
1031 (15) -> TO
1032 }
1033 Tuple16 {
1034 (0) -> TA
1035 (1) -> TB
1036 (2) -> TC
1037 (3) -> TD
1038 (4) -> TE
1039 (5) -> TF
1040 (6) -> TG
1041 (7) -> TH
1042 (8) -> TI
1043 (9) -> TJ
1044 (10) -> TK
1045 (11) -> TL
1046 (13) -> TM
1047 (14) -> TN
1048 (15) -> TO
1049 (16) -> TP
1050 }
1051 Tuple17 {
1052 (0) -> TA
1053 (1) -> TB
1054 (2) -> TC
1055 (3) -> TD
1056 (4) -> TE
1057 (5) -> TF
1058 (6) -> TG
1059 (7) -> TH
1060 (8) -> TI
1061 (9) -> TJ
1062 (10) -> TK
1063 (11) -> TL
1064 (13) -> TM
1065 (14) -> TN
1066 (15) -> TO
1067 (16) -> TP
1068 (17) -> TQ
1069 }
1070 Tuple18 {
1071 (0) -> TA
1072 (1) -> TB
1073 (2) -> TC
1074 (3) -> TD
1075 (4) -> TE
1076 (5) -> TF
1077 (6) -> TG
1078 (7) -> TH
1079 (8) -> TI
1080 (9) -> TJ
1081 (10) -> TK
1082 (11) -> TL
1083 (13) -> TM
1084 (14) -> TN
1085 (15) -> TO
1086 (16) -> TP
1087 (17) -> TQ
1088 (18) -> TR
1089 }
1090 Tuple19 {
1091 (0) -> TA
1092 (1) -> TB
1093 (2) -> TC
1094 (3) -> TD
1095 (4) -> TE
1096 (5) -> TF
1097 (6) -> TG
1098 (7) -> TH
1099 (8) -> TI
1100 (9) -> TJ
1101 (10) -> TK
1102 (11) -> TL
1103 (13) -> TM
1104 (14) -> TN
1105 (15) -> TO
1106 (16) -> TP
1107 (17) -> TQ
1108 (18) -> TR
1109 (19) -> TS
1110 }
1111 Tuple20 {
1112 (0) -> TA
1113 (1) -> TB
1114 (2) -> TC
1115 (3) -> TD
1116 (4) -> TE
1117 (5) -> TF
1118 (6) -> TG
1119 (7) -> TH
1120 (8) -> TI
1121 (9) -> TJ
1122 (10) -> TK
1123 (11) -> TL
1124 (13) -> TM
1125 (14) -> TN
1126 (15) -> TO
1127 (16) -> TP
1128 (17) -> TQ
1129 (18) -> TR
1130 (19) -> TS
1131 (20) -> TT
1132 }
1133 Tuple21 {
1134 (0) -> TA
1135 (1) -> TB
1136 (2) -> TC
1137 (3) -> TD
1138 (4) -> TE
1139 (5) -> TF
1140 (6) -> TG
1141 (7) -> TH
1142 (8) -> TI
1143 (9) -> TJ
1144 (10) -> TK
1145 (11) -> TL
1146 (13) -> TM
1147 (14) -> TN
1148 (15) -> TO
1149 (16) -> TP
1150 (17) -> TQ
1151 (18) -> TR
1152 (19) -> TS
1153 (20) -> TT
1154 (21) -> TU
1155 }
1156 Tuple22 {
1157 (0) -> TA
1158 (1) -> TB
1159 (2) -> TC
1160 (3) -> TD
1161 (4) -> TE
1162 (5) -> TF
1163 (6) -> TG
1164 (7) -> TH
1165 (8) -> TI
1166 (9) -> TJ
1167 (10) -> TK
1168 (11) -> TL
1169 (13) -> TM
1170 (14) -> TN
1171 (15) -> TO
1172 (16) -> TP
1173 (17) -> TQ
1174 (18) -> TR
1175 (19) -> TS
1176 (20) -> TT
1177 (21) -> TU
1178 (22) -> TV
1179 }
1180 Tuple23 {
1181 (0) -> TA
1182 (1) -> TB
1183 (2) -> TC
1184 (3) -> TD
1185 (4) -> TE
1186 (5) -> TF
1187 (6) -> TG
1188 (7) -> TH
1189 (8) -> TI
1190 (9) -> TJ
1191 (10) -> TK
1192 (11) -> TL
1193 (13) -> TM
1194 (14) -> TN
1195 (15) -> TO
1196 (16) -> TP
1197 (17) -> TQ
1198 (18) -> TR
1199 (19) -> TS
1200 (20) -> TT
1201 (21) -> TU
1202 (22) -> TV
1203 (23) -> TW
1204 }
1205 Tuple24 {
1206 (0) -> TA
1207 (1) -> TB
1208 (2) -> TC
1209 (3) -> TD
1210 (4) -> TE
1211 (5) -> TF
1212 (6) -> TG
1213 (7) -> TH
1214 (8) -> TI
1215 (9) -> TJ
1216 (10) -> TK
1217 (11) -> TL
1218 (13) -> TM
1219 (14) -> TN
1220 (15) -> TO
1221 (16) -> TP
1222 (17) -> TQ
1223 (18) -> TR
1224 (19) -> TS
1225 (20) -> TT
1226 (21) -> TU
1227 (22) -> TV
1228 (23) -> TW
1229 (24) -> TY
1230 }
1231}