yew_stdweb/services/reader/
std_web.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! `stdweb` implementation for the reader service.

use super::*;
use crate::callback::Callback;
use crate::services::Task;
use std::cmp;
use stdweb::unstable::{TryFrom, TryInto};
use stdweb::web::event::LoadEndEvent;
#[doc(no_inline)]
pub use stdweb::web::{Blob, File, IBlob};
use stdweb::web::{FileReader, FileReaderReadyState, FileReaderResult, IEventTarget, TypedArray};
#[allow(unused_imports)]
use stdweb::{_js_impl, js};

fn new_file_reader() -> Result<FileReader, &'static str> {
    let file_reader = js! {
        try {
            return new FileReader;
        } catch(error) {
            return error;
        }
    };
    FileReader::try_from(js!( return @{file_reader.as_ref()}; ))
        .map_err(|_| "couldn't aquire file reader")
}

impl ReaderService {
    /// Reads all bytes from a file and returns them with a callback.
    pub fn read_file(file: File, callback: Callback<FileData>) -> Result<ReaderTask, &'static str> {
        let file_reader = new_file_reader()?;
        let reader = file_reader.clone();
        let name = file.name();
        file_reader.add_event_listener(move |_event: LoadEndEvent| match reader.result() {
            Some(FileReaderResult::String(_)) => {
                unreachable!();
            }
            Some(FileReaderResult::ArrayBuffer(buffer)) => {
                let array: TypedArray<u8> = buffer.into();
                let data = FileData {
                    name: name.clone(),
                    content: array.to_vec(),
                };
                callback.emit(data);
            }
            None => {}
        });
        file_reader.read_as_array_buffer(&file).unwrap();
        Ok(ReaderTask { file_reader })
    }

    /// Reads data chunks from a file and returns them with a callback.
    pub fn read_file_by_chunks(
        file: File,
        callback: Callback<Option<FileChunk>>,
        chunk_size: usize,
    ) -> Result<ReaderTask, &'static str> {
        let file_reader = new_file_reader()?;
        let name = file.name();
        let mut position = 0;
        let total_size = file.len() as usize;
        let reader = file_reader.clone();
        file_reader.add_event_listener(move |_event: LoadEndEvent| {
            if let Some(result) = reader.result() {
                match result {
                    // This branch is used to start reading
                    FileReaderResult::String(_) => {
                        let started = FileChunk::Started { name: name.clone() };
                        callback.emit(Some(started));
                    }
                    // This branch is used to send a chunk value
                    FileReaderResult::ArrayBuffer(buffer) => {
                        let array: TypedArray<u8> = buffer.into();
                        let chunk = FileChunk::DataChunk {
                            data: array.to_vec(),
                            progress: position as f32 / total_size as f32,
                        };
                        callback.emit(Some(chunk));
                    }
                }

                // Read the next chunk
                if position < total_size {
                    let file = &file;
                    let from = position;
                    let to = cmp::min(position + chunk_size, total_size);
                    position = to;
                    // TODO(#942): Implement `slice` method in `stdweb`
                    let blob: Blob = (js! {
                        return @{file}.slice(@{from as u32}, @{to as u32});
                    })
                    .try_into()
                    .unwrap();
                    reader.read_as_array_buffer(&blob).unwrap();
                } else {
                    let finished = FileChunk::Finished;
                    callback.emit(Some(finished));
                }
            } else {
                callback.emit(None);
            }
        });
        let blob: Blob = (js! {
            return (new Blob());
        })
        .try_into()
        .unwrap();
        file_reader.read_as_text(&blob).unwrap();
        Ok(ReaderTask { file_reader })
    }
}

/// A handle to control reading.
#[must_use = "the reader will abort when the task is dropped"]
pub struct ReaderTask {
    pub(super) file_reader: FileReader,
}

impl Task for ReaderTask {
    fn is_active(&self) -> bool {
        self.file_reader.ready_state() == FileReaderReadyState::Loading
    }
}