utils library
Generic utility functions. Stuff that should possibly be in core.
Functions
Future awaitObject(object) #
Takes a simple data structure (composed of Maps, Iterables, scalar objects, and Futures) and recursively resolves all the Futures contained within. Completes with the fully resolved structure.
Future awaitObject(object) { // Unroll nested futures. if (object is Future) return object.then(awaitObject); if (object is Iterable) { return Future.wait(object.map(awaitObject).toList()); } if (object is! Map) return new Future.value(object); var pairs = <Future<Pair>>[]; object.forEach((key, value) { pairs.add(awaitObject(value) .then((resolved) => new Pair(key, resolved))); }); return Future.wait(pairs).then((resolvedPairs) { var map = {}; for (var pair in resolvedPairs) { map[pair.first] = pair.last; } return map; }); }
String urlDecode(String encoded) #
Decodes a URL-encoded string. Unlike decodeUriComponent
, this includes
replacing +
with
.
String urlDecode(String encoded) => Uri.decodeComponent(encoded.replaceAll("+", " "));
void mapAddAll(Map destination, Map source) #
Add all key/value pairs from source to destination, overwriting any pre-existing values.
void mapAddAll(Map destination, Map source) => source.forEach((key, value) => destination[key] = value);
Uri canonicalizeUri(Uri uri) #
Return uri with redundant port information removed.
Uri canonicalizeUri(Uri uri) { if (uri == null) return null; var sansPort = new Uri( scheme: uri.scheme, userInfo: uri.userInfo, host: uri.host, path: uri.path, query: uri.query, fragment: uri.fragment); if (uri.scheme == 'http' && uri.port == 80) return sansPort; if (uri.scheme == 'https' && uri.port == 443) return sansPort; return uri; }
bool urisEqual(Uri uri1, Uri uri2) #
Whether uri1 and uri2 are equal. This consider HTTP URIs to default to port 80, and HTTPs URIs to default to port 443.
bool urisEqual(Uri uri1, Uri uri2) => canonicalizeUri(uri1) == canonicalizeUri(uri2);
String mapToQuery(Map<String, String> map) #
Convert a Map from parameter names to values to a URL query string.
String mapToQuery(Map<String, String> map) { var pairs = <List<String>>[]; map.forEach((key, value) { key = Uri.encodeComponent(key); value = (value == null || value.isEmpty) ? null : Uri.encodeComponent(value); pairs.add([key, value]); }); return pairs.map((pair) { if (pair[1] == null) return pair[0]; return "${pair[0]}=${pair[1]}"; }).join("&"); }
Map<String, String> queryToMap(String queryList) #
Convert a URL query string (or application/x-www-form-urlencoded
body)
into a Map from parameter names to values.
Map<String, String> queryToMap(String queryList) { var map = {}; for (var pair in queryList.split("&")) { var split = split1(pair, "="); if (split.isEmpty) continue; var key = urlDecode(split[0]); var value = split.length > 1 ? urlDecode(split[1]) : ""; map[key] = value; } return map; }
Uri addQueryParameters(Uri url, Map<String, String> parameters) #
Adds additional query parameters to url, overwriting the original parameters if a name conflict occurs.
Uri addQueryParameters(Uri url, Map<String, String> parameters) { var queryMap = queryToMap(url.query); mapAddAll(queryMap, parameters); return url.resolve("?${mapToQuery(queryMap)}"); }
List<String> split1(String toSplit, String pattern) #
Like String.split, but only splits on the first occurrence of the pattern. This will always return an array of two elements or fewer.
List<String> split1(String toSplit, String pattern) { if (toSplit.isEmpty) return <String>[]; var index = toSplit.indexOf(pattern); if (index == -1) return [toSplit]; return [toSplit.substring(0, index), toSplit.substring(index + pattern.length)]; }
Future<Iterable> futureWhere(Iterable iter, test(value)) #
Like Iterable.where, but allows test to return Futures and uses the results of those Futures as the test.
Future<Iterable> futureWhere(Iterable iter, test(value)) { return Future.wait(iter.map((e) { var result = test(e); if (result is! Future) result = new Future.value(result); return result.then((result) => new Pair(e, result)); })) .then((pairs) => pairs.where((pair) => pair.last)) .then((pairs) => pairs.map((pair) => pair.first)); }
Stream<String> streamToLines(Stream<String> stream) #
Converts a stream of arbitrarily chunked strings into a line-by-line stream. The lines don't include line termination characters. A single trailing newline is ignored.
Stream<String> streamToLines(Stream<String> stream) { var buffer = new StringBuffer(); return stream.transform(new StreamTransformer( handleData: (chunk, sink) { var lines = splitLines(chunk); var leftover = lines.removeLast(); for (var line in lines) { if (!buffer.isEmpty) { buffer.write(line); line = buffer.toString(); buffer = new StringBuffer(); } sink.add(line); } buffer.write(leftover); }, handleDone: (sink) { if (!buffer.isEmpty) sink.add(buffer.toString()); sink.close(); })); }
List<String> splitLines(String text) #
Splits text on its line breaks in a Windows-line-break-friendly way.
List<String> splitLines(String text) => text.split("\n").map((line) => line.replaceFirst(_trailingCR, "")).toList();
Pair<Stream, Stream> tee(Stream stream) #
Creates two single-subscription Streams that each emit all values and errors from stream. This is useful if stream is single-subscription but multiple subscribers are necessary.
Pair<Stream, Stream> tee(Stream stream) { var controller1 = new StreamController(); var controller2 = new StreamController(); stream.listen((value) { controller1.add(value); controller2.add(value); }, onError: (error) { controller1.addError(error); controller2.addError(error); }, onDone: () { controller1.close(); controller2.close(); }); return new Pair<Stream, Stream>(controller1.stream, controller2.stream); }
Pair<Stream, StreamSubscription> streamWithSubscription(Stream stream) #
Returns a wrapped version of stream along with a StreamSubscription that can be used to control the wrapped stream.
Pair<Stream, StreamSubscription> streamWithSubscription(Stream stream) { var controller = new StreamController(); var controllerStream = stream.isBroadcast ? controller.stream.asBroadcastStream() : controller.stream; var subscription = stream.listen(controller.add, onError: controller.addError, onDone: controller.close); return new Pair<Stream, StreamSubscription>(controllerStream, subscription); }
Future streamFirst(Stream stream) #
Returns a Future that will complete to the first element of stream. Unlike Stream.first, this is safe to use with single-subscription streams.
Future streamFirst(Stream stream) { var completer = new Completer(); var subscription; subscription = stream.listen((value) { subscription.cancel(); completer.complete(value); }, onError: (e) { completer.completeError(e); }, onDone: () { completer.completeError(new StateError("No elements")); }, cancelOnError: true); return completer.future; }
void chainToCompleter(Future future, Completer completer) #
Configures future so that its result (success or exception) is passed on to completer.
void chainToCompleter(Future future, Completer completer) { future.then((value) => completer.complete(value), onError: (e) => completer.completeError(e)); }
Future sleep(int milliseconds) #
Returns a Future that completes in milliseconds.
Future sleep(int milliseconds) { var completer = new Completer(); new Timer(new Duration(milliseconds: milliseconds), completer.complete); return completer.future; }
String sha1(String source) #
Returns the hex-encoded sha1 hash of source.
String sha1(String source) { var sha = new SHA1(); sha.add(source.codeUnits); return CryptoUtils.bytesToHex(sha.close()); }
bool endsWithPattern(String str, Pattern matcher) #
Returns whether or not str ends with matcher.
bool endsWithPattern(String str, Pattern matcher) { for (var match in matcher.allMatches(str)) { if (match.end == str.length) return true; } return false; }
String replace(String source, Pattern matcher, String fn(Match)) #
Replace each instance of matcher in source with the return value of fn.
String replace(String source, Pattern matcher, String fn(Match)) { var buffer = new StringBuffer(); var start = 0; for (var match in matcher.allMatches(source)) { buffer.write(source.substring(start, match.start)); start = match.end; buffer.write(fn(match)); } buffer.write(source.substring(start)); return buffer.toString(); }
Set setMinus(Iterable minuend, Iterable subtrahend) #
Returns a set containing all elements in minuend that are not in subtrahend.
Set setMinus(Iterable minuend, Iterable subtrahend) { var minuendSet = new Set.from(minuend); minuendSet.removeAll(subtrahend); return minuendSet; }
dynamic only(Iterable iter) #
Asserts that iter contains only one element, and returns it.
only(Iterable iter) { var iterator = iter.iterator; var currentIsValid = iterator.moveNext(); assert(currentIsValid); var obj = iterator.current; assert(!iterator.moveNext()); return obj; }
List flatten(Iterable nested) #
Flattens nested lists inside an iterable into a single list containing only non-list elements.
List flatten(Iterable nested) { var result = []; helper(list) { for (var element in list) { if (element is List) { helper(element); } else { result.add(element); } } } helper(nested); return result; }