Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(std/io): add toReader & toWriter #5789

Merged
merged 2 commits into from
Jun 27, 2020

Conversation

marcosc90
Copy link
Contributor

Currently, we can do the following with fetch res.body

  const res = await fetch("http://example.com/file.bin");
  const file = await Deno.open("/tmp/file.bin", { create: true, write: true });

  await Deno.copy(res.body, file);
  file.close();

If we make Response (#5787) spec-compliant the above code will stop working. I think it should still work.

@nayeemrmn
Copy link
Collaborator

What about WritableStream in place of Writer, and all of the other APIs that take a Reader? Clearly there should be a more general approach to bridging these APIs.

@marcosc90
Copy link
Contributor Author

marcosc90 commented May 23, 2020

What about WritableStream in place of Writer, and all of the other APIs that take a Reader? Clearly there should be a more general approach to bridging these APIs.

Make sense. We can have the following methods.

toReader(src: ReadableStream): Reader;
toWriter(dst: WritableStream): Writer;

@marcosc90 marcosc90 changed the title feat(cli/io): Allow Deno.copy to take a ReadableStream [WIP] - feat(cli/io): Allow Deno.copy to take a ReadableStream & WritableStream May 23, 2020
@ry
Copy link
Member

ry commented May 23, 2020

Readable and Writeable stream are unfortunate complexities...

@marcosc90
Copy link
Contributor Author

Readable and Writeable stream are unfortunate complexities...

Yeah, I know. But we should definitely support it.

I made some changes to the return types, toWriter returns Writer & Closer to avoid exporting a new type.
We can discuss the API if someone has strong opinions about this.

cli/tests/unit/io_test.ts Outdated Show resolved Hide resolved
@marcosc90 marcosc90 changed the title [WIP] - feat(cli/io): Allow Deno.copy to take a ReadableStream & WritableStream feat(cli/io): Allow Deno.copy to take a ReadableStream & WritableStream May 24, 2020
@bartlomieju bartlomieju added cli related to cli/ dir feat new feature (which has been agreed to/accepted) public API related to "Deno" namespace in JS labels May 26, 2020
cli/js/io.ts Outdated Show resolved Hide resolved
Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only one remark. @ry please review

cli/js/io.ts Outdated
Comment on lines 119 to 125
export function toWriter(dst: WritableStream): Writer & Closer {
return new WritableStreamIOWriter(dst);
}

export function toReader(src: ReadableStream): Reader {
return new ReadableStreamIOReader(src);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess those utils are useful but exposing them as Deno.toWriter() and Deno.toReader() doesn't seem to be a good idea

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial version was only for internal use but thought users will need a way to convert between Streams & Reader/Writer. Don't mind making them internal-only again.

Comment on lines 326 to 224
if (!chunk) return controller.close();

controller.enqueue(chunk);
},
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great tests

cli/js/io.ts Outdated
#reader: ReadableStreamDefaultReader;
#buffer: Uint8Array | null;
#encoder: TextEncoder;
constructor(dst: ReadableStream) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't all of the ReadableStream and WriteableStream be ReadableStream<Uint8Array> and WriteableStream<Uint8Array>?

Copy link
Contributor Author

@marcosc90 marcosc90 Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not used to setting types for streams and I missed it. Should be ReadableStream<Uint8Array | string> and WriteableStream<Uint8Array>.

Copy link
Collaborator

@nayeemrmn nayeemrmn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add the following to the API:

function fromStreamReader(
  streamReader: ReadableStreamDefaultReader<Uint8Array>,
): Deno.Reader {
  const buffer = new Deno.Buffer(new Uint8Array(4096));
  return {
    async read(b: Uint8Array): Promise<number | null> {
      if (buffer.empty()) {
        const { done, value } = await streamReader.read();
        if (done) {
          return null;
        }
        await Deno.writeAll(buffer, value!);
      }
      return buffer.read(b);
    },
  };
}

function fromStreamWriter(
  streamWriter: WritableStreamDefaultWriter<Uint8Array>,
): Deno.Writer {
  return {
    async write(b: Uint8Array): Promise<number> {
      await streamWriter.write(b);
      return b.length;
    },
  };
}

The original example will become:

const res = await fetch("http://example.com/file.bin");
const file = await Deno.open("/tmp/file.bin", { create: true, write: true });

await Deno.copy(Deno.fromStreamReader(res.body.getReader()), file);
file.close();

Comment on lines 319 to 321
export function copy(
src: Reader,
dst: Writer,
src: Reader | ReadableStream,
dst: Writer | WritableStream,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, what is the intuition behind just copy() supporting these and not every other IO function? This shouldn't be included in favour of just the conversion utilities.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wanted to do progressive changes instead of a massive PR (Initially it was meant to be an issue to discuss the API, but since I had some code that I was testing submitted a PR instead).

I'm good with only having utilities to convert from/to and leave Deno.copy as is.

Now If we do only utilities, should they be in the core or in std?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have Deno.iter() so I think these are candidates to be included in the CLI.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's certainly handy for copy to take ReadableStream/WritableStream but in the interest of being conservative in our API changes, I think toReader and toWriter would solve the problem for now. Let's do the most minimal thing and expand on it later.

That is, I agree with @nayeemrmn, please undo this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm working on it and now I'm wondering whether we want to make the API take a ReadableStream or a ReadableStreamDefaultReader / ReadableStreamBYOBReader, the same goes for WritableStream

Copy link
Collaborator

@nayeemrmn nayeemrmn Jun 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter is the logical and minimal choice. It's only one more method call to use on a *ableStream.

cc @kitsonk

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the reader/writer interfaces are what we want to use (the latter).

@marcosc90 marcosc90 marked this pull request as draft June 9, 2020 15:47
/** Turns a WritableStream into a Writer
* @param dst The WritableStream to convert
*/
export function toWriter(dst: WritableStream): Writer & Closer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New API additions should be unstable. Can you move these to lib.deno.unstable.d.ts ?

I'm not sure about the names in particular...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!. Regarding the names, I'm open to suggestions.

@nayeemrmn suggested:

  • fromStreamReader
  • fromStreamWriter

Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @marcosc90 - thanks for this patch - I just want to be careful about expanding our API. I think the most minimal thing is to add toWriter and toReader to the unstable API and not make any changes to the copy function.

@marcosc90
Copy link
Contributor Author

Looks great @marcosc90 - thanks for this patch - I just want to be careful about expanding our API. I think the most minimal thing is to add toWriter and toReader to the unstable API and not make any changes to the copy function.

Sounds good. I'll work on the modifications and update in the next couple of days.

@marcosc90 marcosc90 changed the title feat(cli/io): Allow Deno.copy to take a ReadableStream & WritableStream feat(cli/io): add toReader & toWriter Jun 14, 2020
@marcosc90 marcosc90 marked this pull request as ready for review June 14, 2020 09:31
@marcosc90
Copy link
Contributor Author

This is ready for review. Regarding the names, I don't care much, so whatever you prefer works for me.

toReader
toWriter

or

fromStreamReader
fromStreamWriter

Or any other alternative.

cli/js/io.ts Outdated Show resolved Hide resolved
cli/js/io.ts Outdated Show resolved Hide resolved
@ry
Copy link
Member

ry commented Jun 15, 2020

@marcosc90 Maybe an even more minimal way to address this it to move toReader and toWriter into std. I understand that this would be less ergonomic - but it would allow us to experiment and iterate on these APIs before committing to adding them to the runtime. What do you think?

@marcosc90
Copy link
Contributor Author

I think it is a good idea, I asked about adding it to std here #5789 (comment)

Should I place them in std/io/util.ts?

@nayeemrmn
Copy link
Collaborator

How about std/io/streams.ts for all things WHATWG streams related? Though I don't think there is much to iterate on, honestly.

@ry
Copy link
Member

ry commented Jun 15, 2020

std/io/streams.ts seems reasonable to me

std/io/streams.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@nayeemrmn nayeemrmn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bartlomieju bartlomieju modified the milestone: 1.2.0 Jun 27, 2020
@bartlomieju bartlomieju added std and removed cli related to cli/ dir feat new feature (which has been agreed to/accepted) public API related to "Deno" namespace in JS labels Jun 27, 2020
@bartlomieju bartlomieju changed the title feat(cli/io): add toReader & toWriter feat(std/io): add toReader & toWriter Jun 27, 2020
@bartlomieju bartlomieju requested a review from ry June 27, 2020 11:56
Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thank you for seeing this one through @marcosc90

@ry ry merged commit a829fa8 into denoland:master Jun 27, 2020
@marcosc90 marcosc90 deleted the deno-copy-readable-stream branch June 27, 2020 20:58
tomholford added a commit to tomholford/media-downloader that referenced this pull request Jul 6, 2020
Replace arraybuffer new functionality exposed by `fromStreamReader` .

see this PR: denoland/deno#5789

docs:
https://deno.land/std/io/#fromstreamreader
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 21, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 24, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 24, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 24, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 31, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 31, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 31, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Jan 31, 2021
caspervonb pushed a commit to caspervonb/deno_std that referenced this pull request Feb 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants