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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//! Query envelope combining one structured input with optional tldr content.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use mant_ir::{ResolvedContent, TldrDocument};
use crate::{
ContentSelector, DocumentAddress, DocumentResponse, EntryProjection,
MAX_DOCUMENT_SELECTOR_CHARS, MAX_MANUAL_SECTION_CHARS, MAX_SEMANTIC_ENTRY_CHARS,
MAX_SOURCE_SELECTOR_CHARS, SearchCase, SearchScope, SearchSyntax, default_search_limit,
};
/// Maximum outline selectors accepted by one focused read request.
pub const MAX_NODE_SELECTORS: usize = 16;
/// Exact schema marker for a complete `ManT` query result.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum QuerySchema {
/// Query envelope built around `mant.document/v0.11`.
#[serde(rename = "mant.query/v0.11")]
V0Dot11,
}
impl QuerySchema {
/// Serialized identifier of the current query response contract.
pub const ID: &'static str = "mant.query/v0.11";
}
/// Exact schema marker for a native query request.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum RequestSchema {
/// Query and projection request accepted through `--request-json`.
#[serde(rename = "mant.request/v0.11")]
V0Dot11,
}
impl RequestSchema {
/// Serialized identifier of the current request contract.
pub const ID: &'static str = "mant.request/v0.11";
}
/// Source selected by one public query request.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase",
deny_unknown_fields
)]
pub enum QueryInput {
/// Resolve personal Markdown first, then configured sources around the
/// priority-zero native-manual baseline.
Document {
/// Hierarchical catalog path or unqualified component-suffix selector.
#[schemars(length(min = 1, max = MAX_DOCUMENT_SELECTOR_CHARS))]
selector: String,
/// Optional configured Markdown source. It bypasses root documents and manuals.
#[schemars(length(min = 1, max = MAX_SOURCE_SELECTOR_CHARS))]
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<String>,
/// Optional native manual category such as `1` or `3p`.
#[schemars(length(min = 1, max = MAX_MANUAL_SECTION_CHARS))]
#[serde(skip_serializing_if = "Option::is_none")]
manual_section: Option<String>,
},
/// Read and parse one explicit local Markdown or roff file.
File {
/// Physical path supplied by the caller.
path: String,
/// Parser-selection policy for the file.
format: InputFormat,
},
}
/// Parser selected for an explicit physical input.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum InputFormat {
/// Infer the parser from extension and content conventions.
#[default]
Auto,
/// Parse the input as Markdown.
Markdown,
/// Parse the input as roff with libmandoc.
Roff,
}
/// Projection requested after loading one complete structured document.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase",
deny_unknown_fields
)]
pub enum QueryView {
/// Return the complete structured query bundle.
Full {},
/// Return a navigable structural projection.
Outline {
/// Semantic entry material included beneath structural nodes.
#[serde(default)]
entries: EntryProjection,
/// Optional section or entry used as the outline root.
#[serde(skip_serializing_if = "Option::is_none")]
root: Option<ContentSelector>,
/// Independent bounded inventory of real inline link occurrences.
#[serde(default)]
references: crate::ReferenceProjection,
},
/// Return content selected by one or more explicit local node paths or IDs.
Excerpt {
/// Ordered selectors resolved by the engine.
#[schemars(length(min = 1, max = MAX_NODE_SELECTORS))]
selectors: Vec<ContentSelector>,
},
/// Collect independent semantic and bounded literal evidence.
Explain {
/// Documented name, full authored form, exact entry ID/path, or literal support.
#[schemars(length(min = 1, max = MAX_SEMANTIC_ENTRY_CHARS))]
entry: String,
/// Semantic result and original-content budgets.
#[serde(default)]
options: crate::ExplanationOptions,
},
/// Search visible document content with bounded pagination.
Search {
/// Literal or regular-expression search pattern.
#[schemars(length(min = 1, max = 4096))]
pattern: String,
/// Pattern language.
#[serde(default)]
syntax: SearchSyntax,
/// Case-matching policy.
#[serde(default)]
case: SearchCase,
/// Semantic content included in the search.
#[serde(default)]
scope: SearchScope,
/// Require matches to be bounded by word boundaries.
#[serde(default)]
word: bool,
/// Neighboring rendered lines included around each match.
#[serde(default)]
#[schemars(range(max = 100))]
context_lines: u16,
/// Maximum number of matches returned.
#[serde(default = "default_search_limit")]
#[schemars(range(min = 1, max = 10000))]
limit: u32,
/// Number of matching results skipped before collection.
#[serde(default)]
offset: u32,
},
}
/// Native use-case input. The engine validates semantic constraints before I/O.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[schemars(extend("$id" = "urn:mant:request:v0.11"))]
pub struct QueryRequest {
/// Exact request schema discriminator.
pub schema: RequestSchema,
/// Document source to resolve.
pub input: QueryInput,
/// Projection applied after the document is loaded.
pub view: QueryView,
}
/// Versioned full-query result emitted at CLI and request JSON boundaries.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[schemars(extend("$id" = "urn:mant:query:v0.11"))]
pub struct QueryBundle {
/// Exact response schema discriminator.
pub schema: QuerySchema,
/// Human-readable selected-document label.
pub label: String,
/// Exact registered address selected for this query. Direct input paths
/// and standard input do not belong to the registered catalog.
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<DocumentAddress>,
/// Authoritative structured document, when found.
#[serde(skip_serializing_if = "Option::is_none")]
pub document: Option<DocumentResponse>,
/// Optional quick-reference page resolved alongside the document.
#[serde(skip_serializing_if = "Option::is_none")]
pub tldr: Option<TldrDocument>,
}
impl From<&ResolvedContent> for QueryBundle {
fn from(content: &ResolvedContent) -> Self {
Self {
schema: QuerySchema::V0Dot11,
label: content.label.clone(),
address: content.address.clone(),
document: content.document.as_ref().map(Into::into),
tldr: content.tldr.clone(),
}
}
}
impl From<QueryBundle> for ResolvedContent {
fn from(bundle: QueryBundle) -> Self {
Self {
label: bundle.label,
address: bundle.address,
document: bundle.document.map(Into::into),
tldr: bundle.tldr,
}
}
}