不想为文档转换装一整套 Pandoc:用 Rust CLI Carta 把格式能力、二进制体积与兼容性拆开管理
文档转换常被误解成一个简单的“Markdown 转 HTML”问题。真正进入团队流水线后,输入会来自 GitHub 风格 Markdown、reStructuredText、Org、Jupyter Notebook,输出又可能是 HTML、LaTeX、EPUB、DOCX 或机器可读的中间表示。此时最难的并不是调用一次转换器,而是回答三个工程问题:实际需要哪些格式能力、转换差异是否可追踪、以及为了一个小任务是否必须带上完整的通用工具链。
Carta 是一个用 Rust 编写的通用标记语言转换器,定位是对 Pandoc 的轻量实现。它同时提供 CLI 和库接口;README 明确说明 CLI 是库之上的薄壳。对需要把格式转换嵌进构建、静态站生成或内部内容服务的团队,这个结构意味着可以先用命令行验证,再把同一转换能力放进 Rust 程序,而不是维护两套逻辑。
不过,Carta 仍处于积极开发阶段,API 尚不稳定,也没有实现 Pandoc 的所有格式与特性。因此它更适合“先核对能力矩阵,再对准具体转换路径使用”,而不是把它当作无差别的 Pandoc 替代品。
先把转换需求写成方向,而不是只写“支持 Markdown”
同一个“Markdown 支持”可能对应完全不同的工程风险:只读入 Markdown 与既要读又要写 Markdown 不同;CommonMark 与 GitHub Flavored Markdown 的扩展集合不同;转换成功也不表示复杂表格、引用、任务列表和原始 HTML 的语义完全一致。
Carta 将输入格式与输出格式显式分开。它的状态文档以 Pandoc 3.10 为参照,逐方向列出完成状态和已知差异。当前 CommonMark、CommonMark-X、GFM、Pandoc Markdown、Markdown strict、MultiMarkdown、PHP Markdown Extra 与 legacy GitHub Markdown 都标为可读、可写;HTML、LaTeX、reStructuredText、Org、MediaWiki、DokuWiki、Jira、man、DOCX、ODT、EPUB、Jupyter Notebook、RTF 等也有不同程度的支持。
这份矩阵的价值不在于凑出一个“格式数量”,而在于给上线决策一个可审计的边界。例如,状态页把 pptx 标成尚未开始;Typst 当前只支持写出,不支持读入;PDF 也不是直接的读写格式。若流水线依赖这些方向,应继续使用已有方案或在 Carta 外层补齐,而不该把格式名出现在文档里理解成端到端兼容。
另一个容易被忽略的点是“可用”并不等于没有差异。状态页会把尚存的行为差异直接列出来。比如 legacy GitHub Markdown 中,任务列表后接普通项目时可能被拆成两个列表;某些 reStructuredText directive 也存在已说明的限制。把这些差异当作发布前测试样本,比只在生产文档上发现排版变化要便宜得多。
用最小命令验证一条转换链路
Carta 的基础用法是用 -f 指定输入格式、-t 指定输出格式。先安装 CLI:
cargo install carta cargo binstall carta # 已安装 cargo-binstall 时,可获取匹配的预构建二进制
例如,下面把 CommonMark 文件转换为 HTML,并显式写到目标文件:
carta -f commonmark -t html input.md -o output.html
如果转换处在 Unix 管道中,不必先落盘。CLI 可以从标准输入读取、向标准输出写入:
echo '# Hello' | carta -f commonmark -t html
对于自动化流程,更推荐先把格式探测加入预检。不同构建产物可能因编译 feature 不同而可用格式不同,直接询问当前二进制比假设默认功能更可靠:
carta --list-input-formats carta --list-output-formats carta --list-extensions=gfm
这三个命令适合放在 CI 的诊断步骤中:当某个瘦身构建缺少所需 reader 或 writer 时,失败信息会出现在转换之前;团队也能把实际可用能力随构建产物一起记录。
体积优化的关键:按方向启用 feature
通用转换器通常会把大量解析器和渲染器打进同一个二进制。Carta 提供按“读入方向 / 写出方向”选择 feature 的方式,因此可以只编译一个服务真正要走的路径。比如,一个仅负责 CommonMark 转 HTML 的容器,可构建为:
cargo build -p carta --no-default-features \ --features read-commonmark,write-html
这不是一个抽象的“性能开关”,而是部署边界的设计选择。若服务只有 Markdown 预览能力,就没有必要因为未来可能要处理电子书而把全部格式支持纳入镜像;反过来,若转换任务由同一批处理节点承接,应先列出每条输入输出边,再决定是否适合做特化构建。特化的收益是更小的依赖面和更清晰的能力声明,代价则是新格式进入流程时必须重新构建并补充回归测试。
把格式兼容性当成测试契约
从 Pandoc 或其他转换器迁移时,最危险的做法是只比较生成文件是否存在。更稳妥的测试应分三层。
第一层是格式存在性:用 --list-input-formats、--list-output-formats 确认目标方向确实被编入二进制。第二层是语义样本:为团队常见的标题层级、表格、链接、代码块、任务列表、脚注和原始 HTML 准备小型 fixture,分别检查输出结构。第三层是已知差异:把 Carta 的状态页中与所用格式相关的限制转成明确的“允许差异”或“禁止上线”规则。
例如,某个发布系统只接受 GFM 到 HTML,可以在升级时同时运行原转换器与 Carta,将 DOM 结构或关键片段比较,而不是比较格式化后的整段文本。只要差异聚集在已认可的空白或属性顺序,便可接受;若表格、链接目标或代码内容变化,则应回退并增加最小复现样本。这样,工具替换从一次性的“看起来没问题”变成可重复执行的兼容性契约。
在 CI 中做一条可复现的转换检查
如果转换结果会进入网站发布、知识库同步或制品归档,建议把命令写成最小可复现任务,而不是由开发者在本机临时运行。一个实用的步骤是:先调用格式列表命令保存构建时的能力快照;再对固定 fixture 执行转换;最后对输出做结构性断言。HTML 场景不必锁死所有空格和属性顺序,但应断言标题、链接地址、代码内容和表格单元格等业务语义仍存在。
还应把版本固定在构建配置中。Carta 的状态页以 Pandoc 3.10 为比较基准,但这并不意味着每个后续版本都会保持相同的边缘行为。将 Carta 版本、启用的 Cargo features、fixture 输入和预期结构一起提交,升级时就能知道变化来自格式解析、writer 输出还是构建 feature。对于需要审计的内容管道,保留一次转换的输入摘要和 carta --list-* 输出,也能避免数月后无法解释某台构建机为何缺少某个格式。若文档来自不受信任的外部提交,测试任务还应在隔离工作目录中运行:转换器处理的输入本身可能包含超长表格、异常嵌套或团队并未准备支持的原始标记。能力清单、资源限制和最小样本共同构成了比“命令成功退出”更可靠的发布门槛。
什么时候应该继续使用 Pandoc 或其他工具
Carta 的优势是 Rust 实现、CLI 与库共用转换核心,以及可按格式方向缩减构建;它并不承诺现阶段覆盖 Pandoc 的全部能力。遇到以下情况,保守选择通常更合理:
- 工作流依赖
pptx、直接 PDF 转换,或状态页尚未支持的读写方向; - 文档大量使用尚未核验的复杂扩展,且没有回归样本;
- 现有系统依赖稳定 API,而无法接受 Carta README 所说明的 API 不稳定状态;
- 需要严格复现某个 Pandoc 特定扩展的行为。
反之,如果需求可以收敛为一两条明确转换路径,例如 CommonMark/GFM 到 HTML、文档源格式到可控的中间表示,Carta 值得作为候选。关键不是追求“替代 Pandoc”,而是把转换能力拆成可验证的 reader、writer、扩展和测试样本:二进制只带必需的能力,流水线只承诺已经验证的语义。
Carta 的 Apache-2.0 与 MIT 双许可证也适合在内部工具链中评估;但项目仍在演进,生产接入前应固定版本、保存 fixture 输出,并在每次升级时重新对照状态页。对文档基础设施来说,能说明“这条格式路径为何可用、哪些边界不可用”,通常比声称“所有格式都能转”更有价值。
相关链接