oxygengine_core/fetch/
mod.rs

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