# Moon vCard 项目申报书

## 1. 项目名称和 GitHub 仓库地址

- 项目名称：Moon vCard —— 联系人名片文本解析与生成库
- GitHub 仓库：https://github.com/haol-05/moon-vcard
- Mooncakes 包：https://mooncakes.io/docs/haol-05/moon-vcard
- 参赛者：郝丽莹
- 联系电话：13011410349
- 项目方向：数据交换与内容处理；联系人数据的离线解析与生成

## 2. 项目简介

手机、邮件客户端和网盘工具普遍用 `.vcf` 文件交换联系人。这类文本含折行、转义和多张名片，直接按行切分很容易把姓名或电话读错。Moon vCard 是一个没有第三方依赖的 MoonBit 库，离线解析常见的 vCard 3.0 / 4.0 名片文本，一次返回全部可用联系人与诊断码，并能把支持的字段重新写成标准 CRLF 换行的名片文本。库本身不联网、不申请系统联系人权限，可嵌入浏览器端工具或命令行批处理。

## 3. 项目方向与适用场景

**场景一：本地通讯录迁移。** 迁移工具读入用户从旧手机导出的 `.vcf`，一次拿到文件中全部联系人，按姓名、电话、邮箱展示；对缺少版本或姓名的名片，工具从 `diagnostics` 收到 `missing-fn:4`、`invalid-version:4` 之类的编码，据此提示用户而不是静默导入残缺记录。

**场景二：浏览器联系人编辑器。** 用户在网页里改完电话和邮箱后，编辑器把联系人交给 `format`，得到 CRLF 结尾的 vCard 文本再供下载或分享；解析与生成都在本地完成，联系人数据不经过服务器。

**场景三：批量清理脚本。** 脚本读入一份含多张名片的文件，先处理折行与 `\n`、`\,` 等文本转义，再统一输出姓名、电话、邮箱，交给后续去重与人工核对；遇到格式错误的名片只记录诊断码，其余名片照常处理。

## 4. 拟实现的核心功能（本次提交已全部完成）

1. `parse(String) -> ParseResult`：一次解析文本中的多张名片，识别 `BEGIN`、`END`、`VERSION`、`FN`、`N`、`TEL`、`EMAIL`；
2. 兼容 CRLF 与 LF 换行，按规范展开折行（续行以空格或制表符开头），解码 `\n`、`\\`、`\;`、`\,` 文本转义；
3. 结构化姓名先按未转义的分号分段、再解码转义，因此 `N:Smith\;Jones;Alice;;;` 的家族名是 `Smith;Jones`，不会把转义分号误当字段边界；
4. 保留 `TEL` / `EMAIL` 行上的属性参数（`TYPE=cell`、`TYPE=cell,voice` 多值与 `;PREF` 无值参数、引号包裹的值），`format` 按规范回写，参数不再在往返中丢失；
5. 解码 quoted-printable：识别 `ENCODING=QUOTED-PRINTABLE`，把行尾 `=` 的软换行续行拼回属性值，解码 `=XX` 字节并按 UTF-8 还原中文等多字节文本；解码后丢弃已被消费的 `ENCODING` 参数，避免写出"声明为编码、实际是明文"的名片；
6. 错误不丢数据：错误行产生诊断码（`invalid-escape:行号`、`invalid-quoted-printable:行号`、`unsupported-charset:行号`、`unclosed-card`、`nested-card:行号` 等），同一文件中合法的名片照常返回；非 UTF-8 字符集不猜测，只报诊断并按 UTF-8 宽松解码；
7. `format(Contact) -> String?`：把支持的字段写成 CRLF 结尾的 vCard；版本或姓名无效时返回 `None`，不产出半成品文本；
8. 工程化交付：单元测试覆盖解析、折行、转义、参数保留、quoted-printable、排序与错误路径，`moon run` 示例、README 可复现命令、GitHub Actions 检查（`check` / `build` / `test` 与 `fmt`、`info` 工作区一致性），并已发布到 mooncakes.io；
9. 多值字段稳定排序：`sort_by_preference` 按 `PREF` 数值升序排列（`PREF=2` 排在 `PREF=10` 之前，而不是按字符串比较），无值或非数值 `PREF` 排在其后，同序条目保持文件中原有顺序，且不改动传入的数组。

**公共 API 变更（0.2.0）。** 为保留参数，`Contact.phones` 与 `Contact.emails` 的类型由 `Array[String]` 改为 `Array[Field]`，每个 `Field` 含 `value` 与 `params`；新增 `Param` 类型。项目尚无外部使用者，因此直接调整接口而未保留兼容层。

**实现路径。** 先统一换行并展开折行，再按名片边界逐属性读取；结构性分隔符在转义解码之前处理，输出端只写库明确支持的属性和字段，不做属性猜测。quoted-printable 自成一层：先按软换行拼行、再解 `=XX` 字节、最后做 UTF-8 解码与文本转义解码，顺序与 RFC 2426 一致。

**能力边界（明确不做的部分）。** 不支持 base64 照片与 `VALUE=BINARY` 载荷、属性分组（如 `item1.TEL`）；`ENCODING=QUOTED-PRINTABLE` 之外的字符集不解释（仅支持 UTF-8 与 ASCII，其余报 `unsupported-charset`）；`FN` 与 `N` 行上的参数不保留；写出时不做 quoted-printable 重新编码。这是一个"联系人常用文本字段内核"，不是完整 RFC 6350 实现，也不宣称能校验第三方名片的合法性，更不涉及条码、二维码等码字层能力。

**下一阶段可扩展方向。** 识别并拒绝式诊断 base64 / `VALUE=BINARY` 载荷、支持属性分组（`item1.TEL`）、以及在写出时按 `VERSION:3.0` 需要重新做 quoted-printable 编码的可选策略。

## 5. 是否为原创项目、移植项目或参考已有开源项目

- **原创实现。** 未移植、未复制任何现有解析器代码；格式依据公开规范 [RFC 6350](https://www.rfc-editor.org/rfc/rfc6350)（vCard 4.0）与 [RFC 2426](https://www.rfc-editor.org/rfc/rfc2426)（vCard 3.0）自行编写。许可证 MIT。
- **查重情况（2026-09-25）。** GitHub 检索 `moonbit vcard`、`vcard moonbit`、`moonbit contacts` 均无结果；mooncakes.io 检索 `vcard` 得到 1 个包 `angela/vcf@0.0.5`（描述 "Parse vCard strings"，Apache-2.0，最后发布于 2026-01-12）。下载其源码逐项比对后的实际差异：该包只解析单张名片，仅接受 `VERSION:3.0`，输入含多张名片时直接返回错误；未处理折行折叠与文本转义；`parse`、`validate` 内部保留 `println` 调试输出；其测试文件仍是工具链模板的 `fib` / `sum`，没有 vCard 用例；清单使用旧式 `moon.mod.json` / `moon.pkg.json`，平台标记为 `legacy`，并依赖 `moonbitlang/x` 与 `maria/csv_parser`。Moon vCard 面向多张名片、3.0 与 4.0、折行与转义、非致命诊断和 CRLF 序列化，且零第三方依赖，属独立贡献。
- **数据来源。** 测试联系人全部为虚构数据，仓库内不含真实个人信息，也未引入来源不明的第三方测试集。

## 6. 验收结果

### 6.1 本地验收（Windows，moon 0.1.20260807，2026-09-25）

| 验收项 | 实际结果 |
| --- | --- |
| `moon check --deny-warn --target all` | 通过，0 warning |
| `moon build --target wasm` / `wasm-gc` / `js` | 三个目标全部通过 |
| `moon test --deny-warn --target wasm` | 5 / 5 通过（`wasm-gc`、`js` 同为 5 / 5） |
| `moon run --target wasm examples/demo` | 输出 `Contacts: 1`、`Diagnostics: 0` |
| `moon info` | `.mbti` 接口与模块名 `haol-05/moon-vcard` 一致 |
| `moon build --target native` | 未通过：工具链自带运行时 `<moon-home>/lib/runtime/env.c` 中 `rand_s` 未声明；同机上一个两行测试包报同样错误，与本项目源码无关 |

### 6.2 线上状态（2026-09-25）

- **GitHub 仓库**：已公开（PUBLIC），默认分支 `main`，包含源码、测试、示例、README、申报书、许可证与 CI 配置，无构建缓存与压缩包。
- **GitHub Actions**：工作流 [MoonBit checks](https://github.com/haol-05/moon-vcard/actions) 已通过，`check` / `build` / `test` 覆盖全部目标（含 native），`moon fmt` + `moon info` 工作区一致性检查通过；CI 使用的最新发布版工具链为 moon 0.1.20260920。
- **mooncakes.io**：`haol-05/moon-vcard` 已发布，平台构建状态为 success；首发版本 0.1.0，仓库格式与文档同步后为 0.1.1。

**工具链差异说明。** 本地 moon 0.1.20260807 的 `moon fmt` 会去掉无标签结构体字面量的尾逗号，CI 安装的 moon 0.1.20260920 会保留该尾逗号。仓库统一采用新版本工具链的输出形式，因此用旧版本在本地执行 `moon fmt` 会看到一处一字符差异（升级本地工具链即可消除）；这不影响编译与测试结果。

**备注：** 本地就绪不等于官方验收通过。上述线上状态为平台实际返回；赛事报名、评审及其他需要人工填写的字段由参赛人自行提交。
