use crate::error::Result;
use crate::project_config::ProjectConfig;
use crate::refs;
use super::{g_i64, g_str, IntoParam, Store};
pub type Baseline = Vec<(String, i64, String)>;
impl Store {
pub fn baseline(&mut self) -> Result<Baseline> {
let (_, rows) = self.select("SELECT dkey, id, title FROM docs", vec![])?;
Ok(rows
.iter()
.filter_map(|r| {
let dkey = g_i64(&r[0])?;
let id = g_str(&r[1])?;
let title = g_str(&r[2]).unwrap_or_default();
Some((id, dkey, title))
})
.collect())
}
pub fn cascade_removals(&mut self, pcfg: &ProjectConfig, baseline: &Baseline) -> Result<()> {
let present = self.doc_ids()?;
let removed: Vec<&(String, i64, String)> = baseline
.iter()
.filter(|(id, _, _)| !present.contains(id))
.collect();
if removed.is_empty() {
return Ok(());
}
for (_, dkey, _) in &removed {
self.delete_doc(*dkey)?;
}
for (id, _, title) in &removed {
self.reserve_id(id, title)?;
self.strike_inbound(pcfg, id, &refs::strike(title))?;
}
Ok(())
}
fn reserve_id(&mut self, id: &str, title: &str) -> Result<()> {
let already = self
.scalar(
"SELECT 1 FROM retired WHERE id = $1 LIMIT 1",
vec![id.into_param()],
)?
.is_some();
if already {
return Ok(());
}
self.retire_id(id, title)
}
fn strike_inbound(&mut self, pcfg: &ProjectConfig, id: &str, struck: &str) -> Result<()> {
let (_, rows) = self.select(
"SELECT DISTINCT dkey FROM relations WHERE ref_id = $1",
vec![id.into_param()],
)?;
let dkeys: Vec<i64> = rows.iter().filter_map(|r| g_i64(&r[0])).collect();
for k in dkeys {
let mut doc = self.doc(k)?;
let mut changed = false;
for field in refs::RELATION_FIELDS {
let mut entries = refs::parse_in(&doc.frontmatter, field);
if let Some(e) = entries.iter_mut().find(|(i, _)| i == id) {
if e.1 != struck {
e.1 = struck.to_string();
refs::set_in(&mut doc.frontmatter, field, &entries);
changed = true;
}
}
}
if changed {
self.put_doc(pcfg, Some(k), &doc)?;
}
}
Ok(())
}
}