一個用於 解析 和 反序列化 具有特殊規則的路徑的 library。
格式類似於 ["$proj(com.xy.z): data ? cfg", "$const: os", "$val: rand-16"]
在正式開始前,很抱歉打擾到您,能否請您解答我心中的一個小小的困惑呢?
我們一直以來是如何解決跨平臺路徑配置的問題?
假設有如下配置:
[dir]
data = "C:\\Users\\[username]\\AppData\\Roaming\\[dirname]"也許我們會為配置檔案新建一個 Map (e.g. 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]/ 如何?
然而,不幸的是,並不是所有平臺都支援 XDG 規範,所以我們選擇更通用的 $HOME。
不幸的事情再次到來,在早期的 Windows 上,可能並沒有 %HOME%, 而是隻有 %userprofile%。
envpath 的設計初衷就是為了解決跨平臺目錄配置的問題。
[dir]
data = [
"$proj(com.org-name.app-name): local-data",
"dir"
]
# - Windows: C:\Users\[username]\AppData\Local\org-name\app-name\dir
# - Linux: /home/[username]/.local/share/app-name/dir
# - macOS: /Users/[username]/Library/Application Support/com.org-name.app-name/dirRaw 格式的 EnvPath 本質上是使用了特殊的規則的陣列結構。
它的缺點特別明顯,對於普通使用者來說,這種格式看起來可能會比字串更難看。
請注意,使用者配置檔案是用來給使用者看的,而不是隻是單純地用來反序列化,所以可讀性至關重要。
當不使用特殊規則時,它可以相容普通的路徑陣列。
比如 ["C:", "\\", "Users", "Public"] 相當於 C:\Users\Public
這裡有個容易忽視的小細節,就是第二個元素是 "\"。
Q: 特殊規則?
A: 當陣列的元素以 $[關鍵詞]開頭,並且整個元素的表示式都能正常解析時,它就是一條特殊規則。一個數組可以應用多條特殊規則。
例子:
- 環境變數(關鍵詞 env):
["$env: XDG_CONFIG_HOME", "xx"],在某些系統上,它會被解析為/home/[user]/.config/xx ?代表 fallback:["$dir: dl ? doc"]指定為 Downloads 目錄(不同的平臺的路徑不一樣)- 如果 Downloads 目錄的值不存在,就用 Documents 目錄。
注意:一個 ? 與兩個 ? 是有區別的, 之後我們會提到的。
首先,我們需要新增依賴:
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 資料夾,不巧的是,那個磁碟的檔案系統剛好沒有開啟區分大小寫(Case-sensitive)的功能。
當存在同名路徑時,預設會先解析,解析失敗後,會假設當前存在同名路徑,然後直接返回。
同名路徑碰撞(解析的式子與檔案路徑同名)的機率是存在的,不過,只要稍微花一點技巧就能避開絕大多數的碰撞事件。
技巧:多用空白字元 (空格,換行符,製表符之類的),以及使用
?(下文會介紹) 比如$env: test_qwq可以寫成$env : test-QwQ儘管加了那麼多空格, 但如果成功的話,它們會被解析為同一個值。將上面的表示式用 unix 的 posix sh 來描述是:$TEST_QWQ(i.e. 所有小寫字母全部變為大寫,所有-全部變為_) 儘管您覺得這種做法可能很難接受,但對於全域性系統環境變數來說,這樣做是慣例,我並沒有創造新的規則。
既然都解析失敗了,那為什麼不返回空目錄呢?
用 posix sh 的 env 舉個例子吧!
假設您要訪問的目錄是 $XDG_DATA_HOME/app, 如果相關的 env 是空的話,那麼您訪問的就是 /app ,這與預期結果不同。(我想要回家,但是卻買錯了車票 🎫
您可能會辯解道: 我可以用 ${ENV_NAME:-FALLBACK} 來指定 fallback 啊! 明明是你太笨了。
然而,有時候一不小心的疏忽可能會釀成大錯。我覺得少點抱怨,會讓生活變得更美好。
說到這裡,您可能已經忘記了前面出錯的地方: $env: test_qwq。
那麼要如何解決呢?您可以試試把它修改為 $env: test_qwq ? user ? logname, 或者是新增更多的問號與有效的環境變數名稱。
這裡就先不解釋? 的作用了,自己去探索,往往能發現更多的樂趣。(我寫完這句話後,才想起前面已經解釋過了 QuQ
回到我們剛開始提到的程式碼,然後稍微簡化一下。
EnvPath::from(["$dir: data"]).de();
As is well known, [] 是一個數組。但 .de() 究竟是什麼?
在中文裡,如果要用 de 來指代一個國家的話,那它是德國。如果用來形容人的話,可以說他有 “高尚品德”。
Ohhhh!I got it. 這個函式去了一趟德國(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 envpath::EnvPath;
use serde::{Deserialize, Serialize};
#[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");我們首先定義了一個 Cfg 結構體,然後建立了一個新的 EnvPath instance, 接著把 dir 包裝進 Cfg 裡,用 ron 進行序列化,最後寫入到 test.ron。
輸出的結果是 : (dir:Some(["$env: user ?? userprofile ?? home"]))
除了多了個 dir 作為 key, 看起來它的結構與沒有序列化之前一樣啊!
Yes, you are right. 序列化後,看起來就是這樣。
這種格式的路徑適合跨平臺使用。 由於環境變數以及其他東西可能是動態改變的,因此序列化時保留 raw 格式,在反序列化時獲得它的真實路徑,這種做法是合理的。
接下來,讓我們試試反序列化吧!
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 這種普通字串,而不是路徑的值來說,? 會比 ?? 更有用。
每個人都在扮演著重要的角色,各司其職。
Basic guide 到這裡就快要結束了。 上面所述的都是一些基本功能。
project_dirs 裡有更高階的功能,以下是一些簡單的介紹。
比如說,$proj(com.macro-hard.app-name): data 會為這個專案生成 data 目錄(不會自動建立,只是生成它的值)。
com.macro-hard.app-name 這個名字有點不太妙啊!
All right,它現在是 (com. x. y)
- 在 android 上,它是
/data/data/com.x.y - 在 macOS 上,它是
/Users/[username]/Library/Application Support/com.x.y
在瞭解完基本的用法後,我們將繼續介紹和補充更多內容。
- 最簡單的 : consts
- 常用的基本標準目錄: dirs
- 高階的專案目錄: project
在下文中,我們會介紹到它們的用法,以及它們都有哪些值。
在上文中,我們已經瞭解到了基本用法。
這裡還是再囉嗦幾句。
env 指的是環境變數,$env: home 指的是獲取 HOME 環境變數的值。
$env: xdg-data-home 相當於 $XDG_DATA_HOME。
至於 '?' 的用法,您可以翻看前文,等到您瞭解 $env: userprofile ?? QwQ-Dir ? LocalAppData ? home 的作用的時候。
恭喜,您已經學會了 env 的用法了!
使用 $const: name (e.g. $const: arch) 或者是 $const: alias (e.g. $const: architecture) 來獲取常量值。
這些值是在編譯時獲取的,而不是執行時。
| name | alias | From | example |
|---|---|---|---|
| arch | architecture | consts::ARCH |
x86_64, aarch64 |
| deb-arch | deb_arch | get_deb_arch() |
amd64, arm64 |
| os | consts::OS |
linux, windows, android | |
| family | consts::FAMILY |
unix, windows | |
| exe_suffix | consts::EXE_SUFFIX |
.exe, .nexe |
|
| exe_extension | consts::EXE_EXTENSION |
exe | |
| empty | "" |
下面的表格是 $const: deb-arch 可能會輸出的值。
比如說,您編譯了一個 armv7 的軟體包, 用 $const: arch 得到的值是 arm, 而 $const: deb-arch 可能是 armhf。
| Architecture | deb_arch |
|---|---|
| x86_64 | amd64 |
| aarch64 | arm64 |
| riscv64 (riscv64gc) | riscv64 |
arm (feature = vfp3) |
armhf |
| arm | armel |
| mips (endian = little) | mipsel |
| mips64 (endian = little) | mips64el |
| s390x | s390x |
| powerpc64 (endian = little) | ppc64el |
| x86 (i586/i686) | i386 |
| other | consts::ARCH |
使用 $val:name (e.g. $val: rand-8) 來獲取值。與 $const: 不同,大部分 $val: 的值都是在執行時獲取的,而不是編譯時。
| name | expr | example |
|---|---|---|
rand-[usize] |
$val: rand-16 |
90aU0QqYnx1gPEgN |
| empty | $val: empty |
"" |
rand 用於獲取 random(隨機) 內容,目前僅支援字串。
rand 需要啟用
randfeature碎碎念:咱感覺在寫這個功能的時候,有點走火入魔了,寫著寫著,甚至想要加上時間功能,類似於
$val: time(rfc-3339, now)有時候,功能並非越多越好。 EnvPath 的主要目標是簡單的跨平臺路徑,加太多功能有點違背初衷了。
| syntax | expr | example |
|---|---|---|
env * [env_name] |
env * HOME |
C:\Users\[username] |
const * [const] |
const * arch |
x86_64 |
dir * [dir] |
dir * dl |
C:\Users\[username]\Downloads |
proj * (project): [ident] |
proj * (com.xy.z): local-data |
C:\Users\[username]\AppData\Local\xy\z\data |
val * [val] |
val * rand-32 |
o9kJjQqYc6lkznAPgaGnnY8dPYVzwawO |
["
$const: empty ??
env * home ?
env * HOME
",
"test"
]env* 可用於 fallback, 但與 $env: 不同,它不會自動將小寫字母全部轉換為大寫,也不會將 - 轉換為 _。
env * home獲取的是$home, 而不是$HOME。$env: home=>$HOMEenv * xdg-data-home=>$xdg-data-home, not$XDG_DATA_HOME$env: xdg-data-home=>$XDG_DATA_HOME
注: 如果
$env:式子中包含*, 那麼自動轉換功能也會被停用。
目前支援的語法:
$const: exe_suffix ? env * HOME ? env * XDG_DATA_HOME ? env * EXE_SUFFIX$env: home ? xdg-data-home ? exe_suffix ? const * exe_suffix
不支援:
$const: exe_suffix ? $env: home ? xdg-data-home ? exe_suffix
如果要支援這種語法的話, 那麼解析會變得麻煩,並且 $env: exe_suffix 與 $const: exe_suffix 很容易搞混。
這些是一些基本目錄,也可以說是標準目錄。
使用 $dir: name (e.g. $dir: dl) 或者是 $dir: alias (e.g. $dir: download) 來獲取 dir。
有不少內容都是透過 dirs 來獲取的,不過也有一些補充。
| name | alias | Linux $dir |
|---|---|---|
| home | $home: (/home/[username]) |
|
| cache | $xdg_cache_home:($home/.cache) |
|
| cfg | config | $xdg_config_home:($home/.config) |
| data | $xdg_data_home:($home/.local/share) |
|
| local-data | local_data | $xdg_data_home |
| local-cfg | local_config | $xdg_config_home |
| desktop | $xdg_desktop_dir:($home/Desktop) |
|
| doc | document | $xdg_documents_dir:($home/Documents) |
| dl | download | $xdg_download_dir:($home/Downloads) |
| bin | exe | $xdg_bin_home:($home/.local/bin) |
| first-path | first_path | |
| last-path | last_path | |
| font | typeface | $xdg_data_home/fonts |
| pic | picture | $xdg_pictures_dir:($home/Pictures) |
| pref | preference | $xdg_config_home |
| pub | public | $xdg_publicshare_dir:($home/Public) |
| runtime | $xdg_runtime_dir:(/run/user/[uid]/) |
|
| state | $xdg_state_home:($home/.local/state) |
|
| video | $xdg_video_dir:($home/Videos) |
|
| music | audio | $xdg_music_dir:($home/Music) |
| template | $xdg_templates_dir:($home/Templates) |
|
| tmp | $tmpdir:(/tmp) |
|
| tmp-rand | tmp_random | $tmpdir/[random] |
| temp | temporary | env::temp_dir() |
| cli-data | cli_data | $xdg_data_home |
| cli-cfg | cli_config | $xdg_config_home |
| cli-cache | cli_cache | $xdg_cache_home |
| empty | "" |
first_path 指的是第一個 $PATH 變數, last_path 則是最後一個。
若有 PATH 為 /usr/local/bin:/usr/bin,
則 /usr/local/bin 為 first_path, /usr/bin 為 last_path。
關於 tmp 與 temp
- tmp: 先獲取
$env: tmpdir的值,若存在, 則使用該值。若不存在,使用env::temp_dir()獲取,判斷檔案路徑是否只讀,若是,則使用["$dir: cache", "tmp"]- 有些平臺的 tmp 目錄對於普通使用者可能是隻讀的,沒錯,說的就是你:
/data/local/tmp
- 有些平臺的 tmp 目錄對於普通使用者可能是隻讀的,沒錯,說的就是你:
- temp: 使用
env::temp_dir()獲取, 不進行判斷 - tmp-rand: 生成隨機的臨時目錄,需要啟用
rand功能
-
var:
- sd = "/storage/self/primary"
對於沒有列出的內容,使用 linux 的資料
| name | alias | Android $dir |
|---|---|---|
| home | ||
| cache | ||
| cfg | config | |
| data | ||
| local-data | local_data | $sd/Android/data |
| local-cfg | local_config | $sd/Android/data |
| desktop | ||
| doc | document | $sd/Documents |
| dl | download | $sd/Download |
| bin | exe | |
| first-path | first_path | |
| last-path | last_path | |
| font | typeface | |
| pic | picture | $sd/Pictures |
| pref | preference | |
| pub | public | |
| runtime | ||
| state | ||
| video | $sd/Movies |
|
| music | audio | $sd/Music |
| template | ||
| tmp | $tmpdir |
|
| tmp-rand | tmp_random | $tmpdir/[random] |
| temp | temporary | 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 | |
| empty | "" |
- var:
- ms_dir =
$home\AppData\Roaming\Microsoft
- ms_dir =
| name | alias | Windows $dir |
|---|---|---|
| home | C:\Users\[username] |
|
| cache | $localappdata:($home\AppData\Local) |
|
| cfg | config | $appdata: ($home\AppData\Roaming) |
| data | $home\AppData\Roaming |
|
| local-data | local_data | $home\AppData\Local |
| local-cfg | local_config | $home\AppData\Local |
| desktop | $home\Desktop |
|
| doc | document | $home\Documents |
| dl | download | $home\Downloads |
| bin | exe | $ms_dir\WindowsApps |
| first-path | first_path | |
| last-path | last_path | |
| font | typeface | $ms_dir\Windows\Fonts |
| pic | picture | $home\Pictures |
| pref | preference | $home\AppData\Roaming |
| pub | public | $home\Public |
| runtime | None | |
| state | None | |
| video | $home\Videos |
|
| music | audio | $home\Music |
| template | $ms_dir\Windows\Templates |
|
| tmp | $tmpdir |
|
| tmp-rand | tmp_random | $tmpdir\[random] |
| temp | temporary | env::temp_dir() |
| cli-data | cli_data | $home\AppData\Local |
| cli-cfg | cli_config | $home\AppData\Local |
| cli-cache | cli_cache | $home\AppData\Local |
| progam-files | 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 |
| empty | "" |
| name | alias | macOS $dir |
|---|---|---|
| home | /Users/[username] |
|
| cache | $home/Library/Caches |
|
| cfg | config | $home/Library/Application Support |
| data | $home/Library/Application Support |
|
| local-data | local_data | $home/Library/Application Support |
| local-cfg | local_config | $home/Library/Application Support |
| desktop | $home/Desktop |
|
| doc | document | $home/Documents |
| dl | download | $home/Downloads |
| bin | exe | |
| first-path | first_path | |
| last-path | last_path | |
| font | typeface | $home/Library/Fonts |
| pic | picture | $home/Pictures |
| pref | preference | $home/Library/Preferences |
| pub | public | $home/Public |
| runtime | None | |
| state | None | |
| video | $home/Movies |
|
| music | audio | $home/music |
| template | None | |
| tmp | $tmpdir |
|
| tmp-rand | tmp_random | $tmpdir/[random] |
| temp | temporary | env::temp_dir() |
| cli-data | cli_data | $home/Library/Application Support |
| cli-cfg | cli_config | $home/Library/Application Support |
| cli-cache | cli_cache | $home/Library/Caches |
| empty | "" |
為專案生成指定目錄。 大部分資料從 directories 獲取。
使用 $proj(qualifier. organization. application): name (e.g. $proj(org. moz. ff): data) 或者是 $proj(com.company-name.app-name): alias 來獲取 project dir。
接下來假設專案為 (org. moz. ff)
| name | alias | Linux $proj |
|---|---|---|
| path | (the project path fragment): ff | |
| cache | $xdg_cache_home/$proj_path:($home/.cache/ff) |
|
| cfg | config | $xdg_config_home/$proj_path:($home/.config/ff) |
| data | $xdg_data_home/$proj_path:($home/.local/share/ff) |
|
| local-data | local_data | $xdg_data_home/$proj_path |
| local-cfg | local_config | $xdg_config_home/$proj_path |
| pref | preference | $xdg_config_home/$proj_path |
| runtime | $xdg_runtime_dir/$proj_path:(/run/user/[uid]/ff) |
|
| state | $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 |
| empty | "" |
-
var:
- sd = "/storage/self/primary"
| name | alias | Android $proj |
|---|---|---|
| path | org.moz.ff | |
| cache | /data/data/org.moz.ff/cache | |
| cfg | config | /data/data/org.moz.ff/files |
| data | /data/data/org.moz.ff | |
| local-data | local_data | $sd/Android/data/org.moz.ff |
| local-cfg | local_config | $sd/Android/data/org.moz.ff/files |
| pref | preference | /data/data/org.moz.ff/files |
| runtime | $xdg_runtime_dir/ff |
|
| state | $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 |
| empty | "" |
| name | alias | Windows $proj |
|---|---|---|
| path | moz\ff |
|
| cache | $home\AppData\Local\moz\ff\cache |
|
| cfg | config | $home\AppData\Roaming\moz\ff\config |
| data | $home\AppData\Roaming\moz\ff\data |
|
| local-data | local_data | $home\AppData\Local\moz\ff\data |
| local-cfg | local_config | $home\AppData\Local\moz\ff\config |
| pref | preference | $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 |
| empty | "" |
| name | alias | macOS $proj |
|---|---|---|
| path | org.moz.ff | |
| cache | $home/Library/Caches/org.moz.ff |
|
| cfg | config | $home/Library/Application Support/org.moz.ff |
| data | $home/Library/Application Support/org.moz.ff |
|
| local-data | local_data | $home/Library/Application Support/org.moz.ff |
| local-cfg | local_config | $home/Library/Application Support/org.moz.ff |
| pref | preference | $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 |
| empty | "" |
$proj 支援的 ? 語法比其他的型別要更復雜一點,因為其他型別沒有 (),而它有。
別灰心,如果您已經掌握了核心語法,那麼相信您定能在幾分鐘快速掌握 $proj 的 ??語法。
假設有三個專案:
- (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 專案的 runtime, 很不幸,它不存在。
我們接著解析 data!太好了,我們發現它的值是存在的。
然而,好景不長,好不容易找到了一個存在的值,這時候天道的考驗來了,只有透過才能羽化飛昇。怎麼回事?我現在的修為連元嬰期都沒有,而且靈力和神識還在不斷潰散
(不好意思,拿錯劇本了...
由於有兩個 '?' ,所以還需要判斷檔案路徑是否存在。
(所以說 data 君 的身死道消也不是沒有理由的嗎
由於此專案的 data 的路徑不存在,因此下一個幸運兒是 state。
遠處,一聲充滿不甘,又直透人心的怒吼聲傳來:“我命由我不由天,我只是錯...”
突然間,聲音戛然而止,未幾,遠處傳來了嬰兒的啼哭聲,讓一切都充滿著陰森詭異的味道。
很遺憾,它也沒能透過,因為它的值不存在。
至此,ff 陣營的成員全軍覆沒,沒有一個解析成功。
於是,我們接著解析 cr 專案,幸運的是,第一次就成功了。
cr 的 cfg 不僅值存在,路徑也是存在的。
最終的勝者是 cr 家的 cfg,儘管如此,但她卻不怎麼開心的樣子。在離開前,她嘴角邊還嘟喃道:“明明只是個解析器,竟然敢這麼囂張,哼!”
第二個例子為:
["
$proj (org . moz . ff ):runtime ? data ?? state ?
(com . gg . cr): cfg ?? cache ?
(com . ms . eg): local-data ? data
"]Q: 咦?我怎麼沒看出來,這與第一個例子有何不同? A: 屆時,汝自會知曉。 (此時一道亮光劃破天際,而眼前之人早已不見蹤影) Q: 真是奇怪,我的腦袋裡怎麼會多出些奇奇怪怪的記憶?我怕不是睡迷糊了。對了,我剛剛在和誰說話來著?