From 1c3b1f94b787df621b00681bf1b43b9d6f902857 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Mon, 16 Sep 2024 15:09:55 +0200 Subject: [PATCH] Add rusqlite ToSql/FromSql implementations for CowStr if the corresponding feature is enabled --- Cargo.lock | 123 +++++++++++++++++++++++++++++++++++++++ Justfile | 2 +- merde/Cargo.toml | 7 ++- merde_core/Cargo.toml | 4 +- merde_core/src/cowstr.rs | 66 +++++++++++++++++++++ 5 files changed, 198 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 573e5a7..6a6e15a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,30 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "castaway" version = "0.2.3" @@ -47,6 +65,36 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.11" @@ -83,6 +131,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "merde" version = "5.0.0" @@ -98,6 +156,7 @@ version = "5.0.0" dependencies = [ "compact_str", "rubicon", + "rusqlite", "serde", ] @@ -154,6 +213,18 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "powerfmt" version = "0.2.0" @@ -184,6 +255,20 @@ version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "395f129ed66e98909eca7113a855dd6f6a8ed611d39ac65dfd9270607065b119" +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -216,6 +301,12 @@ dependencies = [ "syn", ] +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "static_assertions" version = "1.1.0" @@ -269,3 +360,35 @@ name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Justfile b/Justfile index 4db6814..603c7cd 100644 --- a/Justfile +++ b/Justfile @@ -2,7 +2,7 @@ check: #!/bin/bash -eux cargo check --example simple --no-default-features --features=json cargo run --example simple --features=core,json - cargo hack --feature-powerset --exclude-features=default,full check + cargo hack --feature-powerset --exclude-features=default,full check -p merde cargo test -F full pushd zerodeps-example diff --git a/merde/Cargo.toml b/merde/Cargo.toml index d91029d..dd52504 100644 --- a/merde/Cargo.toml +++ b/merde/Cargo.toml @@ -44,11 +44,14 @@ merde_time = { version = "4.0.4", path = "../merde_time", optional = true, featu [features] default = ["core", "deserialize"] -full = ["core", "deserialize", "json", "time"] -deserialize = ["core", "merde_time/deserialize"] +full = ["core", "deserialize", "json", "time", "rusqlite", "compact_str"] core = ["dep:merde_core"] +deserialize = ["core", "merde_time/deserialize"] +# merde_core re-exports compact_str = ["merde_core/compact_str"] serde = ["merde_core/serde"] +rusqlite = ["merde_core/rusqlite"] +# non-core crates json = ["dep:merde_json", "merde_time/json"] time = ["dep:merde_time"] diff --git a/merde_core/Cargo.toml b/merde_core/Cargo.toml index 80785e1..41e9d17 100644 --- a/merde_core/Cargo.toml +++ b/merde_core/Cargo.toml @@ -13,10 +13,12 @@ categories = ["encoding", "parser-implementations"] [dependencies] compact_str = { version = "0.8.0", optional = true } rubicon = "3.4.2" +rusqlite = { version = "0.32.1", optional = true } serde = { version = "1", optional = true } [features] default = [] -full = ["compact_str", "serde"] +full = ["compact_str", "serde", "rusqlite"] compact_str = ["dep:compact_str"] serde = ["dep:serde", "compact_str/serde"] +rusqlite = ["dep:rusqlite"] diff --git a/merde_core/src/cowstr.rs b/merde_core/src/cowstr.rs index f1ee16b..44f96d8 100644 --- a/merde_core/src/cowstr.rs +++ b/merde_core/src/cowstr.rs @@ -29,6 +29,17 @@ impl<'s> CowStr<'s> { Ok(Self::Borrowed(std::str::from_utf8(s)?)) } + pub fn from_utf8_owned(s: Vec) -> Result { + #[cfg(feature = "compact_str")] + { + Ok(Self::Owned(CompactString::from_utf8(s)?)) + } + #[cfg(not(feature = "compact_str"))] + { + Ok(String::from_utf8(s).map_err(|e| e.utf8_error())?.into()) + } + } + pub fn from_utf8_lossy(s: &'s [u8]) -> Self { #[cfg(feature = "compact_str")] { @@ -221,6 +232,29 @@ mod serde_impls { } } +#[cfg(feature = "rusqlite")] +mod rusqlite_impls { + use super::*; + use rusqlite::{types::FromSql, types::FromSqlError, types::ToSql, Result as RusqliteResult}; + + impl ToSql for CowStr<'_> { + fn to_sql(&self) -> RusqliteResult> { + Ok(rusqlite::types::ToSqlOutput::Borrowed(self.as_ref().into())) + } + } + + impl FromSql for CowStr<'_> { + fn column_result(value: rusqlite::types::ValueRef<'_>) -> Result { + match value { + rusqlite::types::ValueRef::Text(s) => Ok(CowStr::from_utf8(s) + .map_err(|e| FromSqlError::Other(Box::new(e)))? + .into_static()), + _ => Err(FromSqlError::InvalidType), + } + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -238,4 +272,36 @@ mod tests { assert_ne!("world", cow_str1); assert_ne!(cow_str1, cow_str3); } + + #[cfg(feature = "rusqlite")] + #[test] + fn test_rusqlite_integration() -> Result<(), Box> { + use rusqlite::Connection; + + // Create an in-memory database + let conn = Connection::open_in_memory()?; + + // Create a table + conn.execute( + "CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)", + [], + )?; + + // Insert a CowStr value + let cow_str = CowStr::from("Hello, Rusqlite!"); + conn.execute("INSERT INTO test_table (value) VALUES (?1)", [&cow_str])?; + + // Retrieve the value + let mut stmt = conn.prepare("SELECT value FROM test_table")?; + let mut rows = stmt.query([])?; + + if let Some(row) = rows.next()? { + let retrieved: CowStr = row.get(0)?; + assert_eq!(retrieved, "Hello, Rusqlite!"); + } else { + panic!("No rows returned"); + } + + Ok(()) + } }