#file-path #directory #env #cfg #proj #com #config-file

envpath

一个用于解析和反序列化具有特殊规则的路径的库。格式类似于以下代码:

19 个版本

0.0.1-beta.32023 年 4 月 7 日
0.0.1-beta.22023 年 4 月 2 日
0.0.1-beta.12023 年 3 月 30 日

#545 in 解析器实现

Download history 1/week @ 2024-03-08 97/week @ 2024-03-29 21/week @ 2024-04-05

每月 195 次下载

Apache-2.0

125KB
1.5K SLoC

EnvPath

crates.io

Documentation

Apache-2 licensed

一个用于 解析反序列化 具有特殊规则的路径的库。

格式类似于以下代码:

也许我应该将其改为 反序列化
不谈细节,让我们开始吧!

中文

序言

注意:本说明包含大量非技术内容。

在正式开始之前,如果您不介意,能否回答我的问题?

我们是如何解决跨平台路径配置问题的?

假设以下配置。

[dir]
data = "C:\\Users\\[username]\\AppData\\Roaming\\[dirname]"

我们可能会为配置文件创建一个新的 Map(例如 HashMap<String, Dirs>),允许不同的平台使用不同的配置。

[dir.linux]
data = "/home/[username]/.local/share/[appname]"
cache = "/home/[username]/.cache/[app]"

[dir.macos]
data = "/Users/[username]/Library/Application Support/x.y.z"

[dir.xxxbsd]

[dir.your-os-name]

这是一个好的方法,但有没有更通用的方法呢?

所以人们想到了使用环境变量。

我想你是在想 XDG 规范。既然它很有用,我们为什么不使用所有平台的 $XDG_DATA_HOME/[appname]/ 呢?

不幸的是,并不是所有平台都支持它,所以我们选择了更通用的 $HOME
遗憾的是,在Windows的早期版本中,可能没有%HOME%,而只有%userprofile%
那么我们应该怎么办?我们应该如何做呢?
我们可以使用外部crate自动检索不同平台的路径,或者手动为不同平台编写不同的目录映射关系。
太好了,看起来我们已经解决了跨平台的问题,但我们可能忽略了一件事。那就是不同平台的路径分隔符可能不同。
我们可以为不同的平台生成路径,但生成的格式可能不是非常通用。

  • Windows: C:\path\to\xxx
  • 类Unix系统: /path/to/xxx

路径分隔符

以下是一些补充说明。

在Macintosh操作系统的历史上,路径分隔符经历了几次变化。在Macintosh操作系统的早期版本中,路径分隔符是一个正斜杠(/)。然而,随着1985年分层文件系统(HFS)的引入,路径分隔符被切换到了冒号(:)。
随着2001年macOS操作系统的发布,引入了HFS+文件系统,路径分隔符仍然是一个冒号(:)。但是,截至macOS Catalina(10.15),苹果引入了一种新的只读文件系统APFS,它使用正斜杠(/)作为路径分隔符。
根据苹果技术笔记TN1150:HFS Plus Volume Format,HFS文件系统中使用冒号作为路径分隔符的目的是通过允许用户轻松地浏览目录来使Macintosh操作系统更加用户友好。APFS中切换到正斜杠可能是因为它与基于Unix的其他系统兼容。
来源: 苹果技术笔记TN1150

以下是一个分隔符表。

操作系统 公司 路径分隔符
Windows Microsoft 反斜杠(\)
一些类Unix系统(例如GNU/Linux) N/A(开源) 正斜杠(/)
mac(早期) Apple 正斜杠(/)
mac(早期) Apple 冒号(:)
mac(当前) Apple 正斜杠(/)
MS-DOS Microsoft 反斜杠(\)
CP/M 数字研究 正斜杠(/)
VMS 数字设备公司 方括号([ ])
IBM OS/2 IBM 反斜杠(\)
PrimeOS Prime Computer 撇号(^)
Virtuozzo Virtuozzo International GmbH 双冒号(::)
VOS Stratus Technologies 尖括号(>)
RISC OS Acorn Computers 句号(.)
AmigaOS Commodore International 冒号(:)
TOPS-20 数字设备公司 正斜杠(/)
Plan 9 Bell Labs 正斜杠(/)
Inferno Bell Labs 正斜杠(/)
ZX Spectrum Sinclair Research 反斜杠(\)

注意:一些操作系统可能在版本之间更改它们的文件路径分隔符。我无法保证上述表格的准确性,所以如果出现问题,请报告问题并让我修改它。


由于不同平台使用不同的路径分隔符,我们如何使它们看起来一样呢?

答案是使用一个数组(或向量)。

然而,它的缺点是相当明显的。对于普通用户来说,这种格式可能比字符串更难阅读(尽管开发者可能更喜欢前者)。请注意,用户配置文件是为了用户查看的,而不仅仅是用于反序列化,所以可读性至关重要。

例如,["C:", "\\", "Users", "Public"] 等同于 C:\Users\Public。有一个小细节很容易被忽视,那就是第二个元素是 "\"。

EnvPath(原始)也使用数组结构(实际上是一个向量),但有一些特殊的规则。

例如,["$dir: dl ? doc"] 指定下载目录(在不同平台上路径不同),如果下载目录不存在,则使用文档目录代替。注意:单个 "?" 和双 "?" 是不同的,我们将在后面提到。

说了很多不相关的事情后,让我们开始吧!

快速开始

在我们开始之前,请确保 Rust 版本不要太旧。因为这个库使用了一些相对较新的语法,例如 let-else(需要 1.65+)。

基本指南

首先,我们需要添加依赖项。

cargo add envpath --no-default-features --features=dirs,consts,project

然后,将以下内容添加到我们的 main() 或测试函数中。

use envpath::EnvPath;

let v = EnvPath::from(["$dir: data", "$env: test_qwq", "app"]).de();
dbg!(v.display(), v.exists());

这是一个简单的例子,还有更多功能和概念我们没有在这里提到。

不要担心,一步一步来。

然后它会输出如下内容。

[src/lib.rs:74] v.display() = "/home/m/.local/share/$env: test_qwq/app"
[src/lib.rs:74] v.exists() = false

我们可以看到 $env: test_qwq 没有成功解析。

那发生了什么?是不是出故障了?

不,不是。这是一个故意的设汁决策。当 EnvPath 被设计时,它被有意设计成与常规路径兼容。

如果有一天,EnvPath 添加了一个需要前缀 $recycle 并包含关键词 bin 来解析到特定目录的功能。而你的系统磁盘上恰好有一个 $RECYCLE:BIN 文件夹,不幸的是,那个磁盘上的文件系统没有启用大小写敏感。当发生同名路径冲突时,它将首先尝试解析,如果失败,则假定同名路径存在并返回它。

同名路径冲突的可能性存在,但通过一点技巧,大多数冲突事件都可以避免。

技巧:使用空白字符(空格、换行符、制表符等)和使用 ?(将介绍以下)

例如,$env: test_qwq 可以写成 $env : test-QwQ

尽管添加了很多空格,但如果成功,它们将被解析为相同的值。使用 Unix 上的 POSIX sh 描述上述表达式是:$TEST_QWQ(即所有大写字母都改为小写,所有 - 都改为 _

尽管你可能觉得这种方法难以接受,但这是全局系统环境变量的习惯,我没有创建任何新的规则。

由于解析失败,为什么不返回一个空目录呢?

让我们用一个posix sh中的env命令的例子来说明吧!

假设你想访问的目录是$XDG_DATA_HOME/app,如果相关的环境变量为空,那么你访问的是/app,这与预期的结果不同。(我想回家,但是买错了火车票 🎫

你可能认为:我可以使用${ENV_NAME:-FALLBACK}来指定后备!这显然是因为你不够聪明。

然而,有时候一个粗心的错误可能会导致大问题。我认为少抱怨会让生活更美好。

到了这个时候,你可能已经忘记了之前错误发生的地方:$env: test_qwq
那么我们该如何解决这个问题呢?你可以尝试将其更改为$env: test_qwq ? user ? logname,或者添加更多的问号和有效的环境变量名称。

这里我不会解释?的功能,自己去探索吧,你经常会发现更多乐趣。


回到我们之前提到的代码,让我们稍作简化。EnvPath::from(["$dir: data"]).de();

众所周知,[]是一个数组。但.de()究竟是什么呢?

在中文里,如果我们用"de"来指代一个国家,它意味着德国。用汉字写就是"德"。如果用来描述一个人,它可以意味着他有"高尚的性格"。

哦哦!我明白了。这个函数去了德国(de),所以它发生了变化,变成了一个具有高尚性格的函数。

无论如何,我认为你非常聪明,这个函数确实改变了。
但它只将像$env: QuQ? ?? qwq-dir? AwA-home这样的结构转换为另一个值。

序列化和反序列化

如果你想要序列化/反序列化一个配置文件,你需要启用envpath的serde功能,并添加serde以及其他相关依赖。

接下来,我们将添加一个ron依赖项(你实际上可以使用yaml或json等格式,但你需要添加相关依赖项,而不是使用ron)。

cargo add envpath --features=serde
cargo add serde --features=derive
cargo add ron

序列化

现在让我们尝试序列化。

        use serde::{Deserialize, Serialize};
        use envpath::EnvPath;

        #[derive(Debug, Default, Serialize, Deserialize)]
        #[serde(default)]
        struct Cfg<'a> {
            dir: Option<EnvPath<'a>>,
        }

        let dir = Some(EnvPath::from([
            "$env: user ?? userprofile ?? home",
        ]));

        let ron_str = ron::to_string(&Cfg { dir }).expect("Failed to ser");
        println!("{ron_str}");

        std::fs::write("test.ron", ron_str)
            .expect("Failed to write the ron cfg to test.ron");

输出结果是:(dir: Some(["$env: user ?? userprofile ?? home"]))

看起来序列化前后的结构是一样的,只是增加了额外的dir键。

是的,序列化后看起来就是这样。

这种路径格式适用于跨平台使用。

由于环境变量和其他东西可能会动态更改。

在序列化过程中保持原始格式,并在反序列化过程中获取其实际路径是合理的。

反序列化

接下来,让我们尝试反序列化!

        use envpath::EnvPath;
        use serde::{Deserialize, Serialize};
        use std::fs::File;

        #[derive(Debug, Default, Serialize, Deserialize)]
        #[serde(default)]
        struct Cfg<'a> {
            dir: Option<EnvPath<'a>>,
        }

        let cfg: Cfg = ron::de::from_reader(
            File::open("test.ron").expect("Failed to open the file: text.ron"),
        )
        .expect("Failed to deser ron cfg");

        dbg!(&cfg);

        if let Some(x) = cfg.dir {
            if x.exists() {
                println!("{}", x.display())
            }
        }

上述函数的输出结果为

[src/lib.rs:116] &cfg = Cfg {
    dir: Some(
        EnvPath {
            raw: [
                "$env: user ?? userprofile ?? home",
            ],
            path: Some(
                "/home/m",
            ),
        },
    ),
}
/home/m

?操作符检查值是否存在。如果不存在,则继续检查。如果存在,则使用该值。

另一方面,??操作符需要值和路径都存在。

例如,考虑$env: user ? userprofile。假设user的值为m,而userprofile为空。由于user的值存在,表达式返回m

如果我们将其改为$env: user ?? userprofile ? home,即使user的值存在,但其路径不存在。因此我们继续检查。然后,由于userprofile的值不存在,我们继续检查,直到条件得到满足。

???有不同的功能,添加??并不意味着你可以丢弃?。对于如$const: os之类的普通字符串值,而不是路径,???更有用。每个都有其重要作用。

这就结束了基本指南。上述描述了一些基本功能。

project_dirs具有更多高级功能。以下是简单的介绍。例如,$proj(com.macro-hard.app-name): data将为该项目生成一个data目录(它不会自动创建它,只是生成其值)。

M$,他的笑容如同大峡谷般宽广,但背后却隐藏着一股如同火山般的愤怒,当我接近他并像夏日里甜蜜的蜂蜜一样提出问题时。

对不起,请原谅我。

现在,它是$proj(com. x. y): data

  • 在Android上,它是/data/data/com.x.y
  • 在macOS上,它是/Users/[username]/Library/Application Support/com.x.y

在了解基本用法后,我们将继续介绍和补充更多内容。

  • 最简单的:常量
  • 常见标准目录:dirs
  • 高级项目目录:project

在下文中,我们将介绍它们的功能以及它们所有具有的值。

功能

环境变量

在之前的文本中,我们已经学习了基本用法。以下是需要解释的一些其他内容。

"env" 指的是环境变量。代码 $env:home 用于获取 HOME 环境变量的值。代码 $env:xdg-data-home 等同于 $XDG_DATA_HOME

关于 "?" 的使用,可以参考前面的文本。
当你理解了 $env:userprofile ?? QwQ-Dir ? LocalAppData ? home 的目的时,恭喜你,你已经学会了如何使用 env!

常量

使用 $const:name(例如 $const:arch)或 $const:alias(例如 $const:architecture)来获取常量值。这些值是在编译时而不是在运行时获取的。

名称 别名 来自 示例
架构 架构 常量::ARCH x86_64, aarch64
deb-架构 deb_arch get_deb_arch() amd64, arm64
操作系统 常量::OS linux, windows, android
家族 常量::FAMILY unix, windows
exe后缀 常量::EXE_SUFFIX .exe.nexe
exe扩展名 常量::EXE_EXTENSION exe
""

deb-架构

下表显示了 $const:deb-arch 的可能输出值

架构 deb_arch
x86_64 amd64
aarch64 arm64
riscv64 (riscv64gc) riscv64
arm (feature = +vfpv3) armhf
arm armel
mips (endian = little) mipsel
mips64 (endian = little) mips64el
s390x s390x
powerpc64 (endian = little) ppc64el
x86 (i586/i686) i386
其他 consts::ARCH

例如,如果你为 armv7 编译包,$const:arch 获取的值将是 arm,而 $const:deb-arch 可能是 armhf

需要启用 value 功能。

使用 $val:name(例如 $val: rand-16)来获取值。与 $const: 不同,这里的大多数值是在运行时获取的。

名称 表达式 示例
rand-[usize] $val:rand-8 uzI1izWG
$val: ""

$val: rand-[usize] 语法需要启用 rand 功能。

rand 用于获取随机内容,目前仅支持字符串。

remix

语法 表达式 示例
环境变量* [环境变量名] 环境变量* HOME C:\Users\m
常量 * [常量] 常量 *架构 x86_64
目录* [目录] 目录*dl C:\Users\m\Downloads
proj* (项目): [标识] proj* (com.xy.z):local-data C:\Users\m\AppData\Local\xy\z\data
val* [val] val*rand-32 14Y3DAcJnvDtlLjpxCURV0naRvmvuY3H

示例

["
    $const: empty ??
        env * home ?
        env * HOME
",
    "test"
]

env* 可以用于回退,但与 $env: 不同,它不会自动将小写字母转换为大写,也不会自动将 - 转换为 _

  • env * home 获取 $home,而不是 $HOME
  • $env: home => $HOME
  • env * xdg-data-home => $xdg-data-home,而不是 $XDG_DATA_HOME
  • $env: xdg-data-home => $XDG_DATA_HOME

注意:如果 $env: 表达式中包含 *,则自动转换功能也将被禁用。

以下语法目前受支持

  • $const:exe后缀?环境变量* HOME ?环境变量* XDG_DATA_HOME ?环境变量* EXE_SUFFIX
  • $env:home?xdg-data-home?exe后缀? 常量 *exe后缀

不受支持

  • $const:exe后缀? $env:home?xdg-data-home?exe后缀

如果受支持,解析可能变得复杂,并且可能在 $env: exe_suffix$const: exe_suffix 之间产生混淆。

dirs

这些是一些基本目录,或者可以说是一些标准目录。
使用 $dir:name(例如,$dir:dl)或 $dir:alias(例如,$dir:download)来获取目录。
其中许多内容来自 dirs,但也有一些补充。

Linux

名称 别名 Linux $dir
home $home: (/home/m)
cache $xdg_cache_home:($home/.cache)
cfg 配置 $xdg_config_home:($home/.config)
data $xdg_data_home:($home/.local/share)
local-data 本地数据 $xdg_data_home
local-cfg 本地配置 $xdg_config_home
desktop $xdg_desktop_dir:($home/Desktop)
文档 文档 $xdg_documents_dir:($home/Documents)
dl 下载 $xdg_download_dir:($home/Downloads)
bin exe $xdg_bin_home:($home/.local/bin)
first-path first_path
last-path last_path
字体 字体样式 $xdg_data_home/字体
图片 图片 $xdg_pictures_dir:($home/Pictures)
首选项 偏好设置 $xdg_config_home
pub 公共 $xdg_publicshare_dir:($home/Public)
运行时 $xdg_runtime_dir:(/run/user/[uid]/)
状态 $xdg_state_home:($home/.local/state)
视频 $xdg_video_dir:($home/Videos)
音乐 音频 $xdg_music_dir:($home/Music)
模板 $xdg_templates_dir:($home/Templates)
tmp $tmpdir:(/tmp)
tmp-rand tmp_random $tmpdir/[随机]
临时 暂时 环境变量::临时目录()
cli-data cli_data $xdg_data_home
cli-cfg cli_config $xdg_config_home
cli-cache cli_cache $xdg_cache_home
""

first_path 指的是第一个 $PATH 变量,而 last_path 指的是最后一个。如果 PATH 是 /usr/local/bin:/usr/bin,那么 /usr/local/bin 是 first_path,而 /usr/bin 是 last_path。

关于 tmptemp

  • tmp:首先,获取 $env:tmpdir 的值。如果存在,则使用该值。如果不存在,则使用 env::temp_dir() 获取目录路径并检查它是否只读。如果是,则使用 ["$dir:cache", "tmp"]
    • 在某些平台上,tmp 目录可能对普通用户是只读的,例如 /data/local/tmp
  • temp:使用 env::temp_dir() 获取目录路径,不执行任何检查。
  • tmp-rand:生成一个随机的临时目录,需要启用 rand 功能。

Android

  • 变量

    • sd = "/storage/self/primary"

对于未列出的项目,使用 Linux 数据。

名称 别名 Android $dir
home
cache
cfg 配置
data
local-data 本地数据 $sd/Android/data
local-cfg 本地配置 $sd/Android/data
desktop
文档 文档 $sd/文档
dl 下载 $sd/下载
bin exe
first-path first_path
last-path last_path
字体 字体样式
图片 图片 $sd/图片
首选项 偏好设置
pub 公共
运行时
状态
视频 $sd/电影
音乐 音频 $sd/音乐
模板
tmp $tmpdir
tmp-rand tmp_random $tmpdir/[随机]
临时 暂时 env::temp_dir():(/data/local/tmp)
cli-data cli_data $xdg_data_home
cli-cfg cli_config $xdg_config_home
cli-cache cli_cache $xdg_cache_home
sd /storage/self/primary
""

Windows

  • 变量
    • ms_dir = $home\AppData\Roaming\Microsoft
名称 别名 Windows $dir
home C:\Users\m
cache $localappdata:($home\AppData\Local)
cfg 配置 $appdata: ($home\AppData\Roaming)
data $home\AppData\Roaming
local-data 本地数据 $home\AppData\Local
local-cfg 本地配置 $home\AppData\Local
desktop $home\Desktop
文档 文档 $home\Documents
dl 下载 $home\Downloads
bin exe $ms_dir\WindowsApps
first-path first_path
last-path last_path
字体 字体样式 $ms_dir\Windows\Fonts
图片 图片 $home\Pictures
首选项 偏好设置 $home\AppData\Roaming
pub 公共 $home\Public
运行时
状态
视频 $home\Videos
音乐 音频 $home\Music
模板 $ms_dir\Windows\Templates
tmp $tmpdir
tmp-rand tmp_random $tmpdir\[随机]
临时 暂时 环境变量::临时目录()
cli-data cli_data $home\AppData\Local
cli-cfg cli_config $home\AppData\Local
cli-cache cli_cache $home\AppData\Local
程序文件 program_files $ProgramFiles: (C:\Program Files)
program-files-x86 program_files_x86 $ProgramFiles(x86): (C:\Program Files (x86))
common-program-files common_program_files $CommonProgramFiles: (C:\Program Files\Common Files)
common-program-files-x86 common_program_files_x86 $CommonProgramFiles(x86): (C:\Program Files (x86)\Common Files)
program-data program_data $ProgramData: (C:\ProgramData)
microsoft $home\AppData\Roaming\Microsoft
local-low local_low $home\AppData\LocalLow
""

macOS

名称 别名 macOS $dir
home /Users/m
cache $home/Library/Caches
cfg 配置 $home/Library/Application Support
data $home/Library/Application Support
local-data 本地数据 $home/Library/Application Support
local-cfg 本地配置 $home/Library/Application Support
desktop $home/Desktop
文档 文档 $home/文档
dl 下载 $home/Downloads
bin exe
first-path first_path
last-path last_path
字体 字体样式 $home/Library/Fonts
图片 图片 $home/图片
首选项 偏好设置 $home/Library/Preferences
pub 公共 $home/Public
运行时
状态
视频 $home/电影
音乐 音频 $home/音乐
模板
tmp $tmpdir
tmp-rand tmp_random $tmpdir/[随机]
临时 暂时 环境变量::临时目录()
cli-data cli_data $home/Library/Application Support
cli-cfg cli_config $home/Library/Application Support
cli-cache cli_cache $home/Library/Caches
""

项目

大部分数据来自directories

使用$proj(qualifier.organization.application):name(例如$proj(org.moz.ff):data)或$proj(com.company-name.app-name):alias来获取项目目录。

这些目录将根据操作系统和特定配置而有所不同。

假设项目是(org.moz.ff),这里有一个例子

Linux

名称 别名 Linux $proj
路径 (项目路径片段): ff
cache $xdg_cache_home/$proj_path:($home/.cache/ff)
cfg 配置 $xdg_config_home/$proj_path:($home/.config/ff)
data $xdg_data_home/$proj_path:($home/.local/share/ff)
local-data 本地数据 $xdg_data_home/$proj_path
local-cfg 本地配置 $xdg_config_home/$proj_path
首选项 偏好设置 $xdg_config_home/$proj_path
运行时 $xdg_runtime_dir/$proj_path:(/run/user/[uid]/ff)
状态 $xdg_state_home/$proj_path:($home/.local/state/ff)
cli-data cli_data $xdg_data_home/$proj_path
cli-cfg cli_config $xdg_config_home/$proj_path
cli-cache cli_cache $xdg_cache_home/$proj_path
""

Android

  • 变量

    • sd = "/storage/self/primary"
名称 别名 Android $proj
路径 org.moz.ff
cache /data/data/org.moz.ff/cache
cfg 配置 /data/data/org.moz.ff/files
data /data/data/org.moz.ff
local-data 本地数据 $sd/Android/data/org.moz.ff
local-cfg 本地配置 $sd/Android/data/org.moz.ff/files
首选项 偏好设置 /data/data/org.moz.ff/files
运行时 $xdg_runtime_dir/ff
状态 $xdg_state_home/ff
cli-data cli_data $xdg_data_home/ff
cli-cfg cli_config $xdg_config_home/ff
cli-cache cli_cache $xdg_cache_home/ff
""

Windows

名称 别名 Windows $proj
路径 moz\ff
cache $home\\AppData\\Local\\moz\\ff\\cache
cfg 配置 $home\\AppData\\Roaming\\moz\\ff\\config
data $home\\AppData\\Roaming\\moz\\ff\\data
local-data 本地数据 $home\\AppData\\Local\\moz\\ff\\data
local-cfg 本地配置 $home\\AppData\\Local\\moz\\ff\\config
首选项 偏好设置 $home\\AppData\\Roaming\\moz\\ff\\config
cli-data cli_data $home\\AppData\\Local\\moz\\ff\\data
cli-cfg cli_config $home\\AppData\\Local\\moz\\ff\\config
cli-cache cli_cache $home\\AppData\\Local\\moz\\ff\\cache
local-low local_low $home\\AppData\\LocalLow\\moz\\ff
""

macOS

名称 别名 macOS $proj
路径 org.moz.ff
cache $home/Library/Caches/org.moz.ff
cfg 配置 $home/Library/Application Support/org.moz.ff
data $home/Library/Application Support/org.moz.ff
local-data 本地数据 $home/Library/Application Support/org.moz.ff
local-cfg 本地配置 $home/Library/Application Support/org.moz.ff
首选项 偏好设置 $home/Library/Preferences/org.moz.ff
cli-data cli_data $home/Library/Application Support/org.moz.ff
cli-cfg cli_config $home/Library/Application Support/org.moz.ff
cli-cache cli_cache $home/Library/Caches/org.moz.ff
""

项目中的“??”

?的语法比其他类型稍微复杂一些,因为它有(),而其他类型则没有。

别担心,如果你已经掌握了核心语法,那么你可以在几分钟内快速掌握??的语法。

假设有三个项目

  • (org. moz. ff)
  • (com. gg. cr)
  • (com. ms. eg)

第一个例子是

["
    $proj (org . moz . ff ): runtime ? data ?? state ?
    (com . gg . cr): cfg ?? cache ?
    (com . ms . eg): local-data ? data
"]

让我们开始解析ff项目的运行时,不幸的是,它不存在。

接下来,我们解析数据!太好了,我们发现其值存在。

因为有双?,我们还需要检查文件路径是否存在。

不幸的是,数据目录不存在。

下一个是状态。

不幸的是,它也没有通过,因为其值不存在。

到目前为止,ff阵营的所有成员都已失败,没有一个是成功解析的。

所以,我们继续解析cr项目,幸运的是,第一次尝试就成功了。

cr的cfg值不仅存在,路径也存在。

最终的返回值是cr项目的cfg目录!


第二个例子是

["
    $proj (org . moz . ff ):runtime ? data ?? state ?
    (com . gg . cr): cfg ?? cache ?
    (com . ms . eg): local-data ? data
"]

问:为什么我没有看到与第一个例子有任何不同?

答:它允许你使用全角符号(冒号和问号)作为分隔符,但这是有限的。

这取决于第一个出现的符号。也就是说,如果第一个分隔符是半角“?”( \u{3F})而不是全角“?”( \u{FF1F}),那么其余部分也应该用半角表示。

依赖项

~0-10MB
~59K SLoC