yew_stdweb/services/reader/
std_web.rs

1//! `stdweb` implementation for the reader service.
2
3use super::*;
4use crate::callback::Callback;
5use crate::services::Task;
6use std::cmp;
7use stdweb::unstable::{TryFrom, TryInto};
8use stdweb::web::event::LoadEndEvent;
9#[doc(no_inline)]
10pub use stdweb::web::{Blob, File, IBlob};
11use stdweb::web::{FileReader, FileReaderReadyState, FileReaderResult, IEventTarget, TypedArray};
12#[allow(unused_imports)]
13use stdweb::{_js_impl, js};
14
15fn new_file_reader() -> Result<FileReader, &'static str> {
16    let file_reader = js! {
17        try {
18            return new FileReader;
19        } catch(error) {
20            return error;
21        }
22    };
23    FileReader::try_from(js!( return @{file_reader.as_ref()}; ))
24        .map_err(|_| "couldn't aquire file reader")
25}
26
27impl ReaderService {
28    /// Reads all bytes from a file and returns them with a callback.
29    pub fn read_file(file: File, callback: Callback<FileData>) -> Result<ReaderTask, &'static str> {
30        let file_reader = new_file_reader()?;
31        let reader = file_reader.clone();
32        let name = file.name();
33        file_reader.add_event_listener(move |_event: LoadEndEvent| match reader.result() {
34            Some(FileReaderResult::String(_)) => {
35                unreachable!();
36            }
37            Some(FileReaderResult::ArrayBuffer(buffer)) => {
38                let array: TypedArray<u8> = buffer.into();
39                let data = FileData {
40                    name: name.clone(),
41                    content: array.to_vec(),
42                };
43                callback.emit(data);
44            }
45            None => {}
46        });
47        file_reader.read_as_array_buffer(&file).unwrap();
48        Ok(ReaderTask { file_reader })
49    }
50
51    /// Reads data chunks from a file and returns them with a callback.
52    pub fn read_file_by_chunks(
53        file: File,
54        callback: Callback<Option<FileChunk>>,
55        chunk_size: usize,
56    ) -> Result<ReaderTask, &'static str> {
57        let file_reader = new_file_reader()?;
58        let name = file.name();
59        let mut position = 0;
60        let total_size = file.len() as usize;
61        let reader = file_reader.clone();
62        file_reader.add_event_listener(move |_event: LoadEndEvent| {
63            if let Some(result) = reader.result() {
64                match result {
65                    // This branch is used to start reading
66                    FileReaderResult::String(_) => {
67                        let started = FileChunk::Started { name: name.clone() };
68                        callback.emit(Some(started));
69                    }
70                    // This branch is used to send a chunk value
71                    FileReaderResult::ArrayBuffer(buffer) => {
72                        let array: TypedArray<u8> = buffer.into();
73                        let chunk = FileChunk::DataChunk {
74                            data: array.to_vec(),
75                            progress: position as f32 / total_size as f32,
76                        };
77                        callback.emit(Some(chunk));
78                    }
79                }
80
81                // Read the next chunk
82                if position < total_size {
83                    let file = &file;
84                    let from = position;
85                    let to = cmp::min(position + chunk_size, total_size);
86                    position = to;
87                    // TODO(#942): Implement `slice` method in `stdweb`
88                    let blob: Blob = (js! {
89                        return @{file}.slice(@{from as u32}, @{to as u32});
90                    })
91                    .try_into()
92                    .unwrap();
93                    reader.read_as_array_buffer(&blob).unwrap();
94                } else {
95                    let finished = FileChunk::Finished;
96                    callback.emit(Some(finished));
97                }
98            } else {
99                callback.emit(None);
100            }
101        });
102        let blob: Blob = (js! {
103            return (new Blob());
104        })
105        .try_into()
106        .unwrap();
107        file_reader.read_as_text(&blob).unwrap();
108        Ok(ReaderTask { file_reader })
109    }
110}
111
112/// A handle to control reading.
113#[must_use = "the reader will abort when the task is dropped"]
114pub struct ReaderTask {
115    pub(super) file_reader: FileReader,
116}
117
118impl Task for ReaderTask {
119    fn is_active(&self) -> bool {
120        self.file_reader.ready_state() == FileReaderReadyState::Loading
121    }
122}