Skip to content

Commit ba4a641

Browse files
authored
Merge pull request #76 from rylev/file-rfc
Add a mid-level file-rfc
2 parents 01afcff + 6d80c33 commit ba4a641

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

rfcs/001-mid-level-file-api.md

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Mid-Level File Api
2+
3+
## Summary
4+
5+
This is a mid-level API wrapper around the various File related JavaScript apis. The possibility for a higher level API is left open.
6+
7+
The mid-level file API aims to implement File I/O on top of the raw JS apis found here: https://w3c.github.io/FileAPI/ which includes listing files, creating files, and reading files.
8+
9+
## The API
10+
11+
First we have a way to turn a `web_sys::FileList` into a vector of `File`s (we'll look a the `File` struct below):
12+
13+
```rust
14+
impl From<web_sys::FileList> for Vec<File> { ... }
15+
```
16+
17+
Next we the trait `BlobLike`
18+
```rust
19+
trait BlobLike {
20+
fn size(&self) -> u64 { ... }
21+
22+
// the mime crate and mimes from blobs both conform to rfc6838
23+
#[cfg(feature = "mime")]
24+
fn mime_type(&self) -> Result<mime::Mime, mime::FromStrError> { ... }
25+
26+
fn raw_mime_type(&self) -> String { ... }
27+
28+
fn as_raw(&self) -> &web_sys::Blob;
29+
30+
fn slice(&self, start: u64, end: u64) -> Self
31+
}
32+
```
33+
There are two structs that implement this trait: `Blob` and `File`.
34+
35+
```rust
36+
#[derive(Debug, Clone)]
37+
struct Blob { ... }
38+
39+
impl Blob {
40+
fn new<T>(contents: T) -> Blob
41+
where
42+
T: std::convert::Into<BlobContents> // We'll look at BlobContents below
43+
{ ... }
44+
45+
fn new_with_options<T>(contents: T, mime_type: String) -> Blob
46+
where
47+
T: std::convert::Into<BlobContents>
48+
{ ... }
49+
}
50+
51+
impl From<web_sys::Blob> for Blob { ... }
52+
53+
impl BlobLike for Blob { ... }
54+
55+
#[derive(Debug, Clone)]
56+
pub struct File { ... }
57+
58+
impl File {
59+
fn new<T>(
60+
name: String,
61+
contents: T,
62+
) -> File
63+
where
64+
T: std::convert::Into<BlobContents>,
65+
{ ... }
66+
67+
fn new_with_options<T>(
68+
name: String,
69+
contents: T,
70+
mime_type: Option<String>,
71+
last_modified_date: Option<u64>,
72+
) -> File
73+
where
74+
T: std::convert::Into<BlobContents>,
75+
{ ... }
76+
77+
fn name(&self) -> String { ... }
78+
79+
fn last_modified_since_epoch(&self) -> Duration { ... }
80+
81+
fn to_blob(&self) -> Blob { ... }
82+
}
83+
84+
impl BlobLike for File { ... }
85+
```
86+
87+
`BlobContents` is simply a new-type around `wasm_bindgen::JsValue`s that can be used as the content of `Blob`s and `File`s:
88+
89+
```rust
90+
#[derive(Debug, Clone)]
91+
pub struct BlobContents {
92+
inner: wasm_bindgen::JsValue,
93+
}
94+
```
95+
96+
There are there conversions from types into `BlobContents` only for the types that make sense:
97+
98+
```rust
99+
impl std::convert::Into<BlobContents> for &str
100+
impl std::convert::Into<BlobContents> for &[u8]
101+
impl std::convert::Into<BlobContents> for Blob
102+
impl std::convert::Into<BlobContents> for js_sys::ArrayBuffer
103+
```
104+
105+
Lastly there's the `FileReader` which allows reading from BlobLike objects. We'll have two implementations of this, one based on callbacks and the other based on futures.
106+
107+
The callbacks implementation has three categories of callbacks: start, progress and read. Start and progress callbacks are directly related to the `onloadstart` and `onprogress` callbacks on `web_sys::FileReader`. The read variety of callbacks, are a combination of `onload`, `onloadend`, `onloaderror`, and `onabort`. The callback receives a result which is an error if the underlying read was aborted or errored.
108+
109+
The futures implementation likewise exposes success, error, and abort through the fact that futures are `Result`-like. Progress events are exposed as a stream. In the future, we may expose the entire lifecycle of a read through a stream.
110+
111+
```rust
112+
mod callbacks {
113+
#[derive(Debug)]
114+
pub struct FileReader { ... }
115+
116+
impl FileReader {
117+
fn new() -> FileReader { ... }
118+
119+
fn read_to_string<F>(self, blob: &impl BlobLike, callback: F)
120+
where F: FnOnce(Result<String, FileReadError>) { ... };
121+
122+
fn read_to_data_url<F>(self, blob: &impl BlobLike, callback: F)
123+
where F: FnOnce(Result<String, FileReadError>) { ... };
124+
125+
fn read_to_array_buffer<F>(self, blob: &impl BlobLike, callback: F)
126+
where F: FnOnce(Result<&web_sys::ArrayBuffer, FileReadError>) { ... };
127+
128+
fn on_progress<F>(&mut self, callback: F)
129+
where F: FnMut(ProgressEvent) + 'static { ... }
130+
131+
fn on_load_start<F>(&mut self, callback: F)
132+
where F: FnOnce(LoadStartEvent) + 'static { ... }
133+
}
134+
}
135+
136+
mod futures {
137+
#[derive(Debug)]
138+
pub struct FileReader { ... }
139+
140+
impl FileReader {
141+
fn new() -> FileReader { ... }
142+
143+
fn read_to_string(self, blob: &impl BlobLike) -> ReadAsString { ... }
144+
145+
fn read_to_data_url(self, blob: &impl BlobLike) -> ReadAsDataUrl { ... }
146+
147+
fn read_to_array_buffer(self, blob: &impl BlobLike) -> ReadAsArrayBuffer { ... }
148+
149+
fn on_progress(&self) -> OnProgressStream { }
150+
151+
fn on_load_start(&self) -> OnLoadStartFuture { }
152+
}
153+
154+
pub struct ReadAsString { ... }
155+
impl Future for ReadAsString {
156+
type Item = String;
157+
type Error = FileReadError;
158+
...
159+
}
160+
161+
// Make sure that dropping the Future properly aborts the reading
162+
impl std::ops::Drop for ReadAsString {
163+
fn drop(&mut self) {
164+
if self.inner.ready_state() < 2 {
165+
self.inner.abort();
166+
}
167+
}
168+
}
169+
}
170+
```

0 commit comments

Comments
 (0)