use crate::data::{self, Column, Data};
use anyhow::{Result, bail};
use rusqlite::params_from_iter;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Serialize)]
pub struct Page {
pub key: String,
pub name: String,
pub columns: Vec<Column>,
pub total: i64,
pub offset: i64,
pub rows: Vec<Record>,
}
#[derive(Debug, Serialize)]
pub struct Record {
pub rowid: i64,
pub cells: Vec<Value>,
}
#[derive(Debug, Deserialize)]
pub struct Edit {
pub rowid: i64,
pub column: String,
pub value: Value,
}
impl Data {
pub fn read(
&self,
table: &str,
sort: Option<&str>,
desc: bool,
limit: i64,
offset: i64,
) -> Result<Page> {
let key = self.resolve(table)?;
let columns = data::columns_of(&self.reader, &key)?;
let name = self
.reader
.query_row("SELECT name FROM _tables WHERE key = ?1", [&key], |row| {
row.get(0)
})
.unwrap_or_else(|_| key.clone());
let selected = columns
.iter()
.map(|col| data::quote(&col.name))
.collect::<Vec<_>>()
.join(", ");
let projection = match columns.is_empty() {
true => "rowid".to_owned(),
false => format!("rowid, {selected}"),
};
let order = match sort.and_then(|s| columns.iter().find(|col| col.name == s)) {
Some(col) => format!(
" ORDER BY ({0} IS NULL OR {0} = '') , {0} {1}",
data::quote(&col.name),
if desc { "DESC" } else { "ASC" }
),
None => String::new(),
};
let count = columns.len();
let mut stmt = self.reader.prepare(&format!(
"SELECT {projection} FROM {}{order} LIMIT ?1 OFFSET ?2",
data::quote(&key)
))?;
let rows = stmt
.query_map([limit, offset], |row| {
Ok(Record {
rowid: row.get(0)?,
cells: (1..=count).map(|i| data::value(row, i)).collect(),
})
})?
.collect::<rusqlite::Result<Vec<_>>>()?;
Ok(Page {
total: self.reader.query_row(
&format!("SELECT count(*) FROM {}", data::quote(&key)),
[],
|row| row.get(0),
)?,
rows,
name,
key,
columns,
offset,
})
}
pub fn write_cells(&mut self, table: &str, edits: &[Edit]) -> Result<()> {
if edits.is_empty() {
return Ok(());
}
let key = self.resolve(table)?;
let columns = data::columns_of(&self.writer, &key)?;
for edit in edits {
if !columns.iter().any(|col| col.name == edit.column) {
bail!("no column {} on {key}", edit.column);
}
}
let tx = self.writer.transaction()?;
for edit in edits {
tx.execute(
&format!(
"UPDATE {} SET {} = ?1 WHERE rowid = ?2",
data::quote(&key),
data::quote(&edit.column)
),
rusqlite::params![data::bind(&edit.value), edit.rowid],
)?;
}
Ok(tx.commit()?)
}
pub fn add_rows(&mut self, table: &str, count: i64) -> Result<Vec<i64>> {
let key = self.resolve(table)?;
let sql = format!("INSERT INTO {} DEFAULT VALUES", data::quote(&key));
let tx = self.writer.transaction()?;
let mut ids = Vec::new();
for _ in 0..count.max(0) {
tx.execute(&sql, [])?;
ids.push(tx.last_insert_rowid());
}
tx.commit()?;
Ok(ids)
}
pub fn delete_rows(&mut self, table: &str, rowids: &[i64]) -> Result<()> {
if rowids.is_empty() {
return Ok(());
}
let key = self.resolve(table)?;
let places = vec!["?"; rowids.len()].join(", ");
self.writer.execute(
&format!(
"DELETE FROM {} WHERE rowid IN ({places})",
data::quote(&key)
),
params_from_iter(rowids),
)?;
Ok(())
}
pub fn drop_column(&mut self, table: &str, column: &str) -> Result<Vec<Column>> {
let key = self.resolve(table)?;
let columns = data::columns_of(&self.writer, &key)?;
let Some(col) = columns.iter().find(|col| col.name == column) else {
bail!("no column {column} on {key}");
};
if columns.len() == 1 {
bail!("a table keeps at least one column");
}
self.writer.execute_batch(&format!(
"ALTER TABLE {} DROP COLUMN {}",
data::quote(&key),
data::quote(&col.name)
))?;
data::columns_of(&self.writer, &key)
}
}