pub struct SqliteStore { /* private fields */ }Implementations§
Source§impl SqliteStore
impl SqliteStore
Sourcepub fn open_ro() -> Result<Self>
pub fn open_ro() -> Result<Self>
Open read-only (sufficient for all read commands).
Examples found in repository?
examples/smoke.rs (line 15)
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}Sourcepub fn open_rw() -> Result<Self>
pub fn open_rw() -> Result<Self>
Open read-write (required for any mutation).
Examples found in repository?
examples/smoke.rs (line 80)
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}Sourcepub fn resolve_note(
&self,
id: Option<&str>,
title: Option<&str>,
include_trashed: bool,
include_archived: bool,
) -> Result<Note>
pub fn resolve_note( &self, id: Option<&str>, title: Option<&str>, include_trashed: bool, include_archived: bool, ) -> Result<Note>
Resolve a note by ZUNIQUEIDENTIFIER or case-insensitive title. If title matches multiple notes, picks the most recently modified.
Sourcepub fn list_notes(&self, input: &ListInput<'_>) -> Result<Vec<Note>>
pub fn list_notes(&self, input: &ListInput<'_>) -> Result<Vec<Note>>
Examples found in repository?
examples/smoke.rs (line 17)
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}Sourcepub fn get_note(
&self,
id: Option<&str>,
title: Option<&str>,
include_attachments: bool,
include_pins: bool,
) -> Result<Note>
pub fn get_note( &self, id: Option<&str>, title: Option<&str>, include_attachments: bool, include_pins: bool, ) -> Result<Note>
Examples found in repository?
examples/smoke.rs (line 30)
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}Sourcepub fn cat_note(
&self,
id: Option<&str>,
title: Option<&str>,
offset: Option<usize>,
limit: Option<usize>,
) -> Result<String>
pub fn cat_note( &self, id: Option<&str>, title: Option<&str>, offset: Option<usize>, limit: Option<usize>, ) -> Result<String>
Examples found in repository?
examples/smoke.rs (line 47)
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}Sourcepub fn search_notes(
&self,
query: &str,
limit: Option<usize>,
) -> Result<Vec<Note>>
pub fn search_notes( &self, query: &str, limit: Option<usize>, ) -> Result<Vec<Note>>
Examples found in repository?
examples/smoke.rs (line 53)
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}Sourcepub fn search_in_note(
&self,
id: Option<&str>,
title: Option<&str>,
string: &str,
ignore_case: bool,
) -> Result<Vec<(usize, String)>>
pub fn search_in_note( &self, id: Option<&str>, title: Option<&str>, string: &str, ignore_case: bool, ) -> Result<Vec<(usize, String)>>
Returns (line_number, line_text) pairs for lines containing string.
Examples found in repository?
examples/smoke.rs (line 222)
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}Examples found in repository?
examples/smoke.rs (line 68)
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}Sourcepub fn list_pins(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
) -> Result<Vec<PinRecord>>
pub fn list_pins( &self, note_id: Option<&str>, note_title: Option<&str>, ) -> Result<Vec<PinRecord>>
Examples found in repository?
examples/smoke.rs (line 71)
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}Sourcepub fn list_attachments(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
) -> Result<Vec<Attachment>>
pub fn list_attachments( &self, note_id: Option<&str>, note_title: Option<&str>, ) -> Result<Vec<Attachment>>
Examples found in repository?
examples/smoke.rs (line 74)
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}Sourcepub fn read_attachment(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
filename: &str,
) -> Result<Vec<u8>>
pub fn read_attachment( &self, note_id: Option<&str>, note_title: Option<&str>, filename: &str, ) -> Result<Vec<u8>>
Examples found in repository?
examples/smoke.rs (line 286)
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}Sourcepub fn create_note(
&self,
text: &str,
tags: &[&str],
if_not_exists: bool,
) -> Result<Note>
pub fn create_note( &self, text: &str, tags: &[&str], if_not_exists: bool, ) -> Result<Note>
Examples found in repository?
examples/smoke.rs (lines 82-86)
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}Sourcepub fn append_to_note(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
content: &str,
position: InsertPosition,
update_modified: bool,
tag_position: TagPosition,
) -> Result<()>
pub fn append_to_note( &self, note_id: Option<&str>, note_title: Option<&str>, content: &str, position: InsertPosition, update_modified: bool, tag_position: TagPosition, ) -> Result<()>
Examples found in repository?
examples/smoke.rs (lines 135-142)
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}Sourcepub fn write_note(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
content: &str,
base_hash: Option<&str>,
) -> Result<()>
pub fn write_note( &self, note_id: Option<&str>, note_title: Option<&str>, content: &str, base_hash: Option<&str>, ) -> Result<()>
Examples found in repository?
examples/smoke.rs (lines 104-109)
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}Sourcepub fn edit_note(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
ops: &[EditOp],
) -> Result<()>
pub fn edit_note( &self, note_id: Option<&str>, note_title: Option<&str>, ops: &[EditOp], ) -> Result<()>
Examples found in repository?
examples/smoke.rs (lines 169-180)
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}Sourcepub fn trash_note(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
) -> Result<()>
pub fn trash_note( &self, note_id: Option<&str>, note_title: Option<&str>, ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 307)
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}Sourcepub fn archive_note(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
) -> Result<()>
pub fn archive_note( &self, note_id: Option<&str>, note_title: Option<&str>, ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 301)
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}Sourcepub fn restore_note(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
) -> Result<()>
pub fn restore_note( &self, note_id: Option<&str>, note_title: Option<&str>, ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 304)
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}Examples found in repository?
examples/smoke.rs (line 233)
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}Examples found in repository?
examples/smoke.rs (line 239)
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}Sourcepub fn rename_tag(
&self,
old_name: &str,
new_name: &str,
force: bool,
) -> Result<()>
pub fn rename_tag( &self, old_name: &str, new_name: &str, force: bool, ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 245)
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}Sourcepub fn delete_tag(&self, name: &str) -> Result<()>
pub fn delete_tag(&self, name: &str) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 251)
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}Sourcepub fn add_pins(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
contexts: &[&str],
) -> Result<()>
pub fn add_pins( &self, note_id: Option<&str>, note_title: Option<&str>, contexts: &[&str], ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 260)
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}Sourcepub fn remove_pins(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
contexts: &[&str],
) -> Result<()>
pub fn remove_pins( &self, note_id: Option<&str>, note_title: Option<&str>, contexts: &[&str], ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 266)
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}Sourcepub fn add_attachment(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
filename: &str,
data: &[u8],
) -> Result<()>
pub fn add_attachment( &self, note_id: Option<&str>, note_title: Option<&str>, filename: &str, data: &[u8], ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 276)
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}Sourcepub fn delete_attachment(
&self,
note_id: Option<&str>,
note_title: Option<&str>,
filename: &str,
) -> Result<()>
pub fn delete_attachment( &self, note_id: Option<&str>, note_title: Option<&str>, filename: &str, ) -> Result<()>
Examples found in repository?
examples/smoke.rs (line 290)
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}Auto Trait Implementations§
impl !Freeze for SqliteStore
impl !RefUnwindSafe for SqliteStore
impl Send for SqliteStore
impl !Sync for SqliteStore
impl Unpin for SqliteStore
impl UnsafeUnpin for SqliteStore
impl !UnwindSafe for SqliteStore
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