use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum DocumentSchema {
#[serde(rename = "mant.document/v6")]
V6,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct MantDocument {
pub schema: DocumentSchema,
pub producer: Producer,
pub source: DocumentSource,
pub meta: DocumentMeta,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub diagnostics: Vec<Diagnostic>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub blocks: Vec<Block>,
pub sections: Vec<Section>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Producer {
pub name: String,
pub version: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub engine: Option<Engine>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Engine {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum SourceFormat {
Man,
Mdoc,
Markdown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DocumentSource {
pub format: SourceFormat,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DocumentMeta {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub section: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub os: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arch: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub names: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alias_target: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Diagnostic {
pub level: DiagnosticLevel,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<SourceSpan>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum DiagnosticLevel {
Style,
Warning,
Error,
Unsupported,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SourceSpan {
pub line: u32,
pub column: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_line: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_column: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Section {
pub id: String,
pub title: String,
#[serde(default, skip_serializing_if = "is_zero_u16")]
pub spacing_before_lines: u16,
pub blocks: Vec<Block>,
pub children: Vec<Section>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<SourceSpan>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct LayoutHint {
#[serde(default, skip_serializing_if = "is_zero_u16")]
pub indent_columns: u16,
#[serde(default, skip_serializing_if = "is_zero_u16")]
pub spacing_before_lines: u16,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "type",
rename_all = "kebab-case",
rename_all_fields = "camelCase"
)]
pub enum Block {
Paragraph {
children: Vec<Inline>,
#[serde(default, skip_serializing_if = "LayoutHint::is_empty")]
layout: LayoutHint,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
Preformatted {
children: Vec<Inline>,
#[serde(skip_serializing_if = "Option::is_none")]
language: Option<String>,
#[serde(default, skip_serializing_if = "LayoutHint::is_empty")]
layout: LayoutHint,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
List {
kind: ListKind,
#[serde(skip_serializing_if = "Option::is_none")]
start: Option<u64>,
#[serde(default, skip_serializing_if = "is_false")]
compact: bool,
items: Vec<ListItem>,
#[serde(default, skip_serializing_if = "LayoutHint::is_empty")]
layout: LayoutHint,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
DefinitionList {
items: Vec<DefinitionItem>,
#[serde(default, skip_serializing_if = "is_false")]
compact: bool,
#[serde(default, skip_serializing_if = "LayoutHint::is_empty")]
layout: LayoutHint,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
Table {
rows: Vec<TableRow>,
#[serde(default, skip_serializing_if = "LayoutHint::is_empty")]
layout: LayoutHint,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
Equation {
value: String,
#[serde(default, skip_serializing_if = "is_false")]
display: bool,
#[serde(default, skip_serializing_if = "LayoutHint::is_empty")]
layout: LayoutHint,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
VerticalSpace {
lines: u16,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
ThematicBreak {
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
Unsupported {
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
text: String,
#[serde(default, skip_serializing_if = "LayoutHint::is_empty")]
layout: LayoutHint,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<SourceSpan>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum ListKind {
Bullet,
Ordered,
Plain,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ListItem {
pub blocks: Vec<Block>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DefinitionItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub identity: Option<DefinitionIdentity>,
pub terms: Vec<Vec<Inline>>,
pub description: Vec<Block>,
#[serde(default, skip_serializing_if = "is_false")]
pub inline_term: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub spacing_before_lines: Option<u16>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DefinitionIdentity {
pub id: String,
pub role: DefinitionRole,
pub case: DefinitionCase,
pub names: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum DefinitionCase {
Sensitive,
Insensitive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum DefinitionRole {
Option,
Command,
EnvironmentVariable,
Variable,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct TableRow {
pub cells: Vec<TableCell>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct TableCell {
pub blocks: Vec<Block>,
#[serde(default = "one_u16", skip_serializing_if = "is_one_u16")]
pub column_span: u16,
#[serde(default = "one_u16", skip_serializing_if = "is_one_u16")]
pub row_span: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub alignment: Option<TableAlignment>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum TableAlignment {
Left,
Center,
Right,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(
tag = "type",
rename_all = "kebab-case",
rename_all_fields = "camelCase"
)]
pub enum Inline {
Text {
value: String,
},
Strong {
children: Vec<Inline>,
},
Emphasis {
children: Vec<Inline>,
},
Code {
value: String,
},
ExternalLink {
uri: String,
#[serde(skip_serializing_if = "Option::is_none")]
title: Option<String>,
children: Vec<Inline>,
},
EmailLink {
address: String,
children: Vec<Inline>,
},
ManualReference {
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
section: Option<String>,
children: Vec<Inline>,
},
SectionReference {
target: String,
children: Vec<Inline>,
},
Anchor {
id: String,
},
LineBreak,
}
impl LayoutHint {
#[must_use]
pub const fn is_empty(&self) -> bool {
self.indent_columns == 0 && self.spacing_before_lines == 0
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_zero_u16(value: &u16) -> bool {
*value == 0
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_one_u16(value: &u16) -> bool {
*value == 1
}
const fn one_u16() -> u16 {
1
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_false(value: &bool) -> bool {
!*value
}