use rusqlite::types::{FromSql, FromSqlResult, ToSqlOutput, ValueRef};
use rusqlite::{Row, ToSql};
use std::collections::{HashMap, HashSet};
use time::OffsetDateTime;
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub(crate) struct CoreDbId(pub(crate) i64);
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub(crate) struct CoreDbNoteId(pub(crate) CoreDbId);
impl CoreDbNoteId {
}
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct NoteId(String);
impl NoteId {
pub fn new(uuid: String) -> Self {
NoteId(uuid)
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_string(self) -> String {
self.0
}
}
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct TagId(pub(crate) CoreDbId);
impl TagId {
pub fn new(id: i64) -> Self {
TagId(CoreDbId(id))
}
pub fn as_i64(self) -> i64 {
self.0.0
}
}
impl FromSql for CoreDbId {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(Self(value.as_i64()?))
}
}
impl FromSql for CoreDbNoteId {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(Self(FromSql::column_result(value)?))
}
}
impl FromSql for TagId {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(Self(FromSql::column_result(value)?))
}
}
impl ToSql for CoreDbId {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
self.0.to_sql()
}
}
impl ToSql for CoreDbNoteId {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
self.0.to_sql()
}
}
#[derive(Debug, Clone)]
pub struct Tag {
id: TagId,
name: Option<String>,
modified: Option<OffsetDateTime>,
}
impl Tag {
pub fn id(&self) -> TagId {
self.id
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn modified(&self) -> Option<OffsetDateTime> {
self.modified
}
}
pub(crate) fn tag_from_row(row: &Row) -> rusqlite::Result<Tag> {
Ok(Tag {
id: row.get("id")?,
name: row.get("name")?,
modified: row.get("modified")?,
})
}
#[derive(Debug)]
pub struct TagsMap {
pub(crate) tags: HashMap<TagId, Tag>,
}
impl TagsMap {
pub fn get(
&self,
tag_id: &TagId,
) -> Option<&Tag> {
self.tags.get(tag_id)
}
pub fn count(&self) -> usize {
self.tags.len()
}
pub fn iter(&self) -> impl Iterator<Item = &Tag> {
self.tags.values()
}
pub fn names(
&self,
tag_ids: &HashSet<TagId>,
) -> HashSet<String> {
tag_ids
.iter()
.filter_map(|id| self.get(id).and_then(|t| t.name.clone()))
.collect()
}
}
#[derive(Debug)]
pub struct Note {
_core_db_id: CoreDbNoteId,
id: NoteId,
title: String,
content: Option<String>,
modified: OffsetDateTime,
created: OffsetDateTime,
is_pinned: bool,
}
impl Note {
pub(crate) fn _core_db_id(&self) -> CoreDbNoteId {
self._core_db_id
}
pub fn id(&self) -> &NoteId {
&self.id
}
pub fn title(&self) -> &str {
&self.title
}
pub fn content(&self) -> Option<&str> {
self.content.as_deref()
}
pub fn modified(&self) -> OffsetDateTime {
self.modified
}
pub fn created(&self) -> OffsetDateTime {
self.created
}
pub fn is_pinned(&self) -> bool {
self.is_pinned
}
}
pub(crate) fn note_from_row(row: &Row) -> rusqlite::Result<Note> {
Ok(Note {
_core_db_id: row.get("core_db_id")?,
id: NoteId::new(row.get("id")?),
title: row.get("title")?,
content: row.get("content")?,
created: row.get("created")?,
modified: row.get("modified")?,
is_pinned: row.get("is_pinned")?,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_note_id_construction() {
let uuid = "ABC123-DEF456-GHI789".to_string();
let note_id = NoteId::new(uuid.clone());
assert_eq!(note_id.as_str(), &uuid);
let uuid2 = "note-uuid-12345".to_string();
let note_id2 = NoteId::new(uuid2.clone());
assert_eq!(note_id2.into_string(), uuid2);
}
#[test]
fn test_bear_tag_id_construction() {
let tag_id = TagId::new(42);
assert_eq!(tag_id.as_i64(), 42);
let id_value = 12345;
let tag_id = TagId::new(id_value);
assert_eq!(tag_id.as_i64(), id_value);
}
#[test]
fn test_note_id_equality() {
let id1 = NoteId::new("ABC123".to_string());
let id2 = NoteId::new("ABC123".to_string());
let id3 = NoteId::new("DEF456".to_string());
assert_eq!(id1, id2);
assert_ne!(id1, id3);
let mut set = std::collections::HashSet::new();
set.insert(id1);
assert!(set.contains(&id2));
assert!(!set.contains(&id3));
}
#[test]
fn test_bear_tag_id_equality() {
let id1 = TagId::new(42);
let id2 = TagId::new(42);
let id3 = TagId::new(43);
assert_eq!(id1, id2);
assert_ne!(id1, id3);
let mut set = std::collections::HashSet::new();
set.insert(id1);
assert!(set.contains(&id2));
assert!(!set.contains(&id3));
}
}