use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{DefinitionCase, DefinitionRole, DocumentMeta, DocumentSource, SourceSpan};
pub const DEFAULT_SEARCH_LIMIT: u32 = 100;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum SearchSyntax {
#[default]
Literal,
Regex,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum SearchCase {
#[default]
Insensitive,
Sensitive,
Smart,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum SearchScope {
#[default]
Visible,
Markdown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SearchQuery {
#[schemars(length(min = 1, max = 4096))]
pub pattern: String,
#[serde(default)]
pub syntax: SearchSyntax,
#[serde(default)]
pub case: SearchCase,
#[serde(default)]
pub scope: SearchScope,
#[serde(default)]
pub word: bool,
#[serde(default)]
#[schemars(range(max = 100))]
pub context_lines: u16,
#[serde(default = "default_search_limit")]
#[schemars(range(min = 1, max = 10000))]
pub limit: u32,
#[serde(default)]
pub offset: u32,
}
#[must_use]
pub const fn default_search_limit() -> u32 {
DEFAULT_SEARCH_LIMIT
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum SearchSchema {
#[serde(rename = "mant.search/v6")]
V6,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum MarkdownSchema {
#[serde(rename = "mant.markdown/v1")]
V1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum SearchRenderFormat {
Markdown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum SearchRenderScope {
Full,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SearchRender {
pub schema: MarkdownSchema,
pub format: SearchRenderFormat,
pub scope: SearchRenderScope,
pub line_base: u8,
pub column_base: u8,
pub line_count: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(extend("$id" = "urn:mant:search:v6"))]
pub struct QuerySearch {
pub schema: SearchSchema,
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<DocumentSource>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<DocumentMeta>,
pub query: SearchQuery,
pub render: SearchRender,
pub total: u32,
pub returned: u32,
pub offset: u32,
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_offset: Option<u32>,
pub matches: Vec<SearchMatch>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SearchMatch {
pub ordinal: u32,
pub node: SearchNode,
#[serde(skip_serializing_if = "Option::is_none")]
pub section: Option<SearchSectionReference>,
pub matched_text: String,
pub markdown: SearchMarkdownRange,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<SourceSpan>,
pub preview: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub context: Vec<SearchContextLine>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase"
)]
pub enum SearchNode {
Tldr {
path: String,
id: String,
title: String,
},
DocumentRoot {
path: String,
id: String,
title: String,
},
DocumentSection {
path: String,
id: String,
title: String,
},
DocumentEntry {
path: String,
id: String,
title: String,
role: DefinitionRole,
case: DefinitionCase,
names: Vec<String>,
},
}
impl SearchNode {
#[must_use]
pub fn path(&self) -> &str {
match self {
Self::Tldr { path, .. }
| Self::DocumentRoot { path, .. }
| Self::DocumentSection { path, .. }
| Self::DocumentEntry { path, .. } => path,
}
}
#[must_use]
pub fn title(&self) -> &str {
match self {
Self::Tldr { title, .. }
| Self::DocumentRoot { title, .. }
| Self::DocumentSection { title, .. }
| Self::DocumentEntry { title, .. } => title,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SearchSectionReference {
pub path: String,
pub id: String,
pub title: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SearchMarkdownRange {
pub start_byte: u64,
pub end_byte: u64,
pub start_line: u32,
pub start_column: u32,
pub end_line: u32,
pub end_column: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SearchContextLine {
pub line: u32,
pub text: String,
pub matched: bool,
}