1use crate::error::MctxError;
2use std::net::{Ipv4Addr, SocketAddrV4};
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct PublicationConfig {
7 pub group: Ipv4Addr,
9 pub dst_port: u16,
11 pub interface: Option<Ipv4Addr>,
13 pub source_port: Option<u16>,
15 pub ttl: u32,
17 pub loopback: bool,
19}
20
21impl PublicationConfig {
22 pub fn new(group: Ipv4Addr, port: u16) -> Self {
24 Self {
25 group,
26 dst_port: port,
27 interface: None,
28 source_port: None,
29 ttl: 1,
30 loopback: true,
31 }
32 }
33
34 pub fn validate(&self) -> Result<(), MctxError> {
36 if self.dst_port == 0 {
37 return Err(MctxError::InvalidDestinationPort);
38 }
39
40 if !self.group.is_multicast() {
41 return Err(MctxError::InvalidMulticastGroup);
42 }
43
44 if matches!(self.source_port, Some(0)) {
45 return Err(MctxError::InvalidSourcePort);
46 }
47
48 if let Some(interface) = self.interface
49 && interface.is_multicast()
50 {
51 return Err(MctxError::InvalidInterfaceAddress);
52 }
53
54 Ok(())
55 }
56
57 pub fn with_interface(mut self, interface: Ipv4Addr) -> Self {
59 self.interface = Some(interface);
60 self
61 }
62
63 pub fn with_source_port(mut self, source_port: u16) -> Self {
65 self.source_port = Some(source_port);
66 self
67 }
68
69 pub fn with_ttl(mut self, ttl: u32) -> Self {
71 self.ttl = ttl;
72 self
73 }
74
75 pub fn with_loopback(mut self, loopback: bool) -> Self {
77 self.loopback = loopback;
78 self
79 }
80
81 pub fn destination(&self) -> SocketAddrV4 {
83 SocketAddrV4::new(self.group, self.dst_port)
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn valid_multicast_config_passes_validation() {
93 let cfg = PublicationConfig::new(Ipv4Addr::new(239, 1, 2, 3), 5000)
94 .with_source_port(5001)
95 .with_ttl(8)
96 .with_loopback(false);
97
98 assert!(cfg.validate().is_ok());
99 }
100
101 #[test]
102 fn port_zero_fails_validation() {
103 let cfg = PublicationConfig::new(Ipv4Addr::new(239, 1, 2, 3), 0);
104
105 let result = cfg.validate();
106
107 assert!(matches!(result, Err(MctxError::InvalidDestinationPort)));
108 }
109
110 #[test]
111 fn non_multicast_group_fails_validation() {
112 let cfg = PublicationConfig::new(Ipv4Addr::new(192, 168, 1, 10), 5000);
113
114 let result = cfg.validate();
115
116 assert!(matches!(result, Err(MctxError::InvalidMulticastGroup)));
117 }
118
119 #[test]
120 fn multicast_interface_fails_validation() {
121 let cfg = PublicationConfig::new(Ipv4Addr::new(239, 1, 2, 3), 5000)
122 .with_interface(Ipv4Addr::new(239, 9, 9, 9));
123
124 let result = cfg.validate();
125
126 assert!(matches!(result, Err(MctxError::InvalidInterfaceAddress)));
127 }
128}