CUE 能做一切,但它能生成可读文档吗?
Source: Dev.to
请提供您希望翻译的具体文本内容,我将为您翻译成简体中文,并保持原始的格式、Markdown 语法以及技术术语不变。谢谢!
CUE – 文件生成(以及文学化编程)的瑞士军刀
CUE 是当你需要:
- 生成复杂的 JSON,
- 验证 YAML,
- 确保你的整数真的就是整数,
以及,令人惊讶的是,当你想要一个 文学化编程 工作流时,会选择的工具。
为什么要使用文学化编程工具?
当前文学化编程的“王者”是 org‑mode。
它在 Emacs 内部 工作得很好,但一旦你尝试把文档分享给使用 VS Code(或其他编辑器)的人,就会陷入厂商锁定。
你希望你的文档——你的 文学化代码——是 可移植 的,而不是只能在某个编辑器里起作用的魔法咒语。
当你写关于代码的内容(博客文章、教程、内部文档)时,通常不会一次性倾倒一个巨大的文件。相反,你会:
- 展示一段逻辑代码片段。
- 展示一段配置代码片段。
- 也许还有一段 CSS。
然后用文字把这些零散的部分串联起来。
“复制‑粘贴致敬”
你写了一篇精彩的教程,把可运行的代码复制进 Markdown 文件,随后又把一个变量改名(
t→timeout)。你修正了源码,却 没有 修正 Markdown。读者复制的代码已损坏,教程不再可信,而你显得很马虎。
CUE 通过让我们定义 部件 并 以编程方式 将它们 拼接 在一起,解决了这个问题。它不仅仅保存文本——它会验证这些片段是否真正匹配,确保你在解释中展示的代码正是最终构建中使用的代码。
可以把它想象成一套乐高积木,如果结构不稳固,砖块就会拒绝卡合。¹
Source: …
停止致敬:将文档视为构建目标
CUE 的 tool/file 用于创建文件,而 tool/exec 用于运行命令。
我们并非仅仅在对字符串进行模板化,而是在定义一个 依赖图。如果任何依赖(例如代码片段)无效,文档根本就不会生成。
示例 1 – 使用动态版本生成 Markdown 文件
package example
import (
"tool/file"
"tool/exec"
)
// Build command – for every entry in the `files` struct, create a file.
command: example: {
for k, v in files {
"(k)": file.Create & {
filename: k
contents: v
}
}
}
// Run a shell script to obtain a version number.
data: exec.Run & {
cmd: ["sh", "-c", "echo '1.2.3'"]
stdout: string
}
// Final document, stitched together.
// `(data.stdout)` is a promise that `data` will run before the string is finalized.
files: {
"output.md": """
My Awesome Project
Current version: (data.stdout)
"""
}
没有 CUE:你需要手动输入 1.2.3。如果版本后来改为 1.2.4,文件就会变得过时,除非你再次手动编辑。
使用 CUE:如果 data.stdout 失败(例如 shell 命令崩溃),文件将不会生成,构建会失败——从而保护读者不受到错误信息的影响。
运行构建 – cue cmd
CUE 是声明式的:它描述期望的状态,但 不会自行改变它。file.Create 和 exec.Run 只是数据,直到你调用 任务引擎 为止:
cue cmd
cue cmd 是 CUE 中唯一允许产生副作用的部分。它:
- 查找
command:块。 - 顺序 执行任务。
- 在 “我想要一个文件”(定义) 与 “这是你的文件”(实际) 之间架起桥梁。
重要:为了让
cue cmd识别你的命令,文件名 必须以_tool.cue结尾(例如build_tool.cue)。如果命名为example.cue,CUE 将完全忽略这些命令。
扩展 CUE – 使用 tool/exec 的自定义渲染器
简单的 shell 命令适用于玩具示例,但真实项目需要更强大的功能(例如,编译 Haskell、渲染图表)。CUE 让将任意 CLI 工具包装成 渲染器 变得轻而易举。
示例 2 – 多语言渲染器
package example
import (
"tool/exec"
)
// Renderers – wrappers around CLI tools.
// Each renderer takes a `code` string and pipes it into a command.
renderers: pikchr: exec.Run & {
cmd: ["pikchr", "--svg-only", "-"]
code: string
stdin: code
stdout: string
}
renderers: haskell: exec.Run & {
cmd: ["stack", "runghc"]
code: string
stdout: string
// Wrap the snippet in a module so it compiles standalone.
stdin: """
module Program where
(code)
"""
}
renderers: bash: exec.Run & {
cmd: ["bash", "-e"]
code: string
stdin: code
stdout: string
}
// And, because sometimes Bash isn’t hip enough…
renderers: zsh: exec.Run & {
cmd: ["zsh"]
code: string
stdin: code
stdout: string
}
这能为你提供的功能:在 CUE 配置中拥有一个小巧的自定义 CI 运行器。现在你可以:
- 将 Pikchr 图表渲染为 SVG。
- 即时编译并运行 Haskell 代码片段。
- 安全地执行 Bash 或 Zsh 脚本。
测试与 CI
现在,当我想要插入图表或代码输出时,只需把代码片段交给相应的渲染器。CUE 负责调用 shell、通过 stdin 传递输入并捕获 stdout,完成繁重的工作。它把你的文档变成了一个活的程序。
最棒的地方在于,你可以 “测试” 你的文章。运行 cue cmd example 时,CUE 实际上会执行 shell 命令、编译 Haskell、渲染 Pikchr 图表,并生成最终文件。
- 如果你的代码片段有语法错误,生成会失败。
- 如果你的 Haskell 代码无法编译,生成会失败。
- 如果你尝试用无效坐标创建 Pikchr 图表,生成会失败。
它把“写博客”变成了“通过 CI/CD 流水线”。
这有点像拥有一个既是校对员又是极其严格的编译器 2。它确保 我文章中的每个图表都是真正由旁边的代码渲染出来的——没有过时的图片,没有手动导出,也没有我忘记写下的“魔法”步骤。它不依赖任何特定的编辑器生态系统,实际上解决了文档的“在我的机器上能跑”的问题。你不仅仅是在写作;你在对文本进行工程化。
或者如果你想把 Megablok 放在 Lego 上。CUE 有很高的标准,绝不会犹豫去评判你拙劣的结构选择。 ↩
而且不同于人工校对员,CUE 不会要求薪水、喝光你的咖啡,或抱怨你过度使用双关。 ↩
要点
- CUE 让你把文档视为 一等构建产物。
- 你在正文中展示的代码 有保证 与实际运行的代码一致。
- 通过使用
cue cmd并将文件命名为*_tool.cue,你可以获得可复现、对副作用敏感的流水线。 - 使用
tool/exec扩展 CUE 意味着你可以封装任何所需的语言或工具,使你的文学化编程工作流既 可移植 又 可靠。
注脚
- “乐高套装中,如果你构建的结构不稳固,砖块就会拒绝卡合”是对 CUE 验证机制的比喻:如果底层部件不满足约束,它不会让你生成文件。