OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * Helper functionality to make working with IO easier. | |
7 */ | |
8 #library('pub_io'); | |
9 | |
10 #import('dart:io'); | |
11 | |
12 #import('../lib/file_system.dart', prefix: 'fs'); | |
13 | |
14 /** | |
15 * Joins a number of path string parts into a single path. Handles | |
16 * platform-specific path separators. Parts can be [String], [Directory], or | |
17 * [File] objects. | |
18 */ | |
19 String join(part1, [part2, part3, part4]) { | |
20 final parts = _getPath(part1).split('/'); | |
21 | |
22 for (final part in [part2, part3, part4]) { | |
23 if (part == null) continue; | |
24 | |
25 for (final piece in _getPath(part).split('/')) { | |
26 if (piece == '..' && parts.length > 0 && | |
nweiz
2012/04/17 20:08:46
Unless you have a good reason to try and clean up
Bob Nystrom
2012/04/18 18:09:18
This is mostly copied from file_system and I have
| |
27 parts.last() != '.' && parts.last() != '..') { | |
28 parts.removeLast(); | |
29 } else if (piece != '') { | |
30 if (parts.length > 0 && parts.last() == '.') { | |
31 parts.removeLast(); | |
32 } | |
33 parts.add(piece); | |
34 } | |
35 } | |
36 } | |
37 | |
38 return Strings.join(parts, new Platform().pathSeparator()); | |
39 } | |
40 | |
41 /** | |
42 * Gets the basename, the file name without any leading directory path, for | |
43 * [file], which can either be a [String], [File], or [Directory]. | |
44 */ | |
45 String basename(file) { | |
46 return fs.basename(_getPath(file)); | |
47 } | |
48 | |
49 /** | |
50 * Reads the contents of the text file [file], which can either be a [String] or | |
51 * a [File]. | |
52 */ | |
53 Future<String> readTextFile(file) { | |
54 file = new File(_getPath(file)); | |
55 final completer = new Completer<String>(); | |
56 file.onError = (error) => completer.completeException(error); | |
57 file.readAsText(Encoding.UTF_8, (text) { | |
58 completer.complete(text); | |
nweiz
2012/04/17 20:08:46
I don't understand your heuristic for when you use
Bob Nystrom
2012/04/18 18:09:18
It was apparently based on astrology. Made less cr
| |
59 }); | |
60 | |
61 return completer.future; | |
62 } | |
63 | |
64 /** | |
65 * Creates [file] (which can either be a [String] or a [File]), and writes | |
66 * [contents] to it. Completes when the file is written and closed. | |
67 */ | |
68 Future<File> writeTextFile(file, String contents) { | |
69 file = new File(_getPath(file)); | |
70 final completer = new Completer<File>(); | |
71 file.onError = (error) => completer.completeException(error); | |
72 file.open(FileMode.WRITE, (opened) { | |
73 opened.onError = (error) => completer.completeException(error); | |
74 opened.onNoPendingWrites = () { | |
75 opened.close(() { | |
76 completer.complete(file); | |
77 }); | |
78 }; | |
79 opened.writeString(contents); | |
80 }); | |
81 | |
82 return completer.future; | |
83 } | |
84 | |
85 /** | |
86 * Creates a directory [dir]. Returns a [Future] that completes when the | |
87 * directory is created. | |
88 */ | |
89 Future<Directory> createDir(dir) { | |
90 final completer = new Completer<Directory>(); | |
91 dir = _getDirectory(dir); | |
92 dir.onError = (error) => completer.completeException(error); | |
93 dir.create(() { | |
94 completer.complete(dir); | |
95 }); | |
96 | |
97 return completer.future; | |
98 } | |
99 | |
100 /** | |
101 * Creates a temp directory whose name will be based on [dir] with a unique | |
102 * suffix appended to it. Returns a [Future] that completes when the directory | |
103 * is created. | |
104 */ | |
105 Future<Directory> createTempDir(dir) { | |
106 final completer = new Completer<Directory>(); | |
107 dir = _getDirectory(dir); | |
108 dir.onError = (error) => completer.completeException(error); | |
109 dir.createTemp(() { | |
110 completer.complete(dir); | |
111 }); | |
112 | |
113 return completer.future; | |
114 } | |
115 | |
116 /** | |
117 * Asynchronously recursively deletes [dir], which can be a [String] or a | |
118 * [Directory]. Returns a [Future] that completes when the deletion is done. | |
119 */ | |
120 Future<Directory> deleteDir(dir) { | |
121 final completer = new Completer<Directory>(); | |
122 dir = _getDirectory(dir); | |
123 dir.onError = (error) => completer.completeException(error); | |
124 dir.deleteRecursively(() { | |
125 completer.complete(dir); | |
126 }); | |
127 | |
128 return completer.future; | |
129 } | |
130 | |
131 /** | |
132 * Asynchronously lists the contents of [dir], which can be a [String] directory | |
133 * path or a [Directory]. If [recursive] is `true`, lists subdirectory contents | |
134 * (defaults to `false`). If [includeSpecialFiles] is `true`, includes "hidden" | |
135 * files like `.DS_Store` (defaults to `false`). | |
136 */ | |
137 Future<List<String>> listDir(dir, | |
138 [bool recursive = false, bool includeSpecialFiles = false]) { | |
139 final completer = new Completer<List<String>>(); | |
140 final contents = <String>[]; | |
141 | |
142 dir = _getDirectory(dir); | |
143 | |
144 dir.onDone = (done) { | |
nweiz
2012/04/17 20:08:46
nit: extra space after "Done".
What are the seman
Bob Nystrom
2012/04/18 18:09:18
Done.
| |
145 if (done) completer.complete(contents); | |
146 }; | |
147 | |
148 dir.onError = (error) { | |
149 completer.completeException(error); | |
150 }; | |
151 | |
152 dir.onDir = (file) { | |
153 contents.add(file); | |
154 }; | |
155 | |
156 dir.onFile = (file) { | |
157 if (!includeSpecialFiles) { | |
158 if (basename(file) == '.DS_Store') return; | |
nweiz
2012/04/17 20:08:46
The documentation implies that this is going to fi
Bob Nystrom
2012/04/18 18:09:18
Clarified docs.
| |
159 } | |
160 | |
161 contents.add(file); | |
162 }; | |
163 | |
164 dir.list(recursive: recursive); | |
165 | |
166 return completer.future; | |
167 } | |
168 | |
169 /** | |
170 * Spawns and runs the process located at [executable], passing in [args]. | |
171 * Returns a [Future] that will complete the results of the process after it | |
172 * has ended. | |
173 */ | |
174 Future<ProcessResult> runProcess(String executable, List<String> args) { | |
175 var exitCode; | |
nweiz
2012/04/17 20:08:46
Explicitly type this because it's not being initia
Bob Nystrom
2012/04/18 18:09:18
Done.
| |
176 var error; | |
nweiz
2012/04/17 20:08:46
Unused variable
Bob Nystrom
2012/04/18 18:09:18
Done.
| |
177 | |
178 final process = new Process.start(executable, args); | |
179 | |
180 final outStream = new StringInputStream(process.stdout); | |
181 final processStdout = <String>[]; | |
182 | |
183 final errStream = new StringInputStream(process.stderr); | |
184 final processStderr = <String>[]; | |
185 bool processDone = false; | |
nweiz
2012/04/17 20:08:46
var or final
Bob Nystrom
2012/04/18 18:09:18
Unused.
| |
186 | |
187 final completer = new Completer<ProcessResult>(); | |
188 | |
189 checkComplete() { | |
190 // Wait until the process is done and its output streams are closed. | |
191 if (!outStream.closed) return; | |
192 if (!errStream.closed) return; | |
193 if (exitCode == null) return; | |
194 | |
195 completer.complete(new ProcessResult( | |
196 processStdout, processStderr, exitCode)); | |
197 } | |
198 | |
199 outStream.onLine = () { | |
200 processStdout.add(outStream.readLine()); | |
201 }; | |
202 | |
203 outStream.onClosed = checkComplete; | |
204 outStream.onError = (error) => completer.completeException(error); | |
205 | |
206 errStream.onLine = () { | |
207 processStderr.add(errStream.readLine()); | |
208 }; | |
209 | |
210 errStream.onClosed = checkComplete; | |
211 errStream.onError = (error) => completer.completeException(error); | |
212 | |
213 process.onExit = (actualExitCode) { | |
214 exitCode = actualExitCode; | |
215 checkComplete(); | |
216 }; | |
217 | |
218 process.onError = (error) => completer.completeException(error); | |
219 | |
220 return completer.future; | |
221 } | |
222 | |
223 /** | |
224 * Contains the results of invoking a [Process] and waiting for it to complete. | |
225 */ | |
226 class ProcessResult { | |
227 final List<String> stdout; | |
228 final List<String> stderr; | |
229 final int exitCode; | |
230 | |
231 const ProcessResult(this.stdout, this.stderr, this.exitCode); | |
232 } | |
233 | |
234 /** | |
235 * Gets the path string for [entry], which can either already be a path string, | |
236 * or be a [File] or [Directory]. Allows working generically with "file-like" | |
237 * objects. | |
238 */ | |
239 String _getPath(entry) { | |
240 if (entry is String) return entry; | |
241 if (entry is File) return entry.name; | |
242 if (entry is Directory) return entry.path; | |
243 throw 'Entry $entry is not a supported type.'; | |
244 } | |
245 | |
246 /** | |
247 * Gets a [Directory] for [entry], which can either already be one, or be a | |
248 * [String]. | |
249 */ | |
250 Directory _getDirectory(entry) { | |
251 if (entry is Directory) return entry; | |
252 return new Directory(entry); | |
253 } | |
OLD | NEW |