| 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'; | 7 import 'dart:async'; |
| 8 import 'dart:typed_data'; | 8 import 'dart:typed_data'; |
| 9 | 9 |
| 10 import 'package:async/async.dart'; | 10 import 'package:async/async.dart'; |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 123 // TODO(nweiz): remove "as" when issue 11080 is fixed. | 123 // TODO(nweiz): remove "as" when issue 11080 is fixed. |
| 124 return new Uint8List.view((input as TypedData).buffer); | 124 return new Uint8List.view((input as TypedData).buffer); |
| 125 } | 125 } |
| 126 return new Uint8List.fromList(input); | 126 return new Uint8List.fromList(input); |
| 127 } | 127 } |
| 128 | 128 |
| 129 /// Group the elements in [iter] by the value returned by [fn]. | 129 /// Group the elements in [iter] by the value returned by [fn]. |
| 130 /// | 130 /// |
| 131 /// This returns a map whose keys are the return values of [fn] and whose values | 131 /// This returns a map whose keys are the return values of [fn] and whose values |
| 132 /// are lists of each element in [iter] for which [fn] returned that key. | 132 /// are lists of each element in [iter] for which [fn] returned that key. |
| 133 Map<Object/*=T*/, List/*<S>*/ > groupBy/*<S, T>*/( | 133 Map<Object, List<S>> groupBy<S, T>(Iterable<S> iter, T fn(S element)) { |
| 134 Iterable/*<S>*/ iter, | 134 var map = <T, List<S>>{}; |
| 135 /*=T*/ fn(/*=S*/ element)) { | |
| 136 var map = /*<T, List<S>>*/ {}; | |
| 137 for (var element in iter) { | 135 for (var element in iter) { |
| 138 var list = map.putIfAbsent(fn(element), () => []); | 136 var list = map.putIfAbsent(fn(element), () => []); |
| 139 list.add(element); | 137 list.add(element); |
| 140 } | 138 } |
| 141 return map; | 139 return map; |
| 142 } | 140 } |
| 143 | 141 |
| 144 /// Flattens nested lists inside an iterable into a single list containing only | 142 /// Flattens nested lists inside an iterable into a single list containing only |
| 145 /// non-list elements. | 143 /// non-list elements. |
| 146 List flatten(Iterable nested) { | 144 List flatten(Iterable nested) { |
| 147 var result = []; | 145 var result = []; |
| 148 helper(list) { | 146 helper(list) { |
| 149 for (var element in list) { | 147 for (var element in list) { |
| 150 if (element is List) { | 148 if (element is List) { |
| 151 helper(element); | 149 helper(element); |
| 152 } else { | 150 } else { |
| 153 result.add(element); | 151 result.add(element); |
| 154 } | 152 } |
| 155 } | 153 } |
| 156 } | 154 } |
| 157 | 155 |
| 158 helper(nested); | 156 helper(nested); |
| 159 return result; | 157 return result; |
| 160 } | 158 } |
| 161 | 159 |
| 162 /// Returns the union of all elements in each set in [sets]. | 160 /// Returns the union of all elements in each set in [sets]. |
| 163 Set/*<T>*/ unionAll/*<T>*/(Iterable<Set/*<T>*/ > sets) => | 161 Set<T> unionAll<T>(Iterable<Set<T>> sets) => |
| 164 sets.fold(new Set(), (union, set) => union.union(set)); | 162 sets.fold(new Set(), (union, set) => union.union(set)); |
| 165 | 163 |
| 166 /// Creates a new map from [map] with new keys and values. | 164 /// Creates a new map from [map] with new keys and values. |
| 167 /// | 165 /// |
| 168 /// The return values of [keyFn] are used as the keys and the return values of | 166 /// The return values of [keyFn] are used as the keys and the return values of |
| 169 /// [valueFn] are used as the values for the new map. | 167 /// [valueFn] are used as the values for the new map. |
| 170 Map/*<K2, V2>*/ mapMap/*<K1, V1, K2, V2>*/( | 168 Map<K2, V2> mapMap<K1, V1, K2, V2>(Map<K1, V1> map, K2 keyFn(K1 key, V1 value), |
| 171 Map/*<K1, V1>*/ map, | 169 V2 valueFn(K1 key, V1 value)) => |
| 172 /*=K2*/ keyFn(/*=K1*/ key, /*=V1*/ value), | |
| 173 /*=V2*/ valueFn(/*=K1*/ key, /*=V1*/ value)) => | |
| 174 new Map.fromIterable(map.keys, | 170 new Map.fromIterable(map.keys, |
| 175 key: (key) => keyFn(key as dynamic/*=K1*/, map[key]), | 171 key: (key) => keyFn(key as K1, map[key]), |
| 176 value: (key) => valueFn(key as dynamic/*=K1*/, map[key])); | 172 value: (key) => valueFn(key as K1, map[key])); |
| 177 | 173 |
| 178 /// Creates a new map from [map] with the same keys. | 174 /// Creates a new map from [map] with the same keys. |
| 179 /// | 175 /// |
| 180 /// The return values of [fn] are used as the values for the new map. | 176 /// The return values of [fn] are used as the values for the new map. |
| 181 Map/*<K, V2>*/ mapMapValues/*<K, V1, V2>*/( | 177 Map<K, V2> mapMapValues<K, V1, V2>(Map<K, V1> map, V2 fn(K key, V1 value)) => |
| 182 Map/*<K, V1>*/ map, | |
| 183 /*=V2*/ fn(/*=K*/ key, /*=V1*/ value)) => | |
| 184 // TODO(nweiz): Don't explicitly type [key] when sdk#25490 is fixed. | 178 // TODO(nweiz): Don't explicitly type [key] when sdk#25490 is fixed. |
| 185 mapMap(map, (/*=K*/ key, _) => key, fn); | 179 mapMap(map, (K key, _) => key, fn); |
| 186 | 180 |
| 187 /// Creates a new map from [map] with the same keys. | 181 /// Creates a new map from [map] with the same keys. |
| 188 /// | 182 /// |
| 189 /// The return values of [fn] are used as the keys for the new map. | 183 /// The return values of [fn] are used as the keys for the new map. |
| 190 Map/*<K2, V>*/ mapMapKeys/*<K1, V, K2>*/( | 184 Map<K2, V> mapMapKeys<K1, V, K2>(Map<K1, V> map, K2 fn(K1 key, V value)) => |
| 191 Map/*<K1, V>*/ map, | |
| 192 /*=K2*/ fn(/*=K1*/ key, /*=V*/ value)) => | |
| 193 // TODO(nweiz): Don't explicitly type [value] when sdk#25490 is fixed. | 185 // TODO(nweiz): Don't explicitly type [value] when sdk#25490 is fixed. |
| 194 mapMap(map, fn, (_, /*=V*/ value) => value); | 186 mapMap(map, fn, (_, V value) => value); |
| 195 | 187 |
| 196 /// Returns whether [set1] has exactly the same elements as [set2]. | 188 /// Returns whether [set1] has exactly the same elements as [set2]. |
| 197 bool setEquals(Set set1, Set set2) => | 189 bool setEquals(Set set1, Set set2) => |
| 198 set1.length == set2.length && set1.containsAll(set2); | 190 set1.length == set2.length && set1.containsAll(set2); |
| 199 | 191 |
| 200 /// Merges [streams] into a single stream that emits events from all sources. | 192 /// Merges [streams] into a single stream that emits events from all sources. |
| 201 /// | 193 /// |
| 202 /// If [broadcast] is true, this will return a broadcast stream; otherwise, it | 194 /// If [broadcast] is true, this will return a broadcast stream; otherwise, it |
| 203 /// will return a buffered stream. | 195 /// will return a buffered stream. |
| 204 Stream/*<T>*/ mergeStreams/*<T>*/(Iterable<Stream/*<T>*/ > streams, | 196 Stream<T> mergeStreams<T>(Iterable<Stream<T>> streams, |
| 205 {bool broadcast: false}) { | 197 {bool broadcast: false}) { |
| 206 streams = streams.toList(); | 198 streams = streams.toList(); |
| 207 var doneCount = 0; | 199 var doneCount = 0; |
| 208 // Use a sync stream to preserve the synchrony behavior of the input streams. | 200 // Use a sync stream to preserve the synchrony behavior of the input streams. |
| 209 // If the inputs are sync, then this will be sync as well; if the inputs are | 201 // If the inputs are sync, then this will be sync as well; if the inputs are |
| 210 // async, then the events we receive will also be async, and forwarding them | 202 // async, then the events we receive will also be async, and forwarding them |
| 211 // sync won't change that. | 203 // sync won't change that. |
| 212 var controller = broadcast | 204 var controller = broadcast |
| 213 ? new StreamController/*<T>*/ .broadcast(sync: true) | 205 ? new StreamController<T>.broadcast(sync: true) |
| 214 : new StreamController/*<T>*/(sync: true); | 206 : new StreamController<T>(sync: true); |
| 215 | 207 |
| 216 for (var stream in streams) { | 208 for (var stream in streams) { |
| 217 stream.listen(controller.add, onError: controller.addError, onDone: () { | 209 stream.listen(controller.add, onError: controller.addError, onDone: () { |
| 218 doneCount++; | 210 doneCount++; |
| 219 if (doneCount == streams.length) controller.close(); | 211 if (doneCount == streams.length) controller.close(); |
| 220 }); | 212 }); |
| 221 } | 213 } |
| 222 | 214 |
| 223 return controller.stream; | 215 return controller.stream; |
| 224 } | 216 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 244 if (times == 0) return new Future.value(); | 236 if (times == 0) return new Future.value(); |
| 245 // We use a delayed future to allow microtask events to finish. The | 237 // We use a delayed future to allow microtask events to finish. The |
| 246 // Future.value or Future() constructors use scheduleMicrotask themselves and | 238 // Future.value or Future() constructors use scheduleMicrotask themselves and |
| 247 // would therefore not wait for microtask callbacks that are scheduled after | 239 // would therefore not wait for microtask callbacks that are scheduled after |
| 248 // invoking this method. | 240 // invoking this method. |
| 249 return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1)); | 241 return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1)); |
| 250 } | 242 } |
| 251 | 243 |
| 252 /// Like [new Future], but avoids dartbug.com/11911 by using async/await under | 244 /// Like [new Future], but avoids dartbug.com/11911 by using async/await under |
| 253 /// the covers. | 245 /// the covers. |
| 254 Future/*<T>*/ newFuture/*<T>*/(/*=T*/ callback()) async => await callback(); | 246 Future<T> newFuture<T>(T callback()) async => await callback(); |
| 255 | 247 |
| 256 /// Returns a buffered stream that will emit the same values as the stream | 248 /// Returns a buffered stream that will emit the same values as the stream |
| 257 /// returned by [future] once [future] completes. | 249 /// returned by [future] once [future] completes. |
| 258 /// | 250 /// |
| 259 /// If [future] completes to an error, the return value will emit that error and | 251 /// If [future] completes to an error, the return value will emit that error and |
| 260 /// then close. | 252 /// then close. |
| 261 /// | 253 /// |
| 262 /// If [broadcast] is true, a broadcast stream is returned. This assumes that | 254 /// If [broadcast] is true, a broadcast stream is returned. This assumes that |
| 263 /// the stream returned by [future] will be a broadcast stream as well. | 255 /// the stream returned by [future] will be a broadcast stream as well. |
| 264 /// [broadcast] defaults to false. | 256 /// [broadcast] defaults to false. |
| 265 Stream/*<T>*/ futureStream/*<T>*/(Future<Stream/*<T>*/ > future, | 257 Stream<T> futureStream<T>(Future<Stream<T>> future, {bool broadcast: false}) { |
| 266 {bool broadcast: false}) { | 258 StreamSubscription<T> subscription; |
| 267 StreamSubscription/*<T>*/ subscription; | 259 StreamController<T> controller; |
| 268 StreamController/*<T>*/ controller; | |
| 269 | 260 |
| 270 future = DelegatingFuture.typed(future.catchError((e, stackTrace) { | 261 future = DelegatingFuture.typed(future.catchError((e, stackTrace) { |
| 271 // Since [controller] is synchronous, it's likely that emitting an error | 262 // Since [controller] is synchronous, it's likely that emitting an error |
| 272 // will cause it to be cancelled before we call close. | 263 // will cause it to be cancelled before we call close. |
| 273 if (controller != null) controller.addError(e, stackTrace); | 264 if (controller != null) controller.addError(e, stackTrace); |
| 274 if (controller != null) controller.close(); | 265 if (controller != null) controller.close(); |
| 275 controller = null; | 266 controller = null; |
| 276 })); | 267 })); |
| 277 | 268 |
| 278 onListen() { | 269 onListen() { |
| 279 future.then((stream) { | 270 future.then((stream) { |
| 280 if (controller == null) return; | 271 if (controller == null) return; |
| 281 subscription = stream.listen(controller.add, | 272 subscription = stream.listen(controller.add, |
| 282 onError: controller.addError, onDone: controller.close); | 273 onError: controller.addError, onDone: controller.close); |
| 283 }); | 274 }); |
| 284 } | 275 } |
| 285 | 276 |
| 286 onCancel() { | 277 onCancel() { |
| 287 if (subscription != null) subscription.cancel(); | 278 if (subscription != null) subscription.cancel(); |
| 288 subscription = null; | 279 subscription = null; |
| 289 controller = null; | 280 controller = null; |
| 290 } | 281 } |
| 291 | 282 |
| 292 if (broadcast) { | 283 if (broadcast) { |
| 293 controller = new StreamController/*<T>*/ .broadcast( | 284 controller = new StreamController<T>.broadcast( |
| 294 sync: true, onListen: onListen, onCancel: onCancel); | 285 sync: true, onListen: onListen, onCancel: onCancel); |
| 295 } else { | 286 } else { |
| 296 controller = new StreamController/*<T>*/( | 287 controller = new StreamController<T>( |
| 297 sync: true, onListen: onListen, onCancel: onCancel); | 288 sync: true, onListen: onListen, onCancel: onCancel); |
| 298 } | 289 } |
| 299 return controller.stream; | 290 return controller.stream; |
| 300 } | 291 } |
| 301 | 292 |
| 302 /// Returns a [Stream] that will emit the same values as the stream returned by | 293 /// Returns a [Stream] that will emit the same values as the stream returned by |
| 303 /// [callback]. | 294 /// [callback]. |
| 304 /// | 295 /// |
| 305 /// [callback] will only be called when the returned [Stream] gets a subscriber. | 296 /// [callback] will only be called when the returned [Stream] gets a subscriber. |
| 306 Stream/*<T>*/ callbackStream/*<T>*/(Stream/*<T>*/ callback()) { | 297 Stream<T> callbackStream<T>(Stream<T> callback()) { |
| 307 StreamSubscription/*<T>*/ subscription; | 298 StreamSubscription<T> subscription; |
| 308 StreamController/*<T>*/ controller; | 299 StreamController<T> controller; |
| 309 controller = new StreamController/*<T>*/( | 300 controller = new StreamController<T>( |
| 310 onListen: () { | 301 onListen: () { |
| 311 subscription = callback().listen(controller.add, | 302 subscription = callback().listen(controller.add, |
| 312 onError: controller.addError, onDone: controller.close); | 303 onError: controller.addError, onDone: controller.close); |
| 313 }, | 304 }, |
| 314 onCancel: () => subscription.cancel(), | 305 onCancel: () => subscription.cancel(), |
| 315 onPause: () => subscription.pause(), | 306 onPause: () => subscription.pause(), |
| 316 onResume: () => subscription.resume(), | 307 onResume: () => subscription.resume(), |
| 317 sync: true); | 308 sync: true); |
| 318 return controller.stream; | 309 return controller.stream; |
| 319 } | 310 } |
| 320 | 311 |
| 321 /// Creates a single-subscription stream from a broadcast stream. | 312 /// Creates a single-subscription stream from a broadcast stream. |
| 322 /// | 313 /// |
| 323 /// The returned stream will enqueue events from [broadcast] until a listener is | 314 /// The returned stream will enqueue events from [broadcast] until a listener is |
| 324 /// attached, then pipe events to that listener. | 315 /// attached, then pipe events to that listener. |
| 325 Stream/*<T>*/ broadcastToSingleSubscription/*<T>*/(Stream/*<T>*/ broadcast) { | 316 Stream<T> broadcastToSingleSubscription<T>(Stream<T> broadcast) { |
| 326 if (!broadcast.isBroadcast) return broadcast; | 317 if (!broadcast.isBroadcast) return broadcast; |
| 327 | 318 |
| 328 // TODO(nweiz): Implement this using a transformer when issues 18588 and 18586 | 319 // TODO(nweiz): Implement this using a transformer when issues 18588 and 18586 |
| 329 // are fixed. | 320 // are fixed. |
| 330 var subscription; | 321 var subscription; |
| 331 var controller = | 322 var controller = |
| 332 new StreamController/*<T>*/(onCancel: () => subscription.cancel()); | 323 new StreamController<T>(onCancel: () => subscription.cancel()); |
| 333 subscription = broadcast.listen(controller.add, | 324 subscription = broadcast.listen(controller.add, |
| 334 onError: controller.addError, onDone: controller.close); | 325 onError: controller.addError, onDone: controller.close); |
| 335 return controller.stream; | 326 return controller.stream; |
| 336 } | 327 } |
| 337 | 328 |
| 338 /// A regular expression to match the exception prefix that some exceptions' | 329 /// A regular expression to match the exception prefix that some exceptions' |
| 339 /// [Object.toString] values contain. | 330 /// [Object.toString] values contain. |
| 340 final _exceptionPrefix = new RegExp(r'^([A-Z][a-zA-Z]*)?(Exception|Error): '); | 331 final _exceptionPrefix = new RegExp(r'^([A-Z][a-zA-Z]*)?(Exception|Error): '); |
| 341 | 332 |
| 342 /// Get a string description of an exception. | 333 /// Get a string description of an exception. |
| 343 /// | 334 /// |
| 344 /// Many exceptions include the exception class name at the beginning of their | 335 /// Many exceptions include the exception class name at the beginning of their |
| 345 /// [toString], so we remove that if it exists. | 336 /// [toString], so we remove that if it exists. |
| 346 String getErrorMessage(error) => | 337 String getErrorMessage(error) => |
| 347 error.toString().replaceFirst(_exceptionPrefix, ''); | 338 error.toString().replaceFirst(_exceptionPrefix, ''); |
| 348 | 339 |
| 349 /// Returns a human-friendly representation of [duration]. | 340 /// Returns a human-friendly representation of [duration]. |
| 350 String niceDuration(Duration duration) { | 341 String niceDuration(Duration duration) { |
| 351 var result = duration.inMinutes > 0 ? "${duration.inMinutes}:" : ""; | 342 var result = duration.inMinutes > 0 ? "${duration.inMinutes}:" : ""; |
| 352 | 343 |
| 353 var s = duration.inSeconds % 59; | 344 var s = duration.inSeconds % 59; |
| 354 var ms = (duration.inMilliseconds % 1000) ~/ 100; | 345 var ms = (duration.inMilliseconds % 1000) ~/ 100; |
| 355 return result + "$s.${ms}s"; | 346 return result + "$s.${ms}s"; |
| 356 } | 347 } |
| OLD | NEW |