diff options
Diffstat (limited to 'src/types/url.rs')
-rw-r--r-- | src/types/url.rs | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/src/types/url.rs b/src/types/url.rs new file mode 100644 index 0000000..1c9c63a --- /dev/null +++ b/src/types/url.rs @@ -0,0 +1,81 @@ +//! `ToSql` and `FromSql` implementation for [`url::Url`]. +use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; +use crate::Result; +use url::Url; + +/// Serialize `Url` to text. +impl ToSql for Url { + fn to_sql(&self) -> Result<ToSqlOutput<'_>> { + Ok(ToSqlOutput::from(self.as_str())) + } +} + +/// Deserialize text to `Url`. +impl FromSql for Url { + fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { + match value { + ValueRef::Text(s) => { + let s = std::str::from_utf8(s).map_err(|e| FromSqlError::Other(Box::new(e)))?; + Url::parse(s).map_err(|e| FromSqlError::Other(Box::new(e))) + } + _ => Err(FromSqlError::InvalidType), + } + } +} + +#[cfg(test)] +mod test { + use crate::{params, Connection, Error, Result}; + use url::{ParseError, Url}; + + fn checked_memory_handle() -> Connection { + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE urls (i INTEGER, v TEXT)") + .unwrap(); + db + } + + fn get_url(db: &Connection, id: i64) -> Result<Url> { + db.query_row("SELECT v FROM urls WHERE i = ?", params![id], |r| r.get(0)) + } + + #[test] + fn test_sql_url() { + let db = &checked_memory_handle(); + + let url0 = Url::parse("http://www.example1.com").unwrap(); + let url1 = Url::parse("http://www.example1.com/👌").unwrap(); + let url2 = "http://www.example2.com/👌"; + + db.execute( + "INSERT INTO urls (i, v) VALUES (0, ?), (1, ?), (2, ?), (3, ?)", + // also insert a non-hex encoded url (which might be present if it was + // inserted separately) + params![url0, url1, url2, "illegal"], + ) + .unwrap(); + + assert_eq!(get_url(db, 0).unwrap(), url0); + + assert_eq!(get_url(db, 1).unwrap(), url1); + + // Should successfully read it, even though it wasn't inserted as an + // escaped url. + let out_url2: Url = get_url(db, 2).unwrap(); + assert_eq!(out_url2, Url::parse(url2).unwrap()); + + // Make sure the conversion error comes through correctly. + let err = get_url(db, 3).unwrap_err(); + match err { + Error::FromSqlConversionFailure(_, _, e) => { + assert_eq!( + *e.downcast::<ParseError>().unwrap(), + ParseError::RelativeUrlWithoutBase, + ); + } + e => { + panic!("Expected conversion failure, got {}", e); + } + } + } +} |