orbok_core/search_scope.rs
1//! Which files a search may return (RFC-060 §7).
2//!
3//! Plain data, shared by the query layer (`orbok-db` builds the SQL) and
4//! the search engine (`orbok-search` fills it in from the UI's filters and
5//! chosen folder). Both halves must agree, and a scope applied anywhere
6//! but the query is not a scope: post-filtering shrinks the result set
7//! below the requested limit and makes "no results" ambiguous
8//! (RFC-041 §25.5).
9
10/// One folder restriction: a registered source, optionally a subfolder of
11/// it, and whether nested folders count.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct FolderScope {
14 pub source_id: String,
15 /// Task 113: the canonical path of a folder **inside** the source, when
16 /// the search is limited to that subfolder. `None` searches the source
17 /// itself. A file is inside when this is a path-component prefix of its
18 /// path (`orbok_core::folder_cover`); "folder only" is then counted from
19 /// this folder, not from the source.
20 pub limit_path: Option<String>,
21 /// `false` restricts to files sitting directly in the folder (the
22 /// source, or `limit_path` when it is set; RFC-045 §6.3's "folder only").
23 pub include_subfolders: bool,
24}
25
26/// The scope of one search. Empty means unrestricted.
27#[derive(Debug, Clone, Default, PartialEq, Eq)]
28pub struct SearchScope {
29 /// Lowercased file extensions, without the dot. Empty means every
30 /// kind; the UI's kind filters expand to these.
31 pub extensions: Vec<String>,
32 pub folder: Option<FolderScope>,
33}
34
35impl SearchScope {
36 /// Whether this scope restricts anything at all.
37 pub fn is_unrestricted(&self) -> bool {
38 self.extensions.is_empty() && self.folder.is_none()
39 }
40}