1use std::time::Duration;
2
3#[derive(Clone, Debug, PartialEq, Eq)]
5pub enum Sub<M> {
6 None,
7 Tick { id: u64, every: Duration },
8 Stream { id: u64, name: &'static str },
9 WebSocket { id: u64, url: &'static str },
10 MapMsg {
11 inner: Box<Sub<()>>,
12 map: fn(()) -> M,
13 },
14 Batch(Vec<Sub<M>>),
15}
16
17impl<M> Sub<M> {
18 pub fn none() -> Self {
19 Self::None
20 }
21
22 pub fn tick(id: u64, every: Duration) -> Self {
23 Self::Tick { id, every }
24 }
25
26 pub fn stream(id: u64, name: &'static str) -> Self {
27 Self::Stream { id, name }
28 }
29
30 pub fn websocket(id: u64, url: &'static str) -> Self {
31 Self::WebSocket { id, url }
32 }
33
34 pub fn map<N>(self, map: fn(M) -> N) -> Sub<N> {
35 match self {
36 Self::None => Sub::None,
37 Self::Tick { id, every } => Sub::Tick { id, every },
38 Self::Stream { id, name } => Sub::Stream { id, name },
39 Self::WebSocket { id, url } => Sub::WebSocket { id, url },
40 Self::Batch(items) => Sub::Batch(items.into_iter().map(|s| s.map(map)).collect()),
41 Self::MapMsg { .. } => Sub::None,
42 }
43 }
44
45 pub fn batch(subs: impl IntoIterator<Item = Sub<M>>) -> Self {
46 let subs: Vec<_> = subs.into_iter().collect();
47 if subs.is_empty() {
48 Self::None
49 } else if subs.len() == 1 {
50 subs.into_iter().next().unwrap()
51 } else {
52 Self::Batch(subs)
53 }
54 }
55
56 pub fn id(&self) -> Option<u64> {
57 match self {
58 Self::None => None,
59 Self::Tick { id, .. } | Self::Stream { id, .. } | Self::WebSocket { id, .. } => {
60 Some(*id)
61 }
62 Self::MapMsg { inner, .. } => inner.id(),
63 Self::Batch(items) => items.first().and_then(Sub::id),
64 }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn tick_has_stable_id() {
74 let sub = Sub::<u32>::tick(42, Duration::from_secs(1));
75 assert_eq!(sub.id(), Some(42));
76 }
77}