use crate::{get_blob, Message, PackageId, Request};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SqliteRequest {
pub package_id: PackageId,
pub db: String,
pub action: SqliteAction,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SqliteAction {
Open,
RemoveDb,
Write {
statement: String,
tx_id: Option<u64>,
},
Query(String),
BeginTx,
Commit { tx_id: u64 },
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SqliteResponse {
Ok,
Read,
BeginTx { tx_id: u64 },
Err(SqliteError),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum SqlValue {
Integer(i64),
Real(f64),
Text(String),
Blob(Vec<u8>),
Boolean(bool),
Null,
}
#[derive(Clone, Debug, Serialize, Deserialize, Error)]
pub enum SqliteError {
#[error("db [{0}, {1}] does not exist")]
NoDb(PackageId, String),
#[error("no transaction {0} found")]
NoTx(u64),
#[error("no write capability for requested DB")]
NoWriteCap,
#[error("no read capability for requested DB")]
NoReadCap,
#[error("request to open or remove DB with mismatching package ID")]
MismatchingPackageId,
#[error("failed to generate capability for new DB")]
AddCapFailed,
#[error("write statement started with non-existent write keyword")]
NotAWriteKeyword,
#[error("read query started with non-existent read keyword")]
NotAReadKeyword,
#[error("parameters blob in read/write was misshapen or contained invalid JSON objects")]
InvalidParameters,
#[error("sqlite got a malformed request that failed to deserialize")]
MalformedRequest,
#[error("rusqlite error: {0}")]
RusqliteError(String),
#[error("IO error: {0}")]
IOError(String),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SqliteCapabilityParams {
pub kind: SqliteCapabilityKind,
pub db_key: (PackageId, String),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SqliteCapabilityKind {
Read,
Write,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sqlite {
pub package_id: PackageId,
pub db: String,
pub timeout: u64,
}
impl Sqlite {
pub fn read(
&self,
query: String,
params: Vec<serde_json::Value>,
) -> anyhow::Result<Vec<HashMap<String, serde_json::Value>>> {
let res = Request::new()
.target(("our", "sqlite", "distro", "sys"))
.body(serde_json::to_vec(&SqliteRequest {
package_id: self.package_id.clone(),
db: self.db.clone(),
action: SqliteAction::Query(query),
})?)
.blob_bytes(serde_json::to_vec(¶ms)?)
.send_and_await_response(self.timeout)?;
match res {
Ok(Message::Response { body, .. }) => {
let response = serde_json::from_slice::<SqliteResponse>(&body)?;
match response {
SqliteResponse::Read => {
let blob = get_blob().ok_or_else(|| SqliteError::MalformedRequest)?;
let values = serde_json::from_slice::<
Vec<HashMap<String, serde_json::Value>>,
>(&blob.bytes)
.map_err(|_| SqliteError::MalformedRequest)?;
Ok(values)
}
SqliteResponse::Err(error) => Err(error.into()),
_ => Err(anyhow::anyhow!(
"sqlite: unexpected response {:?}",
response
)),
}
}
_ => Err(anyhow::anyhow!("sqlite: unexpected message: {:?}", res)),
}
}
pub fn write(
&self,
statement: String,
params: Vec<serde_json::Value>,
tx_id: Option<u64>,
) -> anyhow::Result<()> {
let res = Request::new()
.target(("our", "sqlite", "distro", "sys"))
.body(serde_json::to_vec(&SqliteRequest {
package_id: self.package_id.clone(),
db: self.db.clone(),
action: SqliteAction::Write { statement, tx_id },
})?)
.blob_bytes(serde_json::to_vec(¶ms)?)
.send_and_await_response(self.timeout)?;
match res {
Ok(Message::Response { body, .. }) => {
let response = serde_json::from_slice::<SqliteResponse>(&body)?;
match response {
SqliteResponse::Ok => Ok(()),
SqliteResponse::Err(error) => Err(error.into()),
_ => Err(anyhow::anyhow!(
"sqlite: unexpected response {:?}",
response
)),
}
}
_ => Err(anyhow::anyhow!("sqlite: unexpected message: {:?}", res)),
}
}
pub fn begin_tx(&self) -> anyhow::Result<u64> {
let res = Request::new()
.target(("our", "sqlite", "distro", "sys"))
.body(serde_json::to_vec(&SqliteRequest {
package_id: self.package_id.clone(),
db: self.db.clone(),
action: SqliteAction::BeginTx,
})?)
.send_and_await_response(self.timeout)?;
match res {
Ok(Message::Response { body, .. }) => {
let response = serde_json::from_slice::<SqliteResponse>(&body)?;
match response {
SqliteResponse::BeginTx { tx_id } => Ok(tx_id),
SqliteResponse::Err(error) => Err(error.into()),
_ => Err(anyhow::anyhow!(
"sqlite: unexpected response {:?}",
response
)),
}
}
_ => Err(anyhow::anyhow!("sqlite: unexpected message: {:?}", res)),
}
}
pub fn commit_tx(&self, tx_id: u64) -> anyhow::Result<()> {
let res = Request::new()
.target(("our", "sqlite", "distro", "sys"))
.body(serde_json::to_vec(&SqliteRequest {
package_id: self.package_id.clone(),
db: self.db.clone(),
action: SqliteAction::Commit { tx_id },
})?)
.send_and_await_response(self.timeout)?;
match res {
Ok(Message::Response { body, .. }) => {
let response = serde_json::from_slice::<SqliteResponse>(&body)?;
match response {
SqliteResponse::Ok => Ok(()),
SqliteResponse::Err(error) => Err(error.into()),
_ => Err(anyhow::anyhow!(
"sqlite: unexpected response {:?}",
response
)),
}
}
_ => Err(anyhow::anyhow!("sqlite: unexpected message: {:?}", res)),
}
}
}
pub fn open(package_id: PackageId, db: &str, timeout: Option<u64>) -> anyhow::Result<Sqlite> {
let timeout = timeout.unwrap_or(5);
let res = Request::new()
.target(("our", "sqlite", "distro", "sys"))
.body(serde_json::to_vec(&SqliteRequest {
package_id: package_id.clone(),
db: db.to_string(),
action: SqliteAction::Open,
})?)
.send_and_await_response(timeout)?;
match res {
Ok(Message::Response { body, .. }) => {
let response = serde_json::from_slice::<SqliteResponse>(&body)?;
match response {
SqliteResponse::Ok => Ok(Sqlite {
package_id,
db: db.to_string(),
timeout,
}),
SqliteResponse::Err(error) => Err(error.into()),
_ => Err(anyhow::anyhow!(
"sqlite: unexpected response {:?}",
response
)),
}
}
_ => Err(anyhow::anyhow!("sqlite: unexpected message: {:?}", res)),
}
}
pub fn remove_db(package_id: PackageId, db: &str, timeout: Option<u64>) -> anyhow::Result<()> {
let timeout = timeout.unwrap_or(5);
let res = Request::new()
.target(("our", "sqlite", "distro", "sys"))
.body(serde_json::to_vec(&SqliteRequest {
package_id: package_id.clone(),
db: db.to_string(),
action: SqliteAction::RemoveDb,
})?)
.send_and_await_response(timeout)?;
match res {
Ok(Message::Response { body, .. }) => {
let response = serde_json::from_slice::<SqliteResponse>(&body)?;
match response {
SqliteResponse::Ok => Ok(()),
SqliteResponse::Err(error) => Err(error.into()),
_ => Err(anyhow::anyhow!(
"sqlite: unexpected response {:?}",
response
)),
}
}
_ => Err(anyhow::anyhow!("sqlite: unexpected message: {:?}", res)),
}
}