tauri_plugin_android_fs/api/
writable_stream.rs1use crate::*;
2
3
4pub struct WritableStream<R: tauri::Runtime> {
16 app: tauri::AppHandle<R>,
17 writer: Option<std::fs::File>,
18 writer_attr: Option<WriterAttr>,
19}
20
21enum WriterAttr {
22 ActualTarget,
23 TempBuffer {
24 writer_path: std::path::PathBuf,
25 actual_target_file_uri: FileUri,
26 },
27}
28
29impl<R: tauri::Runtime> WritableStream<R> {
30
31 #[allow(unused)]
32 pub(crate) fn new(
33 app: tauri::AppHandle<R>,
34 file_uri: FileUri,
35 need_write_via_kotlin: bool
36 ) -> Result<Self> {
37
38 let api = app.android_fs();
39 let (writer, writer_attr) = match need_write_via_kotlin {
40 true => {
41 let (tmp_file, tmp_file_path) = api.private_storage().create_new_tmp_file()?;
42 let attr = WriterAttr::TempBuffer {
43 writer_path: tmp_file_path,
44 actual_target_file_uri: file_uri
45 };
46 (tmp_file, attr)
47 },
48 false => {
49 let file = api.open_file_writable(&file_uri)?;
50 (file, WriterAttr::ActualTarget)
51 }
52 };
53
54 Ok(Self {
55 app,
56 writer: Some(writer),
57 writer_attr: Some(writer_attr),
58 })
59 }
60}
61
62impl<R: tauri::Runtime> WritableStream<R> {
63
64 pub fn reflect(mut self) -> Result<()> {
76 let Some(writer) = self.writer.take() else {
77 return Ok(())
78 };
79 let Some(writer_attr) = self.writer_attr.take() else {
80 return Ok(())
81 };
82
83 if let WriterAttr::TempBuffer {
84 writer_path,
85 actual_target_file_uri,
86 } = writer_attr {
87
88 let result1 = writer.sync_data();
90 std::mem::drop(writer);
92
93 let result2 = self.app
94 .android_fs()
95 .copy_via_kotlin(&(writer_path.clone().into()), &actual_target_file_uri, None);
96
97 let result3 = std::fs::remove_file(&writer_path);
98
99 result1?;
100 result2?;
101 result3?;
102 }
103
104 Ok(())
105 }
106
107 #[allow(unused)]
108 pub(crate) fn close_without_reflect(mut self) -> Result<()> {
109 let Some(writer) = self.writer.take() else {
110 return Ok(())
111 };
112 let Some(writer_attr) = self.writer_attr.take() else {
113 return Ok(())
114 };
115
116 if let WriterAttr::TempBuffer { writer_path, .. } = writer_attr {
117 std::mem::drop(writer);
118 std::fs::remove_file(&writer_path)?;
119 }
120
121 Ok(())
122 }
123
124 pub fn sync_all(&mut self) -> std::io::Result<()> {
130 let Some(writer) = self.writer.as_mut() else {
131 return Ok(())
132 };
133 let Some(writer_attr) = self.writer_attr.as_ref() else {
134 return Ok(())
135 };
136
137 if let WriterAttr::ActualTarget = writer_attr {
138 writer.sync_all()?;
139 }
140 Ok(())
141 }
142
143 pub fn sync_data(&mut self) -> std::io::Result<()> {
149 let Some(writer) = self.writer.as_mut() else {
150 return Ok(())
151 };
152 let Some(writer_attr) = self.writer_attr.as_ref() else {
153 return Ok(())
154 };
155
156 if let WriterAttr::ActualTarget = writer_attr {
157 writer.sync_data()?;
158 }
159 Ok(())
160 }
161}
162
163impl<R: tauri::Runtime> std::io::Write for WritableStream<R> {
164
165 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
166 match self.writer.as_mut() {
167 Some(w) => w.write(buf),
168 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
169 }
170 }
171
172 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
173 match self.writer.as_mut() {
174 Some(w) => w.write_all(buf),
175 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
176 }
177 }
178
179 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
180 match self.writer.as_mut() {
181 Some(w) => w.write_vectored(bufs),
182 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
183 }
184 }
185
186 fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> std::io::Result<()> {
187 match self.writer.as_mut() {
188 Some(w) => w.write_fmt(fmt),
189 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
190 }
191 }
192
193 fn flush(&mut self) -> std::io::Result<()> {
194 match self.writer.as_mut() {
195 Some(w) => w.flush(),
196 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
197 }
198 }
199}
200
201impl<R: tauri::Runtime> std::ops::Drop for WritableStream<R> {
202
203 fn drop(&mut self) {
204 let Some(writer) = self.writer.take() else {
206 return
207 };
208 let Some(writer_attr) = self.writer_attr.take() else {
209 return
210 };
211
212 if let WriterAttr::TempBuffer {
217 writer_path,
218 actual_target_file_uri,
219 } = writer_attr {
220
221 let app = self.app.clone();
222 let src = writer;
223 let (src_uri, src_path) = (writer_path.clone().into(), writer_path);
224 let dest_uri = actual_target_file_uri.clone();
225
226 tauri::async_runtime::spawn_blocking(move || {
228 let _ = src.sync_data();
230 std::mem::drop(src);
232
233 let _ = app.android_fs().copy_via_kotlin(&src_uri, &dest_uri, None);
234 let _ = std::fs::remove_file(src_path);
235 });
236 }
237 }
238}