Skip to main content

mant_ast/
query.rs

1//! Query envelope combining one structured input with optional tldr content.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    MantDocument, OutlineDetail, SearchCase, SearchScope, SearchSyntax, TldrDocument,
8    default_search_limit,
9};
10
11/// Exact schema marker for a complete `ManT` query result.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
13pub enum QuerySchema {
14    /// Query envelope built around `mant.document/v4`.
15    #[serde(rename = "mant.query/v4")]
16    V4,
17}
18
19/// Exact schema marker for a native query request.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
21pub enum RequestSchema {
22    /// Query and projection request accepted through `--request-json`.
23    #[serde(rename = "mant.request/v4")]
24    V4,
25}
26
27/// Source selected by one public query request.
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
29#[serde(
30    tag = "kind",
31    rename_all = "kebab-case",
32    rename_all_fields = "camelCase",
33    deny_unknown_fields
34)]
35pub enum QueryInput {
36    /// Resolve a registered Markdown document first, then a local manual page.
37    Document {
38        /// Lookup name shared by registered documents and manual pages.
39        name: String,
40        #[serde(skip_serializing_if = "Option::is_none")]
41        section: Option<String>,
42    },
43    /// Read and parse one local Markdown file.
44    MarkdownFile { path: String },
45}
46
47/// Projection requested after loading one complete structured document.
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
49#[serde(
50    tag = "kind",
51    rename_all = "kebab-case",
52    rename_all_fields = "camelCase",
53    deny_unknown_fields
54)]
55pub enum QueryView {
56    Full {},
57    Outline {
58        detail: OutlineDetail,
59    },
60    Excerpt {
61        #[schemars(length(min = 1))]
62        nodes: Vec<String>,
63    },
64    Search {
65        #[schemars(length(min = 1, max = 4096))]
66        pattern: String,
67        #[serde(default)]
68        syntax: SearchSyntax,
69        #[serde(default)]
70        case: SearchCase,
71        #[serde(default)]
72        scope: SearchScope,
73        #[serde(default)]
74        word: bool,
75        #[serde(default)]
76        #[schemars(range(max = 100))]
77        context_lines: u16,
78        #[serde(default = "default_search_limit")]
79        #[schemars(range(min = 1, max = 10000))]
80        limit: u32,
81        #[serde(default)]
82        offset: u32,
83    },
84}
85
86/// Validated use-case input accepted by the native query boundary.
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
88#[serde(rename_all = "camelCase", deny_unknown_fields)]
89#[schemars(extend("$id" = "urn:mant:request:v4"))]
90pub struct QueryRequest {
91    pub schema: RequestSchema,
92    pub input: QueryInput,
93    pub view: QueryView,
94}
95
96/// Native result consumed by JSON, Markdown, and interactive frontends.
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
98#[serde(rename_all = "camelCase")]
99#[schemars(extend("$id" = "urn:mant:query:v4"))]
100pub struct QueryBundle {
101    pub schema: QuerySchema,
102    pub label: String,
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub document: Option<MantDocument>,
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub tldr: Option<TldrDocument>,
107}