use std::cmp::*;
use std::collections::hash_map::RandomState;
use std::collections::HashSet;
use chrono::prelude::*;
use std::error::Error;
use trees::{tr, Forest, ForestWalk, Visit};
use yatt_orm::errors::{DBError, DBResult};
use yatt_orm::sqlite::DB;
use yatt_orm::statement::*;
use yatt_orm::{FieldVal, Identifiers, Storage};
type PinNode<'a> = std::pin::Pin<&'a mut trees::Node<Node>>;
#[derive(Debug)]
pub(crate) struct ImportedPath {
}
pub(crate) trait PathSource {
fn get_path(
&self,
source: &str,
) -> Result<ImportedPath, Box<dyn Error>>;
}
pub trait DBRoot: Storage {
fn cur_running(&self) -> DBResult<Option<(Node, Interval)>>
where
Self: Sized,
{
let mut interval: Vec<Interval> =
self.get_by_filter(eq(Interval::end_n(), FieldVal::Null))?;
if interval.is_empty() {
return Ok(None);
}
let interval = interval.pop().unwrap();
let node: Vec<Node> = self
.get_by_filter(eq(Node::id_n(), interval.node_id.unwrap()))?;
if node.is_empty() {
return Err(DBError::Unexpected {
message: format!(
"Task with id={} for interval with id={}, not exists",
interval.node_id.unwrap_or(0),
interval.id,
),
});
}
let node = node[0].clone();
Ok(Some((node, interval)))
}
fn last_running(&self) -> DBResult<Option<(Node, Interval)>>
where
Self: Sized,
{
let interval: Vec<Interval> = self.get_by_statement(
filter(ne(Interval::deleted_n(), 1))
.sort(Interval::end_n(), SortDir::Descend)
.limit(1),
)?;
if interval.is_empty() {
return Ok(None);
}
let interval = interval.first().unwrap();
let node: Vec<Node> = self
.get_by_filter(eq(Node::id_n(), interval.node_id.unwrap()))?;
if node.is_empty() {
return Err(DBError::Unexpected {
message: format!(
"Task with id={} for interval with id={}, not exists",
interval.node_id.unwrap_or(0),
interval.id,
),
});
}
let node = node[0].clone();
Ok(Some((node, interval.to_owned())))
}
fn find_path(&self, path: &[&str]) -> DBResult<Vec<Node>>
where
Self: Sized,
{
let mut parent = FieldVal::Null;
let mut res = Vec::new();
for p in path.iter() {
let node = self.find_path_part(p, &parent)?;
if let Some(node) = node {
parent = FieldVal::Usize(node.id);
res.push(node);
} else {
return Ok(res);
}
}
Ok(res)
}
fn create_path(&self, path: &[&str]) -> DBResult<Vec<Node>>
where
Self: Sized,
{
if path.is_empty() {
return Err(DBError::Unexpected {
message: "provided value for path is empty".to_string(),
});
}
let mut nodes = self.find_path(path)?;
let p_len = path.len();
let n_len = nodes.len();
let high = p_len - (p_len - n_len);
for node in nodes.iter_mut().filter(|n| n.deleted) {
node.deleted = false;
self.save(node)?;
}
if high == p_len {
return Ok(nodes);
}
let mut parent_id = None;
if !nodes.is_empty() {
parent_id = Some(nodes.last().unwrap().id)
}
for n in path.iter().take(p_len).skip(high) {
let mut node = Node {
parent_id,
label: n.to_string(),
..Node::default()
};
let id = self.save(&node)?;
node.id = id;
parent_id = Some(id);
nodes.push(node);
}
Ok(nodes)
}
fn ancestors(&self, id: usize) -> DBResult<Vec<Node>>
where
Self: Sized,
{
let mut res = Vec::new();
let mut next = Some(id);
while next.is_some() {
let node: Node = self.get_by_id(next.unwrap())?;
next = node.parent_id;
res.push(node);
}
res.reverse();
Ok(res)
}
fn find_path_part(
&self,
name: &str,
parent_id: &FieldVal,
) -> DBResult<Option<Node>>
where
Self: Sized,
{
let mut nodes: Vec<Node> = self.get_by_filter(and(
eq(Node::parent_id_n(), parent_id),
eq(Node::label_n(), name),
))?;
if nodes.is_empty() {
return Ok(None);
};
if nodes.len() > 1 {
return Err(DBError::Unexpected {
message: "query return multiple rows".to_string(),
});
};
Ok(Some(nodes.remove(0)))
}
fn get_list_with_ancestors(
&self,
filt: Filter,
) -> DBResult<Vec<Node>>
where
Self: Sized,
{
let stmt = filter(and(
filt,
not(exists(from("nodes").filter(and(
eq(Node::parent_id_n(), FieldVal::FieldName("id".into())),
eq(Node::deleted_n(), 0),
)))),
))
.recursive_on(Node::parent_id_n())
.sort(Node::parent_id_n(), SortDir::Ascend)
.sort(Node::id_n(), SortDir::Ascend);
let matched: Vec<Node> = self.get_by_statement(stmt)?;
Ok(matched)
}
fn get_filtered_forest(
&self,
filt: Filter,
) -> DBResult<Option<Forest<Node>>>
where
Self: Sized,
{
let mut matched: Vec<Node> =
self.get_list_with_ancestors(filt)?;
if matched.is_empty() {
return Ok(None);
}
let mut root: Forest<Node> = Forest::new();
let first_child = matched
.iter()
.enumerate()
.find(|(_, el)| el.parent_id.is_some())
.map(|(idx, _)| idx)
.unwrap_or(matched.len());
if first_child > 0 {
for node in matched.drain(0..first_child) {
root.push_back(tr(node));
}
if matched.is_empty() {
return Ok(Some(root));
}
}
let mut dangling: Forest<Node> = Forest::new();
for node in matched.drain(..) {
adopt_node(node, &mut root, &mut dangling);
}
root.append(dangling);
Ok(Some(root))
}
fn remove_intervals(&self, node_id: usize) -> DBResult<usize>
where
Self: Sized,
{
self.remove_by_filter::<Interval>(and(
eq(Interval::node_id_n(), node_id),
ne(Interval::deleted_n(), 1),
))
}
fn remove_children(
&self,
node_id: usize,
) -> DBResult<(usize, usize)>
where
Self: Sized,
{
let children: Vec<Node> = self.get_by_filter(and(
eq(Node::parent_id_n(), node_id),
ne(Node::deleted_n(), 1),
))?;
let mut node_cnt = 0;
let mut interval_cnt = 0;
for node in children {
let (n, i) = self.remove_children(node.id)?;
node_cnt += n;
interval_cnt += i;
interval_cnt += self.remove_intervals(node.id)?;
}
node_cnt += self.remove_by_filter::<Node>(and(
eq(Node::parent_id_n(), node_id),
ne(Node::deleted_n(), 1),
))?;
Ok((node_cnt, interval_cnt))
}
fn remove_node(&self, node_id: usize) -> DBResult<(usize, usize)>
where
Self: Sized,
{
let mut node_cnt = 0;
let mut interval_cnt = 0;
let (n, i) = self.remove_children(node_id)?;
node_cnt += n;
interval_cnt += i;
interval_cnt += self.remove_intervals(node_id)?;
node_cnt += self.remove_by_filter::<Node>(eq("id", node_id))?;
Ok((node_cnt, interval_cnt))
}
fn has_children(&self, node_id: usize) -> DBResult<bool>
where
Self: Sized,
{
Ok(
self
.get_by_statement::<Node>(
filter(and(
eq(Node::parent_id_n(), node_id),
ne(Node::deleted_n(), 1),
))
.limit(1),
)?
.len()
== 1,
)
}
}
fn adopt_node(
node: Node,
root: &mut Forest<Node>,
dangling: &mut Forest<Node>,
) {
if let Some(node) = try_adopt_node(node, dangling) {
if let Some(node) = try_adopt_node(node, root) {
dangling.push_back(tr(node));
}
}
}
fn try_adopt_node(
node: Node,
forest: &mut Forest<Node>,
) -> Option<Node> {
let mut node = node;
for tree in forest.iter_mut() {
node = try_add_child(tree, node)?
}
Some(node)
}
fn try_add_child(tree: PinNode, node: Node) -> Option<Node> {
let mut tree = tree;
if tree.data.id == node.parent_id.unwrap() {
tree.push_back(tr(node));
None
} else {
let mut node = node;
for child in tree.iter_mut() {
let res = try_add_child(child, node);
if let Some(res) = res {
node = res;
continue;
}
return None;
}
Some(node)
}
}
pub struct FlattenForestIter<'a, T: Clone> {
walk: &'a mut ForestWalk<T>,
path: Vec<T>,
}
impl<'a, T: Clone> FlattenForestIter<'a, T> {
pub fn new(walk: &'a mut ForestWalk<T>) -> Self {
FlattenForestIter { walk, path: vec![] }
}
}
impl<'a, T: Clone> Iterator for FlattenForestIter<'a, T> {
type Item = Vec<T>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(next) = self.walk.get() {
let need_return = match next {
Visit::Begin(node) => {
self.path.push(node.data.clone());
false
}
Visit::Leaf(node) => {
self.path.push(node.data.clone());
true
}
Visit::End(_) => {
self.path.pop();
false
}
};
self.walk.forward();
if need_return {
let res = self.path.clone();
self.path.pop();
return Some(res);
}
}
None
}
}
#[allow(clippy::derive_ord_xor_partial_ord)]
#[derive(Debug, Clone, Identifiers, PartialEq, Eq, Ord)]
pub struct Node {
pub id: usize,
pub parent_id: Option<usize>,
pub label: String,
pub created: DateTime<Utc>,
pub closed: bool,
pub deleted: bool,
pub tags: String,
}
impl PartialOrd for Node {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.label.cmp(&other.label))
}
}
impl Default for Node {
fn default() -> Self {
Node {
id: 0,
parent_id: None,
label: String::new(),
created: Utc::now(),
closed: false,
deleted: false,
tags: String::new(),
}
}
}
impl ToString for Node {
fn to_string(&self) -> String {
self.label.to_owned()
}
}
impl Node {
pub fn get_tags(&self) -> Vec<String> {
self
.tags
.trim_matches(',')
.split(',')
.map(String::from)
.collect()
}
pub fn set_tags(&mut self, tags: &[String]) {
let tags: HashSet<String, RandomState> =
tags.iter().map(String::from).collect();
self.tags = format!(
",{},",
tags.iter().map(String::from).collect::<Vec<_>>().join(","),
);
}
pub fn add_tags(&mut self, tags: &[String]) {
let mut new_tags = self.get_tags();
for tag in tags {
new_tags.push(tag.to_owned());
}
self.set_tags(&new_tags);
}
pub fn remove_tags(&mut self, tags: &[String]) {
let cur_tags = self.get_tags();
let mut new_tags = Vec::new();
for tag in cur_tags {
if !tags.contains(&tag) {
new_tags.push(tag)
}
}
self.set_tags(&new_tags);
}
pub fn get_comma_tags(&self) -> Vec<String> {
self.get_tags().iter().map(|v| format!(",{},", v)).collect()
}
}
#[derive(Debug, Clone, Copy, Identifiers)]
pub struct Interval {
pub id: usize,
pub node_id: Option<usize>,
pub begin: DateTime<Utc>,
pub end: Option<DateTime<Utc>>,
pub deleted: bool,
pub closed: bool,
}
impl Default for Interval {
fn default() -> Self {
Interval {
id: 0,
node_id: None,
begin: Utc::now(),
end: None,
deleted: false,
closed: false,
}
}
}
impl ToString for Interval {
fn to_string(&self) -> String {
let end = match self.end {
Some(d) => d.to_rfc3339(),
None => "never".to_string(),
};
format!("[started: {} stopped: {}]", self.begin, end)
}
}
impl DBRoot for DB<'_> {}