envpath 0.0.1

A library for parsing and deserialising paths with special rules. The format is similar to `["$proj(com.xy.z): data ? cfg", "$const: os", "$val: rand-16"]`
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
# EnvPath

[![envpath.crate](https://img.shields.io/crates/v/envpath.svg?logo=rust&logoColor=lightsalmon&label=envpath)](https://crates.io/crates/envpath)

[![Documentation](https://docs.rs/envpath/badge.svg)](https://docs.rs/envpath)
[![Apache-2 licensed](https://img.shields.io/crates/l/envpath.svg?logo=apache)](../License)

一個用於 **解析** 和 **反序列化** 具有特殊規則的路徑的 library。

格式類似於 `["$proj(com.xy.z): data ? cfg", "$const: os", "$val: rand-16"]`

<!-- Language -->
<details open>
<summary>
<img alt="Language/語言" src="./svg/language.svg" />
</summary>

- [zh-Hant: 繁體中文]Readme-zh-Hant.md
- [en: English]Readme.md
- [zh: 簡體中文]Readme-zh.md

</details>

<!-- TOC -->
<details open>
<summary>
<img alt="目錄" src="./svg/toc/目錄.svg"/>
</summary>

- [preface]#preface
  - [結構]#結構
- [Quick Start]#quick-start
  - [Basic guide]#basic-guide
  - [serialisation \& deserialisation](#serialisation--deserialisation)
    - [serialisation]#serialisation
    - [deserialisation]#deserialisation
- [Features]#features
  - [env]#env
  - [const]#const
    - [deb-arch]#deb-arch
  - [val]#val
  - [remix]#remix
    - [example]#example
  - [base]#base
    - [Linux]#linux
    - [Android]#android
    - [Windows]#windows
    - [macOS]#macos
  - [project]#project
    - [Linux]#linux-1
    - [Android]#android-1
    - [Windows]#windows-1
    - [macOS]#macos-1
    - [project 中的 "??"]#project-中的-

</details>

## preface

在正式開始前,很抱歉打擾到您,能否請您解答我心中的一個小小的困惑呢?

我們一直以來是如何解決跨平臺路徑配置的問題?

假設有如下配置:

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

也許我們會為配置檔案新建一個 Map (e.g. `HashMap<String, Dirs>`), 讓不同平臺使用不同配置。

```toml
[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 的設計初衷就是為了解決跨平臺目錄配置的問題。

```toml
[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/dir
```

### 結構

Raw 格式的 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 目錄。

注意:一個 `?` 與兩個 `?` 是有區別的, 之後我們會提到的。

## Quick Start

### Basic guide

首先,我們需要新增依賴:

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

然後在我們的 `main()` 或者是測試函數里新增以下內容。

```rust
use envpath::EnvPath;

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

這是一個簡單的例子,還有更多的功能和概念,我沒有在這裡提到。
不要著急,一步一步慢慢來。

然後它會輸出類似於以下的內容

```js
[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` 的結構轉換成另一個值。

### serialisation & deserialisation

如果您想要序列化/反序列化配置檔案,需要啟用 envpath 的 `serde` 功能,並且還要新增 serde 依賴,以及與之有關的其他依賴。

下面我們將新增一個 `ron` 依賴(實際上您還可以用 yaml 或 json 等格式,不過相關依賴就不是 ron 了)

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

#### serialisation

接著讓我們一起寫程式碼吧!

```rust
        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 格式,在反序列化時獲得它的真實路徑,這種做法是合理的。

#### deserialisation

接下來,讓我們試試反序列化吧!

```rust
        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())
            }
        }
```

上面的函式輸出的結果為

```rs
[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

在下文中,我們會介紹到它們的用法,以及它們都有哪些值。

## Features

### env

在上文中,我們已經瞭解到了基本用法。
這裡還是再囉嗦幾句。
env 指的是環境變數,`$env: home` 指的是獲取 HOME 環境變數的值。
`$env:   xdg-data-home` 相當於 `$XDG_DATA_HOME`。
至於 '?' 的用法,您可以翻看前文,等到您瞭解 `$env: userprofile ??  QwQ-Dir ? LocalAppData ? home` 的作用的時候。
恭喜,您已經學會了 env 的用法了!

### const

使用 `$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         |              |                         | ""                      |

#### deb-arch

下面的表格是 `$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]https://doc.rust-lang.org/nightly/std/env/consts/constant.ARCH.html |

### val

使用 `$val:name` (e.g. `$val: rand-8`) 來獲取值。與 `$const:` 不同,大部分 `$val:` 的值都是在執行時獲取的,而不是編譯時。

| name           | expr            | example          |
| -------------- | --------------- | ---------------- |
| `rand-[usize]` | `$val: rand-16` | 90aU0QqYnx1gPEgN |
| empty          | `$val: empty`   | ""               |

rand 用於獲取 random(隨機) 內容,目前僅支援字串。

> rand 需要啟用 `rand` feature
>
> 碎碎念:咱感覺在寫這個功能的時候,有點走火入魔了,寫著寫著,甚至想要加上時間功能,類似於 `$val: time(rfc-3339, now)`
>
> 有時候,功能並非越多越好。
> EnvPath 的主要目標是簡單的跨平臺路徑,加太多功能有點違背初衷了。

### remix

| 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              |

#### example

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

`env*` 可用於 fallback, 但與 `$env:` 不同,它不會自動將小寫字母全部轉換為大寫,也不會將 `-` 轉換為 `_`。

- `env * home` 獲取的是 `$home` , 而不是 `$HOME`- `$env: home` => `$HOME`
- `env * 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` 很容易搞混。

### base

這些是一些基本目錄,也可以說是標準目錄。
使用 `$dir: name` (e.g. `$dir: dl`) 或者是 `$dir: alias` (e.g. `$dir: download`) 來獲取 dir。
有不少內容都是透過 [dirs](https://docs.rs/dirs/latest/dirs/) 來獲取的,不過也有一些補充。

#### Linux

| 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`
- temp: 使用 `env::temp_dir()` 獲取, 不進行判斷
- tmp-rand: 生成隨機的臨時目錄,需要啟用 `rand` 功能

#### Android

- 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      |              | ""                                    |

#### Windows

- var:
  - ms_dir = `$home\AppData\Roaming\Microsoft`

| 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                    |                          | ""                                                                  |

#### macOS

| 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      |              | ""                                  |

### project

為專案生成指定目錄。
大部分資料從 [directories](https://docs.rs/directories/latest/directories/struct.ProjectDirs.html) 獲取。

使用 `$proj(qualifier.  organization.   application): name` (e.g. `$proj(org. moz. ff): data`) 或者是 `$proj(com.company-name.app-name): alias` 來獲取 project dir。

接下來假設專案為 `(org. moz. ff)`

#### Linux

| 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      |              | ""                                                     |

#### Android

- 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      |              | ""                                  |

#### Windows

| 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      |              | ""                                    |

#### macOS

| 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      |              | ""                                             |

#### project 中的 "??"

`$proj` 支援的 `?` 語法比其他的型別要更復雜一點,因為其他型別沒有 `()`,而它有。
別灰心,如果您已經掌握了核心語法,那麼相信您定能在幾分鐘快速掌握 `$proj` 的 `??`語法。

假設有三個專案:

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

第一個例子為:

```rs
["
    $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,儘管如此,但她卻不怎麼開心的樣子。在離開前,她嘴角邊還嘟喃道:“明明只是個解析器,竟然敢這麼囂張,哼!”

第二個例子為:

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

Q: 咦?我怎麼沒看出來,這與第一個例子有何不同?
A: 屆時,汝自會知曉。
(此時一道亮光劃破天際,而眼前之人早已不見蹤影)
Q: 真是奇怪,我的腦袋裡怎麼會多出些奇奇怪怪的記憶?我怕不是睡迷糊了。對了,我剛剛在和誰說話來著?