rustpress 0.1.27

增量编译倒分页无后端 Rust 纯静态博客程序
Documentation
# RustPress — 增量编译倒分页无后端 Rust 纯静态博客程序

一个用 Rust 构建的无后端静态博客程序,支持增量编译与倒分页。每次构建只重建受影响的页面(首页及相关标签/分类/年份页),无论文章是 1 篇还是几千篇,构建速度都保持稳定。内置 Tera 模板、纯前端搜索(search.json + JS)、RSS 与 Sitemap;并可通过 `source/build.toml` 的 `compile_mode` 在本地与 CI 中按需切换增量或全量构建。

## 特性

- 🚀 **快速**:使用Rust语言编写,编译速度快,生成网站高效
- 📝 **支持Markdown**:使用Markdown格式编写文章,简单易用
- 🎨 **模板系统**:使用Tera模板引擎,支持自定义网站外观
- 📦 **轻量级**:无运行时依赖,生成的网站可以直接部署
- 🔧 **简单易用**:提供直观的命令行界面

## 设计理念

RustPress采用**倒分页设计**,整体按日期降序、固定每页 `M = homepage.posts_per_page`(可配置)。命名规则:`index.html` 表示第 `n` 页(最新页),`index(n-1).html`、…、`index1.html` 依次更旧,其中 `index1.html` 为最早页。余数分配策略为“余数放到首页”:当总数 `len` 不是 `M` 的整数倍时,首页 `index.html` 展示余数 `r = len % M` 条,其余各页(含 `index1.html`)均满 `M` 条。范围文案统一按“最旧为 1 开始编号”显示:例如 `index1.html` 显示“第 1 到 M 条”、`index2.html` 显示“第 M+1 到 2M 条”、首页显示“第 len - r + 1 到 len 条”(若 `r = 0` 则为“第 (n-1)M+1 到 nM 条”)。导航语义也采用倒分页:上一页指向更“新”,下一页指向更“旧”,首页无“上一页”,`index1.html` 无“下一页”。该设计支持增量编译:新增文章只影响最新页及其上游页面,旧页面保持稳定,构建效率随文章数量线性增长。单标签文章列表页沿用相同的倒分页与余数策略。

**归档页设计**:按年归档,archives.html为归档首页,包含年份索引链接(倒序排列)。年份归档页位于archives/2024.html等路径,文章不分页,已过完年份的归档页无需重新生成,每次编译只生成总归档页和当前年份归档页。

**分类页设计**:categories.html展示source目录结构,便于结构化写作。分类页只显示分类关系和文章数量,点击分类跳转到子分类文章列表页。子分类文章列表页不分页,直接展示该分类下所有文章。采用扁平URL结构:categories/技术架构/微服务架构/index.html、categories/技术架构/index.html。

**标签页设计**:tags.html展示所有标签及文章数目,不展示文章列表。点击标签跳转到单标签文章列表页,与首页相同,也采用倒分页设计:第1页包含最旧文章,最大页数包含最新文章。URL结构例如:tags/rust/index.html(第1页)、tags/rust/index3.html(第3页)、tags/rust/index2.html(第2页)。

**搜索页设计**:search.html作为搜索功能入口,采用纯前端实现方案。构建时预生成search.json索引文件,客户端通过JavaScript在内存中完成实时搜索,无需后端支持,实现静态网站的高效搜索体验。

**侧边栏设计**:采用组件化设计,在以下页面展示侧边栏:
- **首页**(index.html)- 已实现,展示网站概览和导航

侧边栏内容包含:
- **作者信息** - 姓名、头像、简介、位置、社交链接
- **广告位2** - 可配置的推广内容
- **热门文章** - 基于标签和分类相似度计算的智能推荐文章列表(已实现)
- **热门分类** - 最近更新的一些分类
- **热门标签** - 文章最多的一些标签
- **广告位3** - 可配置的推广内容

设计原则是保持内容稳定性与用户体验的平衡,侧边栏内容以相对稳定信息为主,同时包含必要的动态更新内容。相关文章功能采用智能算法,根据当前文章的标签和分类计算相似度,为用户提供真正有价值的内容推荐。

## 文件复制策略(重要)

RustPress 仅解析并渲染 `source` 目录中的 Markdown(`.md`)为 HTML。对非 Markdown 的静态文件,按如下策略复制到输出目录(默认 `public`):

- 递归复制:遍历 `source` 下所有子目录,将“非隐藏且非 `.md`”文件按原相对路径复制到 `public`。
- 根层文本文件:如 `CNAME`、`robots.txt` 等放在 `source` 根层,构建后会出现在 `public` 根层。
- 资产与附件:图片(如 `.png`、`.jpg`、`.jpeg`、`.gif`)、文本(如 `.txt`)、其它附件,保持原有相对目录层次,方便子目录独立移动;不再仅限于 `source/assets`,子目录中的 `assets` 也会被扫描并复制。
- 隐藏文件:以 `.` 开头的文件将跳过复制(例如 `.DS_Store`)。

示例:

- `source/assets/img.png` -> `public/assets/img.png`
- `source/posts/abc/assets/logo.jpg` -> `public/posts/abc/assets/logo.jpg`
- `source/CNAME` -> `public/CNAME`

## 如何使用

下面提供四种使用方式,按你的场景选择其一即可:

### 1)通过 crates.io 安装并使用(推荐)

- 已发布到 crates.io,推荐直接使用 `cargo install` 安装。
- 快速开始:

```bash
# 安装(固定版本)
cargo install rustpress

# 构建静态站点
rustpress -m source build -o public -c config.toml
或 rustpress build

# 开发预览(含热重载与主题编译)
rustpress dev --hotreload -m source -c config.toml -p 1111 -o public
或 rustpress dev --hotreload
```

- 热重载(模板实时预览):如需监听模板变化自动重建,请使用 CLI:

```bash
cargo dev --hotreload --md-dir source --config config.toml -p 8000
```

热重载适合在编写主题模板时使用。

### 2)作为工程依赖使用

在你自己的项目中添加依赖,并以代码方式调用构建与预览:

```toml
# Cargo.toml
[dependencies]
rustpress = "0.1.5" 
```

```rust
// src/main.rs
fn main() -> rustpress::Result<()> {
    let config = rustpress::Config::from_file(std::path::Path::new("config.toml"))?;
    let gen = rustpress::Generator::new(config.clone(), std::path::Path::new("source"))?;

    // 构建站点到 public
    gen.build("source", "public")?;

    // 启动本地预览
    rustpress::server::DevServer::serve_sync(1111, "public")?;
    Ok(())
}
```

### 3)Fork 源码自由定制

- Fork 本仓库并克隆到本地,按需修改源码与模板:
  - 模板路径:`themes/default/templates/`
  - 静态资源(主题):`themes/default/static/`
- 本地开发建议:

```bash
# 开发环境构建(编译 CSS + 构建站点)
cargo run -- build-dev

# 开发模式(构建 + 启动预览)
cargo run -- dev

# 开启模板热重载
cargo run -- dev --hotreload
```

### 4)通过 GitHub Actions 自动部署(示例 deploy.yml)

将以下文件保存为 `.github/workflows/deploy.yml`,每次推送到 `main` 分支时自动构建并部署到 GitHub Pages:

```yaml
name: deploy

on:
  # 每当 push 到 main 分支时触发部署
  # Deployment is triggered whenever a push is made to the main branch.
  push:
    branches: [main]
  # 手动触发部署
  # Manually trigger deployment
  workflow_dispatch:

# 统一版本号,后续只需改这里
env:
  RUSTPRESS_VERSION: "0.1.12"

jobs:
  docs:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          # “最近更新时间” 等 git 日志相关信息,需要拉取全部提交记录
          # "Last updated time" and other git log-related information require fetching all commit records.
          fetch-depth: 0

      - name: Setup Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Cache cargo
        uses: Swatinem/rust-cache@v2

      # 缓存 rustpress 二进制,命中后跳过安装
      - name: Cache rustpress binary
        id: cache-rustpress
        uses: actions/cache@v3
        with:
          path: ~/.cargo/bin/rustpress
          key: rustpress-bin-${{ runner.os }}-${{ env.RUSTPRESS_VERSION }}

      # 可选:缓存 crates 索引与源码,加速首次安装或版本升级
      - name: Cache cargo registry
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
          key: cargo-registry-${{ runner.os }}-stable

      # 仅在未命中二进制缓存时安装固定版本的 RustPress
      - name: Install RustPress (if missing)
        if: steps.cache-rustpress.outputs.cache-hit != 'true'
        run: cargo install rustpress --version ${{ env.RUSTPRESS_VERSION }} --locked

      - name: Verify RustPress
        run: rustpress -V

      - name: Build site with RustPress
        run: rustpress -m source build -o public

      # 上传构建结果到 GitHub Pages
      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          path: public

      # 部署到外部仓库
      - name: Deploy to external repository
        uses: cpina/github-action-push-to-another-repository@v1.7.2
        env:
          API_TOKEN_GITHUB: ${{ secrets.EXTERNAL_REPOSITORY_PERSONAL_ACCESS_TOKEN }}
        with:
          source-directory: public/
          destination-github-username: rixingyike
          destination-repository-name: rixingyike.github.io
          user-email: 9830131@qq.com
          target-branch: "main"
```

> 注:站点的静态文件输出目录为 `public`;根层文本(如 `CNAME`、`robots.txt`)与所有非 `.md` 附件会按原相对路径递归复制到 `public`。

## 配置

编辑`config.toml`文件来自定义您的博客:

```toml
[site]
name = "我的博客"           # 博客名称
description = "使用RustPress创建的博客"  # 博客描述
author = "作者"            # 作者名称
base_url = "https://example.com"  # 博客的基础URL

# 分类分页配置(单分类文章列表每页显示多少条)
[categories]
posts_per_page = 8

# 标签分页配置(单标签文章列表每页显示多少条)
[tags]
posts_per_page = 8
……
```

## 模板主题自定义

您可以修改`templates`目录下默认的 default 主题文件来自定义网站的外观:

- `base.html`:基础模板,包含HTML结构和CSS样式
- `index.html`:首页模板,显示文章列表
- `post.html`:文章详情页模板
- 其他等,使用 hotreload 模式方便修改主题

## 许可证

本项目使用 Apache 2.0 许可证 - 详见[LICENSE](LICENSE)文件

## 致谢

受到以下项目的启发:
- [Zola](https://www.getzola.org/)
- [Hugo](https://gohugo.io/)