Rust — mdwire-core
The core. Standard library only — no dependencies. The crate is mdwire-core; the library is
imported as mdwire. Full rustdoc: docs.rs/mdwire-core.
cargo add mdwire-core
render
use mdwire::{render, Channel};
let parts: Vec<String> = render("## 제목\n\n**굵게** 있는 문단", Channel::TelegramHtml);
assert_eq!(parts, vec!["<b>제목</b>\n\n<b>굵게</b> 있는 문단"]);
render(input: &str, channel: Channel) -> Vec<String> converts a finished document. Over the limit,
it splits where no markup is open — chosen from the structure, never by cutting the rendered text.
render_with
use mdwire::{render_with, Channel, Dialect, Options};
let options = Options { from: Dialect::SlackMrkdwn, ..Default::default() };
let out = render_with("*굵게* 는 **영향 범위", Channel::SlackMarkdown, options);
assert_eq!(out.parts, vec!["**굵게** 는 **영향 범위**"]);
assert_eq!(out.repairs.closed_emphasis, 1);
render_with(input: &str, channel: Channel, options: Options) -> Rendered, where
Rendered { parts: Vec<String>, repairs: Repairs }.
Options
pub struct Options {
pub from: Dialect, // default Dialect::Markdown
pub limit: Option<usize>, // None = channel.limit(); below MIN_LIMIT (256) is raised
pub html: HtmlOptions, // Channel::Html only
}
pub struct HtmlOptions {
pub line_breaks: LineBreaks, // Br (default) | Space
pub images: Images, // Link (default) | Load
pub schemes: Option<Vec<String>>, // None = http, https, mailto; Some replaces the default
}
Fields may be added — build it as Options { from, ..Default::default() }. MIN_LIMIT is 256.
Streamer
use mdwire::{Channel, Streamer};
let mut s = Streamer::new(Channel::TelegramHtml);
let mut acc = String::new();
s.push_into("앞말 **굵", &mut acc);
assert_eq!(acc, "앞말 "); // final so far
assert_eq!(format!("{acc}{}", s.preview()), "앞말 <b>굵</b>"); // what to draw now
s.push_into("게** 끝", &mut acc);
let last = format!("{acc}{}", s.preview());
s.finish_into(&mut acc);
assert_eq!(acc, last);
assert!(!s.revised()); // the last frame was the result
Streamer::new(channel) · Streamer::with_options(channel, options) |
One streamer per document. |
push(&mut self, chunk) -> &str |
What is safe to send now; borrowed until the next call. |
push_into(&mut self, chunk, out: &mut String) |
The same, appended to your buffer — no allocation per chunk. |
finish(&mut self) -> &str · finish_into(&mut self, out) |
The rest, with open markup closed. |
preview(&mut self) -> &str · preview_into(&mut self, out) |
The tail to append when redrawing the whole message. Costs as much as the open block; call it when you draw. |
close_open(&self, out: &mut String) |
Appends only the closers for block markup already sent. |
revised(&self) -> bool |
After finish: does the result differ from the last preview? true means “may differ”. |
repairs(&self) -> Repairs |
What was repaired so far. |
The contract — push output is final, pieces concatenated equal render — is on
Concepts.
Channel and Dialect
pub enum Channel { TelegramHtml, SlackMarkdown, GithubMarkdown, NotionMarkdown, Plain, Html }
pub enum Dialect { Markdown, SlackMrkdwn }
NotionMarkdown next release
Channel::name(self) -> &'static str · Channel::parse(&str) -> Option<Channel> |
The string names ("telegram-html", …). |
Channel::all() |
Every channel. |
Channel::limit(self) -> usize |
The part limit; usize::MAX for Html. |
Dialect::name · Dialect::parse |
"markdown", "slack-mrkdwn". |
Repairs
pub struct Repairs {
pub closed_emphasis: usize,
pub closed_fence: usize,
pub reverted_code_span: usize,
pub dropped_marker: usize,
// channel rewrites — next release
pub escaped_char: usize,
pub tag_emphasis: usize,
pub stripped_html: usize,
pub rewritten_bullet: usize,
pub rewritten_table: usize,
pub converted_marker: usize,
}
any() — did it repair anything (the first four). changed() next release — did it repair or rewrite
anything. What each field counts: Concepts.
mdwire::width
char_width(char) -> usize (0, 1 or 2), str_width(&str) -> usize and is_cjk(char) -> bool —
the display widths the core uses to align monospace tables.