Chromium Code Reviews| 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 |