Generate html/js/css with rust

Related tags

Web programming rtml
Overview

rtml

在 Rust 中尝试描述 html/css

在通常情况下要在 Rust 中生成 html 除了手动拼字符串外最常用的方式是使用模板引擎, 但这回带来2个主要问题

  1. 需要学习模板语法, 且不同模板引擎之间语法可能差异很大
  2. 在模板中难以利用 Rust 类型系统, 以及在 IDE 中支持可能不佳

那有没有可能在 Rust 实现 html 那样可以比较方便地展示文档树形结构, 同时又能充分利用 Rust 的类型系统呢, rtml 正是这个想法的一次尝试.

使用

先看一个简单例子

use rtml::tags::*;

fn main() {
    let page = html(
        h1("hello world!")
    );
    println!("{}", page);
}

生成 html

<html>
    <h1>
        hello world!
    h1>
html>

可以看到在 rtml 中, html, h1 和 html 的 ,

标签对应, 往 html 传入的参数是 的 children, html(h1(...)) 就如同往 标签中间放入 h1 标签一样.

注意, 在 rtml 中, h1, div, 和 title 等都是普通的函数(以下简称标签函数), 没有使用任何过程宏, 对于有 LSP 支持的 IDE 来说, 悬浮显示, 智能补全, 跳转定义等都没有问题. 而且 rtml 对于标准 html 标签都提供了详细的文档, 方便使用.

rtml_hover

灵活的 children 传入

对于 rtml 中任意一个标签函数, 其参数虽然只有一个, 但可传入其中, 作为 children 的类型非常多.

常见的字面类型

// 布尔值
span(true)

// 浮点数
span(1.1f64)

// 字符串
span("hello")

// format! 也OK
let name = "Rookie";
span(format!("hello {name}"))

// 整数
span(666usize)

更多类型

>() ) // const generic array 也可以, 如传入 [Span; 2] div([span(1), span(2)])">
// 任意一个实现 Tag trait 的结构体
div(h1("hello"))

// 元组也OK
// div 传入的参数是一个 (H1, Hr, P) 元组
div((
 h1("hello"),
 hr(()),
 p("this is some desc")
 ))

// Vec 也行
// ul 传入的是一个 
ul(
    [1, 2, 3]
        .iter()
        .map(|i| li(format!("item {i}")))
        .collect::<Vec<_>>()
)

// const generic array 也可以, 如传入 [Span; 2]
div([span(1), span(2)])

设置属性, 样式和绑定事件处理函数

在 html 标签上, 用户可以设置 id, class 样式等. 为了兼容 html 风格,样式设置格式, rtml 提供了 style!, 和 prop! 两个宏以方便属性设置. 而且 rtml 传入属性的方式也非常灵活.

如果查看 rtml 文档里某个标签函数参数结构体 xxArgs 的文档, 你会发现它实现很多 From 方法.

from_xxx

正是这些实现让 rtml 的构造函数很灵活.

对于 等标签, 通常我们不关系他的 children, 而是要传入 charset 等属性, 这时候你可以这样传参.

meta(prop! {
    charset = "utf-8"
})

对于某些标签来说, 属性, 样式和 children 都需要设置, 除了要求 children 必须在最后一个位置外, 属性和样式可以乱序

// 注意, 传入的一个三元素元组 
div((
    style!{color: "red"},
    prop! { id = "app" },
    p("hello")
))

下面是一个更复杂的例子

>()"#, ), )), div([1, 2, 3] .iter() .map(|i| p(format!("paragraph {}", i))) .collect::>()), hr(()), h2("任意字面量"), div(( style! { color: "black" }, (span(true), span(false), span(1u8), span(1.30f32)), )), hr(()), footer(b("power by Rust!")), ), )), ), )); println!("{}", page); }">
use rtml::prop;
use rtml::style;
use rtml::tags::*;

fn main() {
    let page = html((
        prop! { lang = "zh-cn" },
        (
            header((
                meta(prop! { charset = "utf-8" }),
                title("html file generated by Rust!"),
            )),
            body((
                style! {
                    color: "olive";
                    text-align: "center"
                },
                (
                    h1("WOW"),
                    hr(()),
                    h2("循环"),
                    pre((
                        style! {},
                        code(
                            r#"[1, 2, 3].iter().map(|i| p(format!("paragraph {}", i))).collect::>()"#,
                        ),
                    )),
                    div([1, 2, 3]
                        .iter()
                        .map(|i| p(format!("paragraph {}", i)))
                        .collect::<Vec<_>>()),
                    hr(()),
                    h2("任意字面量"),
                    div((
                        style! { color: "black" },
                        (span(true), span(false), span(1u8), span(1.30f32)),
                    )),
                    hr(()),
                    footer(b("power by Rust!")),
                ),
            )),
        ),
    ));
    println!("{}", page);
}

生成的 html

WOW


循环

            
                [1, 2, 3].iter().map(|i| p(format!("paragraph {}", i))).collect::>()
            
        

paragraph 1

paragraph 2

paragraph 3


任意字面量

true false 1 1.3

power by Rust!
">
<html lang=zh-cn>
    <header>
        <meta charset=utf-8>
        meta>
        <title>
            html file generated by Rust!
        title>
    header>
    <body style="color: olive; text-align: center; ">
        <h1>
            WOW
        h1>
        <hr>
        hr>
        <h2>
            循环
        h2>
        <pre>
            <code>
                [1, 2, 3].iter().map(|i| p(format!("paragraph {}", i))).collect::<Vec<_>>()
            code>
        pre>
        <div>
            <p>
                paragraph 1
            p>
            <p>
                paragraph 2
            p>
            <p>
                paragraph 3
            p>
        div>
        <hr>
        hr>
        <h2>
            任意字面量
        h2>
        <div style="color: black; ">
            <span>
                true
            span>
            <span>
                false
            span>
            <span>
                1
            span>
            <span>
                1.3
            span>
        div>
        <hr>
        hr>
        <footer>
            <b>
                power by Rust!
            b>
        footer>
    body>
html>

浏览器打开效果如下.

rendered

TODO 事件函数绑定

You might also like...
A Rust web framework

cargonauts - a Rust web framework Documentation cargonauts is a Rust web framework intended for building maintainable, well-factored web apps. This pr

openapi schema serialization for rust

open api Rust crate for serializing and deserializing open api documents Documentation install add the following to your Cargo.toml file [dependencies

📮 An elegant Telegram bots framework for Rust
📮 An elegant Telegram bots framework for Rust

teloxide A full-featured framework that empowers you to easily build Telegram bots using the async/.await syntax in Rust. It handles all the difficult

Sōzu HTTP reverse proxy, configurable at runtime, fast and safe, built in Rust. It is awesome! Ping us on gitter to know more

Sōzu · Sōzu is a lightweight, fast, always-up reverse proxy server. Why use Sōzu? Hot configurable: Sozu can receive configuration changes at runtime

A Rust application which funnels external webhook event data to an Urbit chat.
A Rust application which funnels external webhook event data to an Urbit chat.

Urbit Webhook Funnel This is a simple Rust application which funnels external webhook event data to an Urbit chat. This application is intended to be

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.

Starlight is a JS engine in Rust which focuses on performance rather than ensuring 100% safety of JS runtime.

starlight Starlight is a JS engine in Rust which focuses on performance rather than ensuring 100% safety of JS runtime. Features Bytecode interpreter

A rust web framework with safety and speed in mind.

darpi A web api framework with speed and safety in mind. One of the big goals is to catch all errors at compile time, if possible. The framework uses

A web framework for Rust.

Rocket Rocket is an async web framework for Rust with a focus on usability, security, extensibility, and speed. #[macro_use] extern crate rocket; #[g

Owner
多少事,从来急。天地转,光阴迫。
null
A Rust library to extract useful data from HTML documents, suitable for web scraping.

select.rs A library to extract useful data from HTML documents, suitable for web scraping. NOTE: The following example only works in the upcoming rele

Utkarsh Kukreti 829 Dec 28, 2022
A html document syntax and operation library written in Rust, use APIs similar to jQuery.

Visdom A server-side html document syntax and operation library written in Rust, it uses apis similar to jQuery, left off the parts thoes only worked

轩子 80 Dec 21, 2022
Sauron is an html web framework for building web-apps. It is heavily inspired by elm.

sauron Guide Sauron is an web framework for creating fast and interactive client side web application, as well as server-side rendering for back-end w

Jovansonlee Cesar 1.7k Dec 26, 2022
The simplest build-time framework for writing web apps with html templates and typescript

Encoped A build-time fast af tool to write static apps with html and TypeScript Features Template-based ESLint, Prettier and Rollup integration No ext

null 1 Dec 11, 2021
jq, but for HTML

hq jq, but for HTML. hq reads HTML and converts it into a JSON object based on a series of CSS selectors. The selectors are expressed in a similar way

Tom Forbes 511 Jan 5, 2023
This code generate coinjoin transaction whici has two inputs and two outputs

How to create coinjoin transaction This code generate coinjoin transaction whici has two inputs and two outputs. First, we create two trasactions. The

kanna 2 Jun 25, 2022
Rust Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.

Rust Embed Rust Custom Derive Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev. Y

Peter 1k Jan 5, 2023
Rust I18n is use Rust codegen for load YAML file storage translations on compile time, and give you a t! macro for simply get translation texts.

Rust I18n Rust I18n is use Rust codegen for load YAML file storage translations on compile time, and give you a t! macro for simply get translation te

Longbridge 73 Dec 27, 2022
lispr is a Rust macro that tries to implement a small subset of LISPs syntax in Rust

lispr lispr is a Rust macro that tries to implement a small subset of LISPs syntax in Rust. It is neither especially beautiful or efficient since it i

Jan Vaorin 0 Feb 4, 2022
Rust/Axum server implementation with PCR(Prisma Client Rust)

Realworld Rust Axum Prisma This project utilizes Rust with the Axum v0.7 framework along with the Prisma Client Rust to build a realworld application.

Neo 3 Dec 9, 2023