hassium_core/fetch/
mod.rs1pub mod engines;
2
3pub mod prelude {
4 pub use super::{engines::prelude::*, engines::*};
5}
6
7use crate::id::ID;
8use std::{
9 mem::replace,
10 sync::{Arc, Mutex},
11};
12
13pub type FetchProcessID = ID<FetchProcess>;
14
15#[derive(Debug, PartialEq, Copy, Clone)]
16pub enum FetchCancelReason {
17 User,
18 Error,
19}
20
21#[derive(Debug, PartialEq, Copy, Clone)]
22pub enum FetchStatus {
23 Empty,
24 InProgress(f32),
25 Done,
26 Canceled(FetchCancelReason),
27}
28
29pub trait FetchProcessReader: Send + Sync {
30 fn status(&self) -> FetchStatus;
31 fn read(&self) -> Option<Vec<u8>>;
32 fn box_clone(&self) -> Box<FetchProcessReader>;
33}
34
35impl Clone for Box<FetchProcessReader> {
36 fn clone(&self) -> Self {
37 self.box_clone()
38 }
39}
40
41#[derive(Clone)]
42pub struct FetchProcess {
43 id: FetchProcessID,
44 inner: Arc<Mutex<(FetchStatus, Option<Vec<u8>>)>>,
45}
46
47impl Default for FetchProcess {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl FetchProcess {
54 #[inline]
55 pub fn new() -> Self {
56 Self {
57 id: FetchProcessID::new(),
58 inner: Arc::new(Mutex::new((FetchStatus::Empty, None))),
59 }
60 }
61
62 #[inline]
63 pub fn new_start() -> Self {
64 Self {
65 id: FetchProcessID::new(),
66 inner: Arc::new(Mutex::new((FetchStatus::InProgress(0.0), None))),
67 }
68 }
69
70 #[inline]
71 pub fn new_done(data: Vec<u8>) -> Self {
72 Self {
73 id: FetchProcessID::new(),
74 inner: Arc::new(Mutex::new((FetchStatus::Done, Some(data)))),
75 }
76 }
77
78 #[inline]
79 pub fn new_cancel(reason: FetchCancelReason) -> Self {
80 Self {
81 id: FetchProcessID::new(),
82 inner: Arc::new(Mutex::new((FetchStatus::Canceled(reason), None))),
83 }
84 }
85
86 #[inline]
87 pub fn id(&self) -> FetchProcessID {
88 self.id
89 }
90
91 pub fn start(&mut self) {
92 if let Ok(mut meta) = self.inner.lock() {
93 *meta = (FetchStatus::InProgress(0.0), None);
94 }
95 }
96
97 pub fn progress(&mut self, value: f32) {
98 if let Ok(mut meta) = self.inner.lock() {
99 *meta = (FetchStatus::InProgress(value), None);
100 }
101 }
102
103 pub fn done(&mut self, data: Vec<u8>) {
104 if let Ok(mut meta) = self.inner.lock() {
105 *meta = (FetchStatus::Done, Some(data));
106 }
107 }
108
109 pub fn cancel(&mut self, reason: FetchCancelReason) {
110 if let Ok(mut meta) = self.inner.lock() {
111 *meta = (FetchStatus::Canceled(reason), None);
112 }
113 }
114
115 pub fn readers_count(&self) -> usize {
116 Arc::strong_count(&self.inner) + Arc::weak_count(&self.inner) - 1
117 }
118}
119
120impl FetchProcessReader for FetchProcess {
121 fn status(&self) -> FetchStatus {
122 if let Ok(meta) = self.inner.lock() {
123 meta.0
124 } else {
125 FetchStatus::Empty
126 }
127 }
128
129 fn read(&self) -> Option<Vec<u8>> {
130 if let Ok(mut meta) = self.inner.lock() {
131 if meta.0 == FetchStatus::Done {
132 let old: (FetchStatus, Option<Vec<u8>>) =
133 replace(&mut meta, (FetchStatus::Empty, None));
134 return old.1;
135 }
136 }
137 None
138 }
139
140 fn box_clone(&self) -> Box<FetchProcessReader> {
141 Box::new((*self).clone())
142 }
143}
144
145pub trait FetchEngine: Send + Sync {
146 fn fetch(&mut self, path: &str) -> Result<Box<FetchProcessReader>, FetchStatus>;
147
148 fn cancel(&mut self, reader: Box<FetchProcessReader>) {
149 #[allow(clippy::cast_ptr_alignment)]
150 let ptr = Box::into_raw(reader) as *mut FetchProcess;
151 unsafe {
152 (*ptr).cancel(FetchCancelReason::User);
153 Box::from_raw(ptr);
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 #[cfg(not(feature = "web"))]
164 fn test_general() {
165 let mut engine = engines::fs::FsFetchEngine::new(&".");
166 let reader = engine.fetch("Cargo.toml").unwrap();
167 let reader2 = reader.clone();
168 #[cfg(feature = "parallel")]
169 {
170 assert_eq!(reader.status(), FetchStatus::InProgress(0.0));
171 assert_eq!(reader2.status(), FetchStatus::InProgress(0.0));
172 }
173 loop {
174 match reader.status() {
175 FetchStatus::InProgress(_) => continue,
176 _ => break,
177 }
178 }
179 assert_eq!(reader.status(), FetchStatus::Done);
180 assert_eq!(reader2.status(), FetchStatus::Done);
181 assert!(!reader.read().unwrap().is_empty());
182 assert_eq!(reader.status(), FetchStatus::Empty);
183 assert_eq!(reader2.status(), FetchStatus::Empty);
184 drop(reader);
185 drop(reader2);
186 }
187}