1 //! [`ToSql`] and [`FromSql`] implementation for [`url::Url`]. 2 use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; 3 use crate::Result; 4 use url::Url; 5 6 /// Serialize `Url` to text. 7 impl ToSql for Url { 8 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>9 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 10 Ok(ToSqlOutput::from(self.as_str())) 11 } 12 } 13 14 /// Deserialize text to `Url`. 15 impl FromSql for Url { 16 #[inline] column_result(value: ValueRef<'_>) -> FromSqlResult<Self>17 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 18 match value { 19 ValueRef::Text(s) => { 20 let s = std::str::from_utf8(s).map_err(|e| FromSqlError::Other(Box::new(e)))?; 21 Url::parse(s).map_err(|e| FromSqlError::Other(Box::new(e))) 22 } 23 _ => Err(FromSqlError::InvalidType), 24 } 25 } 26 } 27 28 #[cfg(test)] 29 mod test { 30 use crate::{params, Connection, Error, Result}; 31 use url::{ParseError, Url}; 32 checked_memory_handle() -> Result<Connection>33 fn checked_memory_handle() -> Result<Connection> { 34 let db = Connection::open_in_memory()?; 35 db.execute_batch("CREATE TABLE urls (i INTEGER, v TEXT)")?; 36 Ok(db) 37 } 38 get_url(db: &Connection, id: i64) -> Result<Url>39 fn get_url(db: &Connection, id: i64) -> Result<Url> { 40 db.query_row("SELECT v FROM urls WHERE i = ?", [id], |r| r.get(0)) 41 } 42 43 #[test] test_sql_url() -> Result<()>44 fn test_sql_url() -> Result<()> { 45 let db = &checked_memory_handle()?; 46 47 let url0 = Url::parse("http://www.example1.com").unwrap(); 48 let url1 = Url::parse("http://www.example1.com/").unwrap(); 49 let url2 = "http://www.example2.com/"; 50 51 db.execute( 52 "INSERT INTO urls (i, v) VALUES (0, ?1), (1, ?2), (2, ?3), (3, ?4)", 53 // also insert a non-hex encoded url (which might be present if it was 54 // inserted separately) 55 params![url0, url1, url2, "illegal"], 56 )?; 57 58 assert_eq!(get_url(db, 0)?, url0); 59 60 assert_eq!(get_url(db, 1)?, url1); 61 62 // Should successfully read it, even though it wasn't inserted as an 63 // escaped url. 64 let out_url2: Url = get_url(db, 2)?; 65 assert_eq!(out_url2, Url::parse(url2).unwrap()); 66 67 // Make sure the conversion error comes through correctly. 68 let err = get_url(db, 3).unwrap_err(); 69 match err { 70 Error::FromSqlConversionFailure(_, _, e) => { 71 assert_eq!( 72 *e.downcast::<ParseError>().unwrap(), 73 ParseError::RelativeUrlWithoutBase, 74 ); 75 } 76 e => { 77 panic!("Expected conversion failure, got {}", e); 78 } 79 } 80 Ok(()) 81 } 82 } 83