| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library barback.utils; | 5 library barback.utils; |
| 6 | 6 |
| 7 import 'dart:async'; |
| 8 |
| 7 /// Converts a number in the range [0-255] to a two digit hex string. | 9 /// Converts a number in the range [0-255] to a two digit hex string. |
| 8 /// | 10 /// |
| 9 /// For example, given `255`, returns `ff`. | 11 /// For example, given `255`, returns `ff`. |
| 10 String byteToHex(int byte) { | 12 String byteToHex(int byte) { |
| 11 assert(byte >= 0 && byte <= 255); | 13 assert(byte >= 0 && byte <= 255); |
| 12 | 14 |
| 13 const DIGITS = "0123456789abcdef"; | 15 const DIGITS = "0123456789abcdef"; |
| 14 return DIGITS[(byte ~/ 16) % 16] + DIGITS[byte % 16]; | 16 return DIGITS[(byte ~/ 16) % 16] + DIGITS[byte % 16]; |
| 15 } | 17 } |
| 18 |
| 19 /// Group the elements in [iter] by the value returned by [fn]. |
| 20 /// |
| 21 /// This returns a map whose keys are the return values of [fn] and whose values |
| 22 /// are lists of each element in [iter] for which [fn] returned that key. |
| 23 Map groupBy(Iterable iter, fn(element)) { |
| 24 var map = {}; |
| 25 for (var element in iter) { |
| 26 var list = map.putIfAbsent(fn(element), () => []); |
| 27 list.add(element); |
| 28 } |
| 29 return map; |
| 30 } |
| 31 |
| 32 /// Flattens nested lists inside an iterable into a single list containing only |
| 33 /// non-list elements. |
| 34 List flatten(Iterable nested) { |
| 35 var result = []; |
| 36 helper(list) { |
| 37 for (var element in list) { |
| 38 if (element is List) { |
| 39 helper(element); |
| 40 } else { |
| 41 result.add(element); |
| 42 } |
| 43 } |
| 44 } |
| 45 helper(nested); |
| 46 return result; |
| 47 } |
| 48 |
| 49 /// Passes each key/value pair in [map] to [fn] and returns a new [Map] whose |
| 50 /// values are the return values of [fn]. |
| 51 Map mapMapValues(Map map, fn(key, value)) => |
| 52 new Map.fromIterable(map.keys, value: (key) => fn(key, map[key])); |
| 53 |
| 54 /// Merges [streams] into a single stream that emits events from all sources. |
| 55 Stream mergeStreams(Iterable<Stream> streams) { |
| 56 streams = streams.toList(); |
| 57 var doneCount = 0; |
| 58 // Use a sync stream to preserve the synchrony behavior of the input streams. |
| 59 // If the inputs are sync, then this will be sync as well; if the inputs are |
| 60 // async, then the events we receive will also be async, and forwarding them |
| 61 // sync won't change that. |
| 62 var controller = new StreamController(sync: true); |
| 63 |
| 64 for (var stream in streams) { |
| 65 stream.listen((value) { |
| 66 controller.add(value); |
| 67 }, onError: (error) { |
| 68 controller.addError(error); |
| 69 }, onDone: () { |
| 70 doneCount++; |
| 71 if (doneCount == streams.length) controller.close(); |
| 72 }); |
| 73 } |
| 74 |
| 75 return controller.stream; |
| 76 } |
| 77 |
| 78 /// Prepends each line in [text] with [prefix]. If [firstPrefix] is passed, the |
| 79 /// first line is prefixed with that instead. |
| 80 String prefixLines(String text, {String prefix: '| ', String firstPrefix}) { |
| 81 var lines = text.split('\n'); |
| 82 if (firstPrefix == null) { |
| 83 return lines.map((line) => '$prefix$line').join('\n'); |
| 84 } |
| 85 |
| 86 var firstLine = "$firstPrefix${lines.first}"; |
| 87 lines = lines.skip(1).map((line) => '$prefix$line').toList(); |
| 88 lines.insert(0, firstLine); |
| 89 return lines.join('\n'); |
| 90 } |
| 91 |
| 92 /// Returns a [Future] that completes after pumping the event queue [times] |
| 93 /// times. By default, this should pump the event queue enough times to allow |
| 94 /// any code to run, as long as it's not waiting on some external event. |
| 95 Future pumpEventQueue([int times=20]) { |
| 96 if (times == 0) return new Future.value(); |
| 97 // We use a delayed future to allow runAsync events to finish. The |
| 98 // Future.value or Future() constructors use runAsync themselves and would |
| 99 // therefore not wait for runAsync callbacks that are scheduled after invoking |
| 100 // this method. |
| 101 return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1)); |
| 102 } |
| 103 |
| OLD | NEW |