diff --git a/src/lib.rs b/src/lib.rs index 3ef9d8e..cf239c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,26 @@ pub const NO_PARAMS: &[&dyn ToSql] = &[]; /// A typedef of the result returned by many methods. pub type Result = result::Result; +/// See the [method documentation](#tymethod.optional). +pub trait OptionalExtension { + /// Converts a `Result` into a `Result>`. + /// + /// By default, Rusqlite treats 0 rows being returned from a query that is expected to return 1 + /// row as an error. This method will + /// handle that error, and give you back an `Option` instead. + fn optional(self) -> Result>; +} + +impl OptionalExtension for Result { + fn optional(self) -> Result> { + match self { + Ok(value) => Ok(Some(value)), + Err(Error::QueryReturnedNoRows) => Ok(None), + Err(e) => Err(e), + } + } +} + unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { let c_slice = CStr::from_ptr(errmsg).to_bytes(); String::from_utf8_lossy(c_slice).into_owned() @@ -372,6 +392,9 @@ impl Connection { /// If the query returns more than one row, all rows except the first are /// ignored. /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the query truly is optional, + /// you can call `.optional()` on the result of this to get a `Result>`. + /// /// # Failure /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string @@ -392,6 +415,9 @@ impl Connection { /// If the query returns more than one row, all rows except the first are /// ignored. /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the query truly is optional, + /// you can call `.optional()` on the result of this to get a `Result>`. + /// /// # Failure /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string @@ -1378,6 +1404,32 @@ mod test { assert!(bad_query_result.is_err()); } + #[test] + fn test_optional() { + let db = checked_memory_handle(); + + let result: Result = + db.query_row("SELECT 1 WHERE 0 <> 0", NO_PARAMS, |r| r.get(0)); + let result = result.optional(); + match result.unwrap() { + None => (), + _ => panic!("Unexpected result"), + } + + let result: Result = + db.query_row("SELECT 1 WHERE 0 == 0", NO_PARAMS, |r| r.get(0)); + let result = result.optional(); + match result.unwrap() { + Some(1) => (), + _ => panic!("Unexpected result"), + } + + let bad_query_result: Result = + db.query_row("NOT A PROPER QUERY", NO_PARAMS, |r| r.get(0)); + let bad_query_result = bad_query_result.optional(); + assert!(bad_query_result.is_err()); + } + #[test] fn test_pragma_query_row() { let db = checked_memory_handle(); diff --git a/src/statement.rs b/src/statement.rs index 1bcf4ed..58d290d 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -377,6 +377,9 @@ impl<'conn> Statement<'conn> { /// If the query returns more than one row, all rows except the first are /// ignored. /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the query truly is optional, + /// you can call `.optional()` on the result of this to get a `Result>`. + /// /// # Failure /// /// Will return `Err` if the underlying SQLite call fails.