1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// src/application/services/bookmark_service.rs
use crate::application::error::ApplicationResult;
use crate::domain::bookmark::Bookmark;
use crate::domain::repositories::query::{BookmarkQuery, SortDirection};
use crate::domain::search::{
HybridSearch, HybridSearchResult, SemanticSearch, SemanticSearchResult,
};
use crate::domain::tag::Tag;
use std::collections::HashSet;
use std::fmt::Debug;
/// Service interface for bookmark-related operations
pub trait BookmarkService: Send + Sync + Debug {
/// Add a new bookmark
///
/// `opener`: optional custom open command. `Some("")` is treated as `None`
/// so callers can pass through CLI-flag values without normalising first.
fn add_bookmark(
&self,
url: &str,
title: Option<&str>,
description: Option<&str>,
tags: Option<&HashSet<Tag>>,
fetch_metadata: bool,
embeddable: bool,
opener: Option<&str>,
) -> ApplicationResult<Bookmark>;
/// Delete a bookmark by ID
fn delete_bookmark(&self, id: i32) -> ApplicationResult<bool>;
/// Get a bookmark by ID
fn get_bookmark(&self, id: i32) -> ApplicationResult<Option<Bookmark>>;
fn set_bookmark_embeddable(&self, id: i32, embeddable: bool) -> ApplicationResult<Bookmark>;
/// Update a bookmark's title and description
fn update_bookmark(
&self,
bookmark: Bookmark,
force_embedding: bool,
) -> ApplicationResult<Bookmark>;
/// Add tags to a bookmark
fn add_tags_to_bookmark(&self, id: i32, tags: &HashSet<Tag>) -> ApplicationResult<Bookmark>;
/// Remove tags from a bookmark
fn remove_tags_from_bookmark(
&self,
id: i32,
tags: &HashSet<Tag>,
) -> ApplicationResult<Bookmark>;
/// Replace all tags on a bookmark
fn replace_bookmark_tags(&self, id: i32, tags: &HashSet<Tag>) -> ApplicationResult<Bookmark>;
fn search_bookmarks_by_text(&self, query: &str) -> ApplicationResult<Vec<Bookmark>>;
// Add a convenience method to create a query for text search
// Replace the complex search_bookmarks method with a simpler interface
fn search_bookmarks(&self, query: &BookmarkQuery) -> ApplicationResult<Vec<Bookmark>>;
/// Perform semantic search with the given parameters
fn semantic_search(
&self,
search: &SemanticSearch,
) -> ApplicationResult<Vec<SemanticSearchResult>>;
/// Perform hybrid search combining FTS and semantic search with RRF fusion
fn hybrid_search(&self, search: &HybridSearch) -> ApplicationResult<Vec<HybridSearchResult>>;
/// Get bookmark by URL
fn get_bookmark_by_url(&self, url: &str) -> ApplicationResult<Option<Bookmark>>;
/// Get all bookmarks
fn get_all_bookmarks(
&self,
sort_direction: Option<SortDirection>,
limit: Option<usize>,
) -> ApplicationResult<Vec<Bookmark>>;
/// Get random bookmarks
fn get_random_bookmarks(&self, count: usize) -> ApplicationResult<Vec<Bookmark>>;
/// Get bookmarks for forced backfill (all embeddable bookmarks except those with _imported_ tag)
fn get_bookmarks_for_forced_backfill(&self) -> ApplicationResult<Vec<Bookmark>>;
/// Check if bookmarks need embedding backfilling
fn get_bookmarks_without_embeddings(&self) -> ApplicationResult<Vec<Bookmark>>;
/// Record that a bookmark was accessed
fn record_bookmark_access(&self, id: i32) -> ApplicationResult<Bookmark>;
/// Bulk-create bookmarks from a JSON array file. Stores full content (url, title,
/// description, tags). Skips bookmarks whose URL already exists. Does NOT support updates.
/// Use case: agent bulk imports, migrations, seeding a database.
fn load_json_bookmarks(
&self,
path: &str,
dry_run: bool,
embeddable: bool,
) -> ApplicationResult<usize>;
/// Import files from directories with frontmatter metadata. Stores full content AND
/// tracks source file (path, mtime, hash) for smart editing and change detection.
/// Supports incremental updates and orphan deletion. Use case: indexing script/doc
/// directories while keeping files as the source of truth.
fn import_files(
&self,
paths: &[String],
update: bool,
delete_missing: bool,
dry_run: bool,
verbose: bool,
base_path_name: Option<&str>,
embeddable: bool,
) -> ApplicationResult<(usize, usize, usize)>; // Returns (added, updated, deleted)
}