2
2
// for details. All rights reserved. Use of this source code is governed by a
3
3
// BSD-style license that can be found in the LICENSE file.
4
4
5
- /// Utilities to locate the Dart SDK.
5
+ /// Utilities for CLI programs written in dart.
6
+ ///
7
+ /// This library contains information for returning the location of the dart
8
+ /// SDK, and other directories that command-line applications may need to
9
+ /// access. This library aims follows best practices for each platform, honoring
10
+ /// the [XDG Base Directory Specification][1] on Linux and
11
+ /// [File System Basics][2] on Mac OS.
12
+ ///
13
+ /// Many functions require a `productName` , as data should be stored in a
14
+ /// directory unique to your application, as to not avoid clashes with other
15
+ /// programs on the same machine. For example, if you are writing a command-line
16
+ /// application named 'zinger' then `productName` on Linux could be `zinger` . On
17
+ /// MacOS, this should be your bundle identifier (for example,
18
+ /// `com.example.Zinger` ).
19
+ ///
20
+ /// [1] : https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
21
+ /// [2] : https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW1
22
+
6
23
library cli_util;
7
24
8
25
import 'dart:async' ;
@@ -17,13 +34,34 @@ String get sdkPath => path.dirname(path.dirname(Platform.resolvedExecutable));
17
34
@Deprecated ("Use 'sdkPath' instead" )
18
35
String getSdkPath () => sdkPath;
19
36
20
- /// The user-specific application configuration folder for the current platform.
37
+ // Executables are also mentioned in the XDG spec, but these do not have as well
38
+ // defined of locations on Windows and MacOS.
39
+ enum _BaseDirectory { cache, config, data, runtime, state }
40
+
41
+ /// Get the user-specific application cache folder for the current platform.
42
+ ///
43
+ /// This is a location appropriate for storing non-essential files that may be
44
+ /// removed at any point. This method won't create the directory; It will merely
45
+ /// return the recommended location.
46
+ ///
47
+ /// The folder location depends on the platform:
48
+ /// * `%LOCALAPPDATA%\<productName>` on **Windows**,
49
+ /// * `$HOME/Library/Caches/<productName>` on **Mac OS**,
50
+ /// * `$XDG_CACHE_HOME/<productName>` on **Linux**
51
+ /// (if `$XDG_CACHE_HOME` is defined), and,
52
+ /// * `$HOME/.cache/` otherwise.
53
+ ///
54
+ /// Throws an [EnvironmentNotFoundException] if necessary environment variables
55
+ /// are undefined.
56
+ String applicationCacheHome (String productName) =>
57
+ path.join (_baseDirectory (_BaseDirectory .cache), productName);
58
+
59
+ /// Get the user-specific application configuration folder for the current
60
+ /// platform.
21
61
///
22
62
/// This is a location appropriate for storing application specific
23
- /// configuration for the current user. The [productName] should be unique to
24
- /// avoid clashes with other applications on the same machine. This method won't
25
- /// actually create the folder, merely return the recommended location for
26
- /// storing user-specific application configuration.
63
+ /// configuration for the current user. This method won't create the directory;
64
+ /// It will merely return the recommended location.
27
65
///
28
66
/// The folder location depends on the platform:
29
67
/// * `%APPDATA%\<productName>` on **Windows**,
@@ -32,42 +70,135 @@ String getSdkPath() => sdkPath;
32
70
/// (if `$XDG_CONFIG_HOME` is defined), and,
33
71
/// * `$HOME/.config/<productName>` otherwise.
34
72
///
35
- /// The chosen location aims to follow best practices for each platform,
36
- /// honoring the [XDG Base Directory Specification][1] on Linux and
37
- /// [File System Basics][2] on Mac OS.
73
+ /// Throws an [EnvironmentNotFoundException] if necessary environment variables
74
+ /// are undefined.
75
+ String applicationConfigHome (String productName) =>
76
+ path.join (_baseDirectory (_BaseDirectory .config), productName);
77
+
78
+ /// Get the user-specific application data folder for the current platform.
38
79
///
39
- /// Throws an [EnvironmentNotFoundException] if an environment entry,
40
- /// `%APPDATA%` or `$HOME` , is needed and not available.
80
+ /// This is a location appropriate for storing application specific
81
+ /// semi-permanent data for the current user. This method won't create the
82
+ /// directory; It will merely return the recommended location.
41
83
///
42
- /// [1] : https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
43
- /// [2] : https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW1
44
- String applicationConfigHome (String productName) =>
45
- path.join (_configHome, productName);
84
+ /// The folder location depends on the platform:
85
+ /// * `%APPDATA%\<productName>` on **Windows**,
86
+ /// * `$HOME/Library/Application Support/<productName>` on **Mac OS**,
87
+ /// * `$XDG_DATA_HOME/<productName>` on **Linux**
88
+ /// (if `$XDG_DATA_HOME` is defined), and,
89
+ /// * `$HOME/.local/share/<productName>` otherwise.
90
+ ///
91
+ /// Throws an [EnvironmentNotFoundException] if necessary environment variables
92
+ /// are undefined.
93
+ String applicationDataHome (String productName) =>
94
+ path.join (_baseDirectory (_BaseDirectory .data), productName);
46
95
47
- String get _configHome {
96
+ /// Get the runtime data folder for the current platform.
97
+ ///
98
+ /// This is a location appropriate for storing runtime data for the current
99
+ /// session. This method won't create the directory; It will merely return the
100
+ /// recommended location.
101
+ ///
102
+ /// The folder location depends on the platform:
103
+ /// * `%LOCALAPPDATA%\<productName>` on **Windows**,
104
+ /// * `$HOME/Library/Application Support/<productName>` on **Mac OS**,
105
+ /// * `$XDG_DATA_HOME/<productName>` on **Linux**
106
+ /// (if `$XDG_DATA_HOME` is defined), and,
107
+ /// * `$HOME/.local/share/<productName>` otherwise.
108
+ ///
109
+ /// Throws an [EnvironmentNotFoundException] if necessary environment variables
110
+ /// are undefined.
111
+ String applicationRuntimeDir (String productName) =>
112
+ path.join (_baseDirectory (_BaseDirectory .runtime), productName);
113
+
114
+ /// Get the user-specific application state folder for the current platform.
115
+ ///
116
+ /// This is a location appropriate for storing application specific state
117
+ /// for the current user. This differs from [applicationDataHome] insomuch as it
118
+ /// should contain data which should persist restarts, but is not important
119
+ /// enough to be backed up. This method won't create the directory;
120
+ // It will merely return the recommended location.
121
+ ///
122
+ /// The folder location depends on the platform:
123
+ /// * `%APPDATA%\<productName>` on **Windows**,
124
+ /// * `$HOME/Library/Application Support/<productName>` on **Mac OS**,
125
+ /// * `$XDG_DATA_HOME/<productName>` on **Linux**
126
+ /// (if `$XDG_DATA_HOME` is defined), and,
127
+ /// * `$HOME/.local/share/<productName>` otherwise.
128
+ ///
129
+ /// Throws an [EnvironmentNotFoundException] if necessary environment variables
130
+ /// are undefined.
131
+ String applicationStateHome (String productName) =>
132
+ path.join (_baseDirectory (_BaseDirectory .state), productName);
133
+
134
+ String _baseDirectory (_BaseDirectory dir) {
48
135
if (Platform .isWindows) {
49
- return _requireEnv ('APPDATA' );
136
+ switch (dir) {
137
+ case _BaseDirectory .config:
138
+ case _BaseDirectory .data:
139
+ return _requireEnv ('APPDATA' );
140
+ case _BaseDirectory .cache:
141
+ case _BaseDirectory .runtime:
142
+ case _BaseDirectory .state:
143
+ return _requireEnv ('LOCALAPPDATA' );
144
+ }
50
145
}
51
146
52
147
if (Platform .isMacOS) {
53
- return path.join (_requireEnv ('HOME' ), 'Library' , 'Application Support' );
148
+ switch (dir) {
149
+ case _BaseDirectory .config:
150
+ case _BaseDirectory .data:
151
+ case _BaseDirectory .state:
152
+ return path.join (_home, 'Library' , 'Application Support' );
153
+ case _BaseDirectory .cache:
154
+ return path.join (_home, 'Library' , 'Caches' );
155
+ case _BaseDirectory .runtime:
156
+ // https://stackoverflow.com/a/76799489
157
+ return path.join (_home, 'Library' , 'Caches' , 'TemporaryItems' );
158
+ }
54
159
}
55
160
56
161
if (Platform .isLinux) {
57
- final xdgConfigHome = _env['XDG_CONFIG_HOME' ];
58
- if (xdgConfigHome != null ) {
59
- return xdgConfigHome;
162
+ String xdgEnv;
163
+ switch (dir) {
164
+ case _BaseDirectory .config:
165
+ xdgEnv = 'XDG_CONFIG_HOME' ;
166
+ break ;
167
+ case _BaseDirectory .data:
168
+ xdgEnv = 'XDG_DATA_HOME' ;
169
+ break ;
170
+ case _BaseDirectory .state:
171
+ xdgEnv = 'XDG_STATE_HOME' ;
172
+ break ;
173
+ case _BaseDirectory .cache:
174
+ xdgEnv = 'XDG_CACHE_HOME' ;
175
+ break ;
176
+ case _BaseDirectory .runtime:
177
+ xdgEnv = 'XDG_RUNTIME_HOME' ;
178
+ break ;
179
+ }
180
+ final val = _env[xdgEnv];
181
+ if (val != null ) {
182
+ return val;
60
183
}
61
- // XDG Base Directory Specification says to use $HOME/.config/ when
62
- // $XDG_CONFIG_HOME isn't defined.
63
- return path.join (_requireEnv ('HOME' ), '.config' );
64
184
}
65
185
66
- // We have no guidelines, perhaps we should just do: $HOME/.config/
67
- // same as XDG specification would specify as fallback.
68
- return path.join (_requireEnv ('HOME' ), '.config' );
186
+ switch (dir) {
187
+ case _BaseDirectory .runtime:
188
+ // not a great fallback
189
+ case _BaseDirectory .cache:
190
+ return path.join (_home, '.cache' );
191
+ case _BaseDirectory .config:
192
+ return path.join (_home, '.config' );
193
+ case _BaseDirectory .data:
194
+ return path.join (_home, '.local' , 'share' );
195
+ case _BaseDirectory .state:
196
+ return path.join (_home, '.local' , 'state' );
197
+ }
69
198
}
70
199
200
+ String get _home => _requireEnv ('HOME' );
201
+
71
202
String _requireEnv (String name) =>
72
203
_env[name] ?? (throw EnvironmentNotFoundException (name));
73
204
0 commit comments