rsynko_memory/
download.rs1use crate::DownloadSyntax;
2use crate::{DownloadEvent, DownloadProgress};
3use rsynko_download::*;
4use std::cell::{Cell, RefCell};
5use std::collections::BTreeMap;
6use std::io::{Cursor, Read};
7use std::path::{Path, PathBuf};
8use thiserror::Error;
9
10#[derive(Debug, Error, PartialEq, Eq)]
12pub enum ReferenceFetchError {
13 #[error("unknown resource {0}")]
15 UnknownResource(String),
16}
17
18#[derive(Debug, Error, PartialEq, Eq)]
20#[error("publication refused")]
21pub struct ReferencePublishError;
22
23#[derive(Debug, Default)]
25pub struct ReferenceDownloadEnv {
26 resources: BTreeMap<String, Vec<u8>>,
27 refuse_publication: bool,
28 files: RefCell<BTreeMap<PathBuf, Vec<u8>>>,
29 abandoned: Cell<usize>,
30 events: RefCell<Vec<DownloadEvent>>,
31 progress: RefCell<Vec<DownloadProgress>>,
32}
33
34impl ReferenceDownloadEnv {
35 pub fn register_resource(&mut self, url: impl Into<String>, bytes: impl Into<Vec<u8>>) -> Option<Vec<u8>> {
37 self.resources.insert(url.into(), bytes.into())
38 }
39
40 pub fn refuse_publication(&mut self) {
42 self.refuse_publication = true;
43 }
44
45 #[must_use]
47 pub fn abandoned(&self) -> usize {
48 self.abandoned.get()
49 }
50
51 #[must_use]
53 pub fn file(&self, path: &Path) -> Option<Vec<u8>> {
54 self.files.borrow().get(path).cloned()
55 }
56
57 #[must_use]
59 pub fn events(&self) -> Vec<DownloadEvent> {
60 self.events.borrow().clone()
61 }
62
63 #[must_use]
65 pub fn progress(&self) -> Vec<DownloadProgress> {
66 self.progress.borrow().clone()
67 }
68}
69
70impl FetchStreamAlg for ReferenceDownloadEnv {
71 type Error = ReferenceFetchError;
72 type Stream = Cursor<Vec<u8>>;
73
74 fn open_fetch(&self, url: &str) -> Result<FetchStream<Self::Stream>, Self::Error> {
75 let bytes =
76 self.resources.get(url).cloned().ok_or_else(|| ReferenceFetchError::UnknownResource(url.to_owned()))?;
77 let total = u64::try_from(bytes.len()).ok();
78 Ok(FetchStream::new(Cursor::new(bytes), total))
79 }
80
81 fn read_fetch(&self, stream: &mut Self::Stream, buffer: &mut [u8]) -> Result<usize, Self::Error> {
82 Ok(stream.read(buffer).unwrap_or(0))
83 }
84}
85
86impl AtomicPublishAlg for ReferenceDownloadEnv {
87 type Error = ReferencePublishError;
88 type Publication = (PathBuf, Vec<u8>);
89
90 fn begin_publication(&self, destination: &Path) -> Result<Self::Publication, Self::Error> {
91 Ok((destination.to_owned(), Vec::default()))
92 }
93
94 fn write_publication(&self, publication: &mut Self::Publication, bytes: &[u8]) -> Result<(), Self::Error> {
95 if self.refuse_publication {
96 return Err(ReferencePublishError);
97 }
98 publication.1.extend_from_slice(bytes);
99 Ok(())
100 }
101
102 fn commit_publication(&self, publication: Self::Publication) -> Result<(), Self::Error> {
103 self.files.borrow_mut().insert(publication.0, publication.1);
104 Ok(())
105 }
106
107 fn abort_publication(&self, _: Self::Publication) {
108 self.abandoned.set(self.abandoned.get() + 1);
109 }
110}
111
112impl DownloadReportAlg for ReferenceDownloadEnv {
113 type Event = DownloadEvent;
114
115 fn report_download(&self, event: DownloadEvent) {
116 self.events.borrow_mut().push(event);
117 }
118}
119
120impl DownloadProgressAlg for ReferenceDownloadEnv {
121 type Progress = DownloadProgress;
122
123 fn report_progress(&self, progress: DownloadProgress) {
124 self.progress.borrow_mut().push(progress);
125 }
126}
127
128impl DownloadObservationAlg for ReferenceDownloadEnv {
129 type Event = DownloadEvent;
130 type Progress = DownloadProgress;
131
132 fn download_progress(&self, destination: &Path, downloaded: u64, total: Option<u64>) -> Self::Progress {
133 DownloadSyntax.download_progress(destination, downloaded, total)
134 }
135
136 fn download_succeeded(&self, destination: &Path, bytes: u64) -> Self::Event {
137 DownloadSyntax.download_succeeded(destination, bytes)
138 }
139
140 fn download_failed(&self, destination: &Path, message: String) -> Self::Event {
141 DownloadSyntax.download_failed(destination, message)
142 }
143}