use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize};
use mant_ir::{
Block, DefinitionCase, DefinitionItem, DefinitionRole, Diagnostic, DocumentMeta,
DocumentSource, EntryKind, EntrySummary, NodeId, Section, TldrDocument, ValueDomain,
};
use crate::{NodePath, NodeSelector, Producer};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum OutlineSchema {
#[serde(rename = "mant.outline/v0.10")]
V0Dot10,
}
impl OutlineSchema {
pub const ID: &'static str = "mant.outline/v0.10";
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, JsonSchema)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase",
deny_unknown_fields
)]
pub enum EntryProjection {
None,
#[default]
Summary,
All,
Kinds {
#[schemars(length(min = 1, max = 9))]
kinds: Vec<EntryKind>,
},
}
#[derive(Deserialize)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase",
deny_unknown_fields
)]
enum ClosedEntryProjection {
None {},
Summary {},
All {},
Kinds { kinds: Vec<EntryKind> },
}
impl<'de> Deserialize<'de> for EntryProjection {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(match ClosedEntryProjection::deserialize(deserializer)? {
ClosedEntryProjection::None {} => Self::None,
ClosedEntryProjection::Summary {} => Self::Summary,
ClosedEntryProjection::All {} => Self::All,
ClosedEntryProjection::Kinds { kinds } => Self::Kinds { kinds },
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutlineDetail {
Sections,
Entries,
}
impl From<OutlineDetail> for EntryProjection {
fn from(value: OutlineDetail) -> Self {
match value {
OutlineDetail::Sections => Self::None,
OutlineDetail::Entries => Self::All,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(extend("$id" = "urn:mant:outline:v0.10"))]
pub struct QueryOutline {
pub schema: OutlineSchema,
pub entries: EntryProjection,
#[serde(skip_serializing_if = "Option::is_none")]
pub root: Option<NodeSelector>,
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>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub diagnostics: Vec<Diagnostic>,
#[serde(default = "default_true", skip_serializing_if = "is_true")]
pub entries_complete: bool,
pub nodes: Vec<OutlineNode>,
}
const fn default_true() -> bool {
true
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_true(value: &bool) -> bool {
*value
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase"
)]
pub enum OutlineNode {
Tldr {
path: NodePath,
id: NodeId,
title: String,
},
DocumentRoot {
path: NodePath,
id: NodeId,
title: String,
#[serde(skip_serializing_if = "Option::is_none")]
entry_summary: Option<EntrySummary>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
children: Vec<OutlineNode>,
},
DocumentSection {
path: NodePath,
id: NodeId,
title: String,
#[serde(skip_serializing_if = "Option::is_none")]
entry_summary: Option<EntrySummary>,
children: Vec<OutlineNode>,
},
DocumentEntry {
path: NodePath,
id: NodeId,
title: String,
entry_kind: EntryKind,
case: DefinitionCase,
aliases: Vec<String>,
forms: Vec<String>,
targets: Vec<NodeId>,
#[serde(skip_serializing_if = "Option::is_none")]
value_domain: Option<ValueDomain>,
#[serde(skip_serializing_if = "Option::is_none")]
entry_summary: Option<EntrySummary>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
children: Vec<OutlineNode>,
},
}
impl OutlineNode {
#[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 id(&self) -> &str {
match self {
Self::Tldr { id, .. }
| Self::DocumentRoot { id, .. }
| Self::DocumentSection { id, .. }
| Self::DocumentEntry { id, .. } => id,
}
}
#[must_use]
pub fn title(&self) -> &str {
match self {
Self::Tldr { title, .. }
| Self::DocumentRoot { title, .. }
| Self::DocumentSection { title, .. }
| Self::DocumentEntry { title, .. } => title,
}
}
#[must_use]
pub fn children(&self) -> &[Self] {
match self {
Self::DocumentRoot { children, .. }
| Self::DocumentSection { children, .. }
| Self::DocumentEntry { children, .. } => children,
Self::Tldr { .. } => &[],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum ExcerptSchema {
#[serde(rename = "mant.excerpt/v0.10")]
V0Dot10,
}
impl ExcerptSchema {
pub const ID: &'static str = "mant.excerpt/v0.10";
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(extend("$id" = "urn:mant:excerpt:v0.10"))]
pub struct QueryExcerpt {
pub schema: ExcerptSchema,
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub producer: Option<Producer>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<DocumentSource>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<DocumentMeta>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub diagnostics: Vec<Diagnostic>,
pub selections: Vec<ExcerptSelection>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase"
)]
pub enum ExcerptSelection {
Tldr {
outline: OutlineTrail,
document: TldrDocument,
},
DocumentRoot {
outline: OutlineTrail,
blocks: Vec<Block>,
},
DocumentSection {
outline: OutlineTrail,
section: Section,
},
DocumentEntry {
outline: OutlineTrail,
entry: DefinitionItem,
},
}
impl ExcerptSelection {
#[must_use]
pub const fn outline(&self) -> &OutlineTrail {
match self {
Self::Tldr { outline, .. }
| Self::DocumentRoot { outline, .. }
| Self::DocumentSection { outline, .. }
| Self::DocumentEntry { outline, .. } => outline,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct OutlineTrail {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub ancestors: Vec<OutlineReference>,
pub node: OutlineNodeReference,
}
impl OutlineTrail {
#[must_use]
pub fn path(&self) -> &str {
self.node.path()
}
#[must_use]
pub fn title(&self) -> &str {
self.node.title()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct OutlineReference {
pub path: NodePath,
pub id: NodeId,
pub title: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "kind",
rename_all = "kebab-case",
rename_all_fields = "camelCase"
)]
pub enum OutlineNodeReference {
Tldr {
path: NodePath,
id: NodeId,
title: String,
},
DocumentRoot {
path: NodePath,
id: NodeId,
title: String,
},
DocumentSection {
path: NodePath,
id: NodeId,
title: String,
},
DocumentEntry {
path: NodePath,
id: NodeId,
title: String,
role: DefinitionRole,
case: DefinitionCase,
names: Vec<String>,
},
}
impl OutlineNodeReference {
#[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 id(&self) -> &str {
match self {
Self::Tldr { id, .. }
| Self::DocumentRoot { id, .. }
| Self::DocumentSection { id, .. }
| Self::DocumentEntry { id, .. } => id,
}
}
#[must_use]
pub fn title(&self) -> &str {
match self {
Self::Tldr { title, .. }
| Self::DocumentRoot { title, .. }
| Self::DocumentSection { title, .. }
| Self::DocumentEntry { title, .. } => title,
}
}
}