Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1153)

Side by Side Diff: lib/src/utils.dart

Issue 2184303002: Make pub strong-mode clean. (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Code review changes Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/system_cache.dart ('k') | lib/src/validator.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// Generic utility functions. Stuff that should possibly be in core. 5 /// Generic utility functions. Stuff that should possibly be in core.
6 import 'dart:async'; 6 import 'dart:async';
7 import "dart:convert"; 7 import "dart:convert";
8 import 'dart:io'; 8 import 'dart:io';
9 import 'dart:math' as math; 9 import 'dart:math' as math;
10 10
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 String toString() => '($first, $last)'; 48 String toString() => '($first, $last)';
49 49
50 bool operator==(other) { 50 bool operator==(other) {
51 if (other is! Pair) return false; 51 if (other is! Pair) return false;
52 return other.first == first && other.last == last; 52 return other.first == first && other.last == last;
53 } 53 }
54 54
55 int get hashCode => first.hashCode ^ last.hashCode; 55 int get hashCode => first.hashCode ^ last.hashCode;
56 } 56 }
57 57
58 /// A completer that waits until all added [Future]s complete.
59 // TODO(rnystrom): Copied from web_components. Remove from here when it gets
60 // added to dart:core. (See #6626.)
61 class FutureGroup<T> {
62 int _pending = 0;
63 Completer<List<T>> _completer = new Completer<List<T>>();
64 final List<Future<T>> futures = <Future<T>>[];
65 bool completed = false;
66
67 final List<T> _values = <T>[];
68
69 /// Wait for [task] to complete.
70 Future<T> add(Future<T> task) {
71 if (completed) {
72 throw new StateError("The FutureGroup has already completed.");
73 }
74
75 _pending++;
76 futures.add(task.then((value) {
77 if (completed) return;
78
79 _pending--;
80 _values.add(value);
81
82 if (_pending <= 0) {
83 completed = true;
84 _completer.complete(_values);
85 }
86 }).catchError((e, stackTrace) {
87 if (completed) return;
88
89 completed = true;
90 _completer.completeError(e, stackTrace);
91 }));
92
93 return task;
94 }
95
96 Future<List> get future => _completer.future;
97 }
98
99 /// Like [new Future], but avoids around issue 11911 by using [new Future.value] 58 /// Like [new Future], but avoids around issue 11911 by using [new Future.value]
100 /// under the covers. 59 /// under the covers.
101 Future newFuture(callback()) => new Future.value().then((_) => callback()); 60 Future newFuture(callback()) => new Future.value().then((_) => callback());
102 61
103 /// Runs [callback] in an error zone and pipes any unhandled error to the 62 /// Runs [callback] in an error zone and pipes any unhandled error to the
104 /// returned [Future]. 63 /// returned [Future].
105 /// 64 ///
106 /// If the returned [Future] produces an error, its stack trace will always be a 65 /// If the returned [Future] produces an error, its stack trace will always be a
107 /// [Chain]. By default, this chain will contain only the local stack trace, but 66 /// [Chain]. By default, this chain will contain only the local stack trace, but
108 /// if [captureStackChains] is passed, it will contain the full stack chain for 67 /// if [captureStackChains] is passed, it will contain the full stack chain for
(...skipping 29 matching lines...) Expand all
138 }); 97 });
139 } 98 }
140 99
141 return completer.future; 100 return completer.future;
142 } 101 }
143 102
144 /// Like [Future.wait], but prints all errors from the futures as they occur and 103 /// Like [Future.wait], but prints all errors from the futures as they occur and
145 /// only returns once all Futures have completed, successfully or not. 104 /// only returns once all Futures have completed, successfully or not.
146 /// 105 ///
147 /// This will wrap the first error thrown in a [SilentException] and rethrow it. 106 /// This will wrap the first error thrown in a [SilentException] and rethrow it.
148 Future waitAndPrintErrors(Iterable<Future> futures) { 107 Future<List/*<T>*/> waitAndPrintErrors/*<T>*/(Iterable<Future/*<T>*/> futures) {
149 return Future.wait(futures.map((future) { 108 return Future.wait(futures.map((future) {
150 return future.catchError((error, stackTrace) { 109 return future.catchError((error, stackTrace) {
151 log.exception(error, stackTrace); 110 log.exception(error, stackTrace);
152 throw error; 111 throw error;
153 }); 112 });
154 })).catchError((error, stackTrace) { 113 })).catchError((error, stackTrace) {
155 throw new SilentException(error, stackTrace); 114 throw new SilentException(error, stackTrace);
156 }); 115 });
157 } 116 }
158 117
159 /// Returns a [StreamTransformer] that will call [onDone] when the stream 118 /// Returns a [StreamTransformer] that will call [onDone] when the stream
160 /// completes. 119 /// completes.
161 /// 120 ///
162 /// The stream will be passed through unchanged. 121 /// The stream will be passed through unchanged.
163 StreamTransformer onDoneTransformer(void onDone()) { 122 StreamTransformer/*<T, T>*/ onDoneTransformer/*<T>*/(void onDone()) {
164 return new StreamTransformer.fromHandlers(handleDone: (sink) { 123 return new StreamTransformer/*<T, T>*/.fromHandlers(handleDone: (sink) {
165 onDone(); 124 onDone();
166 sink.close(); 125 sink.close();
167 }); 126 });
168 } 127 }
169 128
170 // TODO(rnystrom): Move into String? 129 // TODO(rnystrom): Move into String?
171 /// Pads [source] to [length] by adding spaces at the end. 130 /// Pads [source] to [length] by adding spaces at the end.
172 String padRight(String source, int length) { 131 String padRight(String source, int length) {
173 final result = new StringBuffer(); 132 final result = new StringBuffer();
174 result.write(source); 133 result.write(source);
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 226
268 try { 227 try {
269 return new InternetAddress(host).isLoopback; 228 return new InternetAddress(host).isLoopback;
270 } on ArgumentError catch (_) { 229 } on ArgumentError catch (_) {
271 // The host isn't an IP address and isn't "localhost', so it's almost 230 // The host isn't an IP address and isn't "localhost', so it's almost
272 // certainly not a loopback host. 231 // certainly not a loopback host.
273 return false; 232 return false;
274 } 233 }
275 } 234 }
276 235
277 /// Flattens nested lists inside an iterable into a single list containing only
278 /// non-list elements.
279 List flatten(Iterable nested) {
280 var result = [];
281 helper(list) {
282 for (var element in list) {
283 if (element is List) {
284 helper(element);
285 } else {
286 result.add(element);
287 }
288 }
289 }
290 helper(nested);
291 return result;
292 }
293
294 /// Randomly chooses a single element in [elements]. 236 /// Randomly chooses a single element in [elements].
295 /*=T*/ choose/*<T>*/(List/*<T>*/ elements) => 237 /*=T*/ choose/*<T>*/(List/*<T>*/ elements) =>
296 elements[random.nextInt(elements.length)]; 238 elements[random.nextInt(elements.length)];
297 239
298 /// Returns a set containing all elements in [minuend] that are not in 240 /// Returns a set containing all elements in [minuend] that are not in
299 /// [subtrahend]. 241 /// [subtrahend].
300 Set setMinus(Iterable minuend, Iterable subtrahend) { 242 Set setMinus(Iterable minuend, Iterable subtrahend) {
301 var minuendSet = new Set.from(minuend); 243 var minuendSet = new Set.from(minuend);
302 minuendSet.removeAll(subtrahend); 244 minuendSet.removeAll(subtrahend);
303 return minuendSet; 245 return minuendSet;
304 } 246 }
305 247
306 /// Returns whether there's any overlap between [set1] and [set2]. 248 /// Returns whether there's any overlap between [set1] and [set2].
307 bool overlaps(Set set1, Set set2) { 249 bool overlaps(Set set1, Set set2) {
308 // Iterate through the smaller set. 250 // Iterate through the smaller set.
309 var smaller = set1.length > set2.length ? set1 : set2; 251 var smaller = set1.length > set2.length ? set1 : set2;
310 var larger = smaller == set1 ? set2 : set1; 252 var larger = smaller == set1 ? set2 : set1;
311 return smaller.any(larger.contains); 253 return smaller.any(larger.contains);
312 } 254 }
313 255
314 /// Returns a list containing the sorted elements of [iter]. 256 /// Returns a list containing the sorted elements of [iter].
315 List ordered(Iterable<Comparable> iter) { 257 List/*<T>*/ ordered/*<T extends Comparable<T>>*/(Iterable/*<T>*/ iter) {
316 var list = iter.toList(); 258 var list = iter.toList();
317 list.sort(); 259 list.sort();
318 return list; 260 return list;
319 } 261 }
320 262
321 /// Returns the element of [iter] for which [f] returns the minimum value. 263 /// Returns the element of [iter] for which [f] returns the minimum value.
322 minBy(Iterable iter, Comparable f(element)) { 264 minBy(Iterable iter, Comparable f(element)) {
323 var min = null; 265 var min = null;
324 var minComparable = null; 266 var minComparable = null;
325 for (var element in iter) { 267 for (var element in iter) {
(...skipping 13 matching lines...) Expand all
339 /// (3, 4)]`. 281 /// (3, 4)]`.
340 Iterable<Pair> pairs(Iterable iter) { 282 Iterable<Pair> pairs(Iterable iter) {
341 var previous = iter.first; 283 var previous = iter.first;
342 return iter.skip(1).map((element) { 284 return iter.skip(1).map((element) {
343 var oldPrevious = previous; 285 var oldPrevious = previous;
344 previous = element; 286 previous = element;
345 return new Pair(oldPrevious, element); 287 return new Pair(oldPrevious, element);
346 }); 288 });
347 } 289 }
348 290
349 /// Creates a new map from [map] with new keys and values.
350 ///
351 /// The return values of [key] are used as the keys and the return values of
352 /// [value] are used as the values for the new map.
353 ///
354 /// [key] defaults to returning the original key and [value] defaults to
355 /// returning the original value.
356 Map mapMap(Map map, {key(key, value), value(key, value)}) {
357 if (key == null) key = (key, _) => key;
358 if (value == null) value = (_, value) => value;
359
360 var result = {};
361 map.forEach((mapKey, mapValue) {
362 result[key(mapKey, mapValue)] = value(mapKey, mapValue);
363 });
364 return result;
365 }
366
367 /// Like [Map.fromIterable], but [key] and [value] may return [Future]s.
368 Future<Map> mapFromIterableAsync(Iterable iter, {key(element),
369 value(element)}) {
370 if (key == null) key = (element) => element;
371 if (value == null) value = (element) => element;
372
373 var map = new Map();
374 return Future.wait(iter.map((element) {
375 return Future.wait([
376 new Future.sync(() => key(element)),
377 new Future.sync(() => value(element))
378 ]).then((results) {
379 map[results[0]] = results[1];
380 });
381 })).then((_) => map);
382 }
383
384 /// Returns a new map with all entries in both [map1] and [map2].
385 ///
386 /// If there are overlapping keys, [map2]'s value wins.
387 Map mergeMaps(Map map1, Map map2) {
388 var result = {};
389 result.addAll(map1);
390 result.addAll(map2);
391 return result;
392 }
393
394 /// Returns the transitive closure of [graph].
395 ///
396 /// This assumes [graph] represents a graph with a vertex for each key and an
397 /// edge betweek each key and the values for that key.
398 Map<dynamic, Set> transitiveClosure(Map<dynamic, Iterable> graph) {
399 // This uses the Floyd-Warshall algorithm
400 // (https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm).
401 var result = {};
402 graph.forEach((vertex, edges) {
403 result[vertex] = new Set.from(edges)..add(vertex);
404 });
405
406 for (var vertex1 in graph.keys) {
407 for (var vertex2 in graph.keys) {
408 for (var vertex3 in graph.keys) {
409 if (result[vertex2].contains(vertex1) &&
410 result[vertex1].contains(vertex3)) {
411 result[vertex2].add(vertex3);
412 }
413 }
414 }
415 }
416
417 return result;
418 }
419
420 /// Given a list of filenames, returns a set of patterns that can be used to 291 /// Given a list of filenames, returns a set of patterns that can be used to
421 /// filter for those filenames. 292 /// filter for those filenames.
422 /// 293 ///
423 /// For a given path, that path ends with some string in the returned set if 294 /// For a given path, that path ends with some string in the returned set if
424 /// and only if that path's basename is in [files]. 295 /// and only if that path's basename is in [files].
425 Set<String> createFileFilter(Iterable<String> files) { 296 Set<String> createFileFilter(Iterable<String> files) {
426 return files.expand((file) { 297 return files.expand/*<String>*/((file) {
427 var result = ["/$file"]; 298 var result = ["/$file"];
428 if (Platform.operatingSystem == 'windows') result.add("\\$file"); 299 if (Platform.operatingSystem == 'windows') result.add("\\$file");
429 return result; 300 return result;
430 }).toSet(); 301 }).toSet();
431 } 302 }
432 303
433 /// Given a blacklist of directory names, returns a set of patterns that can 304 /// Given a blacklist of directory names, returns a set of patterns that can
434 /// be used to filter for those directory names. 305 /// be used to filter for those directory names.
435 /// 306 ///
436 /// For a given path, that path contains some string in the returned set if 307 /// For a given path, that path contains some string in the returned set if
437 /// and only if one of that path's components is in [dirs]. 308 /// and only if one of that path's components is in [dirs].
438 Set<String> createDirectoryFilter(Iterable<String> dirs) { 309 Set<String> createDirectoryFilter(Iterable<String> dirs) {
439 return dirs.expand((dir) { 310 return dirs.expand/*<String>*/((dir) {
440 var result = ["/$dir/"]; 311 var result = ["/$dir/"];
441 if (Platform.operatingSystem == 'windows') { 312 if (Platform.operatingSystem == 'windows') {
442 result..add("/$dir\\")..add("\\$dir/")..add("\\$dir\\"); 313 result..add("/$dir\\")..add("\\$dir/")..add("\\$dir\\");
443 } 314 }
444 return result; 315 return result;
445 }).toSet(); 316 }).toSet();
446 } 317 }
447 318
448 /// Returns the maximum value in [iter] by [compare]. 319 /// Returns the maximum value in [iter] by [compare].
449 /// 320 ///
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
526 /// 397 ///
527 /// For example, reading asynchronously from a non-existent file will return a 398 /// For example, reading asynchronously from a non-existent file will return a
528 /// stream that fails on the first chunk. In order to handle that more 399 /// stream that fails on the first chunk. In order to handle that more
529 /// gracefully, you may want to check that the stream looks like it's working 400 /// gracefully, you may want to check that the stream looks like it's working
530 /// before you pipe the stream to something else. 401 /// before you pipe the stream to something else.
531 /// 402 ///
532 /// This lets you do that. It returns a [Future] that completes to a [Stream] 403 /// This lets you do that. It returns a [Future] that completes to a [Stream]
533 /// emitting the same values and errors as [stream], but only if at least one 404 /// emitting the same values and errors as [stream], but only if at least one
534 /// value can be read successfully. If an error occurs before any values are 405 /// value can be read successfully. If an error occurs before any values are
535 /// emitted, the returned Future completes to that error. 406 /// emitted, the returned Future completes to that error.
536 Future<Stream> validateStream(Stream stream) { 407 Future<Stream/*<T>*/> validateStream/*<T>*/(Stream/*<T>*/ stream) {
537 var completer = new Completer<Stream>(); 408 var completer = new Completer<Stream>();
538 var controller = new StreamController(sync: true); 409 var controller = new StreamController(sync: true);
539 410
540 StreamSubscription subscription; 411 StreamSubscription subscription;
541 subscription = stream.listen((value) { 412 subscription = stream.listen((value) {
542 // We got a value, so the stream is valid. 413 // We got a value, so the stream is valid.
543 if (!completer.isCompleted) completer.complete(controller.stream); 414 if (!completer.isCompleted) completer.complete(controller.stream);
544 controller.add(value); 415 controller.add(value);
545 }, onError: (error, [stackTrace]) { 416 }, onError: (error, [stackTrace]) {
546 // If the error came after values, it's OK. 417 // If the error came after values, it's OK.
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
587 Pair<Stream, StreamSubscription> streamWithSubscription(Stream stream) { 458 Pair<Stream, StreamSubscription> streamWithSubscription(Stream stream) {
588 var controller = 459 var controller =
589 stream.isBroadcast ? new StreamController.broadcast(sync: true) 460 stream.isBroadcast ? new StreamController.broadcast(sync: true)
590 : new StreamController(sync: true); 461 : new StreamController(sync: true);
591 var subscription = stream.listen(controller.add, 462 var subscription = stream.listen(controller.add,
592 onError: controller.addError, 463 onError: controller.addError,
593 onDone: controller.close); 464 onDone: controller.close);
594 return new Pair<Stream, StreamSubscription>(controller.stream, subscription); 465 return new Pair<Stream, StreamSubscription>(controller.stream, subscription);
595 } 466 }
596 467
597 // TODO(nweiz): remove this when issue 7787 is fixed.
598 /// Creates two single-subscription [Stream]s that each emit all values and
599 /// errors from [stream].
600 ///
601 /// This is useful if [stream] is single-subscription but multiple subscribers
602 /// are necessary.
603 Pair<Stream, Stream> tee(Stream stream) {
604 var controller1 = new StreamController(sync: true);
605 var controller2 = new StreamController(sync: true);
606 stream.listen((value) {
607 controller1.add(value);
608 controller2.add(value);
609 }, onError: (error, [stackTrace]) {
610 controller1.addError(error, stackTrace);
611 controller2.addError(error, stackTrace);
612 }, onDone: () {
613 controller1.close();
614 controller2.close();
615 });
616 return new Pair<Stream, Stream>(controller1.stream, controller2.stream);
617 }
618
619 /// Merges [stream1] and [stream2] into a single stream that emits events from
620 /// both sources.
621 Stream mergeStreams(Stream stream1, Stream stream2) {
622 var doneCount = 0;
623 var controller = new StreamController(sync: true);
624
625 for (var stream in [stream1, stream2]) {
626 stream.listen(
627 controller.add,
628 onError: controller.addError,
629 onDone: () {
630 doneCount++;
631 if (doneCount == 2) controller.close();
632 });
633 }
634
635 return controller.stream;
636 }
637
638 /// Returns a [Stream] that will emit the same values as the stream returned by
639 /// [callback].
640 ///
641 /// [callback] will only be called when the returned [Stream] gets a subscriber.
642 Stream callbackStream(Stream callback()) {
643 var subscription;
644 var controller;
645 controller = new StreamController(onListen: () {
646 subscription = callback().listen(controller.add,
647 onError: controller.addError,
648 onDone: controller.close);
649 },
650 onCancel: () => subscription.cancel(),
651 onPause: () => subscription.pause(),
652 onResume: () => subscription.resume(),
653 sync: true);
654 return controller.stream;
655 }
656
657 /// A regular expression matching a trailing CR character. 468 /// A regular expression matching a trailing CR character.
658 final _trailingCR = new RegExp(r"\r$"); 469 final _trailingCR = new RegExp(r"\r$");
659 470
660 // TODO(nweiz): Use `text.split(new RegExp("\r\n?|\n\r?"))` when issue 9360 is 471 // TODO(nweiz): Use `text.split(new RegExp("\r\n?|\n\r?"))` when issue 9360 is
661 // fixed. 472 // fixed.
662 /// Splits [text] on its line breaks in a Windows-line-break-friendly way. 473 /// Splits [text] on its line breaks in a Windows-line-break-friendly way.
663 List<String> splitLines(String text) => 474 List<String> splitLines(String text) =>
664 text.split("\n").map((line) => line.replaceFirst(_trailingCR, "")).toList(); 475 text.split("\n").map((line) => line.replaceFirst(_trailingCR, "")).toList();
665 476
666 /// Converts a stream of arbitrarily chunked strings into a line-by-line stream. 477 /// Converts a stream of arbitrarily chunked strings into a line-by-line stream.
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 /// parameters if a name conflict occurs. 532 /// parameters if a name conflict occurs.
722 Uri addQueryParameters(Uri url, Map<String, String> parameters) { 533 Uri addQueryParameters(Uri url, Map<String, String> parameters) {
723 var queryMap = queryToMap(url.query); 534 var queryMap = queryToMap(url.query);
724 queryMap.addAll(parameters); 535 queryMap.addAll(parameters);
725 return url.resolve("?${mapToQuery(queryMap)}"); 536 return url.resolve("?${mapToQuery(queryMap)}");
726 } 537 }
727 538
728 /// Convert a URL query string (or `application/x-www-form-urlencoded` body) 539 /// Convert a URL query string (or `application/x-www-form-urlencoded` body)
729 /// into a [Map] from parameter names to values. 540 /// into a [Map] from parameter names to values.
730 Map<String, String> queryToMap(String queryList) { 541 Map<String, String> queryToMap(String queryList) {
731 var map = {}; 542 var map = <String, String>{};
732 for (var pair in queryList.split("&")) { 543 for (var pair in queryList.split("&")) {
733 var split = split1(pair, "="); 544 var split = split1(pair, "=");
734 if (split.isEmpty) continue; 545 if (split.isEmpty) continue;
735 var key = urlDecode(split[0]); 546 var key = urlDecode(split[0]);
736 var value = split.length > 1 ? urlDecode(split[1]) : ""; 547 var value = split.length > 1 ? urlDecode(split[1]) : "";
737 map[key] = value; 548 map[key] = value;
738 } 549 }
739 return map; 550 return map;
740 } 551 }
741 552
742 /// Convert a [Map] from parameter names to values to a URL query string. 553 /// Convert a [Map] from parameter names to values to a URL query string.
743 String mapToQuery(Map<String, String> map) { 554 String mapToQuery(Map<String, String> map) {
744 var pairs = <List<String>>[]; 555 var pairs = <List<String>>[];
745 map.forEach((key, value) { 556 map.forEach((key, value) {
746 key = Uri.encodeQueryComponent(key); 557 key = Uri.encodeQueryComponent(key);
747 value = (value == null || value.isEmpty) 558 value = (value == null || value.isEmpty)
748 ? null : Uri.encodeQueryComponent(value); 559 ? null : Uri.encodeQueryComponent(value);
749 pairs.add([key, value]); 560 pairs.add([key, value]);
750 }); 561 });
751 return pairs.map((pair) { 562 return pairs.map((pair) {
752 if (pair[1] == null) return pair[0]; 563 if (pair[1] == null) return pair[0];
753 return "${pair[0]}=${pair[1]}"; 564 return "${pair[0]}=${pair[1]}";
754 }).join("&"); 565 }).join("&");
755 } 566 }
756 567
757 /// Returns the union of all elements in each set in [sets]. 568 /// Returns the union of all elements in each set in [sets].
758 Set unionAll(Iterable<Set> sets) => 569 Set/*<T>*/ unionAll/*<T>*/(Iterable<Set/*<T>*/> sets) =>
759 sets.fold(new Set(), (union, set) => union.union(set)); 570 sets.fold(new Set(), (union, set) => union.union(set));
760 571
761 // TODO(nweiz): remove this when issue 9068 has been fixed. 572 // TODO(nweiz): remove this when issue 9068 has been fixed.
762 /// Whether [uri1] and [uri2] are equal. 573 /// Whether [uri1] and [uri2] are equal.
763 /// 574 ///
764 /// This consider HTTP URIs to default to port 80, and HTTPs URIs to default to 575 /// This consider HTTP URIs to default to port 80, and HTTPs URIs to default to
765 /// port 443. 576 /// port 443.
766 bool urisEqual(Uri uri1, Uri uri2) => 577 bool urisEqual(Uri uri1, Uri uri2) =>
767 canonicalizeUri(uri1) == canonicalizeUri(uri2); 578 canonicalizeUri(uri1) == canonicalizeUri(uri2);
768 579
(...skipping 19 matching lines...) Expand all
788 /// Returns a human-friendly representation of [duration]. 599 /// Returns a human-friendly representation of [duration].
789 String niceDuration(Duration duration) { 600 String niceDuration(Duration duration) {
790 var hasMinutes = duration.inMinutes > 0; 601 var hasMinutes = duration.inMinutes > 0;
791 var result = hasMinutes ? "${duration.inMinutes}:" : ""; 602 var result = hasMinutes ? "${duration.inMinutes}:" : "";
792 603
793 var s = duration.inSeconds % 60; 604 var s = duration.inSeconds % 60;
794 var ms = duration.inMilliseconds % 1000; 605 var ms = duration.inMilliseconds % 1000;
795 606
796 // If we're using verbose logging, be more verbose but more accurate when 607 // If we're using verbose logging, be more verbose but more accurate when
797 // reporting timing information. 608 // reporting timing information.
798 if (log.verbosity.isLevelVisible(log.Level.FINE)) { 609 var msString = log.verbosity.isLevelVisible(log.Level.FINE)
799 ms = padLeft(ms.toString(), 3, '0'); 610 ? padLeft(ms.toString(), 3, '0')
800 } else { 611 : (ms ~/ 100).toString();
801 ms ~/= 100;
802 }
803 612
804 return "$result${hasMinutes ? padLeft(s.toString(), 2, '0') : s}.${ms}s"; 613 return "$result${hasMinutes ? padLeft(s.toString(), 2, '0') : s}"
614 ".${msString}s";
805 } 615 }
806 616
807 /// Decodes a URL-encoded string. 617 /// Decodes a URL-encoded string.
808 /// 618 ///
809 /// Unlike [Uri.decodeComponent], this includes replacing `+` with ` `. 619 /// Unlike [Uri.decodeComponent], this includes replacing `+` with ` `.
810 String urlDecode(String encoded) => 620 String urlDecode(String encoded) =>
811 Uri.decodeComponent(encoded.replaceAll("+", " ")); 621 Uri.decodeComponent(encoded.replaceAll("+", " "));
812 622
813 /// Takes a simple data structure (composed of [Map]s, [Iterable]s, scalar 623 /// Takes a simple data structure (composed of [Map]s, [Iterable]s, scalar
814 /// objects, and [Future]s) and recursively resolves all the [Future]s contained 624 /// objects, and [Future]s) and recursively resolves all the [Future]s contained
815 /// within. 625 /// within.
816 /// 626 ///
817 /// Completes with the fully resolved structure. 627 /// Completes with the fully resolved structure.
818 Future awaitObject(object) { 628 Future/*<T>*/ awaitObject/*<T>*/(/*=T*/ object) async {
819 // Unroll nested futures. 629 // Unroll nested futures.
820 if (object is Future) return object.then(awaitObject); 630 if (object is Future) return await awaitObject(await object);
631
821 if (object is Iterable) { 632 if (object is Iterable) {
822 return Future.wait(object.map(awaitObject).toList()); 633 // TODO(nweiz): Remove the unnecessary as check when sdk#26965 is fixed.
634 return await Future.wait((object as Iterable).map(awaitObject))
635 as List/*=T*/;
823 } 636 }
824 if (object is! Map) return new Future.value(object);
825 637
826 var pairs = <Future<Pair>>[]; 638 if (object is Map) {
827 object.forEach((key, value) { 639 // TODO(nweiz): Remove the unnecessary as check when sdk#26965 is fixed.
828 pairs.add(awaitObject(value) 640 var oldMap = object as Map;
829 .then((resolved) => new Pair(key, resolved))); 641 var newMap = {};
830 }); 642 await Future.wait(oldMap.keys.map((key) async {
831 return Future.wait(pairs).then((resolvedPairs) { 643 newMap[key] = await awaitObject(await oldMap[key]);
832 var map = {}; 644 }));
833 for (var pair in resolvedPairs) { 645 return newMap as Map/*=T*/;
834 map[pair.first] = pair.last; 646 }
835 } 647
836 return map; 648 return object;
837 });
838 } 649 }
839 650
840 /// Whether "special" strings such as Unicode characters or color escapes are 651 /// Whether "special" strings such as Unicode characters or color escapes are
841 /// safe to use. 652 /// safe to use.
842 /// 653 ///
843 /// On Windows or when not printing to a terminal, only printable ASCII 654 /// On Windows or when not printing to a terminal, only printable ASCII
844 /// characters should be used. 655 /// characters should be used.
845 bool get canUseSpecialChars => !runningFromTest && !runningAsTest && 656 bool get canUseSpecialChars => !runningFromTest && !runningAsTest &&
846 Platform.operatingSystem != 'windows' && 657 Platform.operatingSystem != 'windows' &&
847 stdioType(stdout) == StdioType.TERMINAL; 658 stdioType(stdout) == StdioType.TERMINAL;
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
972 } else { 783 } else {
973 throw new ApplicationException(message); 784 throw new ApplicationException(message);
974 } 785 }
975 } 786 }
976 787
977 /// Throw a [DataException] with [message] to indicate that the command has 788 /// Throw a [DataException] with [message] to indicate that the command has
978 /// failed because of invalid input data. 789 /// failed because of invalid input data.
979 /// 790 ///
980 /// This will report the error and cause pub to exit with [exit_codes.DATA]. 791 /// This will report the error and cause pub to exit with [exit_codes.DATA].
981 void dataError(String message) => throw new DataException(message); 792 void dataError(String message) => throw new DataException(message);
OLDNEW
« no previous file with comments | « lib/src/system_cache.dart ('k') | lib/src/validator.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698