First we have FileList
which is simply a list of File
s. This is an opaque struct that offers a way to iterate over Files
and get individual files by index:
#[derive(Debug, Clone)]
struct FileList { ... }
impl FileList {
fn get(index: usize) -> Option<File> { ... }
fn len(&self) -> usize { ... }
fn iter(&self) -> FileListIter { ... }
fn as_raw(&self) -> &web_sys::FileList { ... }
}
impl AsRef<web_sys::FileList> for FileList { ... }
impl From<web_sys::FileList> for FileList { ... }
impl Index<usize> for FileList {
type Output = File;
fn index(&self, index: usize) -> &Self::Output { ... }
}
There is no FileList::new
since creating a raw web_sys::FileList
is not possible without going through a web_sys::HtmlInputElement
.
Next we have a blob
module that includes the trait BlobLike
trait BlobLike {
fn size(&self) -> u64 { ... }
// the mime crate and mimes from blobs both conform to rfc6838
#[cfg(feature = "mime")]
fn mime_type(&self) -> Result<mime::Mime, mime::FromStrError> { ... }
fn raw_mime_type(&self) -> String { ... }
fn as_raw(&self) -> &web_sys::Blob;
}
There are two structs that implement this trait: Blob
and File
.
#[derive(Debug, Clone)]
struct Blob { ... }
impl Blob {
fn new<T>(content: T) -> Blob
where
T: std::convert::Into<BlobContents> // We'll look at BlobContents below
{ ... }
fn new_with_options<T>(content: T, mime_type: Option<String>) -> Blob
where
T: std::convert::Into<BlobContents>
{ ... }
fn slice(&self, start: usize, end: usize) -> Blob { ... }
}
impl From<web_sys::Blob> for Blob { ... }
impl BlobLike for Blob { ... }
#[derive(Debug, Clone)]
pub struct File { ... }
impl File {
fn new<T>(
name: String,
contents: Option<T>,
mime_type: Option<String>,
last_modified_date: Option<u64>,
) -> File
where
T: std::convert::Into<BlobContents>,
{ ... }
fn name(&self) -> String { ... }
fn last_modified_date(&self) -> u64 { ... }
fn slice(&self, start: usize, end: usize) -> File { ... }
fn as_blob(self) -> Blob { ... }
}
impl BlobLike for File { ... }
Both Blob and File come with builders that allow for creating new Blobs and Files in the builder style and not having to use new
directly.
BlobContents
is simply a new-type around wasm_bindgen::JsValue
s that can be used as the content of Blob
s and File
s:
#[derive(Debug, Clone)]
pub struct BlobContents {
inner: wasm_bindgen::JsValue,
}
There are there conversions from types into BlobContents
only for the types that make sense:
impl std::convert::Into<BlobContents> for &str
impl std::convert::Into<BlobContents> for &[u8]
impl std::convert::Into<BlobContents> for Blob
impl std::convert::Into<BlobContents> for js_sys::ArrayBuffer
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.
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 varierty 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.
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.
mod callbacks {
#[derive(Debug)]
pub struct FileReader { ... }
impl FileReader {
fn new() -> FileReader { ... }
fn read_as_string<F>(self, blob: &impl BlobLike, callback: F)
where F: FnOnce(Result<String, FileReadError>) { ... };
fn read_as_data_url<F>(self, blob: &impl BlobLike, callback: F)
where F: FnOnce(Result<String, FileReadError>) { ... };
fn read_as_array_buffer<F>(self, blob: &impl BlobLike, callback: F)
where F: FnOnce(Result<&web_sys::ArrayBuffer, FileReadError>) { ... };
fn on_progress<F>(&mut self, callback: F)
where F: FnMut(ProgressEvent) + 'static { ... }
fn on_load_start<F>(&mut self, callback: F)
where F: FnOnce(LoadStartEvent) + 'static { ... }
}
}
mod futures {
#[derive(Debug)]
pub struct FileReader { ... }
impl FileReader {
fn new() -> FileReader { ... }
fn read_as_string(self, blob: &impl BlobLike) -> ReadAs<String> { ... }
fn read_as_data_url(self, blob: &impl BlobLike) -> ReadAs<String> { ... }
fn read_as_array_buffer(self, blob: &impl BlobLike) -> ReadAs<js_sys::ArrayBuffer>
fn on_progress(&self) -> OnProgressStream { }
fn on_load_start(&self) -> OnLoadStartFuture { }
}
pub struct ReadAs<T> { ... }
impl<T> Future for ReadAs<T> {
type Item = T;
type Error = FileReadError;
...
}
// Make sure that dropping the Future properly aborts the reading
impl<T> std::ops::Drop for ReadAs<T> {
fn drop(&mut self) {
if self.inner.ready_state() < 2 {
self.inner.abort();
}
}
}
}