Skip to main content

bear_rs/
model.rs

1use serde::{Deserialize, Serialize};
2
3/// A Bear note, populated from ZSFNOTE + related tables.
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Note {
6    /// ZUNIQUEIDENTIFIER: external ID used in CLI commands.
7    pub id: String,
8    /// Z_PK: internal SQLite primary key, used only for joins.
9    #[serde(skip)]
10    pub pk: i64,
11    /// ZTITLE: empty string when the column is NULL.
12    pub title: String,
13    /// ZTEXT: empty string when the note is encrypted or the column is NULL.
14    pub text: String,
15    /// Tag names, sorted alphabetically, from the Z_5TAGS join.
16    pub tags: Vec<String>,
17    /// ZCREATIONDATE converted to Unix timestamp (seconds).
18    pub created: i64,
19    /// ZMODIFICATIONDATE converted to Unix timestamp (seconds).
20    pub modified: i64,
21    pub trashed: bool,
22    pub archived: bool,
23    pub pinned: bool,
24    pub locked: bool,
25    pub encrypted: bool,
26    pub has_images: bool,
27    pub has_files: bool,
28    pub has_source_code: bool,
29    pub todo_completed: i64,
30    pub todo_incompleted: i64,
31    /// Populated on demand (e.g. for `show --fields attachments`).
32    pub attachments: Vec<Attachment>,
33    /// Pin contexts: "global" for the All Notes pin, or a tag name.
34    /// Populated on demand.
35    pub pinned_in_tags: Vec<String>,
36}
37
38impl Note {
39    /// SHA-256 hex digest of the note text.
40    pub fn hash(&self) -> String {
41        use sha2::{Digest, Sha256};
42        let digest = Sha256::digest(self.text.as_bytes());
43        format!("{digest:x}")
44    }
45
46    /// Byte length of the note text.
47    pub fn length(&self) -> i64 {
48        self.text.len() as i64
49    }
50}
51
52/// A Bear tag, from ZSFNOTETAG.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct Tag {
55    /// ZTITLE: full tag path, e.g. "work/meetings".
56    pub name: String,
57    /// Z_PK: internal primary key.
58    #[serde(skip)]
59    pub pk: i64,
60}
61
62/// A Bear note attachment, from ZSFNOTEFILE.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Attachment {
65    /// ZFILENAME.
66    pub filename: String,
67    /// ZFILESIZE in bytes.
68    pub size: i64,
69    /// ZUNIQUEIDENTIFIER: used to locate the file on disk.
70    pub uuid: String,
71}
72
73/// A pin record: a note pinned in a specific context.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct PinRecord {
76    /// The note's ZUNIQUEIDENTIFIER.
77    pub note_id: String,
78    /// "global" for All Notes pin; tag name for tag-scoped pin.
79    pub pin: String,
80}
81
82/// Where to insert content relative to existing body.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum InsertPosition {
85    Beginning,
86    End,
87}
88
89/// Tag position preference from Bear settings.
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
91pub enum TagPosition {
92    Top,
93    #[default]
94    Bottom,
95}
96
97/// Sort field for note lists.
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum SortField {
100    Pinned,
101    Modified,
102    Created,
103    Title,
104}
105
106impl SortField {
107    pub fn sql_column(&self) -> &'static str {
108        match self {
109            SortField::Pinned => "n.ZPINNED",
110            SortField::Modified => "n.ZMODIFICATIONDATE",
111            SortField::Created => "n.ZCREATIONDATE",
112            SortField::Title => "n.ZTITLE",
113        }
114    }
115}
116
117/// Sort direction.
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum SortDir {
120    Asc,
121    Desc,
122}