1use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
3use crate::Result;
4use url::Url;
5
6impl ToSql for Url {
8 #[inline]
9 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
10 Ok(ToSqlOutput::from(self.as_str()))
11 }
12}
13
14impl FromSql for Url {
16 #[inline]
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)]
29mod test {
30 use crate::{params, Connection, Error, Result};
31 use url::{ParseError, Url};
32
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
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]
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 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 let out_url2: Url = get_url(db, 2)?;
65 assert_eq!(out_url2, Url::parse(url2).unwrap());
66
67 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}