Skip to main content

fret_platform_native/
external_drop.rs

1#[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}