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 |