CUE(Configure Unify Execute)是一种数据验证语言,有自己的推理引擎,可作为配置语言使用。CUE 语言的特点是把数据类型(type)和值(value)看作同一概念:Types are Values。如下图展示了 CUE 的核心思想,左边是数据,中间是类型,右边(.cue)是数据与类型的混合,约束了任何 largeCapital 数据必须包含字符串类型的 name 字段,大于 5M 的 pop 字段以及值为 true 的 capital 字段。
CUE 的主要功能包含:
- 数据验证:客户侧数据校验
- 代码导入/导出:支持从 Go 代码、Protobuf、YAML、JSON 等源文件转换成 CUE 文件
- 配置管理:是 JSON 的超集,支持类型检查
- 辅助工具:
cue trim
自动修剪冗余
CUE 示例
下面 cue 文件定义了三个字段,a 是 int 类型,值为 1;b 是对象类型;c 是 string 类型,未赋具体指。
|
|
执行验证命令 cue eval demo.cue
,可以发现 a: int
和 a: 1
合并(Unification),c 因为没有具体值,保留 d: string
:
|
|
由于 d 没有具体指,不完整,因此 cue 不能导出 JSON 格式。修改 d: "hi"
,执行 cue export demo.cue
,JSON 格式正确导出了:
|
|
CUE 原理:格
CUE 支持多个文件合并,正如前面示例中对同一字段 a 的合并。合并(Unification)是 CUE 的一大亮点和设计思想,CUE 中的 U 即代表合并的意思。CUE 合并操作的理论支持来自数学中的格(Lattice)。由于合并操作是顺序无关的,所以 CUE 功能非常强大。下图是合并的逻辑示意图以及对应 cue 文件:
graph TD A("_ (top)") --> N(number) N --> I("int") N --> GEH(">=0.5") N --> LTC("<10") I --> Z("0") I --> One("1") IFI("1.1") GEH --> One GEH --> IFI GEH --> CCF("20.0") LTC --> One LTC --> IFI Z --> E One --> E IFI --> E CCF --> E E("_|_ (bottom)")
|
|
以上 cue 文件合并操作会报错,这是因为在 a: 1.1
和 a: 20.0
这里合并冲突,两个都是原子值(atom)。CUE 中,_
表示任意值,是合并逻辑中的上确界(top),_|_
表示错误,是下确界(bottom)。CUE 使用格理论能很好地处理无序合并逻辑。合并操作也可以使用 & 操作符(a & b)显示地表示,底层逻辑是1:
- a 与自身合并总等于自身 a;
- a 与 b 合并,如果 a ⊑ b,则合并结果总为 a;
- a 与 bottom(_|_)合并等于 bottom。
CUE 语法
1. 定义模板
# 打头的字段是定义模板(definition),用于定义数据格式。# 字段不是被打印,仅被引用。如果引用合并了定义不包含的字段,则合并操作会报错。
|
|
2. 多行字符串
使用 """ 来引用多行字符串。
|
|
3. 开放字段
使用 … 表示允许追加额外字段或数组元素。? 表示可选字段,但如果是开放数组 arr: […],不必加?。
|
|
4. 别名
别名的三种写法,X 都可以作为别名使用:
let X = expr
,expr 值作为 X 的值;- 在标签中,
X=label: expr
,expr 值作为 X 的值 - 在模式限制中,
[X=expr]: value
,把模式匹配(expr)到的值作为 X 的值;
5. 模式限制
模式限制(pattern constraint)使用 [pattern]: value
格式来字段名匹配 pattern
的字段的值需要被 value
约束。
|
|
cue export
输出结果是:
|
|
再看一个例子:
|
|
6. 循环字段
引用循环可以被处理,结构循环会报错。
|
|
7. 隐藏字段
_ 打头的字段是隐藏字段,不会被打印。
8. 循环控制
|
|
另外,cue 不支持 for-break,要实现提前退出可以用 list.Contains 转化写法2:
|
|
9. 条件控制
|
|
10. 包
同一 package <name>
打头的 cue 文件在同一包下。
11. 默认值
* 标记的值是默认值
12. 属性
属性写法如示 @go(Field)
,用于表示字段属性3。
13. 模块
CUE 也支持 module 和 import。比如导入内置包后4,可以使用内置包的函数:
|
|
甚至可以自己创建包,并引用。如 KubeVela 项目自己创建了 kube api 包:
|
|