1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
//!# libcmark bindings for Rust
//!
//! This library contains bindings to the [libcmark][1] C library, which is the
//! C reference implementation of the [CommonMark][2] standard. This binding
//! does no additional parsing work beyond that of the underlying library, so it
//! ought to be as accurate.
//!
//! [1]: https://github.com/jgm/cmark
//! [2]: http://commonmark.org/
//!
//!## Nodes
//!
//! The `Node` is the core abstraction in rcmark. Nodes can be built up
//! programmatically or by parsing CommonMark source. Nodes all have a type and
//! may have parent, child, and sibling nodes. Depending on the node type, a
//! variety of properties are available. If a property is not applicable to a
//! given node type, then attempting to access it will panic.
//!
//!```
//! use rcmark::{Node, NodeType, ListType};
//!
//! let mut root = Node::new(NodeType::Document);
//!
//! let mut heading = Node::new(NodeType::Header);
//! heading.set_header_level(1);
//!
//! let mut heading_text = Node::new(NodeType::Text);
//! heading_text.set_literal("Hello, World!");
//!
//! heading.prepend_child(&mut heading_text);
//! root.prepend_child(&mut heading);
//!```
//!
//!## Parsing a Document
//!
//! Parsing can be done through either a `Parser` instance or
//! the all-in-one `parse_document` function.
//!
//!```
//! use rcmark::{Parser, parse_document, DEFAULT, NORMALIZE};
//!
//! let doc = parse_document("**Hello**, `World!`", DEFAULT);
//! assert_eq!(doc.start_line(), 1);
//!
//! let mut parser = Parser::new(NORMALIZE);
//! parser.feed("# Hello, World!");
//! let doc2 = parser.finish();
//!```
//!
//!## Rendering a Document
//!
//! Rendering could be done manually, but libcmark also provides
//! functions to render to XML, HTML, man pages, and CommonMark.
//!
//!```
//! let doc = rcmark::parse_document("# Hello", rcmark::DEFAULT);
//!
//! assert_eq!(rcmark::render_xml(&doc, rcmark::DEFAULT),
//!            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
//!             <!DOCTYPE CommonMark SYSTEM \"CommonMark.dtd\">\n\
//!             <document>\n  \
//!               <header level=\"1\">\n    \
//!                 <text>Hello</text>\n  \
//!               </header>\n\
//!             </document>\n");
//! assert_eq!(rcmark::render_html(&doc, rcmark::DEFAULT),
//!            "<h1>Hello</h1>\n");
//! assert_eq!(rcmark::render_man(&doc, rcmark::DEFAULT),
//!            ".SH\nHello\n");
//! assert_eq!(rcmark::render_commonmark(&doc, rcmark::DEFAULT, 2),
//!            "# Hello\n");
//!```
#![feature(concat_idents)]
// #![deny(missing_docs)]

extern crate libc;
extern crate libcmark_sys as raw;
#[macro_use] extern crate bitflags;

pub use node::Node;
pub use iter::NodeIterator;
pub use parser::{Parser, parse_document};
pub use render::{render_xml, render_html, render_man, render_commonmark};

use util::Binding;

use std::ffi::CStr;
use std::str;

mod node;
mod iter;
mod parser;
mod render;
mod util;

/// The types of nodes that make up a CommonMark document.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum NodeType {
    /// Error status
    None,
    Document,
    BlockQuote,
    List,
    Item,
    CodeBlock,
    Html,
    Paragraph,
    Header,
    Hrule,
    Text,
    SoftBreak,
    LineBreak,
    Code,
    InlineHtml,
    Emph,
    Strong,
    Link,
    Image,
}

impl Binding for NodeType {
    type Raw = raw::cmark_node_type;

    /// Obtain a `NodeType` from the corresponding raw `cmark_node_type` enum value.
    unsafe fn from_raw(raw: raw::cmark_node_type) -> NodeType {
        match raw {
            raw::CMARK_NODE_NONE => NodeType::None,
            raw::CMARK_NODE_DOCUMENT => NodeType::Document,
            raw::CMARK_NODE_BLOCK_QUOTE => NodeType::BlockQuote,
            raw::CMARK_NODE_LIST => NodeType::List,
            raw::CMARK_NODE_ITEM => NodeType::Item,
            raw::CMARK_NODE_CODE_BLOCK => NodeType::CodeBlock,
            raw::CMARK_NODE_HTML => NodeType::Html,
            raw::CMARK_NODE_PARAGRAPH => NodeType::Paragraph,
            raw::CMARK_NODE_HEADER => NodeType::Header,
            raw::CMARK_NODE_HRULE => NodeType::Hrule,
            raw::CMARK_NODE_TEXT => NodeType::Text,
            raw::CMARK_NODE_SOFTBREAK => NodeType::SoftBreak,
            raw::CMARK_NODE_LINEBREAK => NodeType::LineBreak,
            raw::CMARK_NODE_CODE => NodeType::Code,
            raw::CMARK_NODE_INLINE_HTML => NodeType::InlineHtml,
            raw::CMARK_NODE_EMPH => NodeType::Emph,
            raw::CMARK_NODE_STRONG => NodeType::Strong,
            raw::CMARK_NODE_LINK => NodeType::Link,
            raw::CMARK_NODE_IMAGE => NodeType::Image,
        }
    }

    /// Obtain the raw `cmark_node_type` enum value from a `NodeType`.
    fn raw(&self) -> raw::cmark_node_type {
        match *self {
            NodeType::None => raw::CMARK_NODE_NONE,
            NodeType::Document => raw::CMARK_NODE_DOCUMENT,
            NodeType::BlockQuote => raw::CMARK_NODE_BLOCK_QUOTE,
            NodeType::List => raw::CMARK_NODE_LIST,
            NodeType::Item => raw::CMARK_NODE_ITEM,
            NodeType::CodeBlock => raw::CMARK_NODE_CODE_BLOCK,
            NodeType::Html => raw::CMARK_NODE_HTML,
            NodeType::Paragraph => raw::CMARK_NODE_PARAGRAPH,
            NodeType::Header => raw::CMARK_NODE_HEADER,
            NodeType::Hrule => raw::CMARK_NODE_HRULE,
            NodeType::Text => raw::CMARK_NODE_TEXT,
            NodeType::SoftBreak => raw::CMARK_NODE_SOFTBREAK,
            NodeType::LineBreak => raw::CMARK_NODE_LINEBREAK,
            NodeType::Code => raw::CMARK_NODE_CODE,
            NodeType::InlineHtml => raw::CMARK_NODE_INLINE_HTML,
            NodeType::Emph => raw::CMARK_NODE_EMPH,
            NodeType::Strong => raw::CMARK_NODE_STRONG,
            NodeType::Link => raw::CMARK_NODE_LINK,
            NodeType::Image => raw::CMARK_NODE_IMAGE,
        }
    }
}

/// The type of CommonMark list.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum ListType {
    NoList,
    Bullet,
    Ordered
}

impl Binding for ListType {
    type Raw = raw::cmark_list_type;

    unsafe fn from_raw(raw: raw::cmark_list_type) -> ListType {
        match raw {
            raw::CMARK_NO_LIST => ListType::NoList,
            raw::CMARK_BULLET_LIST => ListType::Bullet,
            raw::CMARK_ORDERED_LIST => ListType::Ordered,
        }
    }

    fn raw(&self) -> raw::cmark_list_type {
        match *self {
            ListType::NoList => raw::CMARK_NO_LIST,
            ListType::Bullet => raw::CMARK_BULLET_LIST,
            ListType::Ordered => raw::CMARK_ORDERED_LIST,
        }
    }
}

// The type of list delimiter in an ordered list.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum DelimType {
    NoDelim,
    /// Numbers are written as `1.`
    Period,
    /// Numbers are written as `1)`
    Paren
}

impl Binding for DelimType {
    type Raw = raw::cmark_delim_type;

    unsafe fn from_raw(raw: raw::cmark_delim_type) -> DelimType {
        match raw {
            raw::CMARK_NO_DELIM => DelimType::NoDelim,
            raw::CMARK_PERIOD_DELIM => DelimType::Period,
            raw::CMARK_PAREN_DELIM => DelimType::Paren,
        }
    }

    fn raw(&self) -> raw::cmark_delim_type {
        match *self {
            DelimType::NoDelim => raw::CMARK_NO_DELIM,
            DelimType::Period => raw::CMARK_PERIOD_DELIM,
            DelimType::Paren => raw::CMARK_PAREN_DELIM,
        }
    }
}

/// The event types that may be produced by a node iterator.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum EventType {
    None,
    Done,
    Enter,
    Exit
}

impl Binding for EventType {
    type Raw = raw::cmark_event_type;

    unsafe fn from_raw(raw: raw::cmark_event_type) -> EventType {
        match raw {
            raw::CMARK_EVENT_NONE => EventType::None,
            raw::CMARK_EVENT_DONE => EventType::Done,
            raw::CMARK_EVENT_ENTER => EventType::Enter,
            raw::CMARK_EVENT_EXIT => EventType::Exit,
        }
    }

    fn raw(&self) -> raw::cmark_event_type {
        match *self {
            EventType::None => raw::CMARK_EVENT_NONE,
            EventType::Done => raw::CMARK_EVENT_DONE,
            EventType::Enter => raw::CMARK_EVENT_ENTER,
            EventType::Exit => raw::CMARK_EVENT_EXIT,
        }
    }
}

/// Options for parsing and rendering a node tree.
bitflags! {
    flags CmarkOptions: i32 {
        #[doc="Default writer options"]
        const DEFAULT = raw::CMARK_OPT_DEFAULT as i32,
        #[doc="Include a `data-sourcepos` attribute on block elements"]
        const SOURCEPOS = raw::CMARK_OPT_SOURCEPOS as i32,
        #[doc="Render `softbreak` elements as hard line breaks"]
        const HARDBREAKS = raw::CMARK_OPT_HARDBREAKS as i32,
        #[doc="Normalize the tree by consolidating adjacent text nodes"]
        const NORMALIZE = raw::CMARK_OPT_NORMALIZE as i32,
        #[doc="Convert straight quotes to curly quotes, `---` to `—`, and `--` to `–`"]
        const SMART = raw::CMARK_OPT_SMART as i32,
    }
}

impl Binding for CmarkOptions {
    type Raw = libc::c_int;

    unsafe fn from_raw(raw: libc::c_int) -> CmarkOptions {
        CmarkOptions::from_bits_truncate(Binding::from_raw(raw))
    }

    fn raw(&self) -> libc::c_int {
        self.bits as libc::c_int
    }
}

pub fn cmark_version() -> i32 {
    raw::cmark_version as i32
}

pub fn version<'a>() -> &'a str {
    unsafe {
        str::from_utf8(
            CStr::from_ptr(raw::cmark_version_string).to_bytes()).unwrap()
    }
}