<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TORM · Tokio Async ORM for Rust</title>
<meta name="description" content="A Tokio-based async ORM for Rust with GORM-like API, supporting SQLite, MySQL and PostgreSQL via native wire protocols.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🦀</text></svg>">
<style>
:root {
--bg: #0d1117;
--bg-sub: #161b22;
--bg-card: #1c2129;
--border: #30363d;
--border-light: #21262d;
--fg: #e6edf3;
--fg-muted: #8b949e;
--fg-subtle: #6e7681;
--accent: #58a6ff;
--accent-strong: #79c0ff;
--green: #3fb950;
--orange: #d29922;
--purple: #bc8cff;
--red: #f85149;
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
font-family: var(--font-sans);
background: var(--bg);
color: var(--fg);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* ── Header / Nav ─────────────────────────────── */
header {
position: sticky; top: 0; z-index: 100;
background: var(--bg-sub);
border-bottom: 1px solid var(--border-light);
backdrop-filter: blur(8px);
}
.nav {
max-width: 1280px; margin: 0 auto; padding: 0 24px;
height: 60px; display: flex; align-items: center; gap: 24px;
}
.nav .brand {
display: flex; align-items: center; gap: 10px;
font-weight: 700; font-size: 17px; color: var(--fg); cursor: pointer;
}
.nav .brand:hover { text-decoration: none; }
.brand .logo {
width: 30px; height: 30px; border-radius: 6px;
background: linear-gradient(135deg, var(--orange), var(--red));
display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 15px;
}
.nav .links { display: flex; gap: 20px; margin-left: auto; align-items: center; }
.nav .links a { color: var(--fg-muted); font-size: 14px; font-weight: 500; }
.nav .links a:hover { color: var(--fg); text-decoration: none; }
.nav .github-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 14px; border: 1px solid var(--border);
border-radius: 6px; color: var(--fg); font-size: 13px; font-weight: 500;
background: var(--bg-sub);
}
.nav .github-btn:hover { background: var(--bg-card); text-decoration: none; }
/* ── Hero ─────────────────────────────────────── */
.hero {
max-width: 1280px; margin: 0 auto; padding: 96px 24px 72px;
text-align: center;
}
.hero .badge-row {
display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-bottom: 24px;
}
.badge {
font-family: var(--font-mono); font-size: 12px;
padding: 4px 12px; border-radius: 20px;
border: 1px solid var(--border); background: var(--bg-sub); color: var(--fg-muted);
}
.badge.green { color: var(--green); border-color: rgba(63,185,80,.3); }
.badge.blue { color: var(--accent-strong); border-color: rgba(88,166,255,.3); }
.badge.purple{ color: var(--purple); border-color: rgba(188,140,255,.3); }
.hero h1 {
font-size: clamp(38px, 6vw, 64px); font-weight: 800; letter-spacing: -1.5px; line-height: 1.1;
margin-bottom: 20px;
background: linear-gradient(180deg, #f0f6fc 30%, #9da7b3 100%);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero h1 .accent { background: none; color: var(--accent-strong); -webkit-text-fill-color: currentColor; }
.hero p.tagline {
max-width: 720px; margin: 0 auto 36px;
font-size: 19px; color: var(--fg-muted);
}
.hero .cta { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.btn {
display: inline-flex; align-items: center; gap: 8px;
padding: 12px 26px; border-radius: 8px; font-size: 15px; font-weight: 600;
border: 1px solid var(--border); cursor: pointer; transition: all .15s;
}
.btn.primary { background: #238636; border-color: #2ea043; color: #fff; }
.btn.primary:hover { background: #2ea043; text-decoration: none; }
.btn.secondary { background: var(--bg-sub); color: var(--fg); }
.btn.secondary:hover { background: var(--bg-card); text-decoration: none; }
/* ── Sections ─────────────────────────────────── */
.section { max-width: 1280px; margin: 0 auto; padding: 64px 24px; }
.section h2 {
font-size: 28px; font-weight: 700; letter-spacing: -0.5px; margin-bottom: 8px;
}
.section .sub { color: var(--fg-muted); margin-bottom: 32px; font-size: 16px; }
.section .anchor {
display: flex; align-items: center; gap: 10px;
}
.section .anchor::before {
content: ""; width: 4px; height: 26px; border-radius: 2px;
background: linear-gradient(180deg, var(--accent), var(--green));
}
/* ── Feature cards ────────────────────────────── */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; }
.card {
background: var(--bg-sub); border: 1px solid var(--border-light); border-radius: 10px;
padding: 24px; transition: transform .15s, border-color .15s;
}
.card:hover { transform: translateY(-3px); border-color: var(--border); }
.card .icon { width: 40px; height: 40px; border-radius: 8px; display: grid; place-items: center; font-size: 20px; margin-bottom: 14px; }
.card h3 { font-size: 17px; margin-bottom: 8px; }
.card p { font-size: 14px; color: var(--fg-muted); }
/* ── DB support ───────────────────────────────── */
.db-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 18px; }
.db-card {
background: var(--bg-sub); border: 1px solid var(--border-light); border-radius: 10px;
padding: 28px 24px; text-align: center; transition: border-color .15s;
}
.db-card:hover { border-color: var(--border); }
.db-card .emoji { font-size: 40px; display: block; margin-bottom: 10px; }
.db-card .name { font-weight: 700; font-size: 16px; }
.db-card .tag {
display: inline-block; margin-top: 8px; font-family: var(--font-mono); font-size: 11px;
padding: 2px 10px; border-radius: 12px; background: rgba(63,185,80,.12); color: var(--green);
}
/* ── Code blocks ──────────────────────────────── */
pre {
background: var(--bg-sub); border: 1px solid var(--border-light); border-radius: 10px;
padding: 22px 24px; overflow-x: auto; font-family: var(--font-mono); font-size: 13.5px; line-height: 1.7;
}
pre code { color: var(--fg); }
.code-header {
display: flex; align-items: center; gap: 8px; padding: 10px 16px;
background: #0d1117; border: 1px solid var(--border-light); border-bottom: none;
border-radius: 10px 10px 0 0; font-size: 12px; color: var(--fg-muted); font-family: var(--font-mono);
}
.code-header .dot { width: 11px; height: 11px; border-radius: 50%; }
.code-header .dot:nth-child(1){ background:#ff5f57; } .code-header .dot:nth-child(2){ background:#febc2e; } .code-header .dot:nth-child(3){ background:#28c840; }
.code-block { margin: 18px 0; }
.code-block pre { border-radius: 0 0 10px 10px; }
.tok-kw { color: #ff7b72; }
.tok-fn { color: #d2a8ff; }
.tok-str { color: #a5d6ff; }
.tok-com { color: #8b949e; font-style: italic; }
.tok-ty { color: #79c0ff; }
.tok-num { color: #79c0ff; }
.tok-p { color: #e6edf3; }
/* ── Migration table ──────────────────────────── */
.table-wrap { overflow-x: auto; border: 1px solid var(--border-light); border-radius: 10px; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
thead { background: var(--bg-sub); }
th, td { text-align: left; padding: 12px 18px; border-bottom: 1px solid var(--border-light); }
th { color: var(--fg-muted); font-weight: 600; }
tr:last-child td { border-bottom: none; }
.arrow { color: var(--accent); font-weight: 700; margin: 0 6px; }
td code, .tag-inline { font-family: var(--font-mono); font-size: 12px; background: var(--bg-card); padding: 2px 8px; border-radius: 5px; color: var(--accent-strong); }
/* ── Footer ───────────────────────────────────── */
footer {
border-top: 1px solid var(--border-light); background: var(--bg-sub);
padding: 40px 24px; text-align: center; color: var(--fg-subtle); font-size: 13px;
}
footer a { color: var(--fg-muted); }
/* ── Responsive ───────────────────────────────── */
@media (max-width: 720px) {
.nav .links { display: none; }
.hero { padding: 64px 24px 48px; }
.section { padding: 48px 20px; }
}
</style>
</head>
<body>
<!-- ═══ Header ═══ -->
<header>
<div class="nav">
<div class="brand" onclick="scrollToTop()">
<div class="logo">T</div>
<span>TORM</span>
</div>
<div class="links">
<a href="#features">特性</a>
<a href="#databases">数据库</a>
<a href="#quickstart">快速开始</a>
<a href="#migration">数据迁移</a>
<a href="#api">API</a>
<a class="github-btn" href="https://github.com/WLmutou/torm" target="_blank" rel="noopener">
<svg height="16" width="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
GitHub
</a>
</div>
</div>
</header>
<!-- ═══ Hero ═══ -->
<section class="hero">
<div class="badge-row">
<span class="badge blue">v0.2.0</span>
<span class="badge green">100% Rust</span>
<span class="badge purple">Async / Tokio</span>
<span class="badge">Native Wire Protocols</span>
<span class="badge">MIT License</span>
</div>
<h1>基于 Tokio 的 Rust 异步 <span class="accent">ORM</span></h1>
<p class="tagline">
面向 Rust 的异步 ORM,采用 GORM 风格 API,通过原生协议驱动
直连 <strong>SQLite · MySQL · PostgreSQL</strong>,零额外数据库驱动依赖。
</p>
<div class="cta">
<a class="btn primary" href="#quickstart">
<svg height="16" width="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0Zm0 2.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11Zm0 1.5a1 1 0 0 1 .7.3l2 2a1 1 0 0 1-1.4 1.4L9 7.41V10a1 1 0 0 1-2 0V7.41L6.7 8.7a1 1 0 0 1-1.4-1.4l2-2A1 1 0 0 1 8 4Z"/></svg>
快速开始
</a>
<a class="btn secondary" href="#features">了解更多</a>
</div>
</section>
<!-- ═══ Features ═══ -->
<section class="section" id="features">
<div class="anchor"><h2>核心特性</h2></div>
<p class="sub">为生产级应用打造的现代 ORM 能力。</p>
<div class="grid">
<div class="card">
<div class="icon" style="background:rgba(88,166,255,.15);">⚡</div>
<h3>原生协议直连</h3>
<p>通过数据库原生 wire protocol 通信,无需 libmysql / libpq 等外部驱动,纯 Rust 实现,部署零负担。</p>
</div>
<div class="card">
<div class="icon" style="background:rgba(63,185,80,.15);">🌊</div>
<h3>Async / Tokio</h3>
<p>基于 Tokio 运行时的高并发异步访问,天然支持大规模并发场景。</p>
</div>
<div class="card">
<div class="icon" style="background:rgba(188,140,255,.15);">🧩</div>
<h3>GORM 风格 API</h3>
<p>链式查询构建器、Where/Order/Join/分页/聚合等,语法直观、上手快。</p>
</div>
<div class="card">
<div class="icon" style="background:rgba(210,168,255,.15);">📦</div>
<h3>派生宏模型</h3>
<p>通过 <code>#[derive(Model)]</code> 定义模型,自动生成表结构、字段映射与关联关系。</p>
</div>
<div class="card">
<div class="icon" style="background:rgba(242,151,24,.15);">💾</div>
<h3>三库驱动一体</h3>
<p>统一 API 同时支持 SQLite、MySQL、PostgreSQL,一套代码多库切换。</p>
</div>
<div class="card">
<div class="icon" style="background:rgba(248,81,73,.15);">🔒</div>
<h3>SQL 安全</h3>
<p>参数化查询与标识符安全检查,有效防止 SQL 注入,保障数据安全。</p>
</div>
<div class="card">
<div class="icon" style="background:rgba(63,185,80,.15);">🛠️</div>
<h3>迁移工具</h3>
<p>内置 MySQL ↔ PostgreSQL、SQLite ↔ PostgreSQL 等数据库间迁移 CLI 工具。</p>
</div>
<div class="card">
<div class="icon" style="background:rgba(121,192,255,.15);">📊</div>
<h3>监控与性能</h3>
<p>内置日志、SQL 监控与性能统计模块,助力定位瓶颈。</p>
</div>
</div>
</section>
<!-- ═══ Databases ═══ -->
<section class="section" id="databases">
<div class="anchor"><h2>支持的数据库</h2></div>
<p class="sub">通过原生协议驱动,无需安装任何外部数据库驱动。</p>
<div class="db-grid">
<div class="db-card">
<span class="emoji">🟠</span>
<div class="name">MySQL</div>
<span class="tag">原生 wire protocol</span>
</div>
<div class="db-card">
<span class="emoji">🐘</span>
<div class="name">PostgreSQL</div>
<span class="tag">原生 wire protocol</span>
</div>
<div class="db-card">
<span class="emoji">🗄️</span>
<div class="name">SQLite</div>
<span class="tag">bundled · 零外部依赖</span>
</div>
</div>
</section>
<!-- ═══ Quickstart ═══ -->
<section class="section" id="quickstart">
<div class="anchor"><h2>快速开始</h2></div>
<p class="sub">在你的 <code>Cargo.toml</code> 中添加依赖并开始。</p>
<div class="code-block">
<div class="code-header"><span class="dot"></span><span class="dot"></span><span class="dot"></span> Cargo.toml</div>
<pre><code><span class="tok-kw">[dependencies]</span>
torm = <span class="tok-str">"0.2"</span>
tokio = { version = <span class="tok-str">"1"</span>, features = [<span class="tok-str">"full"</span>] }</code></pre>
</div>
<div class="code-block">
<div class="code-header"><span class="dot"></span><span class="dot"></span><span class="dot"></span> src/main.rs</div>
<pre><code><span class="tok-kw">use</span> torm::db::database::Database;
<span class="tok-kw">use</span> torm::orm::model::Model;
<span class="tok-kw">use</span> torm_derive::Model;
<span class="tok-com">// 定义模型</span>
<span class="tok-p">#[</span><span class="tok-kw">derive</span><span class="tok-p">(</span>Model<span class="tok-p">)]</span>
<span class="tok-kw">struct</span> <span class="tok-ty">User</span> {
<span class="tok-kw">#[id</span> <span class="tok-kw">(auto_increment)]</span>
id: i64,
name: <span class="tok-ty">String</span>,
age: <span class="tok-ty">Option</span><i32>,
}
<span class="tok-p">#[</span><span class="tok-kw">tokio</span>::<span class="tok-kw">main</span><span class="tok-p">]</span>
<span class="tok-kw">async fn</span> <span class="tok-fn">main</span>() -> torm::db::error::<span class="tok-ty">Result</span><()> {
<span class="tok-com">// 连接数据库(MySQL / PostgreSQL / SQLite 通用)</span>
<span class="tok-kw">let</span> db = Database::mysql(<span class="tok-str">"127.0.0.1"</span>, <span class="tok-num">3306</span>, <span class="tok-str">"mydb"</span>, <span class="tok-str">"root"</span>, <span class="tok-str">"pass"</span>).await?;
<span class="tok-com">// 插入</span>
<span class="tok-kw">let</span> user = User { id: <span class="tok-num">0</span>, name: <span class="tok-str">"Alice"</span>.into(), age: <span class="tok-ty">Some</span>(<span class="tok-num">25</span>) };
user.insert(&db).await?;
<span class="tok-com">// 查询</span>
<span class="tok-kw">let</span> users = User::query(&db).await?;
<span class="tok-kw">for</span> u <span class="tok-kw">in</span> users { println!(<span class="tok-str">"{:?}"</span>, u.name); }
<span class="tok-ty">Ok</span>(())
}</code></pre>
</div>
</section>
<!-- ═══ Migration ═══ -->
<section class="section" id="migration">
<div class="anchor"><h2>数据库迁移工具</h2></div>
<p class="sub">内置 CLI 工具,在数据库之间迁移表结构与数据。</p>
<div class="table-wrap">
<table>
<thead>
<tr><th>工具</th><th>迁移方向</th><th>示例</th></tr>
</thead>
<tbody>
<tr>
<td><code>mysql2postgresql</code></td>
<td>MySQL <span class="arrow">→</span> PostgreSQL</td>
<td><span class="tag-inline">--mhost 127.0.0.1 --mport 3306 --mdb mydb ...</span></td>
</tr>
<tr>
<td><code>postgresql2mysql</code></td>
<td>PostgreSQL <span class="arrow">→</span> MySQL</td>
<td><span class="tag-inline">--phost 127.0.0.1 --pport 5432 --pdb mydb ...</span></td>
</tr>
<tr>
<td><code>sqlite2postgres</code></td>
<td>SQLite <span class="arrow">→</span> PostgreSQL</td>
<td><span class="tag-inline">./sqlite2postgres data.db --phost ...</span></td>
</tr>
<tr>
<td><code>postgres2sqlite</code></td>
<td>PostgreSQL <span class="arrow">→</span> SQLite</td>
<td><span class="tag-inline">./postgres2sqlite out.db --phost ...</span></td>
</tr>
</tbody>
</table>
</div>
<div class="code-block" style="margin-top:20px;">
<div class="code-header"><span class="dot"></span><span class="dot"></span><span class="dot"></span> 迁移示例</div>
<pre><code><span class="tok-com"># MySQL → PostgreSQL(不指定 --tables 默认迁移全部表)</span>
./target/release/mysql2postgresql \
--mhost 127.0.0.1 --mport 3306 --mdb mydb --muser root --mpass pw \
--phost 127.0.0.1 --pport 5432 --pdb mydb --puser postgres --ppass pw
<span class="tok-com"># 可选参数:--tables t1,t2 / --batch 1000 / --create-only / --data-only</span></code></pre>
</div>
</section>
<!-- ═══ API ═══ -->
<section class="section" id="api">
<div class="anchor"><h2>核心 API</h2></div>
<p class="sub">统一的抽象层,一套代码访问多种数据库。</p>
<div class="table-wrap">
<table>
<thead>
<tr><th>模块</th><th>说明</th></tr>
</thead>
<tbody>
<tr><td><code>db::database</code></td><td>统一 <code>Database</code> / <code>Transaction</code> 抽象,MySQL / PostgreSQL / SQLite 通用</td></tr>
<tr><td><code>orm::model</code></td><td>模型 trait 与 <code>#[derive(Model)]</code> 派生宏</td></tr>
<tr><td><code>orm::query</code></td><td>链式查询构建器:Where / Order / Pagination</td></tr>
<tr><td><code>orm::advanced_query</code></td><td>高级查询:Join / Aggregation / Union / Subquery</td></tr>
<tr><td><code>orm::relations</code></td><td>关联关系:belongs_to / has_many 等</td></tr>
<tr><td><code>orm::migration</code></td><td>Schema 迁移工具</td></tr>
<tr><td><code>db::pool</code></td><td>连接池</td></tr>
<tr><td><code>monitoring</code></td><td>日志、SQL 监控与性能统计</td></tr>
</tbody>
</table>
</div>
</section>
<!-- ═══ Footer ═══ -->
<footer>
<p><strong>TORM</strong> · A Tokio-based async ORM for Rust</p>
<p style="margin-top:8px;">
<a href="https://github.com/WLmutou/torm" target="_blank" rel="noopener">GitHub</a>
<span style="margin:0 8px;color:var(--border)">·</span>
<a href="https://docs.rs/torm" target="_blank" rel="noopener">docs.rs</a>
<span style="margin:0 8px;color:var(--border)">·</span>
<span>MIT License</span>
</p>
</footer>
<script>
function scrollToTop() { window.scrollTo({ top: 0, behavior: 'smooth' }); }
</script>
</body>
</html>