instance_chart/chart/
to_vec.rs

1use crate::Id;
2use std::net::SocketAddr;
3
4use super::builder::Port;
5use super::{Chart, Entry};
6
7impl<const N: usize> Chart<N, Port> {
8    /// Returns an vector with each discovered node's socketadresses.
9    /// # Note
10    /// - vector order is random
11    /// - only availible for Chart configured with
12    /// [`ChartBuilder::with_service_ports`](crate::ChartBuilder::with_service_ports)
13    /// and build using [`ChartBuilder::finish`](crate::ChartBuilder::finish).
14    /// ```rust
15    /// # use std::error::Error;
16    /// # use instance_chart::{discovery, ChartBuilder};
17    /// #
18    /// # #[tokio::main]
19    /// # async fn main() -> Result<(), Box<dyn Error>> {
20    /// let chart = ChartBuilder::new()
21    ///     .with_id(1)
22    /// #   .with_discovery_port(43785)
23    ///     .with_service_ports([8042, 8043, 8044])
24    ///     .finish()?;
25    /// let maintain = discovery::maintain(chart.clone());
26    /// let _ = tokio::spawn(maintain); // maintain task will run forever
27    /// let port_lists = chart.addr_lists_vec();
28    /// #   Ok(())
29    /// # }
30    /// ```
31
32    // lock poisoning happens only on crash in another thread, in which
33    // case panicing here is expected
34    #[allow(clippy::missing_panics_doc)]
35    #[must_use]
36    pub fn addr_lists_vec(&self) -> Vec<(Id, [SocketAddr; N])> {
37        self.map
38            .lock()
39            .unwrap()
40            .iter()
41            .map(|(id, entry)| {
42                let Entry { ip, msg: ports } = entry;
43                let addr = ports.map(|p| SocketAddr::new(*ip, p));
44                (*id, addr)
45            })
46            .collect()
47    }
48}
49
50impl<const N: usize> Chart<N, Port> {
51    /// Returns a vector over each discoverd node's nth-socketadress
52    /// # Note
53    /// - vector order is random
54    /// - only availible for Chart configured with
55    /// [`ChartBuilder::with_service_ports`](crate::ChartBuilder::with_service_ports)
56    /// and build using [`ChartBuilder::finish`](crate::ChartBuilder::finish).
57    ///
58    /// # Examples
59    /// ```rust
60    /// # use std::error::Error;
61    /// # use instance_chart::{discovery, ChartBuilder};
62    /// #
63    /// # #[tokio::main]
64    /// # async fn main() -> Result<(), Box<dyn Error>> {
65    /// let web_server_port = 8043;
66    /// let chart = ChartBuilder::new()
67    ///     .with_id(1)
68    /// #   .with_discovery_port(43784)
69    ///     .with_service_ports([8042, web_server_port, 8044])
70    ///     .finish()?;
71    /// let maintain = discovery::maintain(chart.clone());
72    /// let _ = tokio::spawn(maintain); // maintain task will run forever
73    /// let web_server_ports = chart.nth_addr_vec::<2>();
74    /// #   Ok(())
75    /// # }
76    /// ```
77
78    // lock poisoning happens only on crash in another thread, in which
79    // case panicing here is expected
80    #[allow(clippy::missing_panics_doc)]
81    #[must_use]
82    pub fn nth_addr_vec<const IDX: usize>(&self) -> Vec<(Id, SocketAddr)> {
83        self.map
84            .lock()
85            .unwrap()
86            .iter()
87            .map(|(id, entry)| {
88                let Entry { ip, msg: ports } = entry;
89                let port = ports[IDX];
90                (*id, SocketAddr::new(*ip, port))
91            })
92            .collect()
93    }
94}
95
96impl<'a> Chart<1, Port> {
97    /// Returns a vector over each discoverd nodes's socketadress
98    /// # Note
99    /// - vector order is random
100    /// - only availible for Chart configured with
101    /// [`ChartBuilder::with_service_port`](crate::ChartBuilder::with_service_port)
102    /// and build using [`ChartBuilder::finish`](crate::ChartBuilder::finish).
103    /// ```rust
104    /// # use std::error::Error;
105    /// # use instance_chart::{discovery, ChartBuilder};
106    /// #
107    /// # #[tokio::main]
108    /// # async fn main() -> Result<(), Box<dyn Error>> {
109    /// let chart = ChartBuilder::new()
110    ///     .with_id(1)
111    /// #   .with_discovery_port(43782)
112    ///     .with_service_port(8042)
113    ///     .finish()?;
114    /// let maintain = discovery::maintain(chart.clone());
115    /// let _ = tokio::spawn(maintain); // maintain task will run forever
116    /// let ports = chart.addr_vec();
117    /// #   Ok(())
118    /// # }
119    /// ```
120
121    // lock poisoning happens only on crash in another thread, in which
122    // case panicing here is expected
123    #[allow(clippy::missing_panics_doc)]
124    #[must_use]
125    pub fn addr_vec(&'a self) -> Vec<(Id, SocketAddr)> {
126        self.map
127            .lock()
128            .unwrap()
129            .iter()
130            .map(|(id, entry)| {
131                let Entry { ip, msg: [port] } = entry;
132                (*id, SocketAddr::new(*ip, *port))
133            })
134            .collect()
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::chart::{Entry, Interval};
141    use crate::{Chart, Id};
142    use serde::Serialize;
143    use std::collections::{HashMap, HashSet};
144    use std::fmt::Debug;
145    use std::net::{IpAddr, Ipv4Addr, SocketAddr};
146    use std::sync::{Arc, Mutex};
147    use tokio::net::UdpSocket;
148
149    impl<const N: usize, T: Serialize + Debug + Clone> Chart<N, T> {
150        pub async fn test<F>(mut gen_kv: F) -> Self
151        where
152            F: FnMut(u8) -> (Id, Entry<[T; N]>) + Copy,
153        {
154            let msg = gen_kv(0).1.msg;
155            let map: HashMap<Id, Entry<_>> = (1..10).map(gen_kv).collect();
156            Self {
157                header: 0,
158                service_id: 0,
159                msg,
160                sock: Arc::new(UdpSocket::bind("127.0.0.1:0").await.unwrap()),
161                interval: Interval::test(),
162                map: Arc::new(Mutex::new(map)),
163                broadcast: tokio::sync::broadcast::channel(1).0,
164            }
165        }
166    }
167
168    #[tokio::test]
169    async fn iter_ports() {
170        fn test_kv(n: u8) -> (Id, Entry<[u16; 1]>) {
171            let ip = IpAddr::V4(Ipv4Addr::new(n, 0, 0, 1));
172            let port = 8000 + n as u16;
173            (n as u64, Entry { ip, msg: [port] })
174        }
175
176        let chart = Chart::test(test_kv).await;
177        let iter: HashSet<_> = chart.addr_vec().into_iter().collect();
178        let correct: HashSet<_> = (1..10)
179            .map(test_kv)
180            .map(|(id, e)| (id, SocketAddr::new(e.ip, e.msg[0])))
181            .collect();
182
183        assert_eq!(iter, correct)
184    }
185
186    fn entry_3ports(n: u8) -> (Id, Entry<[u16; 3]>) {
187        let ip = IpAddr::V4(Ipv4Addr::new(n, 0, 0, 1));
188        let port1 = 8000 + n as u16;
189        let port2 = 7000 + n as u16;
190        let port3 = 6000 + n as u16;
191        (
192            n as u64,
193            Entry {
194                ip,
195                msg: [port1, port2, port3],
196            },
197        )
198    }
199
200    #[tokio::test]
201    async fn iter_addr_lists() {
202        let chart = Chart::test(entry_3ports).await;
203        let iter: HashSet<_> = chart.addr_lists_vec().into_iter().collect();
204        let correct: HashSet<_> = (1..10)
205            .map(entry_3ports)
206            .map(|(id, e)| {
207                let addr = e.msg.map(|p| (e.ip, p)).map(SocketAddr::from);
208                (id, addr)
209            })
210            .collect();
211        assert_eq!(iter, correct)
212    }
213    #[tokio::test]
214    async fn iter_nth_port() {
215        let chart = Chart::test(entry_3ports).await;
216        let iter: HashSet<_> = chart.nth_addr_vec::<1>().into_iter().collect();
217        let correct: HashSet<_> = (1..10)
218            .map(entry_3ports)
219            .map(|(id, e)| (id, SocketAddr::new(e.ip, e.msg[1])))
220            .collect();
221        assert_eq!(iter, correct)
222    }
223}