1use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use mant_ir::{DocumentMeta, DocumentSource, SourceSpan};
7
8use crate::OutlineTrail;
9
10pub const DEFAULT_SEARCH_LIMIT: u32 = 100;
12
13#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
15#[serde(rename_all = "kebab-case")]
16pub enum SearchSyntax {
17 #[default]
19 Literal,
20 Regex,
22}
23
24#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
26#[serde(rename_all = "kebab-case")]
27pub enum SearchCase {
28 #[default]
30 Insensitive,
31 Sensitive,
33 Smart,
35}
36
37#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
39#[serde(rename_all = "kebab-case")]
40pub enum SearchScope {
41 #[default]
43 Visible,
44 Markdown,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
50#[serde(rename_all = "camelCase", deny_unknown_fields)]
51pub struct SearchQuery {
52 #[schemars(length(min = 1, max = 4096))]
54 pub pattern: String,
55 #[serde(default)]
57 pub syntax: SearchSyntax,
58 #[serde(default)]
60 pub case: SearchCase,
61 #[serde(default)]
63 pub scope: SearchScope,
64 #[serde(default)]
66 pub word: bool,
67 #[serde(default)]
69 #[schemars(range(max = 100))]
70 pub context_lines: u16,
71 #[serde(default = "default_search_limit")]
73 #[schemars(range(min = 1, max = 10000))]
74 pub limit: u32,
75 #[serde(default)]
77 pub offset: u32,
78}
79
80#[must_use]
81pub const fn default_search_limit() -> u32 {
83 DEFAULT_SEARCH_LIMIT
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
88pub enum SearchSchema {
89 #[serde(rename = "mant.search/v0.10")]
91 V0Dot10,
92}
93
94impl SearchSchema {
95 pub const ID: &'static str = "mant.search/v0.10";
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
101pub enum MarkdownSchema {
102 #[serde(rename = "mant.markdown/v1")]
104 V1,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
109#[serde(rename_all = "kebab-case")]
110pub enum SearchRenderFormat {
111 Markdown,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
117#[serde(rename_all = "kebab-case")]
118pub enum SearchRenderScope {
119 Full,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
125#[serde(rename_all = "camelCase")]
126pub struct SearchRender {
127 pub schema: MarkdownSchema,
129 pub format: SearchRenderFormat,
131 pub scope: SearchRenderScope,
133 #[schemars(range(min = 1, max = 1))]
135 pub line_base: u8,
136 #[schemars(range(min = 1, max = 1))]
138 pub column_base: u8,
139 pub line_count: u32,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
145#[serde(rename_all = "camelCase")]
146#[schemars(extend("$id" = "urn:mant:search:v0.10"))]
147pub struct QuerySearch {
148 pub schema: SearchSchema,
150 pub label: String,
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub source: Option<DocumentSource>,
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub meta: Option<DocumentMeta>,
158 pub query: SearchQuery,
160 pub render: SearchRender,
162 pub total: u32,
164 pub returned: u32,
166 pub offset: u32,
168 pub truncated: bool,
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub next_offset: Option<u32>,
173 pub matches: Vec<SearchHit>,
175}
176
177#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
179#[serde(rename_all = "camelCase")]
180pub struct SearchHit {
181 #[schemars(range(min = 1))]
183 pub ordinal: u32,
184 pub outline: OutlineTrail,
186 #[schemars(length(min = 1, max = 256))]
188 pub occurrences: Vec<SearchOccurrence>,
189 #[schemars(range(min = 1))]
191 pub occurrence_count: u32,
192 pub occurrences_truncated: bool,
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub node_source: Option<SourceSpan>,
197 pub preview: String,
199 #[serde(default, skip_serializing_if = "Vec::is_empty")]
201 pub context: Vec<SearchContextLine>,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
206#[serde(rename_all = "camelCase")]
207pub struct SearchOccurrence {
208 pub matched_text: String,
210 pub markdown: SearchMarkdownRange,
212 #[schemars(length(min = 1))]
214 pub line_ranges: Vec<SearchLineRange>,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
219#[serde(rename_all = "camelCase")]
220pub struct SearchLineRange {
221 #[schemars(range(min = 1))]
223 pub line: u32,
224 pub start_byte: u32,
226 pub end_byte: u32,
228}
229
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
232#[serde(rename_all = "camelCase")]
233pub struct SearchMarkdownRange {
234 pub start_byte: u64,
236 pub end_byte: u64,
238 #[schemars(range(min = 1))]
240 pub start_line: u32,
241 #[schemars(range(min = 1))]
243 pub start_column: u32,
244 #[schemars(range(min = 1))]
246 pub end_line: u32,
247 #[schemars(range(min = 1))]
249 pub end_column: u32,
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
254#[serde(rename_all = "camelCase")]
255pub struct SearchContextLine {
256 #[schemars(range(min = 1))]
258 pub line: u32,
259 pub text: String,
261 pub matched: bool,
263}