use crate::wit_bindgen;
#[doc(hidden)]
pub mod wit {
#![allow(missing_docs)]
use crate::wit_bindgen;
wit_bindgen::generate!({
runtime_path: "crate::wit_bindgen::rt",
world: "spin-sdk-sqlite",
path: "wit",
generate_all,
});
pub use spin::sqlite::sqlite;
}
#[doc(inline)]
pub use wit::sqlite::{Error, Value};
pub struct Connection(wit::sqlite::Connection);
impl Connection {
pub async fn open_default() -> Result<Self, Error> {
Self::open("default").await
}
pub async fn open(database: impl AsRef<str>) -> Result<Self, Error> {
wit::sqlite::Connection::open_async(database.as_ref().to_string())
.await
.map(Connection)
}
pub async fn execute(
&self,
statement: impl AsRef<str>,
parameters: impl IntoIterator<Item = Value>,
) -> Result<QueryResult, Error> {
let (columns, rows, result) = self
.0
.execute_async(
statement.as_ref().to_string(),
parameters.into_iter().collect(),
)
.await?;
Ok(QueryResult {
columns,
rows,
result,
})
}
pub async fn last_insert_rowid(&self) -> i64 {
self.0.last_insert_rowid_async().await
}
pub async fn changes(&self) -> u64 {
self.0.changes_async().await
}
}
pub struct QueryResult {
columns: Vec<String>,
rows: wit_bindgen::StreamReader<RowResult>,
result: wit_bindgen::FutureReader<Result<(), Error>>,
}
impl QueryResult {
pub fn columns(&self) -> &[String] {
&self.columns
}
pub async fn next(&mut self) -> Option<RowResult> {
self.rows.next().await
}
pub async fn result(self) -> Result<(), Error> {
self.result.await
}
pub async fn collect(self) -> Result<Vec<RowResult>, Error> {
let rows = self.rows.collect().await;
self.result.await?;
Ok(rows)
}
#[allow(clippy::type_complexity, reason = "that's what the inner bits are")]
pub fn into_inner(
self,
) -> (
Vec<String>,
wit_bindgen::StreamReader<RowResult>,
wit_bindgen::FutureReader<Result<(), Error>>,
) {
(self.columns, self.rows, self.result)
}
}
#[doc(inline)]
pub use wit::sqlite::RowResult;
impl RowResult {
pub fn get<'a, T: TryFrom<&'a Value>>(&'a self, index: usize) -> Option<T> {
self.values.get(index).and_then(|c| c.try_into().ok())
}
}
impl<'a> TryFrom<&'a Value> for bool {
type Error = ();
fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
match value {
Value::Integer(i) => Ok(*i != 0),
_ => Err(()),
}
}
}
macro_rules! int_from_value {
($($t:ty),*) => {
$(impl<'a> TryFrom<&'a Value> for $t {
type Error = ();
fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
match value {
Value::Integer(i) => (*i).try_into().map_err(|_| ()),
_ => Err(()),
}
}
})*
};
}
int_from_value!(u8, u16, u32, u64, i8, i16, i32, i64, usize, isize);
impl<'a> TryFrom<&'a Value> for f64 {
type Error = ();
fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
match value {
Value::Real(f) => Ok(*f),
_ => Err(()),
}
}
}
impl<'a> TryFrom<&'a Value> for &'a str {
type Error = ();
fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
match value {
Value::Text(s) => Ok(s.as_str()),
Value::Blob(b) => std::str::from_utf8(b).map_err(|_| ()),
_ => Err(()),
}
}
}
impl<'a> TryFrom<&'a Value> for &'a [u8] {
type Error = ();
fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
match value {
Value::Blob(b) => Ok(b.as_slice()),
Value::Text(s) => Ok(s.as_bytes()),
_ => Err(()),
}
}
}
impl Value {
pub fn text(value: impl Into<String>) -> Self {
Self::Text(value.into())
}
pub fn integer(value: impl Into<i64>) -> Self {
Self::Integer(value.into())
}
pub fn real(value: impl Into<f64>) -> Self {
Self::Real(value.into())
}
pub fn blob(value: impl Into<Vec<u8>>) -> Self {
Self::Blob(value.into())
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Self::Text(value.into())
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Self::Text(value)
}
}
macro_rules! value_from_int {
($($t:ty),*) => {
$(impl From<$t> for Value {
fn from(value: $t) -> Self {
Self::integer(value)
}
})*
};
}
value_from_int!(u8, u16, u32, i8, i16, i32, i64);
impl From<f32> for Value {
fn from(value: f32) -> Self {
Self::Real(value.into())
}
}
impl From<f64> for Value {
fn from(value: f64) -> Self {
Self::Real(value)
}
}
impl From<&[u8]> for Value {
fn from(value: &[u8]) -> Self {
Self::Blob(value.into())
}
}
impl<const N: usize> From<[u8; N]> for Value {
fn from(value: [u8; N]) -> Self {
Self::Blob(value.into())
}
}
impl<const N: usize> From<&[u8; N]> for Value {
fn from(value: &[u8; N]) -> Self {
Self::Blob(value.into())
}
}
impl From<Vec<u8>> for Value {
fn from(value: Vec<u8>) -> Self {
Self::Blob(value)
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(value: Option<T>) -> Self {
match value {
None => Value::Null,
Some(value) => value.into(),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Integer(l0), Self::Integer(r0)) => l0 == r0,
(Self::Real(l0), Self::Real(r0)) => l0 == r0,
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
(Self::Blob(l0), Self::Blob(r0)) => l0 == r0,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn value_conversions() {
let expected_text = Value::Text("a".to_string());
let expected_int = Value::Integer(123);
let expected_real = Value::Real(1234.5); let expected_real_int = Value::Real(123.0);
let expected_blob = Value::Blob(vec![1, 2, 3]);
assert_eq!(expected_text, Value::text("a"));
assert_eq!(expected_text, "a".into());
assert_eq!(expected_text, "a".to_string().into());
assert_eq!(expected_int, Value::integer(123u8));
assert_eq!(expected_int, Value::integer(123i16));
assert_eq!(expected_int, Value::integer(123u32));
assert_eq!(expected_int, Value::integer(123i64));
assert_eq!(expected_int, 123u8.into());
assert_eq!(expected_int, 123i16.into());
assert_eq!(expected_int, 123u32.into());
assert_eq!(expected_int, 123i64.into());
assert_eq!(expected_real, Value::real(1234.5f32));
assert_eq!(expected_real, Value::real(1234.5f64));
assert_eq!(expected_real, 1234.5f32.into());
assert_eq!(expected_real, 1234.5f64.into());
assert_eq!(expected_real_int, Value::real(123u32));
assert_eq!(expected_blob, Value::blob([1, 2, 3]));
assert_eq!(expected_blob, Value::blob(vec![1, 2, 3]));
assert_eq!(expected_blob, (&[1, 2, 3]).into());
assert_eq!(expected_blob, ([1, 2, 3][..]).into());
assert_eq!(expected_blob, [1, 2, 3].into());
assert_eq!(expected_blob, (vec![1, 2, 3]).into());
assert_eq!(Value::Null, None::<i16>.into());
assert_eq!(expected_int, Some(123u32).into());
}
}