pub struct Note {Show 19 fields
pub id: String,
pub pk: i64,
pub title: String,
pub text: String,
pub tags: Vec<String>,
pub created: i64,
pub modified: i64,
pub trashed: bool,
pub archived: bool,
pub pinned: bool,
pub locked: bool,
pub encrypted: bool,
pub has_images: bool,
pub has_files: bool,
pub has_source_code: bool,
pub todo_completed: i64,
pub todo_incompleted: i64,
pub attachments: Vec<Attachment>,
pub pinned_in_tags: Vec<String>,
}Expand description
A Bear note, populated from ZSFNOTE + related tables.
Fields§
§id: StringZUNIQUEIDENTIFIER: external ID used in CLI commands.
pk: i64Z_PK: internal SQLite primary key, used only for joins.
title: StringZTITLE: empty string when the column is NULL.
text: StringZTEXT: empty string when the note is encrypted or the column is NULL.
Tag names, sorted alphabetically, from the Z_5TAGS join.
created: i64ZCREATIONDATE converted to Unix timestamp (seconds).
modified: i64ZMODIFICATIONDATE converted to Unix timestamp (seconds).
trashed: bool§archived: bool§pinned: bool§locked: bool§encrypted: bool§has_images: bool§has_files: bool§has_source_code: bool§todo_completed: i64§todo_incompleted: i64§attachments: Vec<Attachment>Populated on demand (e.g. for show --fields attachments).
Pin contexts: “global” for the All Notes pin, or a tag name. Populated on demand.
Implementations§
Source§impl Note
impl Note
Sourcepub fn hash(&self) -> String
pub fn hash(&self) -> String
SHA-256 hex digest of the note text.
Examples found in repository?
examples/smoke.rs (line 118)
11fn main() -> anyhow::Result<()> {
12 // ── list / search ─────────────────────────────────────────────────────────
13
14 section("reads");
15 let store = SqliteStore::open_ro()?;
16
17 let notes = store.list_notes(&ListInput::default())?;
18 println!(" {:<30} {} notes", "list_notes (default)", notes.len());
19 assert!(!notes.is_empty(), "expected at least one note");
20
21 let limited = store.list_notes(&ListInput {
22 limit: Some(1),
23 include_tags: true,
24 ..Default::default()
25 })?;
26 println!(" {:<30} {} note", "list_notes (limit=1)", limited.len());
27 assert_eq!(limited.len(), 1);
28
29 let first = ¬es[0];
30 let note = store.get_note(Some(&first.id), None, true, true)?;
31 println!(
32 " {:<30} title={:?} tags={:?} attachments={} pins={:?}",
33 "get_note (id)",
34 note.title,
35 note.tags,
36 note.attachments.len(),
37 note.pinned_in_tags
38 );
39
40 let by_title = store.get_note(None, Some(¬e.title), false, false)?;
41 assert_eq!(
42 by_title.id, note.id,
43 "get_note by title returned wrong note"
44 );
45 println!(" {:<30} ok", "get_note (--title)");
46
47 let cat = store.cat_note(Some(&first.id), None, None, Some(40))?;
48 println!(" {:<30} {:?}", "cat_note (limit=40)", cat);
49
50 let cat_offset = store.cat_note(Some(&first.id), None, Some(10), Some(20))?;
51 println!(" {:<30} {:?}", "cat_note (offset=10 limit=20)", cat_offset);
52
53 let search_results = store.search_notes("@todo", None)?;
54 println!(
55 " {:<30} {} notes",
56 "search_notes (@todo)",
57 search_results.len()
58 );
59
60 let search_limited = store.search_notes("@todo", Some(1))?;
61 assert!(search_limited.len() <= 1);
62 println!(
63 " {:<30} {} note",
64 "search_notes (@todo limit=1)",
65 search_limited.len()
66 );
67
68 let tags = store.list_tags(None, None)?;
69 println!(" {:<30} {} tags", "list_tags (all)", tags.len());
70
71 let pins = store.list_pins(None, None)?;
72 println!(" {:<30} {} pins", "list_pins (all)", pins.len());
73
74 let atts = store.list_attachments(Some(&first.id), None)?;
75 println!(" {:<30} {} attachments", "list_attachments", atts.len());
76
77 // ── setup: create note ────────────────────────────────────────────────────
78
79 section("create / write");
80 let store = SqliteStore::open_rw()?;
81
82 let note = store.create_note(
83 "# bear-rs smoke test\n\nOriginal body.",
84 &["bear-rs-test"],
85 false,
86 )?;
87 println!(" {:<30} id={}", "create_note", note.id);
88 assert_eq!(note.title, "bear-rs smoke test");
89 assert_eq!(note.tags, vec!["bear-rs-test"]);
90
91 // if_not_exists returns existing note
92 let same = store.create_note("# bear-rs smoke test\n\nDuplicate.", &[], true)?;
93 assert_eq!(
94 same.id, note.id,
95 "if_not_exists should return existing note"
96 );
97 println!(
98 " {:<30} ok (returned same id)",
99 "create_note if_not_exists"
100 );
101
102 // ── write_note ────────────────────────────────────────────────────────────
103
104 store.write_note(
105 Some(¬e.id),
106 None,
107 "# bear-rs smoke test\n\nRewritten.",
108 None,
109 )?;
110 let after = store.get_note(Some(¬e.id), None, false, false)?;
111 assert!(
112 after.text.contains("Rewritten"),
113 "write_note did not update text"
114 );
115 println!(" {:<30} {:?}", "write_note", after.text);
116
117 // write_note with correct base hash
118 let hash = after.hash();
119 store.write_note(
120 Some(¬e.id),
121 None,
122 "# bear-rs smoke test\n\nHash-guarded write.",
123 Some(&hash),
124 )?;
125 println!(" {:<30} ok", "write_note (base hash)");
126
127 // write_note with wrong hash should fail
128 let bad = store.write_note(Some(¬e.id), None, "bad", Some("deadbeef"));
129 assert!(bad.is_err(), "write_note with wrong hash should fail");
130 println!(" {:<30} correctly rejected", "write_note (bad hash)");
131
132 // ── append ────────────────────────────────────────────────────────────────
133
134 section("append");
135 store.append_to_note(
136 Some(¬e.id),
137 None,
138 "Appended at end.",
139 InsertPosition::End,
140 true,
141 Default::default(),
142 )?;
143 let after = store.get_note(Some(¬e.id), None, false, false)?;
144 assert!(
145 after.text.ends_with("Appended at end."),
146 "append End failed: {:?}",
147 after.text
148 );
149 println!(" {:<30} {:?}", "append (end)", after.text);
150
151 store.append_to_note(
152 Some(¬e.id),
153 None,
154 "Prepended line.\n\n",
155 InsertPosition::Beginning,
156 true,
157 Default::default(),
158 )?;
159 let after = store.get_note(Some(¬e.id), None, false, false)?;
160 assert!(
161 after.text.contains("Prepended line."),
162 "append Beginning failed"
163 );
164 println!(" {:<30} {:?}", "append (beginning)", after.text);
165
166 // ── edit ──────────────────────────────────────────────────────────────────
167
168 section("edit");
169 store.edit_note(
170 Some(¬e.id),
171 None,
172 &[EditOp {
173 at: "Appended at end.".into(),
174 replace: Some("Replaced text.".into()),
175 insert: None,
176 all: false,
177 ignore_case: false,
178 word: false,
179 }],
180 )?;
181 let after = store.get_note(Some(¬e.id), None, false, false)?;
182 assert!(after.text.contains("Replaced text."), "edit replace failed");
183 println!(" {:<30} {:?}", "edit (replace)", after.text);
184
185 store.edit_note(
186 Some(¬e.id),
187 None,
188 &[EditOp {
189 at: "Prepended".into(),
190 replace: Some("PREPENDED".into()),
191 insert: None,
192 all: false,
193 ignore_case: true,
194 word: false,
195 }],
196 )?;
197 let after = store.get_note(Some(¬e.id), None, false, false)?;
198 assert!(after.text.contains("PREPENDED"), "edit ignore_case failed");
199 println!(" {:<30} {:?}", "edit (ignore_case)", after.text);
200
201 // edit insert (append to match)
202 store.edit_note(
203 Some(¬e.id),
204 None,
205 &[EditOp {
206 at: "Replaced text.".into(),
207 replace: None,
208 insert: Some(" (inserted)".into()),
209 all: false,
210 ignore_case: false,
211 word: false,
212 }],
213 )?;
214 let after = store.get_note(Some(¬e.id), None, false, false)?;
215 assert!(
216 after.text.contains("Replaced text. (inserted)"),
217 "edit insert failed"
218 );
219 println!(" {:<30} {:?}", "edit (insert)", after.text);
220
221 // search_in_note
222 let matches = store.search_in_note(Some(¬e.id), None, "PREPENDED", false)?;
223 assert!(!matches.is_empty(), "search_in_note returned no matches");
224 println!(
225 " {:<30} {} line(s) matched",
226 "search_in_note",
227 matches.len()
228 );
229
230 // ── tags ──────────────────────────────────────────────────────────────────
231
232 section("tags");
233 store.add_tags(Some(¬e.id), None, &["bear-rs-extra", "bear-rs-extra2"])?;
234 let note_tags = store.list_tags(Some(¬e.id), None)?;
235 let tag_names: Vec<&str> = note_tags.iter().map(|t| t.name.as_str()).collect();
236 assert!(tag_names.contains(&"bear-rs-extra"), "add_tags failed");
237 println!(" {:<30} {:?}", "add_tags", tag_names);
238
239 store.remove_tags(Some(¬e.id), None, &["bear-rs-extra2"])?;
240 let note_tags = store.list_tags(Some(¬e.id), None)?;
241 let tag_names: Vec<&str> = note_tags.iter().map(|t| t.name.as_str()).collect();
242 assert!(!tag_names.contains(&"bear-rs-extra2"), "remove_tags failed");
243 println!(" {:<30} {:?}", "remove_tags", tag_names);
244
245 store.rename_tag("bear-rs-extra", "bear-rs-renamed", false)?;
246 let note_tags = store.list_tags(Some(¬e.id), None)?;
247 let tag_names: Vec<&str> = note_tags.iter().map(|t| t.name.as_str()).collect();
248 assert!(tag_names.contains(&"bear-rs-renamed"), "rename_tag failed");
249 println!(" {:<30} {:?}", "rename_tag", tag_names);
250
251 store.delete_tag("bear-rs-renamed")?;
252 let note_tags = store.list_tags(Some(¬e.id), None)?;
253 let tag_names: Vec<&str> = note_tags.iter().map(|t| t.name.as_str()).collect();
254 assert!(!tag_names.contains(&"bear-rs-renamed"), "delete_tag failed");
255 println!(" {:<30} {:?}", "delete_tag", tag_names);
256
257 // ── pins ──────────────────────────────────────────────────────────────────
258
259 section("pins");
260 store.add_pins(Some(¬e.id), None, &["global"])?;
261 let note_pins = store.list_pins(Some(¬e.id), None)?;
262 let pin_names: Vec<&str> = note_pins.iter().map(|p| p.pin.as_str()).collect();
263 assert!(pin_names.contains(&"global"), "add_pins global failed");
264 println!(" {:<30} {:?}", "add_pins (global)", pin_names);
265
266 store.remove_pins(Some(¬e.id), None, &["global"])?;
267 let note_pins = store.list_pins(Some(¬e.id), None)?;
268 let pin_names: Vec<&str> = note_pins.iter().map(|p| p.pin.as_str()).collect();
269 assert!(!pin_names.contains(&"global"), "remove_pins global failed");
270 println!(" {:<30} {:?}", "remove_pins (global)", pin_names);
271
272 // ── attachments ───────────────────────────────────────────────────────────
273
274 section("attachments");
275 let content = b"hello from bear-rs attachment test";
276 store.add_attachment(Some(¬e.id), None, "bear-rs-test.txt", content)?;
277 let atts = store.list_attachments(Some(¬e.id), None)?;
278 let found = atts.iter().find(|a| a.filename == "bear-rs-test.txt");
279 assert!(found.is_some(), "add_attachment: file not in list");
280 println!(
281 " {:<30} {:?}",
282 "add_attachment",
283 atts.iter().map(|a| &a.filename).collect::<Vec<_>>()
284 );
285
286 let bytes = store.read_attachment(Some(¬e.id), None, "bear-rs-test.txt")?;
287 assert_eq!(bytes, content, "read_attachment returned wrong bytes");
288 println!(" {:<30} {} bytes", "read_attachment", bytes.len());
289
290 store.delete_attachment(Some(¬e.id), None, "bear-rs-test.txt")?;
291 let atts = store.list_attachments(Some(¬e.id), None)?;
292 assert!(
293 atts.iter().all(|a| a.filename != "bear-rs-test.txt"),
294 "delete_attachment failed"
295 );
296 println!(" {:<30} ok", "delete_attachment");
297
298 // ── trash / archive / restore ─────────────────────────────────────────────
299
300 section("trash / archive / restore");
301 store.archive_note(Some(¬e.id), None)?;
302 println!(" {:<30} ok", "archive_note");
303
304 store.restore_note(Some(¬e.id), None)?;
305 println!(" {:<30} ok", "restore_note (from archive)");
306
307 store.trash_note(Some(¬e.id), None)?;
308 println!(" {:<30} ok", "trash_note");
309
310 store.restore_note(Some(¬e.id), None)?;
311 println!(" {:<30} ok", "restore_note (from trash)");
312
313 // ── cleanup ───────────────────────────────────────────────────────────────
314
315 section("cleanup");
316 store.trash_note(Some(¬e.id), None)?;
317 println!(" test note trashed (id={})", note.id);
318
319 // clean up the remaining bear-rs-test tag
320 let _ = store.delete_tag("bear-rs-test");
321
322 println!("\n✓ all checks passed");
323 Ok(())
324}Trait Implementations§
Source§impl<'de> Deserialize<'de> for Note
impl<'de> Deserialize<'de> for Note
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
Source§impl From<&Note> for ExportNote
impl From<&Note> for ExportNote
Source§impl From<Note> for ExportNote
impl From<Note> for ExportNote
Auto Trait Implementations§
impl Freeze for Note
impl RefUnwindSafe for Note
impl Send for Note
impl Sync for Note
impl Unpin for Note
impl UnsafeUnpin for Note
impl UnwindSafe for Note
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more