haprox_rs/protocol_v1/
protocol_composer.rs

1/*-
2 * haprox-rs - a HaProxy protocol parser.
3 * 
4 * Copyright 2025 (c) Aleksandr Morozov
5 * The scram-rs crate can be redistributed and/or modified
6 * under the terms of either of the following licenses:
7 *
8 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
9 *
10 *   2. The MIT License (MIT)
11 *                     
12 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
13 */
14
15use std::{borrow::Cow, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}};
16
17use crate::{HaProxRes, HapProtoV1, map_error};
18
19/// A trait which provides a conversion of strings into
20/// [SocketAddr].
21pub trait TryIntoSockAddr
22{
23    fn try_into_sock(self) -> HaProxRes<SocketAddr>;
24}
25
26impl TryIntoSockAddr for String
27{
28    #[inline]
29    fn try_into_sock(self) -> HaProxRes<SocketAddr> 
30    {
31        return self.as_str().try_into_sock();
32    }
33}
34
35impl TryIntoSockAddr for &String
36{
37    fn try_into_sock(self) -> HaProxRes<SocketAddr> 
38    {
39        return self.as_str().try_into_sock();
40    }
41}
42
43impl TryIntoSockAddr for &str
44{
45    fn try_into_sock(self) -> HaProxRes<SocketAddr> 
46    {
47        return 
48            self
49                .parse()
50                .map_err(|e|
51                    map_error!(ArgumentEinval, "cannot parse string: '{}' to SocketAddr, err: '{}", self, e)
52                );
53    }
54}
55
56impl<'cow> TryIntoSockAddr for Cow<'cow, str>
57{
58    fn try_into_sock(self) -> HaProxRes<SocketAddr> 
59    {
60        return self.as_ref().try_into_sock();
61    }
62}
63
64/// A HaProxyV1 composer.
65/// 
66/// Provides an interface to [HapProtoV1].
67/// 
68/// A function [Self::from_str] provides an option to avoid unnecesary conversions
69/// of `string` literals and variables to [SocketAddr]. A [TryIntoSockAddr] is 
70/// implemented to all string types.
71/// 
72/// ## Example
73/// 
74/// ```ignore
75/// let res = 
76///     ProxyHdrV1::from_str(ProtocolV1Inet::Tcp4, "192.168.1.1:333", "127.0.0.1:444")
77///         .unwrap();
78/// 
79/// let msg = res.to_string();
80/// ```
81#[derive(Clone, Copy, Debug)]
82pub struct ProxyHdrV1;
83
84impl ProxyHdrV1
85{
86    /// Creates new message.
87    /// 
88    /// The `src` and `dst` must be of the same IP version type.
89    /// 
90    /// # Returns
91    /// 
92    /// The following errors are returned:
93    /// 
94    /// * [crate::error::HaProxErrType::ArgumentEinval] - if `src` or `dst` are not 
95    ///     of same type.
96    #[inline]
97    pub 
98    fn new(src: SocketAddr, dst: SocketAddr) -> HaProxRes<HapProtoV1>
99    {
100        return Self::new_ipaddr(src.ip(), src.port(), dst.ip(), dst.port());
101    }
102
103    /// Creates new message.
104    /// 
105    /// The `src` and `dst` must be of the same IP version type.
106    /// 
107    /// The function takes the argument of the following formats:
108    /// 
109    /// * ipv4:port i.e "127.0.0.1:4444"
110    /// 
111    /// * [ipv6]:port i,e "[0acf:5d35:b4c4:731c:2442:2f17:c6f9:5b7f]:333"
112    /// 
113    /// # Returns
114    /// 
115    /// The following errors are returned:
116    /// 
117    /// * [crate::error::HaProxErrType::ArgumentEinval] - if `src` or `dst` are not 
118    ///     of same type / parsing error.
119    pub 
120    fn from_str<S: TryIntoSockAddr>(src: S, dst: S) -> HaProxRes<HapProtoV1>
121    {
122        let src_addr = src.try_into_sock()?;
123        let dst_addr = dst.try_into_sock()?;
124
125        return HapProtoV1::from_ip_port(src_addr.ip(), dst_addr.ip(), src_addr.port(), dst_addr.port());
126    }
127
128    /// Creates new message.
129    /// 
130    /// The `src` and `dst` must be of the same IP version type.
131    /// 
132    /// # Returns
133    /// 
134    /// The following errors are returned:
135    /// 
136    /// * [crate::error::HaProxErrType::ArgumentEinval] - if `src` or `dst` are not 
137    ///     of same type.
138    #[inline]
139    pub 
140    fn new_ipaddr(src_addr: IpAddr, src_port: u16, dst_addr: IpAddr, dst_port: u16) -> HaProxRes<HapProtoV1>
141    {
142        return HapProtoV1::from_ip_port( src_addr, dst_addr, src_port, dst_port);
143    }
144
145    /// Creates new message for `inet` TCP4.
146    /// 
147    /// # Returns
148    /// 
149    /// Should never return error.
150    #[inline]
151    pub 
152    fn new_ip4(src_addr: Ipv4Addr, src_port: u16, dst_addr: Ipv4Addr, dst_port: u16) -> HaProxRes<HapProtoV1>
153    {
154        return 
155            HapProtoV1::from_ip_port(IpAddr::V4(src_addr), IpAddr::V4(dst_addr), src_port, dst_port);
156    }
157
158    /// Creates new message for `inet` TCP6.
159    /// 
160    /// # Returns
161    /// 
162    /// Should never return error.
163    #[inline]
164    pub 
165    fn new_ip6(src_addr: Ipv6Addr, src_port: u16, dst_addr: Ipv6Addr, dst_port: u16) -> HaProxRes<HapProtoV1>
166    {
167        return 
168            HapProtoV1::from_ip_port(IpAddr::V6(src_addr), IpAddr::V6(dst_addr), src_port, dst_port);
169    }
170
171    /// Creates new message for `inet` UNKNOWN.
172    #[inline]
173    pub 
174    fn new_unknown() -> HapProtoV1
175    {
176        return HapProtoV1::unknown();
177    }
178}
179
180#[cfg(test)]
181mod tests_composer
182{
183    use crate::{protocol_v1::protocol_composer::ProxyHdrV1};
184
185    #[test]
186    fn test_from_string()
187    {
188        let res = 
189            ProxyHdrV1::from_str("192.168.1.1:333", "127.0.0.1:444")
190                .unwrap();
191
192        assert_eq!(res.to_string(), "PROXY TCP4 192.168.1.1 127.0.0.1 333 444\r\n");
193    }
194
195    #[test]
196    fn test0()
197    {
198        let res = 
199            ProxyHdrV1
200                ::new("192.168.1.1:333".parse().unwrap(), "127.0.0.1:444".parse().unwrap())
201                    .unwrap();
202
203        assert_eq!(res.to_string(), "PROXY TCP4 192.168.1.1 127.0.0.1 333 444\r\n");
204
205        let res = 
206            ProxyHdrV1
207                ::new("[0acf:5d35:b4c4:731c:2442:2f17:c6f9:5b7f]:333".parse().unwrap(), "[4d7f:8980:38d6:e0c3:7301:70e9:f8ef:e393]:444".parse().unwrap())
208                    .unwrap();
209
210        assert_eq!(res.to_string(), "PROXY TCP6 0acf:5d35:b4c4:731c:2442:2f17:c6f9:5b7f 4d7f:8980:38d6:e0c3:7301:70e9:f8ef:e393 333 444\r\n");
211    }
212
213    #[should_panic]
214    #[test]
215    fn test1_fail()
216    {
217        let _res = 
218            ProxyHdrV1
219                ::new("[0acf:5d35:b4c4:731c:2442:2f17:c6f9:5b7f]:333".parse().unwrap(), "127.0.0.1:444".parse().unwrap())
220                    .unwrap();
221    }
222
223    #[should_panic]
224    #[test]
225    fn test2_fail()
226    {
227        let _res = 
228            ProxyHdrV1
229                ::new("192.168.1.1:333".parse().unwrap(), "[0acf:5d35:b4c4:731c:2442:2f17:c6f9:5b7f]:333".parse().unwrap())
230                    .unwrap();
231    }
232
233}