{"id":26365416,"url":"https://github.com/2moe/envpath","last_synced_at":"2026-03-18T00:04:23.040Z","repository":{"id":143446870,"uuid":"615703783","full_name":"2moe/envpath","owner":"2moe","description":"A library for parsing and deserialising paths with special rules.","archived":false,"fork":false,"pushed_at":"2025-05-20T10:09:26.000Z","size":154,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2025-11-16T17:09:52.687Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/2moe.png","metadata":{"files":{"readme":"docs/Readme-zh-Hant.md","changelog":null,"contributing":null,"funding":null,"license":"License","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-03-18T12:42:19.000Z","updated_at":"2025-05-20T10:09:30.000Z","dependencies_parsed_at":"2025-07-20T01:21:39.606Z","dependency_job_id":"bb754302-1546-4a97-9a1a-52e99d72c7aa","html_url":"https://github.com/2moe/envpath","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/2moe/envpath","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fenvpath","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fenvpath/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fenvpath/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fenvpath/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/2moe","download_url":"https://codeload.github.com/2moe/envpath/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/2moe%2Fenvpath/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30636706,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T23:56:54.546Z","status":"ssl_error","status_checked_at":"2026-03-17T23:56:28.952Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-16T19:36:30.610Z","updated_at":"2026-03-18T00:04:23.029Z","avatar_url":"https://github.com/2moe.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EnvPath\n\n[![envpath.crate](https://img.shields.io/crates/v/envpath.svg?logo=rust\u0026logoColor=lightsalmon\u0026label=envpath)](https://crates.io/crates/envpath)\n\n[![Documentation](https://docs.rs/envpath/badge.svg)](https://docs.rs/envpath)\n[![Apache-2 licensed](https://img.shields.io/crates/l/envpath.svg?logo=apache)](../License)\n\n一個用於 **解析** 和 **反序列化** 具有特殊規則的路徑的 library。\n\n格式類似於 `[\"$proj(com.xy.z): data ? cfg\", \"$const: os\", \"$val: rand-16\"]`\n\n\u003c!-- Language --\u003e\n\u003cdetails open\u003e\n\u003csummary\u003e\n\u003cimg alt=\"Language/語言\" src=\"./svg/language.svg\" /\u003e\n\u003c/summary\u003e\n\n- [zh-Hant: 繁體中文](Readme-zh-Hant.md)\n- [en: English](Readme.md)\n- [zh: 簡體中文](Readme-zh.md)\n\n\u003c/details\u003e\n\n\u003c!-- TOC --\u003e\n\u003cdetails open\u003e\n\u003csummary\u003e\n\u003cimg alt=\"目錄\" src=\"./svg/toc/目錄.svg\"/\u003e\n\u003c/summary\u003e\n\n- [preface](#preface)\n  - [結構](#結構)\n- [Quick Start](#quick-start)\n  - [Basic guide](#basic-guide)\n  - [serialisation \\\u0026 deserialisation](#serialisation--deserialisation)\n    - [serialisation](#serialisation)\n    - [deserialisation](#deserialisation)\n- [Features](#features)\n  - [env](#env)\n  - [const](#const)\n    - [deb-arch](#deb-arch)\n  - [val](#val)\n  - [remix](#remix)\n    - [example](#example)\n  - [base](#base)\n    - [Linux](#linux)\n    - [Android](#android)\n    - [Windows](#windows)\n    - [macOS](#macos)\n  - [project](#project)\n    - [Linux](#linux-1)\n    - [Android](#android-1)\n    - [Windows](#windows-1)\n    - [macOS](#macos-1)\n    - [project 中的 \"??\"](#project-中的-)\n\n\u003c/details\u003e\n\n## preface\n\n在正式開始前，很抱歉打擾到您，能否請您解答我心中的一個小小的困惑呢？\n\n我們一直以來是如何解決跨平臺路徑配置的問題？\n\n假設有如下配置：\n\n```toml\n[dir]\ndata = \"C:\\\\Users\\\\[username]\\\\AppData\\\\Roaming\\\\[dirname]\"\n```\n\n也許我們會為配置檔案新建一個 Map (e.g. `HashMap\u003cString, Dirs\u003e`), 讓不同平臺使用不同配置。\n\n```toml\n[dir.linux]\ndata = \"/home/[username]/.local/share/[appname]\"\ncache = \"/home/[username]/.cache/[app]\"\n\n[dir.macos]\ndata = \"/Users/[username]/Library/Application Support/x.y.z\"\n\n[dir.xxxbsd]\n\n[dir.your-os-name]\n```\n\n這是一個好方法，但是有沒有更通用的方法呢？\n於是，人們想到了使用環境變數。\n\n我猜您想到了 XDG 規範，既然它這麼有用的話，那我們在所有平臺上都使用 `$XDG_DATA_HOME/[appname]/` 如何？\n然而，不幸的是，並不是所有平臺都支援 XDG 規範，所以我們選擇更通用的 `$HOME`。\n\n不幸的事情再次到來，在早期的 Windows 上，可能並沒有 `%HOME%`, 而是隻有 `%userprofile%`。\n\nenvpath 的設計初衷就是為了解決跨平臺目錄配置的問題。\n\n```toml\n[dir]\ndata = [\n    \"$proj(com.org-name.app-name): local-data\",\n    \"dir\"\n]\n# - Windows: C:\\Users\\[username]\\AppData\\Local\\org-name\\app-name\\dir\n# - Linux: /home/[username]/.local/share/app-name/dir\n# - macOS: /Users/[username]/Library/Application Support/com.org-name.app-name/dir\n```\n\n### 結構\n\nRaw 格式的 EnvPath 本質上是使用了特殊的規則的陣列結構。\n\n它的缺點特別明顯，對於普通使用者來說，這種格式看起來可能會比字串更難看。\n\n請注意，使用者配置檔案是用來給使用者看的，而不是隻是單純地用來反序列化，所以可讀性至關重要。\n\n當不使用特殊規則時，它可以相容普通的路徑陣列。\n\n比如 `[\"C:\", \"\\\\\", \"Users\", \"Public\"]` 相當於 `C:\\Users\\Public`\n\n\u003e 這裡有個容易忽視的小細節，就是第二個元素是 \"\\\\\"。\n\nQ: 特殊規則？\n\nA: 當陣列的元素以 `$[關鍵詞]`開頭，並且整個元素的表示式都能正常解析時，它就是一條特殊規則。一個數組可以應用多條特殊規則。\n\n例子：\n\n- 環境變數（關鍵詞 env）： `[\"$env: XDG_CONFIG_HOME\", \"xx\"]`，在某些系統上，它會被解析為 `/home/[user]/.config/xx`\n- `?` 代表 fallback：\n  - `[\"$dir: dl ? doc\"]` 指定為 Downloads 目錄（不同的平臺的路徑不一樣）\n  - 如果 Downloads 目錄的值不存在，就用 Documents 目錄。\n\n注意：一個 `?` 與兩個 `?` 是有區別的, 之後我們會提到的。\n\n## Quick Start\n\n### Basic guide\n\n首先，我們需要新增依賴:\n\n```sh\ncargo add envpath --no-default-features --features=dirs,consts,project\n```\n\n然後在我們的 `main()` 或者是測試函數里新增以下內容。\n\n```rust\nuse envpath::EnvPath;\n\nlet v = EnvPath::from([\"$dir: data\", \"$env: test_qwq\", \"app\"]).de();\ndbg!(v.display(), v.exists());\n```\n\n這是一個簡單的例子，還有更多的功能和概念，我沒有在這裡提到。\n不要著急，一步一步慢慢來。\n\n然後它會輸出類似於以下的內容\n\n```js\n[src/lib.rs:74] v.display() = \"/home/m/.local/share/$env: test_qwq/app\"\n[src/lib.rs:74] v.exists() = false\n```\n\n我們可以看到 `$env: test_qwq` 並沒有被成功解析。\n\n所以到底發生了什麼？是它出故障了嗎？\n\n不，並不是，這是有意為之的設計。 EnvPath 在設計之初，就有意相容普通的路徑。\n\n如果有一天， envpath 新增了一個功能，需要以 `$recycle` 為字首，加上 `bin` 關鍵詞就能解析到特定目錄。\n而您的系統磁碟上，剛好有個 `$RECYCLE:BIN` 資料夾，不巧的是，那個磁碟的檔案系統剛好沒有開啟區分大小寫（Case-sensitive）的功能。\n當存在同名路徑時，預設會先解析，解析失敗後，會假設當前存在同名路徑，然後直接返回。\n同名路徑碰撞（解析的式子與檔案路徑同名）的機率是存在的，不過，只要稍微花一點技巧就能避開絕大多數的碰撞事件。\n\n\u003e 技巧：多用空白字元 (空格，換行符，製表符之類的)，以及使用 `?`(下文會介紹)\n\u003e 比如 `$env: test_qwq` 可以寫成 `$env    ：          test-QwQ`\n\u003e 儘管加了那麼多空格， 但如果成功的話，它們會被解析為同一個值。將上面的表示式用 unix 的 posix sh 來描述是： `$TEST_QWQ` (i.e. 所有小寫字母全部變為大寫，所有 `-` 全部變為 `_`)\n\u003e 儘管您覺得這種做法可能很難接受，但對於全域性系統環境變數來說，這樣做是慣例，我並沒有創造新的規則。\n\n既然都解析失敗了，那為什麼不返回空目錄呢?\n\n用 posix sh 的 env 舉個例子吧！\n\n假設您要訪問的目錄是 `$XDG_DATA_HOME/app`, 如果相關的 env 是空的話，那麼您訪問的就是 /app ，這與預期結果不同。（~~我想要回家，但是卻買錯了車票 🎫~~\n您可能會辯解道： 我可以用 `${ENV_NAME:-FALLBACK}` 來指定 fallback 啊！ 明明是你太笨了。\n\n然而，有時候一不小心的疏忽可能會釀成大錯。我覺得少點抱怨，會讓生活變得更美好。\n\n說到這裡，您可能已經忘記了前面出錯的地方： `$env: test_qwq`。\n那麼要如何解決呢？您可以試試把它修改為 `$env: test_qwq ? user ? logname`， 或者是新增更多的問號與有效的環境變數名稱。\n\n~~這裡就先不解釋`?` 的作用了，自己去探索，往往能發現更多的樂趣。（我寫完這句話後，才想起前面已經解釋過了 QuQ~~\n\n---\n\n回到我們剛開始提到的程式碼，然後稍微簡化一下。\n`EnvPath::from([\"$dir: data\"]).de();`\n\nAs is well known, `[]` 是一個數組。但 `.de()` 究竟是什麼？\n在中文裡，如果要用 `de` 來指代一個國家的話，那它是德國。如果用來形容人的話，可以說他有 “高尚品德”。\nOhhhh！I got it. 這個函式去了一趟德國（de），所以發生了改變，變成了有品德的函式。\n\n總之，我覺得您很聰明，這個函式的確發生了變化。\n不過它只是將類似於 `$env: QuQ ?? qwq-dir ? AwA-home` 的結構轉換成另一個值。\n\n### serialisation \u0026 deserialisation\n\n如果您想要序列化/反序列化配置檔案，需要啟用 envpath 的 `serde` 功能，並且還要新增 serde 依賴，以及與之有關的其他依賴。\n\n下面我們將新增一個 `ron` 依賴（實際上您還可以用 yaml 或 json 等格式，不過相關依賴就不是 ron 了）\n\n```sh\ncargo add envpath --features=serde\ncargo add serde --features=derive\ncargo add ron\n```\n\n#### serialisation\n\n接著讓我們一起寫程式碼吧！\n\n```rust\n        use envpath::EnvPath;\n        use serde::{Deserialize, Serialize};\n\n        #[derive(Debug, Default, Serialize, Deserialize)]\n        #[serde(default)]\n        struct Cfg\u003c'a\u003e {\n            dir: Option\u003cEnvPath\u003c'a\u003e\u003e,\n        }\n\n        let dir = Some(EnvPath::from([\n            \"$env: user ?? userprofile ?? home\",\n        ]));\n\n        let ron_str = ron::to_string(\u0026Cfg { dir }).expect(\"Failed to ser\");\n        println!(\"{ron_str}\");\n\n        std::fs::write(\"test.ron\", ron_str)\n            .expect(\"Failed to write the ron cfg to test.ron\");\n```\n\n我們首先定義了一個 Cfg 結構體，然後建立了一個新的 EnvPath instance, 接著把 dir 包裝進 Cfg 裡，用 ron 進行序列化，最後寫入到 `test.ron`。\n\n輸出的結果是 : `(dir:Some([\"$env: user ?? userprofile ?? home\"]))`\n\n除了多了個 `dir` 作為 key， 看起來它的結構與沒有序列化之前一樣啊！\nYes, you are right. 序列化後，看起來就是這樣。\n\n這種格式的路徑適合跨平臺使用。\n由於環境變數以及其他東西可能是動態改變的，因此序列化時保留 raw 格式，在反序列化時獲得它的真實路徑，這種做法是合理的。\n\n#### deserialisation\n\n接下來，讓我們試試反序列化吧！\n\n```rust\n        use envpath::EnvPath;\n        use serde::{Deserialize, Serialize};\n        use std::fs::File;\n\n        #[derive(Debug, Default, Serialize, Deserialize)]\n        #[serde(default)]\n        struct Cfg\u003c'a\u003e {\n            dir: Option\u003cEnvPath\u003c'a\u003e\u003e,\n        }\n\n        let cfg: Cfg = ron::de::from_reader(\n            File::open(\"test.ron\").expect(\"Failed to open the file: text.ron\"),\n        )\n        .expect(\"Failed to deser ron cfg\");\n\n        dbg!(\u0026cfg);\n\n        if let Some(x) = cfg.dir {\n            if x.exists() {\n                println!(\"{}\", x.display())\n            }\n        }\n```\n\n上面的函式輸出的結果為\n\n```rs\n[src/lib.rs:116] \u0026cfg = Cfg {\n    dir: Some(\n        EnvPath {\n            raw: [\n                \"$env: user ?? userprofile ?? home\",\n            ],\n            path: Some(\n                \"/home/m\",\n            ),\n        },\n    ),\n}\n/home/m\n```\n\n`?` 會判斷值是否存在，如果不存在，那就繼續判斷。如果存在，那就使用這個值。\n\n而 `??` 指的是值和路徑都要存在。\n\n比如說 `$env: user ? userprofile`, 這裡假設 user 的值為 m, userprofile 的值為空。\n因為 user 的值存在，所以這條表示式的返回值為 m。\n\n如果把它改成 `$env: user ?? userprofile ? home` 的話，\n儘管 user 的值存在，但它的路徑不存在，所以繼續判斷。\n然後，userprofile 的值不存在，所以繼續判斷，直到滿足條件為止。\n\n`?` 和 `??` 有著不同的作用，並不是說有了 `??` 後就可以拋棄 `?`。\n對於 `$const: os` 這種普通字串，而不是路徑的值來說，`?` 會比 `??` 更有用。\n每個人都在扮演著重要的角色，各司其職。\n\nBasic guide 到這裡就快要結束了。\n上面所述的都是一些基本功能。\n\nproject_dirs 裡有更高階的功能，以下是一些簡單的介紹。\n比如說，`$proj(com.macro-hard.app-name): data` 會為這個專案生成 data 目錄（不會自動建立，只是生成它的值）。\n\n\u003e com.macro-hard.app-name 這個名字有點不太妙啊！\n\nAll right，它現在是 `(com. x. y)`\n\n- 在 android 上，它是 `/data/data/com.x.y`\n- 在 macOS 上，它是 `/Users/[username]/Library/Application Support/com.x.y`\n\n在瞭解完基本的用法後，我們將繼續介紹和補充更多內容。\n\n- 最簡單的 ： consts\n- 常用的基本標準目錄： dirs\n- 高階的專案目錄： project\n\n在下文中，我們會介紹到它們的用法，以及它們都有哪些值。\n\n## Features\n\n### env\n\n在上文中，我們已經瞭解到了基本用法。\n這裡還是再囉嗦幾句。\nenv 指的是環境變數，`$env: home` 指的是獲取 HOME 環境變數的值。\n`$env:   xdg-data-home` 相當於 `$XDG_DATA_HOME`。\n至於 '?' 的用法，您可以翻看前文，等到您瞭解 `$env: userprofile ??  QwQ-Dir ? LocalAppData ? home` 的作用的時候。\n恭喜，您已經學會了 env 的用法了！\n\n### const\n\n使用 `$const: name` (e.g. `$const: arch`) 或者是 `$const: alias` (e.g. `$const: architecture`) 來獲取常量值。\n這些值是在編譯時獲取的，而不是執行時。\n\n| name          | alias        | From                    | example                 |\n| ------------- | ------------ | ----------------------- | ----------------------- |\n| arch          | architecture | `consts::ARCH`          | x86_64, aarch64         |\n| deb-arch      | deb_arch     | `get_deb_arch()`        | amd64, arm64            |\n| os            |              | `consts::OS`            | linux, windows, android |\n| family        |              | `consts::FAMILY`        | unix, windows           |\n| exe_suffix    |              | `consts::EXE_SUFFIX`    | `.exe`, `.nexe`         |\n| exe_extension |              | `consts::EXE_EXTENSION` | exe                     |\n| empty         |              |                         | \"\"                      |\n\n#### deb-arch\n\n下面的表格是 `$const: deb-arch` 可能會輸出的值。\n\n比如說，您編譯了一個 `armv7` 的軟體包， 用 `$const:  arch` 得到的值是 arm, 而 `$const:  deb-arch` 可能是 armhf。\n\n| Architecture                | deb_arch                                                                            |\n| --------------------------- | ----------------------------------------------------------------------------------- |\n| x86_64                      | amd64                                                                               |\n| aarch64                     | arm64                                                                               |\n| riscv64 (riscv64gc)         | riscv64                                                                             |\n| arm (feature = `vfp3`)      | armhf                                                                               |\n| arm                         | armel                                                                               |\n| mips (endian = little)      | mipsel                                                                              |\n| mips64 (endian = little)    | mips64el                                                                            |\n| s390x                       | s390x                                                                               |\n| powerpc64 (endian = little) | ppc64el                                                                             |\n| x86 (i586/i686)             | i386                                                                                |\n| other                       | [consts::ARCH](https://doc.rust-lang.org/nightly/std/env/consts/constant.ARCH.html) |\n\n### val\n\n使用 `$val:name` (e.g. `$val: rand-8`) 來獲取值。與 `$const:` 不同，大部分 `$val:` 的值都是在執行時獲取的，而不是編譯時。\n\n| name           | expr            | example          |\n| -------------- | --------------- | ---------------- |\n| `rand-[usize]` | `$val: rand-16` | 90aU0QqYnx1gPEgN |\n| empty          | `$val: empty`   | \"\"               |\n\nrand 用於獲取 random(隨機) 內容，目前僅支援字串。\n\n\u003e rand 需要啟用 `rand` feature\n\u003e\n\u003e 碎碎念：咱感覺在寫這個功能的時候，有點走火入魔了，寫著寫著，甚至想要加上時間功能，類似於 `$val: time(rfc-3339, now)`\n\u003e\n\u003e 有時候，功能並非越多越好。\n\u003e EnvPath 的主要目標是簡單的跨平臺路徑，加太多功能有點違背初衷了。\n\n### remix\n\n| syntax                      | expr                            | example                                       |\n| --------------------------- | ------------------------------- | --------------------------------------------- |\n| `env * [env_name]`          | `env * HOME`                    | `C:\\Users\\[username]`                         |\n| `const * [const]`           | `const * arch`                  | `x86_64`                                      |\n| `dir * [dir]`               | `dir * dl`                      | `C:\\Users\\[username]\\Downloads`               |\n| `proj * (project): [ident]` | `proj * (com.xy.z): local-data` | `C:\\Users\\[username]\\AppData\\Local\\xy\\z\\data` |\n| `val * [val]`               | `val * rand-32`                 | o9kJjQqYc6lkznAPgaGnnY8dPYVzwawO              |\n\n#### example\n\n```rs\n[\"\n    $const: empty ??\n        env * home ?\n        env * HOME\n\",\n    \"test\"\n]\n```\n\n`env*` 可用於 fallback, 但與 `$env:` 不同，它不會自動將小寫字母全部轉換為大寫，也不會將 `-` 轉換為 `_`。\n\n- `env * home` 獲取的是 `$home` , 而不是 `$HOME`。\n- `$env: home` =\u003e `$HOME`\n- `env * xdg-data-home` =\u003e `$xdg-data-home`, not `$XDG_DATA_HOME`\n- `$env: xdg-data-home` =\u003e `$XDG_DATA_HOME`\n\n\u003e 注： 如果 `$env:` 式子中包含 `*`， 那麼自動轉換功能也會被停用。\n\n目前支援的語法：\n\n- `$const: exe_suffix ?   env * HOME ?   env * XDG_DATA_HOME ?   env * EXE_SUFFIX`\n- `$env: home ? xdg-data-home ? exe_suffix ?    const * exe_suffix`\n\n不支援:\n\n- `$const: exe_suffix ? $env: home ? xdg-data-home ? exe_suffix`\n\n如果要支援這種語法的話, 那麼解析會變得麻煩，並且 `$env: exe_suffix` 與 `$const: exe_suffix` 很容易搞混。\n\n### base\n\n這些是一些基本目錄，也可以說是標準目錄。\n使用 `$dir: name` (e.g. `$dir: dl`) 或者是 `$dir: alias` (e.g. `$dir: download`) 來獲取 dir。\n有不少內容都是透過 [dirs](https://docs.rs/dirs/latest/dirs/) 來獲取的，不過也有一些補充。\n\n#### Linux\n\n| name       | alias        | Linux `$dir`                             |\n| ---------- | ------------ | ---------------------------------------- |\n| home       |              | `$home`: (`/home/[username]`)            |\n| cache      |              | `$xdg_cache_home`:(`$home/.cache`)       |\n| cfg        | config       | `$xdg_config_home`:(`$home/.config`)     |\n| data       |              | `$xdg_data_home`:(`$home/.local/share`)  |\n| local-data | local_data   | `$xdg_data_home`                         |\n| local-cfg  | local_config | `$xdg_config_home`                       |\n| desktop    |              | `$xdg_desktop_dir`:(`$home/Desktop`)     |\n| doc        | document     | `$xdg_documents_dir`:(`$home/Documents`) |\n| dl         | download     | `$xdg_download_dir`:(`$home/Downloads`)  |\n| bin        | exe          | `$xdg_bin_home`:(`$home/.local/bin`)     |\n| first-path | first_path   |                                          |\n| last-path  | last_path    |                                          |\n| font       | typeface     | `$xdg_data_home/fonts`                   |\n| pic        | picture      | `$xdg_pictures_dir`:(`$home/Pictures`)   |\n| pref       | preference   | `$xdg_config_home`                       |\n| pub        | public       | `$xdg_publicshare_dir`:(`$home/Public`)  |\n| runtime    |              | `$xdg_runtime_dir`:(`/run/user/[uid]/`)  |\n| state      |              | `$xdg_state_home`:(`$home/.local/state`) |\n| video      |              | `$xdg_video_dir`:(`$home/Videos`)        |\n| music      | audio        | `$xdg_music_dir`:(`$home/Music`)         |\n| template   |              | `$xdg_templates_dir`:(`$home/Templates`) |\n| tmp        |              | `$tmpdir`:(`/tmp`)                       |\n| tmp-rand   | tmp_random   | `$tmpdir/[random]`                       |\n| temp       | temporary    | `env::temp_dir()`                        |\n| cli-data   | cli_data     | `$xdg_data_home`                         |\n| cli-cfg    | cli_config   | `$xdg_config_home`                       |\n| cli-cache  | cli_cache    | `$xdg_cache_home`                        |\n| empty      |              | \"\"                                       |\n\nfirst_path 指的是第一個 `$PATH` 變數， last_path 則是最後一個。\n若有 PATH 為 `/usr/local/bin:/usr/bin`，\n則 `/usr/local/bin` 為 first_path, `/usr/bin` 為 last_path。\n\n關於 tmp 與 temp\n\n- tmp: 先獲取 `$env: tmpdir` 的值，若存在, 則使用該值。若不存在，使用 `env::temp_dir()` 獲取，判斷檔案路徑是否只讀，若是，則使用 `[\"$dir: cache\", \"tmp\"]`\n  - 有些平臺的 tmp 目錄對於普通使用者可能是隻讀的，沒錯，說的就是你： `/data/local/tmp`\n- temp: 使用 `env::temp_dir()` 獲取, 不進行判斷\n- tmp-rand: 生成隨機的臨時目錄，需要啟用 `rand` 功能\n\n#### Android\n\n- var:\n\n  - sd = \"/storage/self/primary\"\n\n對於沒有列出的內容，使用 linux 的資料\n\n| name       | alias        | Android `$dir`                        |\n| ---------- | ------------ | ------------------------------------- |\n| home       |              |                                       |\n| cache      |              |                                       |\n| cfg        | config       |                                       |\n| data       |              |                                       |\n| local-data | local_data   | `$sd/Android/data`                    |\n| local-cfg  | local_config | `$sd/Android/data`                    |\n| desktop    |              |                                       |\n| doc        | document     | `$sd/Documents`                       |\n| dl         | download     | `$sd/Download`                        |\n| bin        | exe          |                                       |\n| first-path | first_path   |                                       |\n| last-path  | last_path    |                                       |\n| font       | typeface     |                                       |\n| pic        | picture      | `$sd/Pictures`                        |\n| pref       | preference   |                                       |\n| pub        | public       |                                       |\n| runtime    |              |                                       |\n| state      |              |                                       |\n| video      |              | `$sd/Movies`                          |\n| music      | audio        | `$sd/Music`                           |\n| template   |              |                                       |\n| tmp        |              | `$tmpdir`                             |\n| tmp-rand   | tmp_random   | `$tmpdir/[random]`                    |\n| temp       | temporary    | `env::temp_dir()`:(`/data/local/tmp`) |\n| cli-data   | cli_data     | `$xdg_data_home`                      |\n| cli-cfg    | cli_config   | `$xdg_config_home`                    |\n| cli-cache  | cli_cache    | `$xdg_cache_home`                     |\n| sd         |              | /storage/self/primary                 |\n| empty      |              | \"\"                                    |\n\n#### Windows\n\n- var:\n  - ms_dir = `$home\\AppData\\Roaming\\Microsoft`\n\n| name                     | alias                    | Windows `$dir`                                                      |\n| ------------------------ | ------------------------ | ------------------------------------------------------------------- |\n| home                     |                          | `C:\\Users\\[username]`                                               |\n| cache                    |                          | `$localappdata`:(`$home\\AppData\\Local`)                             |\n| cfg                      | config                   | `$appdata`: (`$home\\AppData\\Roaming`)                               |\n| data                     |                          | `$home\\AppData\\Roaming`                                             |\n| local-data               | local_data               | `$home\\AppData\\Local`                                               |\n| local-cfg                | local_config             | `$home\\AppData\\Local`                                               |\n| desktop                  |                          | `$home\\Desktop`                                                     |\n| doc                      | document                 | `$home\\Documents`                                                   |\n| dl                       | download                 | `$home\\Downloads`                                                   |\n| bin                      | exe                      | `$ms_dir\\WindowsApps`                                               |\n| first-path               | first_path               |                                                                     |\n| last-path                | last_path                |                                                                     |\n| font                     | typeface                 | `$ms_dir\\Windows\\Fonts`                                             |\n| pic                      | picture                  | `$home\\Pictures`                                                    |\n| pref                     | preference               | `$home\\AppData\\Roaming`                                             |\n| pub                      | public                   | `$home\\Public`                                                      |\n| runtime                  |                          | None                                                                |\n| state                    |                          | None                                                                |\n| video                    |                          | `$home\\Videos`                                                      |\n| music                    | audio                    | `$home\\Music`                                                       |\n| template                 |                          | `$ms_dir\\Windows\\Templates`                                         |\n| tmp                      |                          | `$tmpdir`                                                           |\n| tmp-rand                 | tmp_random               | `$tmpdir\\[random]`                                                  |\n| temp                     | temporary                | `env::temp_dir()`                                                   |\n| cli-data                 | cli_data                 | `$home\\AppData\\Local`                                               |\n| cli-cfg                  | cli_config               | `$home\\AppData\\Local`                                               |\n| cli-cache                | cli_cache                | `$home\\AppData\\Local`                                               |\n| progam-files             | program_files            | `$ProgramFiles`: (`C:\\Program Files`)                               |\n| program-files-x86        | program_files_x86        | `$ProgramFiles(x86)`: (`C:\\Program Files (x86)`)                    |\n| common-program-files     | common_program_files     | `$CommonProgramFiles`: (`C:\\Program Files\\Common Files`)            |\n| common-program-files-x86 | common_program_files_x86 | `$CommonProgramFiles(x86)`: (`C:\\Program Files (x86)\\Common Files`) |\n| program-data             | program_data             | `$ProgramData`: (`C:\\ProgramData`)                                  |\n| microsoft                |                          | `$home\\AppData\\Roaming\\Microsoft`                                   |\n| local-low                | local_low                | `$home\\AppData\\LocalLow`                                            |\n| empty                    |                          | \"\"                                                                  |\n\n#### macOS\n\n| name       | alias        | macOS `$dir`                        |\n| ---------- | ------------ | ----------------------------------- |\n| home       |              | `/Users/[username]`                 |\n| cache      |              | `$home/Library/Caches`              |\n| cfg        | config       | `$home/Library/Application Support` |\n| data       |              | `$home/Library/Application Support` |\n| local-data | local_data   | `$home/Library/Application Support` |\n| local-cfg  | local_config | `$home/Library/Application Support` |\n| desktop    |              | `$home/Desktop`                     |\n| doc        | document     | `$home/Documents`                   |\n| dl         | download     | `$home/Downloads`                   |\n| bin        | exe          |                                     |\n| first-path | first_path   |                                     |\n| last-path  | last_path    |                                     |\n| font       | typeface     | `$home/Library/Fonts`               |\n| pic        | picture      | `$home/Pictures`                    |\n| pref       | preference   | `$home/Library/Preferences`         |\n| pub        | public       | `$home/Public`                      |\n| runtime    |              | None                                |\n| state      |              | None                                |\n| video      |              | `$home/Movies`                      |\n| music      | audio        | `$home/music`                       |\n| template   |              | None                                |\n| tmp        |              | `$tmpdir`                           |\n| tmp-rand   | tmp_random   | `$tmpdir/[random]`                  |\n| temp       | temporary    | `env::temp_dir()`                   |\n| cli-data   | cli_data     | `$home/Library/Application Support` |\n| cli-cfg    | cli_config   | `$home/Library/Application Support` |\n| cli-cache  | cli_cache    | `$home/Library/Caches`              |\n| empty      |              | \"\"                                  |\n\n### project\n\n為專案生成指定目錄。\n大部分資料從 [directories](https://docs.rs/directories/latest/directories/struct.ProjectDirs.html) 獲取。\n\n使用 `$proj(qualifier.  organization.   application): name` (e.g. `$proj(org. moz. ff): data`) 或者是 `$proj(com.company-name.app-name): alias` 來獲取 project dir。\n\n接下來假設專案為 `(org. moz. ff)`\n\n#### Linux\n\n| name       | alias        | Linux `$proj`                                          |\n| ---------- | ------------ | ------------------------------------------------------ |\n| path       |              | (the project path fragment): ff                        |\n| cache      |              | `$xdg_cache_home/$proj_path`:(`$home/.cache/ff`)       |\n| cfg        | config       | `$xdg_config_home/$proj_path`:(`$home/.config/ff`)     |\n| data       |              | `$xdg_data_home/$proj_path`:(`$home/.local/share/ff`)  |\n| local-data | local_data   | `$xdg_data_home/$proj_path`                            |\n| local-cfg  | local_config | `$xdg_config_home/$proj_path`                          |\n| pref       | preference   | `$xdg_config_home/$proj_path`                          |\n| runtime    |              | `$xdg_runtime_dir/$proj_path`:(`/run/user/[uid]/ff`)   |\n| state      |              | `$xdg_state_home/$proj_path`:(`$home/.local/state/ff`) |\n| cli-data   | cli_data     | `$xdg_data_home/$proj_path`                            |\n| cli-cfg    | cli_config   | `$xdg_config_home/$proj_path`                          |\n| cli-cache  | cli_cache    | `$xdg_cache_home/$proj_path`                           |\n| empty      |              | \"\"                                                     |\n\n#### Android\n\n- var:\n\n  - sd = \"/storage/self/primary\"\n\n| name       | alias        | Android `$proj`                     |\n| ---------- | ------------ | ----------------------------------- |\n| path       |              | org.moz.ff                          |\n| cache      |              | /data/data/org.moz.ff/cache         |\n| cfg        | config       | /data/data/org.moz.ff/files         |\n| data       |              | /data/data/org.moz.ff               |\n| local-data | local_data   | `$sd/Android/data/org.moz.ff`       |\n| local-cfg  | local_config | `$sd/Android/data/org.moz.ff/files` |\n| pref       | preference   | /data/data/org.moz.ff/files         |\n| runtime    |              | `$xdg_runtime_dir/ff`               |\n| state      |              | `$xdg_state_home/ff`                |\n| cli-data   | cli_data     | `$xdg_data_home/ff`                 |\n| cli-cfg    | cli_config   | `$xdg_config_home/ff`               |\n| cli-cache  | cli_cache    | `$xdg_cache_home/ff`                |\n| empty      |              | \"\"                                  |\n\n#### Windows\n\n| name       | alias        | Windows `$proj`                       |\n| ---------- | ------------ | ------------------------------------- |\n| path       |              | `moz\\ff`                              |\n| cache      |              | `$home\\AppData\\Local\\moz\\ff\\cache`    |\n| cfg        | config       | `$home\\AppData\\Roaming\\moz\\ff\\config` |\n| data       |              | `$home\\AppData\\Roaming\\moz\\ff\\data`   |\n| local-data | local_data   | `$home\\AppData\\Local\\moz\\ff\\data`     |\n| local-cfg  | local_config | `$home\\AppData\\Local\\moz\\ff\\config`   |\n| pref       | preference   | `$home\\AppData\\Roaming\\moz\\ff\\config` |\n| cli-data   | cli_data     | `$home\\AppData\\Local\\moz\\ff\\data`     |\n| cli-cfg    | cli_config   | `$home\\AppData\\Local\\moz\\ff\\config`   |\n| cli-cache  | cli_cache    | `$home\\AppData\\Local\\moz\\ff\\cache`    |\n| local-low  | local_low    | `$home\\AppData\\LocalLow\\moz\\ff`       |\n| empty      |              | \"\"                                    |\n\n#### macOS\n\n| name       | alias        | macOS `$proj`                                  |\n| ---------- | ------------ | ---------------------------------------------- |\n| path       |              | org.moz.ff                                     |\n| cache      |              | `$home/Library/Caches/org.moz.ff`              |\n| cfg        | config       | `$home/Library/Application Support/org.moz.ff` |\n| data       |              | `$home/Library/Application Support/org.moz.ff` |\n| local-data | local_data   | `$home/Library/Application Support/org.moz.ff` |\n| local-cfg  | local_config | `$home/Library/Application Support/org.moz.ff` |\n| pref       | preference   | `$home/Library/Preferences/org.moz.ff`         |\n| cli-data   | cli_data     | `$home/Library/Application Support/org.moz.ff` |\n| cli-cfg    | cli_config   | `$home/Library/Application Support/org.moz.ff` |\n| cli-cache  | cli_cache    | `$home/Library/Caches/org.moz.ff`              |\n| empty      |              | \"\"                                             |\n\n#### project 中的 \"??\"\n\n`$proj` 支援的 `?` 語法比其他的型別要更復雜一點，因為其他型別沒有 `()`,而它有。\n別灰心，如果您已經掌握了核心語法，那麼相信您定能在幾分鐘快速掌握 `$proj` 的 `??`語法。\n\n假設有三個專案：\n\n- (org.moz.ff)\n- (com. gg. cr)\n- (com .ms .eg)\n\n第一個例子為：\n\n```rs\n[\"\n    $proj (org. moz. ff ): runtime ? data ?? state ?\n    (com . gg . cr): cfg ?? cache ?\n    (com .ms .eg): local-data ? data\n\"]\n```\n\n我們開始解析 ff 專案的 runtime, 很不幸，它不存在。\n我們接著解析 data！太好了，我們發現它的值是存在的。\n~~然而，好景不長，好不容易找到了一個存在的值，這時候天道的考驗來了，只有透過才能羽化飛昇。怎麼回事？我現在的修為連元嬰期都沒有，而且靈力和神識還在不斷潰散~~\n（不好意思，拿錯劇本了...\n由於有兩個 '?' ，所以還需要判斷檔案路徑是否存在。\n~~（所以說 data 君 的身死道消也不是沒有理由的嗎~~\n由於此專案的 data 的路徑不存在，因此下一個幸運兒是 state。\n~~遠處，一聲充滿不甘，又直透人心的怒吼聲傳來：“我命由我不由天，我只是錯...”\n突然間，聲音戛然而止，未幾，遠處傳來了嬰兒的啼哭聲，讓一切都充滿著陰森詭異的味道。~~\n很遺憾，它也沒能透過，因為它的值不存在。\n至此，ff 陣營的成員全軍覆沒，沒有一個解析成功。\n於是，我們接著解析 cr 專案，幸運的是，第一次就成功了。\ncr 的 cfg 不僅值存在，路徑也是存在的。\n最終的勝者是 cr 家的 cfg，儘管如此，但她卻不怎麼開心的樣子。在離開前，她嘴角邊還嘟喃道：“明明只是個解析器，竟然敢這麼囂張，哼！”\n\n第二個例子為：\n\n```rs\n[\"\n    $proj (org . moz . ff )：runtime ？ data ？？ state ？\n    (com . gg . cr)： cfg ？？ cache ？\n    (com . ms . eg)： local-data ？ data\n\"]\n```\n\nQ: 咦？我怎麼沒看出來，這與第一個例子有何不同？\nA: 屆時，汝自會知曉。\n（此時一道亮光劃破天際，而眼前之人早已不見蹤影）\nQ: 真是奇怪，我的腦袋裡怎麼會多出些奇奇怪怪的記憶？我怕不是睡迷糊了。對了，我剛剛在和誰說話來著？\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F2moe%2Fenvpath","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F2moe%2Fenvpath","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F2moe%2Fenvpath/lists"}