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
123
124
125
126
127
128
129
130
131
132
133
134
135
use std;
use std::io::ErrorKind;
use std::io::Read;
use std::str;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::TryRecvError;
use std::thread::JoinHandle;
/// the root filesystem API
pub struct FileSystem {}
/// synchronous (native) / asynchronous (web) file API
pub enum File {
Native(std::fs::File),
Data(Option<Vec<u8>>),
Pending(Receiver<Result<Option<Vec<u8>>, IoError>>, JoinHandle<()>),
Error(IoError),
}
pub type IoError = std::io::Error;
pub type IoErrorKind = std::io::ErrorKind;
impl FileSystem {
/// open a file.
/// For a file to be accessible from both native and web build, it should be placed
/// in a www/ directory in your project's root directory, for example www/config.json.
/// You can then open this file with `FileSystem::open("config.json")`.
/// When running your native project, use `cd www && cargo run`.
/// When deploying on the web, the file should simply be in the same directory as index.html, as config.json.
/// Note that on wasm target, this works with any URL, you can do :
/// `FileSystem::open('https://raw.githubusercontent.com/unrust/uni-app/master/www/test.txt')`
/// To be able to load data from an URL in native mode, you have to enable the http feature.
pub fn open(s: &str) -> Result<File, IoError> {
if s.starts_with("http") {
if cfg!(feature = "http") {
let (tx, rx) = std::sync::mpsc::channel();
let url = s.to_owned();
let handle = std::thread::spawn(move || match reqwest::blocking::get(url) {
Err(e) => {
tx.send(Err(std::io::Error::new(ErrorKind::Other, e)))
.unwrap();
}
Ok(response) => {
let bytes = response.bytes();
match bytes {
Err(e) => {
tx.send(Err(std::io::Error::new(ErrorKind::Other, e)))
.unwrap();
}
Ok(bytes) => {
tx.send(Ok(Some(bytes.to_vec()))).unwrap();
}
}
}
});
return Ok(File::Pending(rx, handle));
} else {
Err(std::io::Error::new(
ErrorKind::Other,
format!(
"error : could not read {} : http feature not enabled in uni-app::FileSystem",
s
),
))
}
} else {
Ok(File::Native(std::fs::File::open(s)?))
}
}
}
impl File {
/// Once the file has been loaded (see [`File::is_ready`]), returns the file content as `Vec<u8>`
pub fn read_binary(&mut self) -> Result<Vec<u8>, IoError> {
match self {
File::Native(f) => {
let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
Ok(buf)
}
File::Data(data) => Ok(data.take().unwrap()),
File::Error(e) => Err(std::io::Error::new(e.kind(),e.to_string())),
File::Pending(_,_) => Err(std::io::Error::new(ErrorKind::Other, "error: calling read_binary on a pending File. Check is_ready() before calling read_binary")),
}
}
/// Once the file has been loaded (see [`File::is_ready`]), returns the file content as a String
pub fn read_text(&mut self) -> Result<String, IoError> {
let mut data = String::new();
match self {
File::Native(f) => match f.read_to_string(&mut data) {
Ok(_) => Ok(data),
Err(e) => Err(std::io::Error::new(ErrorKind::Other, e)),
},
File::Data(data) => match String::from_utf8(data.take().unwrap()) {
Ok(s) => Ok(s),
Err(e) => Err(std::io::Error::new(ErrorKind::Other, e))
},
File::Error(e) => Err(std::io::Error::new(e.kind(),e.to_string())),
File::Pending(_,_) => Err(std::io::Error::new(ErrorKind::Other, "error: calling read_text on a pending File. Check is_ready() before calling read_text")),
}
}
/// return true if the file has been loaded
/// On native target, files are loaded synchronously.
/// As soon as [`FileSystem::open`] returns, the file is ready.
/// [`File::read_binary`] and [`File::read_text`] can be called immediately.
/// On web target, files are loaded asynchronously.
/// You have to poll [`File::is_ready`] until it returns true.
/// Only then you can call [`File::read_binary`] or [`File::read_text`].
pub fn is_ready(&mut self) -> bool {
match self {
File::Native(_) => true,
File::Pending(rx, _) => match rx.try_recv() {
Ok(result) => match result {
Ok(data) => {
*self = File::Data(data);
return true;
}
Err(e) => {
*self = File::Error(e);
return true;
}
},
Err(TryRecvError::Empty) => {
return false;
}
Err(e) => {
*self = File::Error(std::io::Error::new(ErrorKind::Other, e));
return true;
}
},
File::Error(_) => true,
File::Data(_) => true,
}
}
}