Make databake compatible
This commit is contained in:
parent
35891f8f49
commit
8239b2b51d
9 changed files with 240 additions and 26 deletions
36
src/attr.rs
36
src/attr.rs
|
@ -1,13 +1,17 @@
|
|||
use databake::Bake;
|
||||
|
||||
use crate::CowStr;
|
||||
use std::fmt;
|
||||
use std::{borrow::Cow, fmt};
|
||||
|
||||
/// Parse attributes, assumed to be valid.
|
||||
#[cfg(feature = "parser")]
|
||||
pub(crate) fn parse(src: &str) -> Attributes {
|
||||
let mut a = Attributes::new();
|
||||
a.parse(src);
|
||||
a
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
pub fn valid(src: &str) -> usize {
|
||||
use State::*;
|
||||
|
||||
|
@ -31,7 +35,8 @@ pub fn valid(src: &str) -> usize {
|
|||
|
||||
/// Stores an attribute value that supports backslash escapes of ASCII punctuation upon displaying,
|
||||
/// without allocating.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub struct AttributeValue<'s> {
|
||||
raw: CowStr<'s>,
|
||||
}
|
||||
|
@ -118,8 +123,9 @@ impl<'s> Iterator for AttributeValueParts<'s> {
|
|||
// Attributes are relatively rare, we choose to pay 8 bytes always and sometimes an extra
|
||||
// indirection instead of always 24 bytes.
|
||||
#[allow(clippy::box_vec)]
|
||||
#[derive(Clone, PartialEq, Eq, Default)]
|
||||
pub struct Attributes<'s>(Option<Box<Vec<(&'s str, AttributeValue<'s>)>>>);
|
||||
#[derive(Clone, PartialEq, Eq, Default, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub struct Attributes<'s>(pub Option<Cow<'s, [(&'s str, AttributeValue<'s>)]>>);
|
||||
|
||||
impl<'s> Attributes<'s> {
|
||||
/// Create an empty collection.
|
||||
|
@ -129,11 +135,13 @@ impl<'s> Attributes<'s> {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
#[cfg(feature = "parser")]
|
||||
pub(crate) fn take(&mut self) -> Self {
|
||||
Self(self.0.take())
|
||||
}
|
||||
|
||||
/// Parse and append attributes, assumed to be valid.
|
||||
#[cfg(feature = "parser")]
|
||||
pub(crate) fn parse(&mut self, input: &'s str) {
|
||||
let mut parser = Parser::new(self.take());
|
||||
parser.parse(input);
|
||||
|
@ -141,12 +149,13 @@ impl<'s> Attributes<'s> {
|
|||
}
|
||||
|
||||
/// Combine all attributes from both objects, prioritizing self on conflicts.
|
||||
#[cfg(feature = "parser")]
|
||||
pub(crate) fn union(&mut self, other: Self) {
|
||||
if let Some(attrs0) = &mut self.0 {
|
||||
if let Some(mut attrs1) = other.0 {
|
||||
for (key, val) in attrs1.drain(..) {
|
||||
for (key, val) in attrs1.to_mut().drain(..) {
|
||||
if key == "class" || !attrs0.iter().any(|(k, _)| *k == key) {
|
||||
attrs0.push((key, val));
|
||||
attrs0.to_mut().push((key, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +179,7 @@ impl<'s> Attributes<'s> {
|
|||
|
||||
let attrs = self.0.as_mut().unwrap();
|
||||
if let Some(i) = attrs.iter().position(|(k, _)| *k == key) {
|
||||
let prev = &mut attrs[i].1;
|
||||
let prev = &mut attrs.to_mut()[i].1;
|
||||
if key == "class" {
|
||||
match val.raw {
|
||||
CowStr::Borrowed(s) => prev.extend(s),
|
||||
|
@ -184,7 +193,7 @@ impl<'s> Attributes<'s> {
|
|||
i
|
||||
} else {
|
||||
let i = attrs.len();
|
||||
attrs.push((key, val));
|
||||
attrs.to_mut().push((key, val));
|
||||
i
|
||||
}
|
||||
}
|
||||
|
@ -238,10 +247,12 @@ impl<'s> std::fmt::Debug for Attributes<'s> {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "parser")]
|
||||
pub struct Validator {
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
impl Validator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
@ -274,12 +285,14 @@ impl Validator {
|
|||
///
|
||||
/// Input is assumed to contain a valid series of attribute sets, the attributes are added as they
|
||||
/// are encountered.
|
||||
#[cfg(feature = "parser")]
|
||||
pub struct Parser<'s> {
|
||||
attrs: Attributes<'s>,
|
||||
i_prev: usize,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
impl<'s> Parser<'s> {
|
||||
pub fn new(attrs: Attributes<'s>) -> Self {
|
||||
Self {
|
||||
|
@ -310,7 +323,7 @@ impl<'s> Parser<'s> {
|
|||
Identifier => self.attrs.insert("id", content.into()),
|
||||
Key => self.i_prev = self.attrs.insert_pos(content, "".into()),
|
||||
Value | ValueQuoted | ValueContinued => {
|
||||
self.attrs.0.as_mut().unwrap()[self.i_prev]
|
||||
self.attrs.0.as_mut().unwrap().to_mut()[self.i_prev]
|
||||
.1
|
||||
.extend(&content[usize::from(matches!(st, ValueQuoted))..]);
|
||||
}
|
||||
|
@ -338,6 +351,7 @@ impl<'s> Parser<'s> {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg(feature = "parser")]
|
||||
enum State {
|
||||
Start,
|
||||
Whitespace,
|
||||
|
@ -357,6 +371,7 @@ enum State {
|
|||
Invalid,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
impl State {
|
||||
fn step(self, c: u8) -> State {
|
||||
use State::*;
|
||||
|
@ -399,11 +414,12 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
pub fn is_name(c: u8) -> bool {
|
||||
c.is_ascii_alphanumeric() || matches!(c, b':' | b'_' | b'-')
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, feature = "parser"))]
|
||||
mod test {
|
||||
macro_rules! test_attr {
|
||||
($src:expr $(,$($av:expr),* $(,)?)?) => {
|
||||
|
|
45
src/lib.rs
45
src/lib.rs
|
@ -49,19 +49,25 @@
|
|||
#![allow(clippy::blocks_in_if_conditions)]
|
||||
|
||||
use std::fmt;
|
||||
#[cfg(feature = "parser")]
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::io;
|
||||
#[cfg(feature = "parser")]
|
||||
use std::ops::Range;
|
||||
|
||||
#[cfg(feature = "html")]
|
||||
pub mod html;
|
||||
|
||||
mod attr;
|
||||
#[cfg(feature = "parser")]
|
||||
mod block;
|
||||
#[cfg(feature = "parser")]
|
||||
mod inline;
|
||||
#[cfg(feature = "parser")]
|
||||
mod lex;
|
||||
|
||||
pub use attr::{AttributeValue, AttributeValueParts, Attributes};
|
||||
use databake::Bake;
|
||||
|
||||
type CowStr<'s> = std::borrow::Cow<'s, str>;
|
||||
|
||||
|
@ -197,7 +203,8 @@ impl<'s> AsRef<Event<'s>> for &Event<'s> {
|
|||
/// multiple events. [`Container`] elements are represented by a [`Event::Start`] followed by
|
||||
/// events representing its content, and finally a [`Event::End`]. Atomic elements without any
|
||||
/// inside elements are represented by a single event.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum Event<'s> {
|
||||
/// Start of a container.
|
||||
Start(Container<'s>, Attributes<'s>),
|
||||
|
@ -244,7 +251,8 @@ pub enum Event<'s> {
|
|||
/// - inline, may only contain inline elements,
|
||||
/// - block leaf, may only contain inline elements,
|
||||
/// - block container, may contain any block-level elements.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum Container<'s> {
|
||||
/// A blockquote element.
|
||||
Blockquote,
|
||||
|
@ -398,7 +406,8 @@ impl<'s> Container<'s> {
|
|||
}
|
||||
|
||||
/// Alignment of a table column.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum Alignment {
|
||||
Unspecified,
|
||||
Left,
|
||||
|
@ -407,7 +416,8 @@ pub enum Alignment {
|
|||
}
|
||||
|
||||
/// The type of an inline span link.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum SpanLinkType {
|
||||
/// E.g. `[text](url)`
|
||||
Inline,
|
||||
|
@ -418,7 +428,8 @@ pub enum SpanLinkType {
|
|||
}
|
||||
|
||||
/// The type of an inline link.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum LinkType {
|
||||
/// E.g. `[text](url)`.
|
||||
Span(SpanLinkType),
|
||||
|
@ -429,7 +440,8 @@ pub enum LinkType {
|
|||
}
|
||||
|
||||
/// The type of a list.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum ListKind {
|
||||
/// A bullet list.
|
||||
Unordered,
|
||||
|
@ -444,7 +456,8 @@ pub enum ListKind {
|
|||
}
|
||||
|
||||
/// Numbering type of an ordered list.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum OrderedListNumbering {
|
||||
/// Decimal numbering, e.g. `1)`.
|
||||
Decimal,
|
||||
|
@ -459,7 +472,8 @@ pub enum OrderedListNumbering {
|
|||
}
|
||||
|
||||
/// Style of an ordered list.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
|
||||
#[databake(path = jotdown)]
|
||||
pub enum OrderedListStyle {
|
||||
/// Number is followed by a period, e.g. `1.`.
|
||||
Period,
|
||||
|
@ -470,6 +484,7 @@ pub enum OrderedListStyle {
|
|||
}
|
||||
|
||||
impl OrderedListNumbering {
|
||||
#[cfg(feature = "parser")]
|
||||
fn parse_number(self, n: &str) -> u64 {
|
||||
match self {
|
||||
Self::Decimal => n.parse().unwrap(),
|
||||
|
@ -524,6 +539,7 @@ impl OrderedListNumbering {
|
|||
}
|
||||
|
||||
impl OrderedListStyle {
|
||||
#[cfg(feature = "parser")]
|
||||
fn number(self, marker: &str) -> &str {
|
||||
&marker[usize::from(matches!(self, Self::ParenParen))..marker.len() - 1]
|
||||
}
|
||||
|
@ -534,9 +550,9 @@ type Map<K, V> = std::collections::HashMap<K, V>;
|
|||
#[cfg(feature = "deterministic")]
|
||||
type Map<K, V> = std::collections::BTreeMap<K, V>;
|
||||
|
||||
#[cfg(not(feature = "deterministic"))]
|
||||
#[cfg(all(not(feature = "deterministic"), feature = "parser"))]
|
||||
type Set<T> = std::collections::HashSet<T>;
|
||||
#[cfg(feature = "deterministic")]
|
||||
#[cfg(all(feature = "deterministic", feature = "parser"))]
|
||||
type Set<T> = std::collections::BTreeSet<T>;
|
||||
|
||||
/// A parser that generates [`Event`]s from a Djot document.
|
||||
|
@ -548,6 +564,7 @@ type Set<T> = std::collections::BTreeSet<T>;
|
|||
///
|
||||
/// It is possible to clone the parser to e.g. avoid performing the block parsing multiple times.
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "parser")]
|
||||
pub struct Parser<'s> {
|
||||
src: &'s str,
|
||||
|
||||
|
@ -572,6 +589,7 @@ pub struct Parser<'s> {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "parser")]
|
||||
struct Heading {
|
||||
/// Location of heading in src.
|
||||
location: u32,
|
||||
|
@ -585,6 +603,7 @@ struct Heading {
|
|||
|
||||
/// Because of potential future references, an initial pass is required to obtain all definitions.
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "parser")]
|
||||
struct PrePass<'s> {
|
||||
/// Link definitions and their attributes.
|
||||
link_definitions: Map<&'s str, (CowStr<'s>, attr::Attributes<'s>)>,
|
||||
|
@ -594,6 +613,7 @@ struct PrePass<'s> {
|
|||
headings_lex: Vec<usize>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
impl<'s> PrePass<'s> {
|
||||
#[must_use]
|
||||
fn new(
|
||||
|
@ -782,6 +802,7 @@ impl<'s> PrePass<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
impl<'s> Parser<'s> {
|
||||
#[must_use]
|
||||
pub fn new(src: &'s str) -> Self {
|
||||
|
@ -1165,6 +1186,7 @@ impl<'s> Parser<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
impl<'s> Iterator for Parser<'s> {
|
||||
type Item = Event<'s>;
|
||||
|
||||
|
@ -1177,10 +1199,12 @@ impl<'s> Iterator for Parser<'s> {
|
|||
/// event within the input.
|
||||
///
|
||||
/// See the documentation of [`Parser::into_offset_iter`] for more information.
|
||||
#[cfg(feature = "parser")]
|
||||
pub struct OffsetIter<'s> {
|
||||
parser: Parser<'s>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "parser")]
|
||||
impl<'s> Iterator for OffsetIter<'s> {
|
||||
type Item = (Event<'s>, Range<usize>);
|
||||
|
||||
|
@ -1190,6 +1214,7 @@ impl<'s> Iterator for OffsetIter<'s> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "parser")]
|
||||
mod test {
|
||||
use super::Attributes;
|
||||
use super::Container::*;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue