Skip to main content

rusqlite/types/
url.rs

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