mod shift;
pub(crate) use shift::{Move, Moved, Shift};
use crate::diagnostic::Location;
use crate::named::named;
use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
use serde_json::Value;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct Point {
pub line: usize,
pub column: usize,
pub offset: usize,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize)]
pub struct Position {
pub start: Point,
pub end: Point,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(tag = "type", rename = "root")]
pub struct Root {
pub children: Vec<Node>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum Node {
Blockquote(Blockquote),
Break(Void),
Code(Code),
ComponentContainer(Container),
ComponentDefinition(Definition),
ComponentInline(Leaf),
ComponentLeaf(Leaf),
ComponentSignature(SignatureFence),
ComponentSlot(Slot),
Definition(LinkDefinition),
Delete(Parent),
Emphasis(Parent),
FootnoteDefinition(FootnoteDefinition),
FootnoteReference(Reference),
Heading(Heading),
Html(Literal),
Image(Image),
ImageReference(ImageReference),
InlineCode(Literal),
InlineMath(Literal),
Interpolation(Interpolation),
Link(Link),
LinkReference(LinkReference),
List(List),
ListItem(ListItem),
Math(Math),
Paragraph(Parent),
Strong(Parent),
Table(Table),
TableCell(Parent),
TableRow(Parent),
Text(Literal),
ThematicBreak(Void),
WikiLink(WikiLink),
Yaml(Yaml),
}
impl Node {
#[must_use]
pub fn children(&self) -> &[Node] {
match self {
Self::Blockquote(Blockquote { children, .. })
| Self::Delete(Parent { children, .. })
| Self::Emphasis(Parent { children, .. })
| Self::Paragraph(Parent { children, .. })
| Self::Strong(Parent { children, .. })
| Self::TableCell(Parent { children, .. })
| Self::TableRow(Parent { children, .. })
| Self::FootnoteDefinition(FootnoteDefinition { children, .. })
| Self::Heading(Heading { children, .. })
| Self::Link(Link { children, .. })
| Self::LinkReference(LinkReference { children, .. })
| Self::List(List { children, .. })
| Self::ListItem(ListItem { children, .. })
| Self::Table(Table { children, .. })
| Self::ComponentContainer(Container { children, .. })
| Self::ComponentSlot(Slot { children, .. })
| Self::ComponentDefinition(Definition {
body: DefinitionBody::Compmd { children },
..
}) => children,
Self::ComponentLeaf(leaf) | Self::ComponentInline(leaf) => &leaf.fallback,
_ => &[],
}
}
#[must_use]
pub fn position(&self) -> Option<Position> {
match self {
Self::Blockquote(n) => n.position,
Self::Code(n) => n.position,
Self::ComponentContainer(n) => n.position,
Self::ComponentDefinition(n) => n.position,
Self::ComponentInline(n) | Self::ComponentLeaf(n) => n.position,
Self::ComponentSignature(n) => n.position,
Self::ComponentSlot(n) => n.position,
Self::Definition(n) => n.position,
Self::Delete(n)
| Self::Emphasis(n)
| Self::Paragraph(n)
| Self::Strong(n)
| Self::TableCell(n)
| Self::TableRow(n) => n.position,
Self::FootnoteDefinition(n) => n.position,
Self::FootnoteReference(n) => n.position,
Self::Heading(n) => n.position,
Self::Html(n) | Self::InlineCode(n) | Self::InlineMath(n) | Self::Text(n) => n.position,
Self::Image(n) => n.position,
Self::ImageReference(n) => n.position,
Self::Interpolation(n) => n.position,
Self::Link(n) => n.position,
Self::LinkReference(n) => n.position,
Self::List(n) => n.position,
Self::ListItem(n) => n.position,
Self::Math(n) => n.position,
Self::Table(n) => n.position,
Self::Break(n) | Self::ThematicBreak(n) => n.position,
Self::WikiLink(n) => n.position,
Self::Yaml(n) => n.position,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Parent<N = Node> {
pub children: Vec<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Literal {
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Void {
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Blockquote<N = Node> {
pub children: Vec<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alert: Option<Alert>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Alert {
Note,
Tip,
Important,
Warning,
Caution,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Code {
pub lang: Option<String>,
pub meta: Option<String>,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Math {
pub meta: Option<String>,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Heading<N = Node> {
pub depth: u8,
pub children: Vec<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct List<N = Node> {
pub ordered: bool,
pub start: Option<u32>,
pub spread: bool,
pub children: Vec<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct ListItem<N = Node> {
pub spread: bool,
pub checked: Option<bool>,
pub children: Vec<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Table<N = Node> {
pub align: Vec<Option<Align>>,
pub children: Vec<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Align {
Left,
Right,
Center,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Link<N = Node> {
pub children: Vec<N>,
pub url: String,
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url_parts: Option<Vec<StringPart>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Image {
pub alt: String,
pub url: String,
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url_parts: Option<Vec<StringPart>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LinkDefinition {
pub identifier: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub url: String,
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url_parts: Option<Vec<StringPart>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ReferenceType {
#[default]
Shortcut,
Collapsed,
Full,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LinkReference<N = Node> {
pub children: Vec<N>,
pub reference_type: ReferenceType,
pub identifier: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ImageReference {
pub alt: String,
pub reference_type: ReferenceType,
pub identifier: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct FootnoteDefinition<N = Node> {
pub identifier: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub children: Vec<N>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Reference {
pub identifier: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Yaml {
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub parsed: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct WikiLink {
pub target: String,
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Interpolation {
pub path: String,
pub block: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum StringPart {
Text(Literal),
Interpolation(Interpolation),
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Container {
pub name: String,
pub params: Vec<Param>,
pub children: Vec<Node>,
pub slots: Vec<Slot>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
#[serde(skip_serializing_if = "Option::is_none")]
pub close_position: Option<Position>,
#[serde(skip_serializing_if = "is_false")]
pub auto_closed: bool,
#[serde(skip_serializing_if = "is_false")]
pub recovered: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[serde(tag = "type", rename = "componentSlot")]
pub struct Slot {
pub name: String,
pub children: Vec<Node>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Leaf {
pub name: String,
pub fallback: Vec<Node>,
pub params: Vec<Param>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
#[serde(skip_serializing_if = "is_false")]
pub recovered: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Param {
pub key: Option<String>,
pub breakpoint: Option<String>,
pub value: ParamValue,
pub position: Option<Position>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ParamValue {
Bare(String),
String(Vec<StringPart>),
Json(String),
Expr(String),
Flag,
Class(String),
Id(String),
}
impl Param {
pub(crate) fn is_display_contents(&self) -> bool {
self.key.as_deref() == Some("display") && self.value == ParamValue::Bare("contents".into())
}
}
impl ParamValue {
#[must_use]
pub fn kind(&self) -> &'static str {
match self {
Self::Bare(_) => "bare",
Self::String(_) => "string",
Self::Json(_) => "json",
Self::Expr(_) => "expr",
Self::Flag => "flag",
Self::Class(_) => "class",
Self::Id(_) => "id",
}
}
fn serialize_value<M: SerializeMap>(&self, map: &mut M) -> Result<(), M::Error> {
match self {
Self::Bare(text)
| Self::Json(text)
| Self::Expr(text)
| Self::Class(text)
| Self::Id(text) => map.serialize_entry("value", text),
Self::String(parts) => map.serialize_entry("value", parts),
Self::Flag => map.serialize_entry("value", &()),
}
}
}
impl Serialize for Param {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("key", &self.key)?;
map.serialize_entry("breakpoint", &self.breakpoint)?;
map.serialize_entry("kind", self.value.kind())?;
self.value.serialize_value(&mut map)?;
if let Some(position) = &self.position {
map.serialize_entry("position", position)?;
}
map.end()
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct Definition {
pub name: String,
pub language: String,
pub signature: Option<Signature>,
pub params: Vec<Param>,
#[serde(flatten)]
pub body: DefinitionBody,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum DefinitionBody {
Compmd {
children: Vec<Node>,
},
Host {
source: String,
runtime: Option<String>,
},
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct SignatureFence {
pub name: String,
pub signature: Option<Signature>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<Position>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Signature {
pub params: Vec<Declaration>,
pub slots: Option<Vec<SlotDeclaration>>,
pub headings: Headings,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum SlotDeclaration {
Name(String),
Template {
name: String,
each: String,
#[serde(rename = "as")]
binding: String,
},
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Headings {
#[default]
Relative,
Absolute,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct Declaration {
pub name: String,
#[serde(rename = "type")]
pub declared: DeclaredType,
pub optional: bool,
pub default: Option<DefaultValue>,
pub doc: Option<String>,
#[serde(skip)]
pub(crate) unknown_at: Option<Location>,
}
named! {
pub enum TypeName {
String = "string",
Number = "number",
Boolean = "boolean",
Date = "date",
Array = "array",
Object = "object",
Length = "length",
Space = "space",
Color = "color",
Tracks = "tracks",
Areas = "areas",
Surface = "surface",
Border = "border",
Radius = "radius",
Name = "name",
Enum = "enum",
}
}
impl Serialize for TypeName {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.name())
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum DeclaredType {
Name(TypeName),
Other(String),
Enum {
#[serde(rename = "enum")]
members: Vec<String>,
},
Input {
input: Box<DeclaredType>,
},
}
impl DeclaredType {
pub(crate) fn named(name: &str) -> Self {
TypeName::from_name(name).map_or_else(|| Self::Other(name.to_string()), Self::Name)
}
pub(crate) fn unknown(&self) -> Option<&str> {
match self {
Self::Other(name) => Some(name),
Self::Input { input } => input.unknown(),
Self::Name(_) | Self::Enum { .. } => None,
}
}
pub(crate) fn written(&self) -> String {
match self {
Self::Name(name) => format!("a {}", name.name()),
Self::Other(name) => format!("a {name}"),
Self::Enum { members } => format!("one of {}", members.join(", ")),
Self::Input { input } => format!("an input of {}", input.written()),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum DefaultValue {
Param(ParamValue),
Yaml(Value),
}
impl Serialize for DefaultValue {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(Some(2))?;
match self {
Self::Param(value) => {
map.serialize_entry("kind", value.kind())?;
value.serialize_value(&mut map)?;
}
Self::Yaml(value) => {
map.serialize_entry("kind", "yaml")?;
map.serialize_entry("value", value)?;
}
}
map.end()
}
}
#[allow(
clippy::trivially_copy_pass_by_ref,
reason = "serde's skip_serializing_if passes a reference"
)]
fn is_false(value: &bool) -> bool {
!value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn children_are_a_nodes_content() {
let paragraph = Node::Paragraph(Parent {
children: vec![Node::Text(Literal::default())],
position: None,
});
let slot = Node::ComponentSlot(Slot {
name: "footer".into(),
children: vec![paragraph.clone()],
position: None,
});
assert_eq!(slot.children(), std::slice::from_ref(¶graph));
assert_eq!(paragraph.children().len(), 1);
assert_eq!(Node::Text(Literal::default()).children(), []);
}
}