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
// #![cfg_attr(not(feature = "std"), no_std)]
/*!
# envpath
A library for parsing and deserializing paths with special rules.
## Features:
- A struct [EnvPath] for representing system paths. `raw` is the special rule path(vector), while `path` is the normal path after parsing. Since `Deref` is implemented, you can use it just like [Path](::std::path::Path) of std.
The library also supports optional features for getting common system paths:
- `consts` - Gets the value of some specific constants built into crate.
- `project` - For generating project directories (user-specific data dir)
- `dirs` - Provides standard directories on different platforms.
## Serialization and deserialization
If you want to serialize/deserialize a configuration file, you need to enable the `serde` feature of envpath and add serde, as well as other related dependencies.
Next, we will add a `ron` dependency (You can actually use formats such as yaml or json, but you need to add the relevant dependencies instead of using ron.)
```sh
cargo add envpath --features=serde
cargo add serde --features=derive
cargo add ron
```
### Serialization
Now let's try serialization.
```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");
```
The output result is: `(dir: Some(["$env: user ?? userprofile ?? home"]))`
It looks like the structure is the same as before serialization, except for the additional `dir` key.
Yes, after serialization, it looks like that.
This path format is suitable for cross-platform use.
Since environment variables and other things may be dynamically changed.
Keeping the raw format during serialization and obtaining its true path during deserialization is reasonable.
If you want to save performance overhead, you can change the value of `dir` to `None`, and serde should skip it during serialization.
### Deserialization
Next, let's try deserialization!
```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())
}
}
```
The output result of the above function is:
```rs
[src/lib.rs:116] &cfg = Cfg {
dir: Some(
EnvPath {
raw: [
"$env: user ?? userprofile ?? home",
],
path: Some(
"/home/m",
),
},
),
}
/home/m
```
The `?` operator checks if a value exists. If it doesn't exist, continue checking. If it exists, use that value.
On the other hand, the `??` operator requires both the value and the path to exist.
For example, consider `$env: user ? userprofile`. Let's assume that the value of `user` is `m`, and `userprofile` is empty. Since the value of `user` exists, the expression returns `m`.
If we change it to `$env: user ?? userprofile ? home`, even though the value of `user` exists, its path does not. So we continue checking. Then, since the value of `userprofile` does not exist, we continue checking until the condition is satisfied.
`?` and `??` have different functions, and adding `??` does not mean that you can discard `?`. For values that are normal strings, such as `$const: os`, rather than paths, `?` is more useful than `??`. Each one has an important role to play.
## const
Use `$const:name` (such as `$const:arch`) or `$const:alias` (e.g. `$const:architecture`) to obtain constant values. These values are obtained at compile time rather than runtime.
| 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 | | | "" |
## val
> The `value` feature needs to be enabled.
Use `$val:name` (e.g. `$val: rand-16`) to obtain the values. Unlike `$const:`, most of the values here are obtained at runtime.
| name | expr | example |
| -------------- | --------------- | ---------------- |
| `rand-[usize]` | `$val: rand-16` | 90aU0QqYnx1gPEgN |
| empty | `$val: empty` | "" |
> `$val: rand-[usize]` syntax requires the `rand` feature to be enabled.
rand is used to obtain random content, and currently only supports strings.
## remix
| syntax | expr | example |
| --------------------------- | ------------------------------- | ------------------------------------ |
| `env * [env_name]` | `env * HOME` | `C:\Users\m` |
| `const * [const]` | `const * arch` | `x86_64` |
| `dir * [dir]` | `dir * dl` | `C:\Users\m\Downloads` |
| `proj * (project): [ident]` | `proj * (com.xy.z): local-data` | `C:\Users\m\AppData\Local\xy\z\data` |
| `val * [val]` | `val * rand-32` | VcNCQy5IevkuoQKm70arpRpAC5QGtF9D |
### example
```rs
["
$const: empty ??
env * home ?
env * HOME
",
"test"
]
```
`env*` can be used for fallback, but unlike `$env:`, it does not automatically convert lowercase letters to uppercase, and it does not automatically convert `-` to `_`.
- `env * home` retrieves `$home`, not `$HOME`.
- `$env: home` => `$HOME`
- `env * xdg-data-home` => `$xdg-data-home`, not `$XDG_DATA_HOME`
- `$env: xdg-data-home` => `$XDG_DATA_HOME`
> Note: If the `$env:` expression contains a `*`, the automatic conversion feature will also be disabled.
The following syntax is currently supported:
- `$const: exe_suffix ? env * HOME ? env * XDG_DATA_HOME ? env * EXE_SUFFIX`
- `$env: home ? xdg-data-home ? exe_suffix ? const * exe_suffix`
Not supported:
- `$const: exe_suffix ? $env: home ? xdg-data-home ? exe_suffix`
If it is supported, the parsing may become complicated and there could be confusion between `$env: exe_suffix` and `$const: exe_suffix`.
## dirs
Here is an example of `$dir` for windows, for a full table, see Readme.
These are some base-dirs, or you could say standard directories.
Use `$dir:name` (e.g. `$dir:dl`) or `$dir:alias` (e.g. `$dir:download`) to obtain the directory.
Many of these contents are obtained from [dirs](https://docs.rs/dirs/latest/dirs/), but there are also some additions.
| name | alias | Windows `$dir` |
| ------------------------ | ------------------------ | ------------------------------------------------------------------- |
| home | | `C:\Users\m` |
| 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 | | "" |
Note: `project_dirs` has more advanced features.
Here are some simple introductions.
For example, `$proj(com. x. y): data` will generate a `data` directory for this project (It does not create it automatically, just generates its value).
- On Android, it is `/data/data/com.x.y`
- On macOS, it is `/Users/[username]/Library/Application Support/com.x.y`
*/
use ;
// use compact_str::CompactString as MiniStr;
pub use OsCow;
pub use EnvPathRaw as Raw;
pub use ;