From 377cf5730b65f00bc62b2f57c728c861524a5093 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 27 Jul 2024 10:22:46 +0200 Subject: [PATCH 1/5] Add support to jiff Date / DateTime / Time --- Cargo.toml | 19 ++++++++++++++++--- src/types/mod.rs | 3 +++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bb2f819..7ff92de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,10 @@ trace = [] release_memory = [] bundled = ["libsqlite3-sys/bundled", "modern_sqlite"] bundled-sqlcipher = ["libsqlite3-sys/bundled-sqlcipher", "bundled"] -bundled-sqlcipher-vendored-openssl = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl", "bundled-sqlcipher"] +bundled-sqlcipher-vendored-openssl = [ + "libsqlite3-sys/bundled-sqlcipher-vendored-openssl", + "bundled-sqlcipher", +] buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"] limits = [] loadable_extension = ["libsqlite3-sys/loadable_extension"] @@ -97,6 +100,7 @@ modern-full = [ "functions", "hooks", "i128_blob", + "jiff", "limits", "load_extension", "serde_json", @@ -113,10 +117,19 @@ modern-full = [ bundled-full = ["modern-full", "bundled"] [dependencies] -time = { version = "0.3.36", features = ["formatting", "macros", "parsing"], optional = true } +jiff = { version = "0.1", optional = true, default-features = false, features = [ + "std", +] } +time = { version = "0.3.36", features = [ + "formatting", + "macros", + "parsing", +], optional = true } bitflags = "2.6.0" hashlink = "0.9" -chrono = { version = "0.4.38", optional = true, default-features = false, features = ["clock"] } +chrono = { version = "0.4.38", optional = true, default-features = false, features = [ + "clock", +] } serde_json = { version = "1.0", optional = true } csv = { version = "1.1", optional = true } url = { version = "2.1", optional = true } diff --git a/src/types/mod.rs b/src/types/mod.rs index 74cef3f..24f7059 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -81,6 +81,9 @@ use std::fmt; #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] mod chrono; mod from_sql; +#[cfg(feature = "jiff")] +#[cfg_attr(docsrs, doc(cfg(feature = "jiff")))] +mod jiff; #[cfg(feature = "serde_json")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_json")))] mod serde_json; From dbe53e5d87079a5ce3c4f1c558bd5fe6479abcd9 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 27 Jul 2024 10:24:52 +0200 Subject: [PATCH 2/5] Oops --- src/types/jiff.rs | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 src/types/jiff.rs diff --git a/src/types/jiff.rs b/src/types/jiff.rs new file mode 100644 index 0000000..5b97e87 --- /dev/null +++ b/src/types/jiff.rs @@ -0,0 +1,137 @@ +//! Convert some `jiff` types. + +use jiff::civil::{Date, DateTime, Time}; +use std::str::FromStr; + +use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; +use crate::Result; + +/// Gregorian calendar date => "YYYY-MM-DD" +impl ToSql for Date { + #[inline] + fn to_sql(&self) -> Result> { + let s = self.to_string(); + Ok(ToSqlOutput::from(s)) + } +} + +/// "YYYY-MM-DD" => Gregorian calendar date. +impl FromSql for Date { + #[inline] + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + value.as_str().and_then(|s| match Date::from_str(s) { + Ok(d) => Ok(d), + Err(err) => Err(FromSqlError::Other(Box::new(err))), + }) + } +} +/// time => "HH:MM:SS.SSS" +impl ToSql for Time { + #[inline] + fn to_sql(&self) -> Result> { + let date_str = self.to_string(); + Ok(ToSqlOutput::from(date_str)) + } +} + +/// "HH:MM:SS.SSS" => time. +impl FromSql for Time { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + value.as_str().and_then(|s| match Time::from_str(s) { + Ok(t) => Ok(t), + Err(err) => Err(FromSqlError::Other(Box::new(err))), + }) + } +} + +/// Gregorian datetime => "YYYY-MM-DDTHH:MM:SS.SSS" +impl ToSql for DateTime { + #[inline] + fn to_sql(&self) -> Result> { + let s = self.to_string(); + Ok(ToSqlOutput::from(s)) + } +} + +/// "YYYY-MM-DDTHH:MM:SS.SSS" => Gregorian datetime. +impl FromSql for DateTime { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + value.as_str().and_then(|s| match DateTime::from_str(s) { + Ok(dt) => Ok(dt), + Err(err) => Err(FromSqlError::Other(Box::new(err))), + }) + } +} + +#[cfg(test)] +mod test { + use crate::{Connection, Result}; + use jiff::civil::{Date, DateTime, Time}; + + fn checked_memory_handle() -> Result { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo (t TEXT, b BLOB)")?; + Ok(db) + } + + #[test] + fn test_date() -> Result<()> { + let db = checked_memory_handle()?; + let date = Date::constant(2016, 2, 23); + db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?; + + let s: String = db.one_column("SELECT t FROM foo")?; + assert_eq!("2016-02-23", s); + let t: Date = db.one_column("SELECT t FROM foo")?; + assert_eq!(date, t); + + db.execute("UPDATE foo set b = date(t)", [])?; + let t: Date = db.one_column("SELECT b FROM foo")?; + assert_eq!(date, t); + + let r: Result = db.one_column("SELECT '2023-02-29'"); + assert!(r.is_err()); + Ok(()) + } + + #[test] + fn test_time() -> Result<()> { + let db = checked_memory_handle()?; + let time = Time::constant(23, 56, 4, 0); + db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?; + + let s: String = db.one_column("SELECT t FROM foo")?; + assert_eq!("23:56:04", s); + let v: Time = db.one_column("SELECT t FROM foo")?; + assert_eq!(time, v); + + db.execute("UPDATE foo set b = time(t)", [])?; + let v: Time = db.one_column("SELECT b FROM foo")?; + assert_eq!(time, v); + + let r: Result