Under heavy research and development, please don't use this yet!
rsx
A compiler plugin for using RSX (JSX-like syntax) as advanced templating and metaprogramming in Rust.
Made possible by the Self Tokenize library, a trait derive for transferring data structures outside of procedural macros from compile-time to run-time.
Take a look at the RSX DOM and RSX Stylesheet crates for the underlying types and implementations, or the RSX parser and Servo CSS parser parsing backends for the parser combinators. To convert these data structures into lower level rendering primitives, see RSX Layout and RSX Primitives, which integrate with Facebook's YOGA library and Servo's Graphics component for building a Servo WebRender-powered gfx::display_list::DisplayList
. Finally, rendering to pixels is done via the RSX Renderers crate.
For quick and easy example demos, simply check out here.
Purpose
This compiler plugin allows you to freely intertwine JSX-like syntax anywhere into your Rust code.
RSX implements all of the JSX grammar. The purpose and benefits of JSX and RSX are equivalent.
How to use
To get access to the rsx!
, css!
macros, add this to your Cargo.toml
file:
[dependencies]
rsx = { git = "https://github.com/victorporof/rsx.git" }
rsx-primitives = { git = "https://github.com/victorporof/rsx-primitives.git" }
Then, simply import the library into your code and use the rsx!
, css!
macros to parse RSX and CSS into rsx_dom::DOMNode
, or rsx_dom::Stylesheet
data structures respectively.
For example:
#![feature(proc_macro)]
extern crate rsx;
extern crate rsx_primitives;
use rsx::{rsx, css};
use rsx_primitives::rsx_stylesheet::types::*;
use rsx_primitives::rsx_dom::types::*;
let stylesheet: Stylesheet = css! { .foo { padding: 1px; } };
let node: DOMNode = rsx! { <div>Hello world!</div> };
Here's some code rendering the first example from Facebook's YOGA library:
let stylesheet: Stylesheet = css! {
.root {
width: 500px;
height: 120px;
flex-direction: row;
padding: 20px;
}
.image {
width: 80px;
margin-right: 20px;
}
.text {
height: 25px;
align-self: center;
flex-grow: 1;
}
};
let node: DOMNode = rsx! {
<view style={stylesheet.take(".root")}>
<image style={stylesheet.take(".image")} src="..." />
<text style={stylesheet.take(".text")}>
Hello world!
</text>
</view>
};
Composability
- Mixing Rust and RSX is possible
- Stylesheets can be included as separate CSS files.
- Composing components is achieved through simple function calls (for now).
example.css
.root {
width: 500px;
height: 120px;
flex-direction: row;
padding: 20px;
}
.image {
width: 80px;
margin-right: 20px;
}
.text {
height: 25px;
align-self: center;
flex-grow: 1;
}
example.rs
fn greeting_str(name: &str) -> String {
format!("Hello {}!", name)
}
fn render_greeting(name: &str) -> DOMNode {
let stylesheet = css!("example.css");
rsx! {
<text style={stylesheet.take(".text")}>
{ greeting_str(name) }
</text>
}
}
fn render_children(name: Option<&str>, image: DOMNode) -> DOMNode {
rsx! {
<view>
{ image }
{
match name {
Some(ref n) => render_greeting(n),
None => <text>No greetings!</text>
}
}
</view>
}
}
fn render_root() -> DOMNode {
let stylesheet = css!("example.css");
rsx! {
<view style={stylesheet.take(".root")}>
{
let name = Some("world");
let image = <image style={stylesheet.take(".image")} src="..." />;
render_children(name, image)
}
</view>
}
}
let node = render_root();
The css!
macro returns a rsx_dom::Stylesheet
instance (coming from the RSX Stylesheet library re-exported through the RSX DOM library), because parsing CSS happens at compile-time.
let styles: rsx_stylesheet::Stylesheet = css! { ... }
The rsx!
macro returns a rsx_dom::DOMNode
instance (coming from the RSX DOM library). The convertion is automatic between rsx_parser::RSXElement
abstract syntax trees to the more convenient rsx_dom::DOMNode
elements, because the AST is directly tokenized into a DOM tree to avoid any runtime work! Templating is thus a zero cost abstraction.
let node: rsx_dom::DOMNode = rsx! { ... }