1use crate::{DataItem, DatasetItem};
4use rskit_errors::AppResult;
5use std::pin::Pin;
6use tokio_util::sync::CancellationToken;
7
8pub type BoxItemStream<T> = Pin<Box<dyn futures::Stream<Item = AppResult<T>> + Send + 'static>>;
10
11pub type BoxDataStream = BoxItemStream<DataItem>;
13
14pub trait Source<T: DatasetItem>: Send + Sync {
16 fn name(&self) -> &str;
18
19 fn display_name(&self) -> &str {
21 self.name().rsplit('/').next().unwrap_or(self.name())
22 }
23
24 fn stream(self: Box<Self>, cancel: CancellationToken) -> BoxItemStream<T>;
26
27 fn cache_key(&self) -> serde_json::Value;
29 fn max_items(&self) -> Option<usize>;
31
32 fn set_resume_state(&mut self, _offset: usize, _already_fetched: usize) {}
34}
35
36#[cfg(test)]
37mod tests {
38 use futures::stream;
39 use serde_json::json;
40
41 use super::*;
42
43 struct StaticSource {
44 name: String,
45 resume: Option<(usize, usize)>,
46 }
47
48 struct DefaultResumeSource;
49
50 impl Source<DataItem> for DefaultResumeSource {
51 fn name(&self) -> &str {
52 "default"
53 }
54
55 fn stream(self: Box<Self>, _cancel: CancellationToken) -> BoxDataStream {
56 Box::pin(stream::empty())
57 }
58
59 fn cache_key(&self) -> serde_json::Value {
60 json!("default")
61 }
62
63 fn max_items(&self) -> Option<usize> {
64 None
65 }
66 }
67
68 impl Source<DataItem> for StaticSource {
69 fn name(&self) -> &str {
70 &self.name
71 }
72
73 fn stream(self: Box<Self>, _cancel: CancellationToken) -> BoxDataStream {
74 Box::pin(stream::empty())
75 }
76
77 fn cache_key(&self) -> serde_json::Value {
78 json!({"name": self.name})
79 }
80
81 fn max_items(&self) -> Option<usize> {
82 Some(0)
83 }
84
85 fn set_resume_state(&mut self, offset: usize, already_fetched: usize) {
86 self.resume = Some((offset, already_fetched));
87 }
88 }
89
90 #[test]
91 fn source_defaults_derive_display_name_and_allow_resume_state() {
92 let mut source = StaticSource {
93 name: "fixtures/images".to_string(),
94 resume: None,
95 };
96
97 assert_eq!(source.name(), "fixtures/images");
98 assert_eq!(source.display_name(), "images");
99 assert_eq!(source.cache_key(), json!({"name": "fixtures/images"}));
100 assert_eq!(source.max_items(), Some(0));
101
102 source.set_resume_state(10, 3);
103 assert_eq!(source.resume, Some((10, 3)));
104
105 let stream = Box::new(source).stream(CancellationToken::new());
106 drop(stream);
107
108 let mut default_resume = DefaultResumeSource;
109 default_resume.set_resume_state(1, 1);
110 assert_eq!(default_resume.display_name(), "default");
111 assert_eq!(default_resume.cache_key(), json!("default"));
112 assert_eq!(default_resume.max_items(), None);
113 let stream = Box::new(default_resume).stream(CancellationToken::new());
114 drop(stream);
115 }
116}