Skip to content

Commit

Permalink
Add rusqlite ToSql/FromSql implementations for CowStr if the correspo…
Browse files Browse the repository at this point in the history
…nding feature is enabled
  • Loading branch information
fasterthanlime committed Sep 16, 2024
1 parent b1bff72 commit 1c3b1f9
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 4 deletions.
123 changes: 123 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions merde/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
4 changes: 3 additions & 1 deletion merde_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
66 changes: 66 additions & 0 deletions merde_core/src/cowstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ impl<'s> CowStr<'s> {
Ok(Self::Borrowed(std::str::from_utf8(s)?))
}

pub fn from_utf8_owned(s: Vec<u8>) -> Result<Self, std::str::Utf8Error> {
#[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")]
{
Expand Down Expand Up @@ -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<rusqlite::types::ToSqlOutput<'_>> {
Ok(rusqlite::types::ToSqlOutput::Borrowed(self.as_ref().into()))
}
}

impl FromSql for CowStr<'_> {
fn column_result(value: rusqlite::types::ValueRef<'_>) -> Result<Self, FromSqlError> {
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::*;
Expand All @@ -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<dyn std::error::Error>> {
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(())
}
}

0 comments on commit 1c3b1f9

Please sign in to comment.