Skip to content

Commit

Permalink
Explicitly type Stream argument as List<int> (#56)
Browse files Browse the repository at this point in the history
A breaking change (see [0]) will make `utf8.encode()` return the more precise
`Uint8List` type (instead of the current `List<int>`).

In rare circumstances this can lead to changes in behavior, mainly when
code relies on type inference, a different type got inferred and the code
dependend on the type not being inferred a more precise type.

Here we explicitly use `Stream<List<int>>` instead of relying on type
inference (which would infer `Stream<Uint8List>` in some cases after
[0]). This is necessary as the stream transformer APIs cannot work with
subtypes. Example of code that fails at runtime:

```
  import 'dart:typed_data';
  import 'dart:convert';

  void main() {
    Stream<Uint8List> stream = Stream.fromIterable([]);
    Stream<List<int>> stream2 = stream;
    stream2.transform(utf8.decoder);
         //  ^^^ Will throw due to Utf8Decoder not being subtype of
         //      StreamTransformer<Uint8List, String>.
  }
```

[0] dart-lang/sdk#52801

Co-authored-by: Natalie Weizenbaum <nweiz@google.com>
  • Loading branch information
mkustermann and nex3 committed Jul 6, 2023
1 parent 101db6d commit 36cb86d
Show file tree
Hide file tree
Showing 5 changed files with 13 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,9 @@
## 1.0.0

* Update internal type annotations for compatibility with [dart-lang/sdk#52801].

[dart-lang/sdk#52801]: https://github.com/dart-lang/sdk/issues/52801

## 0.3.2

* Declare support for Dart 3.
Expand Down
3 changes: 2 additions & 1 deletion lib/src/extensions/line_stream.dart
Expand Up @@ -28,7 +28,8 @@ import '../util.dart';
/// (as commonly emitted by a shell script).
extension LineStreamExtensions on Stream<String> {
/// Converts this to a byte stream with newlines baked in.
Stream<List<int>> get bytes => map((line) => utf8.encode("$line\n"));
Stream<List<int>> get bytes =>
map<List<int>>((line) => utf8.encode("$line\n"));

/// Pipes [this] into [script]'s [stdin] with newlines baked in.
///
Expand Down
2 changes: 1 addition & 1 deletion lib/src/script.dart
Expand Up @@ -386,7 +386,7 @@ class Script {
Script.fromByteTransformer(
StreamTransformer.fromBind((stream) => stream.lines
.transform(transformer)
.map((line) => utf8.encode("$line\n"))),
.map<List<int>>((line) => utf8.encode("$line\n"))),
name: name ?? transformer.toString());

/// Creates a [Script] from a function that maps strings to strings.
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
@@ -1,5 +1,5 @@
name: cli_script
version: 0.3.2
version: 1.0.0
description:
Write scripts that call subprocesses with the ease of shell scripting and the
power of Dart.
Expand Down
6 changes: 3 additions & 3 deletions test/pipe_test.dart
Expand Up @@ -289,9 +289,9 @@ void main() {
group("pipes in", () {
group("a byte stream", () {
test("without errors", () {
var pipeline =
Stream.fromIterable([utf8.encode("foo"), utf8.encode("bar")]) |
mainScript("stdin.pipe(stdout);");
var pipeline = Stream<List<int>>.fromIterable(
[utf8.encode("foo"), utf8.encode("bar")]) |
mainScript("stdin.pipe(stdout);");
expect(pipeline.stdout.lines, emitsInOrder(["foobar", emitsDone]));
});

Expand Down

0 comments on commit 36cb86d

Please sign in to comment.