use std::collections::BTreeMap;
use rusqlite::{Connection, params};
use crate::TalonError;
use crate::links::ResolvedLink;
use crate::text::frontmatter::{FrontmatterValue, FrontmatterValueType, normalize_keyword};
pub fn upsert_links(
conn: &Connection,
vault_path: &str,
links: &[ResolvedLink],
) -> Result<(), TalonError> {
conn.execute("DELETE FROM links WHERE from_path = ?", [vault_path])
.map_err(|source| TalonError::Sqlite {
context: "delete old links",
source,
})?;
for link in links {
conn.execute(
"INSERT OR IGNORE INTO links
(from_path, to_path, raw_target, heading, alias)
VALUES (?, ?, ?, ?, ?)",
params![
vault_path,
link.to_path,
link.raw_target,
link.heading,
link.alias,
],
)
.map_err(|source| TalonError::Sqlite {
context: "insert link",
source,
})?;
}
Ok(())
}
pub fn upsert_aliases(
conn: &Connection,
note_id: i64,
aliases: &[String],
) -> Result<(), TalonError> {
conn.execute("DELETE FROM note_aliases WHERE note_id = ?", [note_id])
.map_err(|source| TalonError::Sqlite {
context: "delete old aliases",
source,
})?;
for alias in aliases {
let norm = normalize_keyword(alias);
conn.execute(
"INSERT INTO note_aliases (note_id, alias, alias_norm) VALUES (?, ?, ?)",
params![note_id, alias, norm],
)
.map_err(|source| TalonError::Sqlite {
context: "insert alias",
source,
})?;
}
Ok(())
}
pub fn upsert_tags(conn: &Connection, note_id: i64, tags: &[String]) -> Result<(), TalonError> {
conn.execute("DELETE FROM note_tags WHERE note_id = ?", [note_id])
.map_err(|source| TalonError::Sqlite {
context: "delete old tags",
source,
})?;
for tag in tags {
let norm = normalize_keyword(tag);
conn.execute(
"INSERT INTO note_tags (note_id, tag, tag_norm) VALUES (?, ?, ?)",
params![note_id, tag, norm],
)
.map_err(|source| TalonError::Sqlite {
context: "insert tag",
source,
})?;
}
Ok(())
}
fn flatten_frontmatter(
value: &FrontmatterValue,
prefix: &str,
out: &mut Vec<(String, String, FrontmatterValueType)>,
) {
let key = if prefix.is_empty() {
String::from("value")
} else {
prefix.to_string()
};
match value {
FrontmatterValue::String(s) => out.push((key, s.clone(), FrontmatterValueType::String)),
FrontmatterValue::Date(s) => out.push((key, s.clone(), FrontmatterValueType::Date)),
FrontmatterValue::Number(n) => out.push((key, n.to_string(), FrontmatterValueType::Number)),
FrontmatterValue::Boolean(b) => out.push((key, b.to_string(), FrontmatterValueType::Bool)),
FrontmatterValue::List(items) => {
for item in items {
out.push((key.clone(), item.clone(), FrontmatterValueType::List));
}
}
}
}
pub fn upsert_frontmatter_fields(
conn: &Connection,
note_id: i64,
frontmatter: &BTreeMap<String, FrontmatterValue>,
) -> Result<(), TalonError> {
conn.execute(
"DELETE FROM note_frontmatter_fields WHERE note_id = ?",
[note_id],
)
.map_err(|source| TalonError::Sqlite {
context: "delete old frontmatter fields",
source,
})?;
let mut flat: Vec<(String, String, FrontmatterValueType)> = Vec::new();
for (key, value) in frontmatter {
flatten_frontmatter(value, key, &mut flat);
}
for (field, value, value_type) in flat {
let norm = normalize_keyword(&value);
conn.execute(
"INSERT INTO note_frontmatter_fields (note_id, field, value, value_type, value_norm)
VALUES (?, ?, ?, ?, ?)",
params![note_id, field, value, value_type.as_db_str(), norm],
)
.map_err(|source| TalonError::Sqlite {
context: "insert frontmatter field",
source,
})?;
}
Ok(())
}