use diesel::prelude::*;
use diesel::result::{DatabaseErrorKind, Error};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;
pub trait Model: Sized + Clone {
type Id: ?Sized + PartialEq + Debug + ToOwned;
fn all(conn: &mut SqliteConnection) -> QueryResult<Vec<Self>>;
fn find(conn: &mut SqliteConnection, id: &Self::Id) -> QueryResult<Option<Self>>;
fn id_exists(conn: &mut SqliteConnection, id: &Self::Id) -> QueryResult<bool>;
}
pub trait ModelRelations: Model {
type Resolved;
fn resolve_many(
conn: &mut SqliteConnection,
models: Vec<Self>,
) -> QueryResult<Vec<Self::Resolved>>;
fn resolve(self, conn: &mut SqliteConnection) -> QueryResult<Self::Resolved> {
Self::resolve_many(conn, vec![self])?
.into_iter()
.next()
.ok_or(Error::NotFound)
}
}
pub(crate) fn load_related<K, V>(
conn: &mut SqliteConnection,
ids: impl IntoIterator<Item = K>,
mut load: impl FnMut(&mut SqliteConnection, &[K]) -> QueryResult<Vec<V>>,
key: impl Fn(&V) -> K,
) -> QueryResult<HashMap<K, V>>
where
K: Eq + Hash,
{
let ids: Vec<K> = ids
.into_iter()
.collect::<HashSet<_>>()
.into_iter()
.collect();
let mut rows = HashMap::new();
for chunk in ids.chunks(500) {
for row in load(conn, chunk)? {
rows.insert(key(&row), row);
}
}
Ok(rows)
}
pub trait ModelUpdate: Model {
fn update(self, conn: &mut SqliteConnection) -> QueryResult<Self>;
fn update_all(conn: &mut SqliteConnection, items: Vec<Self>) -> QueryResult<Vec<Self>> {
conn.transaction(|conn| {
let mut results = Vec::with_capacity(items.len());
for item in items {
results.push(item.update(conn)?);
}
Ok(results)
})
}
}
pub trait ModelDelete: Model {
fn delete(conn: &mut SqliteConnection, id: &Self::Id) -> QueryResult<usize>;
fn delete_all(conn: &mut SqliteConnection, ids: Vec<&Self::Id>) -> QueryResult<usize> {
conn.transaction(|conn| {
let mut result: usize = 0;
for id in ids {
result += Self::delete(conn, id)?;
}
Ok(result)
})
}
}
pub trait ModelInsert: Sized {
type Model: Model;
fn insert(self, conn: &mut SqliteConnection) -> QueryResult<Self::Model>;
fn insert_all(conn: &mut SqliteConnection, items: Vec<Self>) -> QueryResult<Vec<Self::Model>> {
conn.transaction(|conn| {
let mut results = Vec::with_capacity(items.len());
for item in items {
results.push(item.insert(conn)?);
}
Ok(results)
})
}
}
pub trait SequenceStore {
type ParentId: ?Sized + PartialEq + Debug;
const START_SEQ: i32 = 1;
fn is_valid_parent(conn: &mut SqliteConnection, parent: &Self::ParentId) -> QueryResult<bool>;
fn count_children(conn: &mut SqliteConnection, parent: &Self::ParentId) -> QueryResult<i32>;
fn get_seq_numbers(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
) -> QueryResult<Vec<i32>>;
fn reset_seq(conn: &mut SqliteConnection, parent: &Self::ParentId) -> QueryResult<usize>;
fn shift_seq(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
range: std::ops::Range<i32>,
delta: i32,
) -> QueryResult<usize>;
fn validate_seq_numbers(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
) -> QueryResult<bool> {
Ok(validate_seq_numbers(
&Self::get_seq_numbers(conn, parent)?,
Self::START_SEQ,
))
}
fn increment_seq_gte(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
seq: i32,
) -> QueryResult<usize> {
let end = sequence_end(Self::START_SEQ, Self::count_children(conn, parent)?)?;
Self::shift_seq(conn, parent, seq..end, 1)
}
fn decrement_seq_gt(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
seq: i32,
) -> QueryResult<usize> {
let start = seq
.checked_add(1)
.ok_or_else(|| ordering_error("Position overflow"))?;
Self::shift_seq(conn, parent, start..i32::MAX, -1)
}
fn increment_seq_gte_lt(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
start: i32,
end: i32,
) -> QueryResult<usize> {
Self::shift_seq(conn, parent, start..end, 1)
}
fn decrement_seq_gt_lte(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
start: i32,
end: i32,
) -> QueryResult<usize> {
let start = start
.checked_add(1)
.ok_or_else(|| ordering_error("Position overflow"))?;
let end = end
.checked_add(1)
.ok_or_else(|| ordering_error("Position overflow"))?;
Self::shift_seq(conn, parent, start..end, -1)
}
fn update_seq_before_insert(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
seq: Option<i32>,
) -> QueryResult<(i32, usize)> {
if !Self::is_valid_parent(conn, parent)? {
return Err(ordering_error("Invalid collection"));
}
let end = sequence_end(Self::START_SEQ, Self::count_children(conn, parent)?)?;
let seq = checked_position(seq, Self::START_SEQ, end)?;
let n = if seq < end {
Self::shift_seq(conn, parent, seq..end, 1)?
} else {
0
};
Ok((seq, n))
}
fn update_seq_before_move_to(
conn: &mut SqliteConnection,
old_parent: &Self::ParentId,
parent: &Self::ParentId,
old_seq: i32,
seq: Option<i32>,
) -> QueryResult<Option<(i32, usize)>> {
if old_parent == parent {
return Ok(None);
}
if !Self::is_valid_parent(conn, parent)? {
return Err(ordering_error("Invalid collection"));
}
let end = sequence_end(Self::START_SEQ, Self::count_children(conn, parent)?)?;
let seq = checked_position(seq, Self::START_SEQ, end)?;
let mut n = Self::decrement_seq_gt(conn, old_parent, old_seq)?;
if seq < end {
n += Self::shift_seq(conn, parent, seq..end, 1)?;
}
Ok(Some((seq, n)))
}
fn update_seq_before_move_in(
conn: &mut SqliteConnection,
parent: &Self::ParentId,
old_seq: i32,
seq: Option<i32>,
) -> QueryResult<Option<(i32, usize)>> {
let end = sequence_end(Self::START_SEQ, Self::count_children(conn, parent)?)?;
checked_position(Some(old_seq), Self::START_SEQ, end - 1)?;
let seq = checked_position(seq, Self::START_SEQ, end - 1)?;
if seq == old_seq {
return Ok(None);
}
let n = if seq > old_seq {
Self::decrement_seq_gt_lte(conn, parent, old_seq, seq)?
} else {
Self::increment_seq_gte_lt(conn, parent, seq, old_seq)?
};
Ok(Some((seq, n)))
}
fn update_seq_before_move(
conn: &mut SqliteConnection,
old_parent: &Self::ParentId,
parent: &Self::ParentId,
old_seq: i32,
seq: Option<i32>,
) -> QueryResult<Option<(i32, usize)>> {
if old_parent == parent {
Self::update_seq_before_move_in(conn, parent, old_seq, seq)
} else {
Self::update_seq_before_move_to(conn, old_parent, parent, old_seq, seq)
}
}
}
pub use SequenceStore as TreeSeq;
pub fn validate_seq_numbers(seqs: &[i32], start: i32) -> bool {
seqs.iter()
.enumerate()
.all(|(i, &seq)| i64::from(seq) == i64::from(start) + i as i64)
}
pub(crate) fn ordering_error(message: &str) -> Error {
Error::DatabaseError(
DatabaseErrorKind::RestrictViolation,
Box::new(message.to_owned()),
)
}
fn sequence_end(start: i32, count: i32) -> QueryResult<i32> {
if count < 0 {
return Err(ordering_error("Invalid collection size"));
}
start
.checked_add(count)
.ok_or_else(|| ordering_error("Position overflow"))
}
fn checked_position(seq: Option<i32>, start: i32, end: i32) -> QueryResult<i32> {
let seq = seq.unwrap_or(end);
if seq < start || seq > end {
return Err(ordering_error("Invalid sequence number"));
}
Ok(seq)
}
pub trait OrderedModel: Model + SequenceStore {
fn order_id(&self) -> <Self::Id as ToOwned>::Owned;
fn order_parent(&self) -> &Self::ParentId;
fn order_position(&self) -> i32;
fn ordered(conn: &mut SqliteConnection, parent: &Self::ParentId) -> QueryResult<Vec<Self>>;
fn write_order(
conn: &mut SqliteConnection,
changes: &[(&Self, &Self::ParentId, i32)],
) -> QueryResult<usize>;
fn renumber(conn: &mut SqliteConnection, parent: &Self::ParentId) -> QueryResult<usize> {
conn.transaction(|conn| {
let rows = Self::ordered(conn, parent)?;
let mut changes = Vec::new();
for (index, row) in rows.iter().enumerate() {
let index =
i32::try_from(index).map_err(|_| ordering_error("Position overflow"))?;
let seq = sequence_end(Self::START_SEQ, index)?;
if row.order_position() != seq {
changes.push((row, parent, seq));
}
}
Self::write_order(conn, &changes)
})
}
}
pub(crate) fn delete_tree_batch<M: ModelTree + ModelDelete>(
conn: &mut SqliteConnection,
ids: Vec<&M::Id>,
) -> QueryResult<usize> {
conn.transaction(|conn| {
let roots = tree_delete_roots::<M>(conn, &ids)?;
let mut count = 0;
for root in roots {
count += M::delete(conn, root)?;
}
Ok(count)
})
}
pub(crate) fn tree_delete_roots<'a, M: ModelTree>(
conn: &mut SqliteConnection,
ids: &[&'a M::Id],
) -> QueryResult<Vec<&'a M::Id>> {
use std::borrow::Borrow;
let mut roots = Vec::new();
for (i, id) in ids.iter().enumerate() {
if ids[..i].contains(id) {
return Err(ordering_error("Duplicate delete ID"));
}
let row = M::find(conn, id)?.ok_or(Error::NotFound)?;
let mut parent = row.order_parent().to_owned();
let mut seen: Vec<<M::Id as ToOwned>::Owned> = vec![(*id).to_owned()];
let mut covered = false;
loop {
let key: &M::Id = parent.borrow();
if seen.iter().any(|p| p.borrow() == key) {
return Err(ordering_error("Cyclic ancestry"));
}
if ids.contains(&key) {
covered = true;
}
let ancestor = M::find(conn, key)?;
seen.push(parent);
match ancestor {
Some(ancestor) => parent = ancestor.order_parent().to_owned(),
None => break,
}
}
if !covered {
roots.push(*id);
}
}
Ok(roots)
}
pub trait ModelTree: OrderedModel<ParentId = <Self as Model>::Id> {
fn validate_destination(
conn: &mut SqliteConnection,
id: &Self::Id,
parent: &Self::Id,
) -> QueryResult<()> {
use std::borrow::Borrow;
if !Self::is_valid_parent(conn, parent)? {
return Err(ordering_error("Invalid parent"));
}
let mut current = parent.to_owned();
let mut seen: Vec<<Self::Id as ToOwned>::Owned> = Vec::new();
loop {
let key: &Self::Id = current.borrow();
if key == id || seen.iter().any(|p| p.borrow() == key) {
return Err(ordering_error(
"A tree move would create or encounter a cycle",
));
}
let node = Self::find(conn, key)?;
seen.push(current);
match node {
Some(node) => current = node.order_parent().to_owned(),
None => return Ok(()), }
}
}
fn move_to(
conn: &mut SqliteConnection,
id: &Self::Id,
parent_id: Option<&Self::Id>,
seq: Option<i32>,
) -> QueryResult<usize>;
fn move_all_to(
conn: &mut SqliteConnection,
ids: &[&Self::Id],
parent_id: &Self::Id,
start_seq: Option<i32>,
) -> QueryResult<usize> {
conn.transaction(|conn| {
if ids.is_empty() {
return Ok(0);
}
for id in ids {
Self::validate_destination(conn, id, parent_id)?;
}
reorder_batch::<Self>(conn, ids, Some(parent_id), start_seq)
})
}
}
pub trait ModelList: OrderedModel {
fn move_to(conn: &mut SqliteConnection, id: &Self::Id, seq: Option<i32>) -> QueryResult<usize>;
fn move_all_to(
conn: &mut SqliteConnection,
ids: &[&Self::Id],
start_seq: Option<i32>,
) -> QueryResult<usize> {
conn.transaction(|conn| reorder_batch::<Self>(conn, ids, None, start_seq))
}
}
fn reorder_batch<M: OrderedModel>(
conn: &mut SqliteConnection,
ids: &[&M::Id],
target: Option<&M::ParentId>,
seq: Option<i32>,
) -> QueryResult<usize> {
use std::borrow::Borrow;
if ids.is_empty() {
return Ok(0);
}
let mut selected = Vec::with_capacity(ids.len());
for (i, id) in ids.iter().enumerate() {
if ids[..i].contains(id) {
return Err(ordering_error("Duplicate move ID"));
}
selected.push(M::find(conn, id)?.ok_or(Error::NotFound)?);
}
let parent = target.unwrap_or_else(|| selected[0].order_parent());
if target.is_none() && selected.iter().any(|row| row.order_parent() != parent) {
return Err(ordering_error("List moves require a single collection"));
}
let mut scopes = vec![parent];
for row in &selected {
if !scopes.contains(&row.order_parent()) {
scopes.push(row.order_parent());
}
}
let mut groups = Vec::new();
for scope in scopes {
let mut members = M::ordered(conn, scope)?;
members.retain(|row| !ids.contains(&row.order_id().borrow()));
if scope == parent {
let count =
i32::try_from(members.len()).map_err(|_| ordering_error("Collection too large"))?;
let end = sequence_end(M::START_SEQ, count)?;
let position = checked_position(seq, M::START_SEQ, end)?;
let index = (position - M::START_SEQ) as usize;
members.splice(index..index, selected.iter().cloned());
}
groups.push((scope, members));
}
let mut changes = Vec::new();
let mut moved = 0;
for (scope, members) in &groups {
for (i, row) in members.iter().enumerate() {
let offset = i32::try_from(i).map_err(|_| ordering_error("Collection too large"))?;
let position = sequence_end(M::START_SEQ, offset)?;
if row.order_parent() != *scope || row.order_position() != position {
if ids.contains(&row.order_id().borrow()) {
moved += 1;
}
changes.push((row, *scope, position));
}
}
}
if !changes.is_empty() {
M::write_order(conn, &changes)?;
}
Ok(moved)
}
pub trait SequenceVisible: SequenceStore<ParentId = ()> {
fn count_visible(conn: &mut SqliteConnection) -> QueryResult<i32> {
Self::count_children(conn, &())
}
fn get_seq_numbers(conn: &mut SqliteConnection) -> QueryResult<Vec<i32>> {
<Self as SequenceStore>::get_seq_numbers(conn, &())
}
fn reset_seq(conn: &mut SqliteConnection) -> QueryResult<usize> {
<Self as SequenceStore>::reset_seq(conn, &())
}
fn validate_seq_numbers(conn: &mut SqliteConnection) -> QueryResult<bool> {
<Self as SequenceStore>::validate_seq_numbers(conn, &())
}
fn increment_seq_before_insert(conn: &mut SqliteConnection, seq: i32) -> QueryResult<usize> {
Self::increment_seq_gte(conn, &(), seq)
}
fn update_seq_before_insert(conn: &mut SqliteConnection, seq: i32) -> QueryResult<i32> {
Ok(<Self as SequenceStore>::update_seq_before_insert(
conn,
&(),
if seq == 0 { None } else { Some(seq) },
)?
.0)
}
fn decrement_seq_after_delete(conn: &mut SqliteConnection, seq: i32) -> QueryResult<usize> {
Self::decrement_seq_gt(conn, &(), seq)
}
}
pub type NodeRef<T> = Rc<RefCell<T>>;
pub trait TreeNode: From<Self::Model> {
type Model: Model;
type Id: ?Sized + Eq + Ord + Debug + Clone + Hash;
fn id(&self) -> Self::Id;
fn sequence(&self) -> i32;
fn parent_id(&self) -> Self::Id;
fn children(&self) -> &Vec<NodeRef<Self>>;
fn children_mut(&mut self) -> &mut Vec<NodeRef<Self>>;
fn sort_children(&mut self) {
self.children_mut().sort_by(|a, b| {
let a_seq = a.borrow().sequence();
let b_seq = b.borrow().sequence();
a_seq
.cmp(&b_seq)
.then_with(|| a.borrow().id().cmp(&b.borrow().id()))
});
}
}
fn sort_tree_ref<N: TreeNode>(item: &mut NodeRef<N>) {
let mut item_borrow = item.borrow_mut();
item_borrow.sort_children();
for child in item_borrow.children_mut().iter_mut() {
sort_tree_ref(child);
}
}
fn sort_tree_list<N: TreeNode>(items: &mut Vec<NodeRef<N>>) {
items.sort_by(|a, b| {
let a_seq = a.borrow().sequence();
let b_seq = b.borrow().sequence();
a_seq
.cmp(&b_seq)
.then_with(|| a.borrow().id().cmp(&b.borrow().id()))
});
for item in items.iter_mut() {
sort_tree_ref(item);
}
}
pub(crate) fn build_tree_list<M, N>(models: Vec<M>, root_id: N::Id) -> Vec<NodeRef<N>>
where
N: TreeNode<Model = M>,
M: Model,
{
let nodes: Vec<N> = models.into_iter().map(N::from).collect();
let mut map: HashMap<N::Id, NodeRef<N>> = HashMap::new();
let mut roots = Vec::new();
for node in nodes.into_iter() {
let id = node.id();
let parent_id = node.parent_id();
let item: NodeRef<N> = Rc::new(RefCell::new(node));
if parent_id == root_id {
roots.push(item.clone());
}
map.insert(id.clone(), item);
}
for node in map.values() {
let parent_id = node.borrow().parent_id();
if let Some(parent_node) = map.get(&parent_id) {
parent_node.borrow_mut().children_mut().push(node.clone());
}
}
sort_tree_list(&mut roots);
roots
}