mant-ast 0.6.1

Versioned, renderer-neutral document and query contracts for ManT
Documentation
//! Query envelope combining one structured input with optional tldr content.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
    MantDocument, OutlineDetail, SearchCase, SearchScope, SearchSyntax, TldrDocument,
    default_search_limit,
};

/// 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/v6`.
    #[serde(rename = "mant.query/v6")]
    V6,
}

/// 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/v6")]
    V6,
}

/// 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 a registered Markdown document first, then a local manual page.
    Document {
        /// Lookup name shared by registered documents and manual pages.
        name: String,
        /// Optional configured Markdown source. It bypasses root documents and manuals.
        #[serde(skip_serializing_if = "Option::is_none")]
        source: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        section: Option<String>,
    },
    /// Read and parse one local Markdown file.
    MarkdownFile { path: String },
}

/// 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 {
    Full {},
    Outline {
        detail: OutlineDetail,
    },
    Excerpt {
        #[schemars(length(min = 1))]
        nodes: Vec<String>,
    },
    /// Resolve exactly one semantic entry and return its complete description.
    Explain {
        #[schemars(length(min = 1))]
        entry: String,
    },
    Search {
        #[schemars(length(min = 1, max = 4096))]
        pattern: String,
        #[serde(default)]
        syntax: SearchSyntax,
        #[serde(default)]
        case: SearchCase,
        #[serde(default)]
        scope: SearchScope,
        #[serde(default)]
        word: bool,
        #[serde(default)]
        #[schemars(range(max = 100))]
        context_lines: u16,
        #[serde(default = "default_search_limit")]
        #[schemars(range(min = 1, max = 10000))]
        limit: u32,
        #[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:v6"))]
pub struct QueryRequest {
    pub schema: RequestSchema,
    pub input: QueryInput,
    pub view: QueryView,
}

/// Native result consumed by JSON, Markdown, and interactive frontends.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(extend("$id" = "urn:mant:query:v6"))]
pub struct QueryBundle {
    pub schema: QuerySchema,
    pub label: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub document: Option<MantDocument>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tldr: Option<TldrDocument>,
}