forked from mars/tml
Add framework for new tag system
This commit is contained in:
parent
618f5dffa6
commit
5ac8032f92
|
@ -0,0 +1,6 @@
|
|||
use crate::tag::BlockTag;
|
||||
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
pub struct Dom {
|
||||
pub tags: Vec<BlockTag>,
|
||||
}
|
47
src/main.rs
47
src/main.rs
|
@ -1,49 +1,12 @@
|
|||
use ansi_term::Style;
|
||||
use std::str::FromStr;
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Stylesheet {
|
||||
pub h1: Style,
|
||||
pub h2: Style,
|
||||
pub h3: Style,
|
||||
pub h1_prefix: String,
|
||||
pub h2_prefix: String,
|
||||
pub h3_prefix: String,
|
||||
pub p: Style,
|
||||
pub underline: Style,
|
||||
pub bold: Style,
|
||||
pub italic: Style,
|
||||
pub strikethrough: Style,
|
||||
pub bullet: Style,
|
||||
pub ul_prefix: String,
|
||||
pub list_indent: usize,
|
||||
}
|
||||
pub mod dom;
|
||||
pub mod parse;
|
||||
pub mod style;
|
||||
pub mod tag;
|
||||
|
||||
impl Default for Stylesheet {
|
||||
fn default() -> Self {
|
||||
use ansi_term::Color::*;
|
||||
|
||||
let header_style = Blue.bold().underline();
|
||||
|
||||
Self {
|
||||
h1: header_style,
|
||||
h2: header_style,
|
||||
h3: header_style,
|
||||
h1_prefix: "# ".to_string(),
|
||||
h2_prefix: "## ".to_string(),
|
||||
h3_prefix: "### ".to_string(),
|
||||
p: Style::new(),
|
||||
underline: Style::new().underline(),
|
||||
bold: Style::new().bold(),
|
||||
italic: Style::new().italic(),
|
||||
strikethrough: Style::new().strikethrough(),
|
||||
bullet: Black.into(),
|
||||
ul_prefix: "* ".to_string(),
|
||||
list_indent: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
use style::Stylesheet;
|
||||
|
||||
#[derive(Clone, Copy, Debug, EnumString)]
|
||||
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
use crate::dom::Dom;
|
||||
use crate::tag::*;
|
||||
use lexpr::{Cons, Value};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub struct Parser {
|
||||
vals: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn parse(document: &Value) -> Dom {
|
||||
let parser = Self::from_value(document);
|
||||
|
||||
let mut tags = Vec::new();
|
||||
|
||||
for val in parser.vals.into_iter() {
|
||||
let mut parser = Self::from_value(&val);
|
||||
|
||||
let kind = parser
|
||||
.parse_any_tag_kind()
|
||||
.as_block()
|
||||
.expect("Root tag must be a block tag");
|
||||
|
||||
let tag = parser.parse_block_tag(kind);
|
||||
|
||||
tags.push(tag);
|
||||
}
|
||||
|
||||
Dom { tags }
|
||||
}
|
||||
|
||||
pub fn from_value(value: &Value) -> Self {
|
||||
let cons = value.as_cons().expect("Must be a cons");
|
||||
Self::from_cons(cons)
|
||||
}
|
||||
|
||||
pub fn from_cons(cons: &Cons) -> Self {
|
||||
let mut vals = cons.to_vec().0;
|
||||
vals.reverse();
|
||||
Self { vals }
|
||||
}
|
||||
|
||||
pub fn parse_args(&mut self) -> (Vec<(String, Value)>, Vec<Value>) {
|
||||
let args = Vec::new();
|
||||
let children = std::mem::replace(&mut self.vals, Vec::new());
|
||||
(args, children)
|
||||
}
|
||||
|
||||
pub fn parse_text_tag(&mut self, kind: TextTagKind) -> TextTag {
|
||||
let (_args, children_vals) = self.parse_args();
|
||||
|
||||
let mut children = Vec::new();
|
||||
|
||||
for child in children_vals.into_iter() {
|
||||
let node = match child {
|
||||
Value::Cons(cons) => {
|
||||
let mut parser = Self::from_cons(&cons);
|
||||
let kind = parser
|
||||
.parse_any_tag_kind()
|
||||
.as_text()
|
||||
.expect("Text tag can only have other text tags as children");
|
||||
TextNode::Tag(parser.parse_text_tag(kind))
|
||||
}
|
||||
Value::String(string) => TextNode::Text(string.to_string()),
|
||||
_ => panic!("Text tag child must be a string or a cons"),
|
||||
};
|
||||
|
||||
children.push(node);
|
||||
}
|
||||
|
||||
TextTag { kind, children }
|
||||
}
|
||||
|
||||
pub fn parse_leaf_tag(&mut self, kind: LeafTagKind) -> LeafTag {
|
||||
let (_args, children) = self.parse_args();
|
||||
|
||||
if !children.is_empty() {
|
||||
panic!("Leaf tag cannot have children");
|
||||
}
|
||||
|
||||
LeafTag { kind }
|
||||
}
|
||||
|
||||
pub fn parse_block_tag(&mut self, kind: BlockTagKind) -> BlockTag {
|
||||
let (_args, children_vals) = self.parse_args();
|
||||
|
||||
let mut children = Vec::new();
|
||||
|
||||
for child in children_vals.into_iter() {
|
||||
let mut parser = Self::from_value(&child);
|
||||
|
||||
let node = match parser.parse_any_tag_kind() {
|
||||
AnyTagKind::Text(kind) => BlockNode::Text(self.parse_text_tag(kind)),
|
||||
AnyTagKind::Leaf(kind) => BlockNode::Leaf(self.parse_leaf_tag(kind)),
|
||||
AnyTagKind::Block(kind) => BlockNode::Block(self.parse_block_tag(kind)),
|
||||
};
|
||||
|
||||
children.push(node);
|
||||
}
|
||||
|
||||
BlockTag { kind, children }
|
||||
}
|
||||
|
||||
pub fn parse_any_tag_kind(&mut self) -> AnyTagKind {
|
||||
let symbol = self.parse_symbol();
|
||||
AnyTagKind::from_str(&symbol).expect("Unrecognized tag")
|
||||
}
|
||||
|
||||
pub fn parse_symbol(&mut self) -> String {
|
||||
self.vals
|
||||
.pop()
|
||||
.as_ref()
|
||||
.map(|v| v.as_symbol())
|
||||
.flatten()
|
||||
.expect("Expected symbol")
|
||||
.to_string()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
use ansi_term::Style;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Stylesheet {
|
||||
pub h1: Style,
|
||||
pub h2: Style,
|
||||
pub h3: Style,
|
||||
pub h1_prefix: String,
|
||||
pub h2_prefix: String,
|
||||
pub h3_prefix: String,
|
||||
pub p: Style,
|
||||
pub underline: Style,
|
||||
pub bold: Style,
|
||||
pub italic: Style,
|
||||
pub strikethrough: Style,
|
||||
pub bullet: Style,
|
||||
pub ul_prefix: String,
|
||||
pub list_indent: usize,
|
||||
}
|
||||
|
||||
impl Default for Stylesheet {
|
||||
fn default() -> Self {
|
||||
use ansi_term::Color::*;
|
||||
|
||||
let header_style = Blue.bold().underline();
|
||||
|
||||
Self {
|
||||
h1: header_style,
|
||||
h2: header_style,
|
||||
h3: header_style,
|
||||
h1_prefix: "# ".to_string(),
|
||||
h2_prefix: "## ".to_string(),
|
||||
h3_prefix: "### ".to_string(),
|
||||
p: Style::new(),
|
||||
underline: Style::new().underline(),
|
||||
bold: Style::new().bold(),
|
||||
italic: Style::new().italic(),
|
||||
strikethrough: Style::new().strikethrough(),
|
||||
bullet: Black.into(),
|
||||
ul_prefix: "* ".to_string(),
|
||||
list_indent: 4,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
use std::str::FromStr;
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, EnumString)]
|
||||
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
|
||||
pub enum TextTagKind {
|
||||
Italic,
|
||||
Bold,
|
||||
Underline,
|
||||
Strikethrough,
|
||||
A,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum TextNode {
|
||||
Text(String),
|
||||
Tag(TextTag),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct TextTag {
|
||||
pub kind: TextTagKind,
|
||||
pub children: Vec<TextNode>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, EnumString)]
|
||||
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
|
||||
pub enum LeafTagKind {
|
||||
Figlet,
|
||||
Code,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct LeafTag {
|
||||
pub kind: LeafTagKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, EnumString)]
|
||||
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
|
||||
pub enum BlockTagKind {
|
||||
P,
|
||||
Ul,
|
||||
Ol,
|
||||
Li,
|
||||
Div,
|
||||
Table,
|
||||
Tr,
|
||||
Td,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum BlockNode {
|
||||
Text(TextTag),
|
||||
Leaf(LeafTag),
|
||||
Block(BlockTag),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BlockTag {
|
||||
pub kind: BlockTagKind,
|
||||
pub children: Vec<BlockNode>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum AnyTagKind {
|
||||
Text(TextTagKind),
|
||||
Leaf(LeafTagKind),
|
||||
Block(BlockTagKind),
|
||||
}
|
||||
|
||||
impl AnyTagKind {
|
||||
pub fn as_text(self) -> Option<TextTagKind> {
|
||||
if let AnyTagKind::Text(text) = self {
|
||||
Some(text)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_leaf(self) -> Option<LeafTagKind> {
|
||||
if let AnyTagKind::Leaf(leaf) = self {
|
||||
Some(leaf)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_block(self) -> Option<BlockTagKind> {
|
||||
if let AnyTagKind::Block(block) = self {
|
||||
Some(block)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for AnyTagKind {
|
||||
type Err = strum::ParseError;
|
||||
|
||||
fn from_str(string: &str) -> Result<Self, strum::ParseError> {
|
||||
if let Ok(text) = TextTagKind::from_str(string) {
|
||||
Ok(AnyTagKind::Text(text))
|
||||
} else if let Ok(leaf) = LeafTagKind::from_str(string) {
|
||||
Ok(AnyTagKind::Leaf(leaf))
|
||||
} else if let Ok(block) = BlockTagKind::from_str(string) {
|
||||
Ok(AnyTagKind::Block(block))
|
||||
} else {
|
||||
Err(strum::ParseError::VariantNotFound)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue