fret_platform_native/
external_drop.rs1#[cfg(not(target_arch = "wasm32"))]
2use fret_core::ExternalDropFileData;
3#[cfg(not(target_arch = "wasm32"))]
4use fret_core::ExternalDropReadError;
5use fret_core::{ExternalDropDataEvent, ExternalDropToken};
6use fret_platform::external_drop::{ExternalDropProvider, ExternalDropReadLimits};
7
8#[cfg(not(target_arch = "wasm32"))]
9use std::{collections::HashMap, path::PathBuf};
10
11#[cfg(not(target_arch = "wasm32"))]
12#[derive(Debug)]
13pub struct NativeExternalDrop {
14 next_token: u64,
15 payloads: HashMap<ExternalDropToken, Vec<PathBuf>>,
16}
17
18#[cfg(target_arch = "wasm32")]
19#[derive(Debug)]
20pub struct NativeExternalDrop;
21
22pub type DesktopExternalDrop = NativeExternalDrop;
23
24impl NativeExternalDrop {
25 pub fn new() -> Self {
26 #[cfg(not(target_arch = "wasm32"))]
27 {
28 Self {
29 next_token: 1,
30 payloads: HashMap::new(),
31 }
32 }
33
34 #[cfg(target_arch = "wasm32")]
35 {
36 Self
37 }
38 }
39
40 #[cfg(not(target_arch = "wasm32"))]
41 pub fn allocate_token(&mut self) -> ExternalDropToken {
42 let token = ExternalDropToken(self.next_token);
43 self.next_token = self.next_token.saturating_add(1);
44 token
45 }
46
47 #[cfg(not(target_arch = "wasm32"))]
48 pub fn set_payload_paths(&mut self, token: ExternalDropToken, paths: Vec<PathBuf>) {
49 self.payloads.insert(token, paths);
50 }
51
52 #[cfg(not(target_arch = "wasm32"))]
53 pub fn paths(&self, token: ExternalDropToken) -> Option<&[PathBuf]> {
54 self.payloads.get(&token).map(|v| v.as_slice())
55 }
56
57 #[cfg(not(target_arch = "wasm32"))]
58 pub fn read_paths(
59 token: ExternalDropToken,
60 paths: Vec<PathBuf>,
61 limits: ExternalDropReadLimits,
62 ) -> ExternalDropDataEvent {
63 let mut files: Vec<ExternalDropFileData> = Vec::new();
64 let mut errors: Vec<ExternalDropReadError> = Vec::new();
65 let mut total: u64 = 0;
66
67 for path in paths.into_iter().take(limits.max_files) {
68 let name = path
69 .file_name()
70 .map(|n| n.to_string_lossy().to_string())
71 .unwrap_or_else(|| path.to_string_lossy().to_string());
72
73 let meta_len = match std::fs::metadata(&path) {
74 Ok(m) => Some(m.len()),
75 Err(err) => {
76 errors.push(ExternalDropReadError {
77 name,
78 message: format!("metadata failed: {err}"),
79 });
80 continue;
81 }
82 };
83
84 if let Some(len) = meta_len
85 && len > limits.max_file_bytes
86 {
87 errors.push(ExternalDropReadError {
88 name,
89 message: format!(
90 "file too large ({} bytes > max_file_bytes {})",
91 len, limits.max_file_bytes
92 ),
93 });
94 continue;
95 }
96
97 if total >= limits.max_total_bytes {
98 errors.push(ExternalDropReadError {
99 name,
100 message: format!(
101 "drop too large (total {} >= max_total_bytes {})",
102 total, limits.max_total_bytes
103 ),
104 });
105 break;
106 }
107
108 let bytes = match std::fs::read(&path) {
109 Ok(bytes) => bytes,
110 Err(err) => {
111 errors.push(ExternalDropReadError {
112 name,
113 message: format!("read failed: {err}"),
114 });
115 continue;
116 }
117 };
118
119 if bytes.len() as u64 > limits.max_file_bytes {
120 errors.push(ExternalDropReadError {
121 name,
122 message: format!(
123 "file too large ({} bytes > max_file_bytes {})",
124 bytes.len(),
125 limits.max_file_bytes
126 ),
127 });
128 continue;
129 }
130
131 let next_total = total.saturating_add(bytes.len() as u64);
132 if next_total > limits.max_total_bytes {
133 errors.push(ExternalDropReadError {
134 name,
135 message: format!(
136 "drop too large (next_total {} > max_total_bytes {})",
137 next_total, limits.max_total_bytes
138 ),
139 });
140 break;
141 }
142
143 total = next_total;
144 files.push(ExternalDropFileData { name, bytes });
145 }
146
147 ExternalDropDataEvent {
148 token,
149 files,
150 errors,
151 }
152 }
153}
154
155impl Default for NativeExternalDrop {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161impl ExternalDropProvider for NativeExternalDrop {
162 fn read_all(
163 &mut self,
164 token: ExternalDropToken,
165 limits: ExternalDropReadLimits,
166 ) -> Option<ExternalDropDataEvent> {
167 #[cfg(not(target_arch = "wasm32"))]
168 {
169 let paths = self.payloads.get(&token)?.clone();
170 Some(Self::read_paths(token, paths, limits))
171 }
172
173 #[cfg(target_arch = "wasm32")]
174 {
175 let _ = token;
176 let _ = limits;
177 None
178 }
179 }
180
181 fn release(&mut self, token: ExternalDropToken) {
182 #[cfg(not(target_arch = "wasm32"))]
183 {
184 self.payloads.remove(&token);
185 }
186
187 #[cfg(target_arch = "wasm32")]
188 {
189 let _ = token;
190 }
191 }
192}