Expand description
This project is a reimplementation of the nice MJML markup language in Rust.
§How to use?
To use it you can simply update your Cargo.toml
by adding
[dependencies]
mrml = { version = "3" }
serde = { version = "1", features = ["derive"] }
And you can then just create a main.rs
with the following code
let root = mrml::parse("<mjml><mj-body></mj-body></mjml>").expect("parse template");
let opts = mrml::prelude::render::Options::default();
match root.element.render(&opts) {
Ok(content) => println!("{}", content),
Err(_) => println!("couldn't render mjml template"),
};
§Using mj-include
You can also use the mj-include
component by specifying a
loader.
use mrml::prelude::parser::ParserOptions;
use mrml::prelude::parser::memory_loader::MemoryIncludeLoader;
let loader = MemoryIncludeLoader::from(vec![("partial.mjml", "<mj-button>Hello</mj-button>")]);
let options = ParserOptions {
include_loader: Box::new(loader),
};
match mrml::parse_with_options("<mjml><mj-head /><mj-body><mj-include path=\"partial.mjml\" /></mj-body></mjml>", &options) {
Ok(_) => println!("Success!"),
Err(err) => eprintln!("Something went wrong: {err:?}"),
}
§Using mj-include
with an async loader
If you want to use the async version to fetch the includes, you’ve to enable
the async
feature and the required loaders (http-loader-async-reqwest
in
this example).
use mrml::prelude::parser::http_loader::{AsyncReqwestFetcher, HttpIncludeLoader};
use mrml::prelude::parser::memory_loader::MemoryIncludeLoader;
use mrml::prelude::parser::local_loader::LocalIncludeLoader;
use mrml::prelude::parser::multi_loader::MultiIncludeLoader;
use mrml::prelude::parser::noop_loader::NoopIncludeLoader;
use mrml::prelude::parser::loader::AsyncIncludeLoader;
use mrml::prelude::parser::AsyncParserOptions;
use mrml::prelude::render::RenderOptions;
use std::path::PathBuf;
use std::sync::Arc;
let resolver = MultiIncludeLoader::<Box<dyn AsyncIncludeLoader + Send + Sync + 'static>>::new()
.with_starts_with("memory://", Box::new(MemoryIncludeLoader::from(vec![("basic.mjml", "<mj-button>Hello</mj-button>")])))
.with_starts_with("file://", Box::new(LocalIncludeLoader::new(PathBuf::default().join("resources").join("compare").join("success"))))
.with_starts_with("https://", Box::new(HttpIncludeLoader::<AsyncReqwestFetcher>::allow_all()))
.with_any(Box::<NoopIncludeLoader>::default());
let parser_options = AsyncParserOptions {
include_loader: Box::new(resolver),
};
let render_options = RenderOptions::default();
let json = r#"<mjml>
<mj-body>
<mj-include path="file://basic.mjml" />
<mj-include path="memory://basic.mjml" />
</mj-body>
</mjml>"#;
match mrml::async_parse_with_options(json, Arc::new(parser_options)).await {
Ok(mjml) => match mjml.render(&render_options) {
Ok(html) => println!("{html}"),
Err(err) => eprintln!("Couldn't render template: {err:?}"),
},
Err(err) => eprintln!("Couldn't parse template: {err:?}"),
}
§Using mrml
in Python
This crate can also be used in Python. The crate is available with pypi and you can find some documentation here.
import mrml
# without options
result = mrml.to_html("<mjml></mjml>")
assert result.startswith("<!doctype html>")
# with options
parser_options = mrml.ParserOptions(include_loader = mrml.memory_loader({
'hello-world.mjml': '<mj-text>Hello World!</mj-text>',
}))
result = mrml.to_html("<mjml><mj-body><mj-include path=\"hello-world.mjml\" /></mj-body></mjml>", parser_options = parser_options)
assert result.startswith("<!doctype html>")
§Why?
A Node.js server rendering an MJML template takes around 20 MB of RAM at
startup and 130 MB under stress test. In Rust, less than 1.7 MB at
startup and a bit less that 3 MB under stress test. The Rust version can
also handle twice as many requests per second. You can perform the
benchmarks by running bash script/run-bench.sh
.
Also, the JavaScript implementation cannot be run in the browser; the Rust one (and WebAssembly one) can be.
Modules§
- comment
- mj_
accordion - Module containing the
mj-accordion
element as defined in the documentation. - mj_
accordion_ element - mj_
accordion_ text - mj_
accordion_ title - mj_
attributes - mj_
attributes_ all - mj_
attributes_ class - mj_
attributes_ element - mj_body
- mj_
breakpoint - mj_
button - mj_
carousel - mj_
carousel_ image - mj_
column - mj_
divider - mj_font
- mj_
group - mj_head
- mj_hero
- mj_
image - mj_
include - mj_
navbar - mj_
navbar_ link - mj_
preview - mj_raw
- mj_
section - mj_
social - mj_
social_ element - mj_
spacer - mj_
style - mj_
table - mj_text
- mj_
title - mj_
wrapper - mjml
- node
- prelude
- text
Functions§
- parse
- Function to parse a raw mjml template using the default parsing options.
- parse_
with_ options - Function to parse a raw mjml template with some parsing
options. This function is just an
alias to the
Mjml::parse_with_options
function.