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<()> {
77 let Some(writer) = self.writer.take() else {
78 return Ok(())
79 };
80 let Some(writer_attr) = self.writer_attr.take() else {
81 return Ok(())
82 };
83
84 if let WriterAttr::TempBuffer {
85 writer_path,
86 actual_target_file_uri,
87 } = writer_attr {
88
89 let result1 = writer.sync_data();
91 std::mem::drop(writer);
93
94 let result2 = self.app
95 .android_fs()
96 .copy_via_kotlin(&(writer_path.clone().into()), &actual_target_file_uri, None);
97
98 let _ = std::fs::remove_file(&writer_path);
99
100 result1?;
101 result2?;
102 }
103
104 Ok(())
105 }
106
107 pub fn sync_all(&mut self) -> std::io::Result<()> {
113 let Some(writer) = self.writer.as_mut() else {
114 return Ok(())
115 };
116 let Some(writer_attr) = self.writer_attr.as_ref() else {
117 return Ok(())
118 };
119
120 if let WriterAttr::ActualTarget = writer_attr {
121 writer.sync_all()?;
122 }
123 Ok(())
124 }
125
126 pub fn sync_data(&mut self) -> std::io::Result<()> {
132 let Some(writer) = self.writer.as_mut() else {
133 return Ok(())
134 };
135 let Some(writer_attr) = self.writer_attr.as_ref() else {
136 return Ok(())
137 };
138
139 if let WriterAttr::ActualTarget = writer_attr {
140 writer.sync_data()?;
141 }
142 Ok(())
143 }
144}
145
146impl<R: tauri::Runtime> std::io::Write for WritableStream<R> {
147
148 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
149 match self.writer.as_mut() {
150 Some(w) => w.write(buf),
151 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
152 }
153 }
154
155 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
156 match self.writer.as_mut() {
157 Some(w) => w.write_all(buf),
158 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
159 }
160 }
161
162 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
163 match self.writer.as_mut() {
164 Some(w) => w.write_vectored(bufs),
165 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
166 }
167 }
168
169 fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> std::io::Result<()> {
170 match self.writer.as_mut() {
171 Some(w) => w.write_fmt(fmt),
172 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
173 }
174 }
175
176 fn flush(&mut self) -> std::io::Result<()> {
177 match self.writer.as_mut() {
178 Some(w) => w.flush(),
179 None => Err(std::io::Error::new(std::io::ErrorKind::Other, "writer missing")),
180 }
181 }
182}
183
184impl<R: tauri::Runtime> std::ops::Drop for WritableStream<R> {
185
186 fn drop(&mut self) {
187 let Some(writer) = self.writer.take() else {
189 return
190 };
191 let Some(writer_attr) = self.writer_attr.take() else {
192 return
193 };
194
195 if let WriterAttr::TempBuffer {
200 writer_path,
201 actual_target_file_uri,
202 } = writer_attr {
203
204 let app = self.app.clone();
205 let src = writer;
206 let (src_uri, src_path) = (writer_path.clone().into(), writer_path);
207 let dest_uri = actual_target_file_uri.clone();
208
209 tauri::async_runtime::spawn_blocking(move || {
211 let _ = src.sync_data();
213 std::mem::drop(src);
215
216 let _ = app.android_fs().copy_via_kotlin(&src_uri, &dest_uri, None);
217 let _ = std::fs::remove_file(src_path);
218 });
219 }
220 }
221}