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
/* Allows for coercion of rusqlite::Error into UserError */

// Third Party Dependencies
use rusqlite::Error as SQLError;

// Intra Library Imports
use super::UserError;

/// Convert a rusqlite::Error into a UserError
///
/// # Example
/// ```
/// use user_error::UserError;
/// use std::path::Path;
/// use rusqlite::{Connection, Result as SQLResult, NO_PARAMS, OpenFlags};
/// 
/// fn bad_connection() -> Result<Connection, UserError> {
///     let c = Connection::open_with_flags(Path::new("nonexistent.db"), OpenFlags::SQLITE_OPEN_READ_WRITE)?;
///     Ok(c)    
/// }
/// 
/// assert!(bad_connection().is_err());
/// ```
impl From<SQLError> for UserError {
    fn from(error: SQLError) -> Self {
        let summary = String::from("SQLite has encountered an issue");
        match error {
            SQLError::SqliteFailure(e, r) => {
                let mut reasons = vec![String::from("Underlying SQLite call failed")];
                if let Some(s) = r { reasons.push(s) }
                UserError {
                    summary,
                    reasons: Some(reasons),
                    subtleties: None,
                    original_errors: Some(vec![Box::new(e)]),
                }
            },
            SQLError::SqliteSingleThreadedMode => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Attempted to open an additional connection when SQLite was configured to allow single-threaded use only")]),
                    subtleties: None,
                    original_errors: None 
                }
            },
            SQLError::FromSqlConversionFailure(c, t, e) => {
                UserError {
                    summary, 
                    reasons: Some(vec![format!("Failed to convert value of column {} to Rust type {}", c, t)]),
                    subtleties: None,
                    original_errors: Some(vec![e]) 
                }
            },
            SQLError::IntegralValueOutOfRange(c, n) => {
                UserError {
                    summary, 
                    reasons: Some(vec![format!("Cannot fit integral value '{}' from column {} into requested type without overflow", n, c)]),
                    subtleties: Some(vec![String::from("e.g., trying to get the value 1000 into a u8")]),
                    original_errors: None 
                }
            },
            SQLError::Utf8Error(e) => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Failed to convert string value to UTF-8")]),
                    subtleties: None,
                    original_errors: Some(vec![Box::new(e)])
                }
            },
            SQLError::NulError(e) => {
                let bad_string = match std::str::from_utf8(&(e.clone().into_vec())) {
                    Ok(s)  => format!("'{}'", s),
                    Err(_) => String::from("String")
                };

                UserError {
                    summary, 
                    reasons: Some(vec![format!("Failed to convert {} to a C-Compatible String", bad_string), 
                                       format!("{} contains a nul byte at position: {}", bad_string, e.nul_position())]),
                    subtleties: Some(vec![String::from("While strings may contain nul bytes in the middle, C strings can't, as that byte would effectively truncate the string.")]),
                    original_errors: Some(vec![Box::new(e)]) 
                }
            },
            SQLError::InvalidParameterName(s) => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Invalid parameter name"),
                                       format!("Parameter: {} not present in the SQL", s)]),
                    subtleties: None,
                    original_errors: None
                }
            },
            SQLError::InvalidPath(_p) => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Invalid path")]),
                    subtleties: Some(vec![String::from("Could not convert the file path to a string.")]),
                    original_errors: None
                }
            },
            SQLError::ExecuteReturnedResults => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Execute call returned rows")]),
                    subtleties: None,
                    original_errors: None
                }
            },
            SQLError::QueryReturnedNoRows => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Query returned no rows")]),
                    subtleties: Some(vec![String::from("Query was expected to return at least one row (e.g., for query_row) but did not return any.")]),
                    original_errors: None
                }
            },
            SQLError::InvalidColumnIndex(c) => {
                UserError {
                    summary, 
                    reasons: Some(vec![format!("Column index: {} is out of range for a statement", c)]),
                    subtleties: None,
                    original_errors: None
                }
            },
            SQLError::InvalidColumnName(s) => {
                UserError {
                    summary, 
                    reasons: Some(vec![format!("No column matching '{}' in statement", s)]),
                    subtleties: None,
                    original_errors: None
                }
            },
            SQLError::InvalidColumnType(c, _s, t) => {
                UserError {
                    summary, 
                    reasons: Some(vec![format!("Failed to convert value of column {} to Rust type {}", c, t)]),
                    subtleties: None,
                    original_errors: None
                }
            },
            SQLError::StatementChangedRows(_c) => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Statement failed to insert row(s)")]),
                    subtleties: None,
                    original_errors: None
                }
            },
            SQLError::InvalidQuery => {
                UserError {
                    summary, 
                    reasons: Some(vec![String::from("Invalid query")]),
                    subtleties: None,
                    original_errors: None
                }
            },
            SQLError::ToSqlConversionFailure(e) => {
                UserError {
                    summary,
                    reasons: Some(vec![String::from("Failed to convert to SQL")]),
                    subtleties: None,
                    original_errors: Some(vec![e])
                }
            }
        }
    }
}