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

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

Issue 808713003: Remove barback from the repo. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library barback.utils;
6
7 import 'dart:async';
8 import 'dart:typed_data';
9
10 import 'package:stack_trace/stack_trace.dart';
11
12 /// A class that represents a value or an error.
13 class Fallible<E> {
14 /// Whether [this] has a [value], as opposed to an [error].
15 final bool hasValue;
16
17 /// Whether [this] has an [error], as opposed to a [value].
18 bool get hasError => !hasValue;
19
20 /// The value.
21 ///
22 /// This will be `null` if [this] has an [error].
23 final E _value;
24
25 /// The value.
26 ///
27 /// This will throw a [StateError] if [this] has an [error].
28 E get value {
29 if (hasValue) return _value;
30 throw new StateError("Fallible has no value.\n"
31 "$_error$_stackTraceSuffix");
32 }
33
34 /// The error.
35 ///
36 /// This will be `null` if [this] has a [value].
37 final _error;
38
39 /// The error.
40 ///
41 /// This will throw a [StateError] if [this] has a [value].
42 get error {
43 if (hasError) return _error;
44 throw new StateError("Fallible has no error.");
45 }
46
47 /// The stack trace for [_error].
48 ///
49 /// This will be `null` if [this] has a [value], or if no stack trace was
50 /// provided.
51 final StackTrace _stackTrace;
52
53 /// The stack trace for [error].
54 ///
55 /// This will throw a [StateError] if [this] has a [value].
56 StackTrace get stackTrace {
57 if (hasError) return _stackTrace;
58 throw new StateError("Fallible has no error.");
59 }
60
61 Fallible.withValue(this._value)
62 : _error = null,
63 _stackTrace = null,
64 hasValue = true;
65
66 Fallible.withError(this._error, [this._stackTrace])
67 : _value = null,
68 hasValue = false;
69
70 /// Returns a completed Future with the same value or error as [this].
71 Future toFuture() {
72 if (hasValue) return new Future.value(value);
73 return new Future.error(error, stackTrace);
74 }
75
76 String toString() {
77 if (hasValue) return "Fallible value: $value";
78 return "Fallible error: $error$_stackTraceSuffix";
79 }
80
81 String get _stackTraceSuffix {
82 if (stackTrace == null) return "";
83 return "\nStack trace:\n${new Chain.forTrace(_stackTrace).terse}";
84 }
85 }
86
87 /// Converts a number in the range [0-255] to a two digit hex string.
88 ///
89 /// For example, given `255`, returns `ff`.
90 String byteToHex(int byte) {
91 assert(byte >= 0 && byte <= 255);
92
93 const DIGITS = "0123456789abcdef";
94 return DIGITS[(byte ~/ 16) % 16] + DIGITS[byte % 16];
95 }
96
97 /// Returns a sentence fragment listing the elements of [iter].
98 ///
99 /// This converts each element of [iter] to a string and separates them with
100 /// commas and/or "and" where appropriate.
101 String toSentence(Iterable iter) {
102 if (iter.length == 1) return iter.first.toString();
103 return iter.take(iter.length - 1).join(", ") + " and ${iter.last}";
104 }
105
106 /// Returns [name] if [number] is 1, or the plural of [name] otherwise.
107 ///
108 /// By default, this just adds "s" to the end of [name] to get the plural. If
109 /// [plural] is passed, that's used instead.
110 String pluralize(String name, int number, {String plural}) {
111 if (number == 1) return name;
112 if (plural != null) return plural;
113 return '${name}s';
114 }
115
116 /// Converts [input] into a [Uint8List].
117 ///
118 /// If [input] is a [TypedData], this just returns a view on [input].
119 Uint8List toUint8List(List<int> input) {
120 if (input is Uint8List) return input;
121 if (input is TypedData) {
122 // TODO(nweiz): remove "as" when issue 11080 is fixed.
123 return new Uint8List.view((input as TypedData).buffer);
124 }
125 return new Uint8List.fromList(input);
126 }
127
128 /// Group the elements in [iter] by the value returned by [fn].
129 ///
130 /// This returns a map whose keys are the return values of [fn] and whose values
131 /// are lists of each element in [iter] for which [fn] returned that key.
132 Map<Object, List> groupBy(Iterable iter, fn(element)) {
133 var map = {};
134 for (var element in iter) {
135 var list = map.putIfAbsent(fn(element), () => []);
136 list.add(element);
137 }
138 return map;
139 }
140
141 /// Flattens nested lists inside an iterable into a single list containing only
142 /// non-list elements.
143 List flatten(Iterable nested) {
144 var result = [];
145 helper(list) {
146 for (var element in list) {
147 if (element is List) {
148 helper(element);
149 } else {
150 result.add(element);
151 }
152 }
153 }
154 helper(nested);
155 return result;
156 }
157
158 /// Returns the union of all elements in each set in [sets].
159 Set unionAll(Iterable<Set> sets) =>
160 sets.fold(new Set(), (union, set) => union.union(set));
161
162 /// Creates a new map from [map] with new keys and values.
163 ///
164 /// The return values of [keyFn] are used as the keys and the return values of
165 /// [valueFn] are used as the values for the new map.
166 Map mapMap(Map map, keyFn(key, value), valueFn(key, value)) =>
167 new Map.fromIterable(map.keys,
168 key: (key) => keyFn(key, map[key]),
169 value: (key) => valueFn(key, map[key]));
170
171 /// Creates a new map from [map] with the same keys.
172 ///
173 /// The return values of [fn] are used as the values for the new map.
174 Map mapMapValues(Map map, fn(key, value)) => mapMap(map, (key, _) => key, fn);
175
176 /// Creates a new map from [map] with the same keys.
177 ///
178 /// The return values of [fn] are used as the keys for the new map.
179 Map mapMapKeys(Map map, fn(key, value)) => mapMap(map, fn, (_, value) => value);
180
181 /// Returns whether [set1] has exactly the same elements as [set2].
182 bool setEquals(Set set1, Set set2) =>
183 set1.length == set2.length && set1.containsAll(set2);
184
185 /// Merges [streams] into a single stream that emits events from all sources.
186 ///
187 /// If [broadcast] is true, this will return a broadcast stream; otherwise, it
188 /// will return a buffered stream.
189 Stream mergeStreams(Iterable<Stream> streams, {bool broadcast: false}) {
190 streams = streams.toList();
191 var doneCount = 0;
192 // Use a sync stream to preserve the synchrony behavior of the input streams.
193 // If the inputs are sync, then this will be sync as well; if the inputs are
194 // async, then the events we receive will also be async, and forwarding them
195 // sync won't change that.
196 var controller = broadcast ? new StreamController.broadcast(sync: true)
197 : new StreamController(sync: true);
198
199 for (var stream in streams) {
200 stream.listen(
201 controller.add,
202 onError: controller.addError,
203 onDone: () {
204 doneCount++;
205 if (doneCount == streams.length) controller.close();
206 });
207 }
208
209 return controller.stream;
210 }
211
212 /// Prepends each line in [text] with [prefix]. If [firstPrefix] is passed, the
213 /// first line is prefixed with that instead.
214 String prefixLines(String text, {String prefix: '| ', String firstPrefix}) {
215 var lines = text.split('\n');
216 if (firstPrefix == null) {
217 return lines.map((line) => '$prefix$line').join('\n');
218 }
219
220 var firstLine = "$firstPrefix${lines.first}";
221 lines = lines.skip(1).map((line) => '$prefix$line').toList();
222 lines.insert(0, firstLine);
223 return lines.join('\n');
224 }
225
226 /// Returns a [Future] that completes after pumping the event queue [times]
227 /// times. By default, this should pump the event queue enough times to allow
228 /// any code to run, as long as it's not waiting on some external event.
229 Future pumpEventQueue([int times=20]) {
230 if (times == 0) return new Future.value();
231 // We use a delayed future to allow microtask events to finish. The
232 // Future.value or Future() constructors use scheduleMicrotask themselves and
233 // would therefore not wait for microtask callbacks that are scheduled after
234 // invoking this method.
235 return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1));
236 }
237
238 /// Like `new Future`, but avoids issue 11911 by using `new Future.value` under
239 /// the covers.
240 // TODO(jmesserly): doc comment changed to due 14601.
241 Future newFuture(callback()) => new Future.value().then((_) => callback());
242
243 /// Like [Future.sync], but wraps the Future in [Chain.track] as well.
244 Future syncFuture(callback()) => Chain.track(new Future.sync(callback));
245
246 /// Returns a buffered stream that will emit the same values as the stream
247 /// returned by [future] once [future] completes.
248 ///
249 /// If [future] completes to an error, the return value will emit that error and
250 /// then close.
251 ///
252 /// If [broadcast] is true, a broadcast stream is returned. This assumes that
253 /// the stream returned by [future] will be a broadcast stream as well.
254 /// [broadcast] defaults to false.
255 Stream futureStream(Future<Stream> future, {bool broadcast: false}) {
256 var subscription;
257 var controller;
258
259 future = future.catchError((e, stackTrace) {
260 // Since [controller] is synchronous, it's likely that emitting an error
261 // will cause it to be cancelled before we call close.
262 if (controller != null) controller.addError(e, stackTrace);
263 if (controller != null) controller.close();
264 controller = null;
265 });
266
267 onListen() {
268 future.then((stream) {
269 if (controller == null) return;
270 subscription = stream.listen(
271 controller.add,
272 onError: controller.addError,
273 onDone: controller.close);
274 });
275 }
276
277 onCancel() {
278 if (subscription != null) subscription.cancel();
279 subscription = null;
280 controller = null;
281 }
282
283 if (broadcast) {
284 controller = new StreamController.broadcast(
285 sync: true, onListen: onListen, onCancel: onCancel);
286 } else {
287 controller = new StreamController(
288 sync: true, onListen: onListen, onCancel: onCancel);
289 }
290 return controller.stream;
291 }
292
293 /// Returns a [Stream] that will emit the same values as the stream returned by
294 /// [callback].
295 ///
296 /// [callback] will only be called when the returned [Stream] gets a subscriber.
297 Stream callbackStream(Stream callback()) {
298 var subscription;
299 var controller;
300 controller = new StreamController(onListen: () {
301 subscription = callback().listen(controller.add,
302 onError: controller.addError,
303 onDone: controller.close);
304 },
305 onCancel: () => subscription.cancel(),
306 onPause: () => subscription.pause(),
307 onResume: () => subscription.resume(),
308 sync: true);
309 return controller.stream;
310 }
311
312 /// Creates a single-subscription stream from a broadcast stream.
313 ///
314 /// The returned stream will enqueue events from [broadcast] until a listener is
315 /// attached, then pipe events to that listener.
316 Stream broadcastToSingleSubscription(Stream broadcast) {
317 if (!broadcast.isBroadcast) return broadcast;
318
319 // TODO(nweiz): Implement this using a transformer when issues 18588 and 18586
320 // are fixed.
321 var subscription;
322 var controller = new StreamController(onCancel: () => subscription.cancel());
323 subscription = broadcast.listen(controller.add,
324 onError: controller.addError,
325 onDone: controller.close);
326 return controller.stream;
327 }
328
329 /// A regular expression to match the exception prefix that some exceptions'
330 /// [Object.toString] values contain.
331 final _exceptionPrefix = new RegExp(r'^([A-Z][a-zA-Z]*)?(Exception|Error): ');
332
333 /// Get a string description of an exception.
334 ///
335 /// Many exceptions include the exception class name at the beginning of their
336 /// [toString], so we remove that if it exists.
337 String getErrorMessage(error) =>
338 error.toString().replaceFirst(_exceptionPrefix, '');
339
340 /// Returns a human-friendly representation of [duration].
341 String niceDuration(Duration duration) {
342 var result = duration.inMinutes > 0 ? "${duration.inMinutes}:" : "";
343
344 var s = duration.inSeconds % 59;
345 var ms = (duration.inMilliseconds % 1000) ~/ 100;
346 return result + "$s.${ms}s";
347 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/transformer/wrapping_aggregate_transformer.dart ('k') | pkg/barback/lib/src/utils/cancelable_future.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698