aboutsummaryrefslogtreecommitdiff
path: root/src/types/url.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/types/url.rs')
-rw-r--r--src/types/url.rs81
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);
+ }
+ }
+ }
+}