ya_sb_proto/
lib.rs

1pub use gsb_api::*;
2use std::convert::TryFrom;
3use std::fmt::{Debug, Display, Formatter};
4use std::path::PathBuf;
5use url::{ParseError, Url};
6
7mod gsb_api {
8    include!(concat!(env!("OUT_DIR"), "/gsb_api.rs"));
9}
10
11#[cfg(feature = "with-codec")]
12pub mod codec;
13
14#[derive(thiserror::Error, Debug)]
15#[error("invalid value: {0}")]
16pub struct EnumError(pub i32);
17
18impl TryFrom<i32> for CallReplyCode {
19    type Error = EnumError;
20
21    fn try_from(value: i32) -> Result<Self, Self::Error> {
22        Ok(match value {
23            0 => CallReplyCode::CallReplyOk,
24            400 => CallReplyCode::CallReplyBadRequest,
25            500 => CallReplyCode::ServiceFailure,
26            _ => return Err(EnumError(value)),
27        })
28    }
29}
30
31impl TryFrom<i32> for CallReplyType {
32    type Error = EnumError;
33
34    fn try_from(value: i32) -> Result<Self, Self::Error> {
35        Ok(match value {
36            0 => CallReplyType::Full,
37            1 => CallReplyType::Partial,
38            _ => return Err(EnumError(value)),
39        })
40    }
41}
42
43pub const GSB_URL_ENV_VAR: &str = "GSB_URL";
44#[cfg(unix)]
45pub const DEFAULT_GSB_URL: &str = "unix:/tmp/yagna.sock";
46#[cfg(not(unix))]
47pub const DEFAULT_GSB_URL: &str = "tcp://127.0.0.1:7464";
48pub const DEFAULT_GSB_PORT: u16 = 7464;
49
50#[derive(Clone, Debug)]
51pub enum GsbAddr {
52    Tcp(String),
53    Unix(PathBuf),
54}
55
56impl GsbAddr {
57    pub fn from_url(gsb_url: Option<Url>) -> Self {
58        let gsb_url = gsb_url.unwrap_or_else(|| {
59            let default_url =
60                std::env::var(GSB_URL_ENV_VAR).unwrap_or_else(|_| DEFAULT_GSB_URL.into());
61            match Url::parse(&default_url) {
62                Err(ParseError::RelativeUrlWithoutBase) => {
63                    Url::parse(&format!("tcp://{}", default_url))
64                }
65                x => x,
66            }
67            .expect("provide GSB URL in format tcp://<ip:port> or unix:<path>")
68        });
69
70        match gsb_url.scheme() {
71            "tcp" => Self::Tcp(parse_tcp_url(gsb_url)),
72            "unix" => Self::Unix(parse_unix_url(gsb_url)),
73            _ => panic!("unimplemented protocol for GSB URL: {}", gsb_url.scheme()),
74        }
75    }
76}
77
78impl Default for GsbAddr {
79    fn default() -> Self {
80        GsbAddr::from_url(None)
81    }
82}
83
84impl Display for GsbAddr {
85    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
86        match self {
87            GsbAddr::Tcp(addr) => std::fmt::Display::fmt(addr, f),
88            GsbAddr::Unix(path) => std::fmt::Display::fmt(&path.to_string_lossy(), f),
89        }
90    }
91}
92
93fn parse_tcp_url(url: Url) -> String {
94    let host = url.host_str().expect("need host for GSB URL");
95    let port = url.port().unwrap_or(DEFAULT_GSB_PORT);
96
97    format!("{}:{}", host, port)
98}
99
100#[cfg(unix)]
101fn parse_unix_url(url: Url) -> PathBuf {
102    url.to_file_path().expect("invalid socket path in GSB URL")
103}
104
105#[cfg(not(unix))]
106fn parse_unix_url(_url: Url) -> PathBuf {
107    panic!("Unix sockets not supported on this OS")
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[cfg(unix)]
115    #[test]
116    #[serial_test::serial]
117    pub fn check_default_gsb_url() {
118        std::env::remove_var(GSB_URL_ENV_VAR);
119        let addr = GsbAddr::from_url(None);
120        let path = match addr {
121            GsbAddr::Unix(path) => path,
122            _ => panic!("Not a UNIX addr"),
123        };
124        assert_eq!(path, PathBuf::from("/tmp/yagna.sock"))
125    }
126
127    #[cfg(not(unix))]
128    #[test]
129    #[serial_test::serial]
130    pub fn check_default_gsb_url() {
131        std::env::remove_var(GSB_URL_ENV_VAR);
132        let addr = GsbAddr::from_url(None);
133        let addr = match addr {
134            GsbAddr::Tcp(addr) => addr,
135            _ => panic!("Not a TCP addr"),
136        };
137        assert_eq!(addr, "127.0.0.1:7464")
138    }
139
140    #[test]
141    #[serial_test::serial]
142    pub fn check_env_var() {
143        std::env::set_var(GSB_URL_ENV_VAR, "tcp://10.9.8.7:2345");
144        let addr = GsbAddr::from_url(None);
145        let addr = match addr {
146            GsbAddr::Tcp(addr) => addr,
147            _ => panic!("Not a TCP addr"),
148        };
149        assert_eq!(addr, "10.9.8.7:2345");
150    }
151
152    #[test]
153    #[serial_test::serial]
154    pub fn check_no_tcp_protocol_gsb_url() {
155        std::env::set_var(GSB_URL_ENV_VAR, "10.9.8.7:1234");
156        let addr = GsbAddr::from_url(None);
157        let addr = match addr {
158            GsbAddr::Tcp(addr) => addr,
159            _ => panic!("Not a TCP addr"),
160        };
161        assert_eq!(addr, "10.9.8.7:1234");
162    }
163
164    #[test]
165    #[serial_test::serial]
166    #[should_panic(expected = "unimplemented protocol for GSB URL: http")]
167    pub fn panic_env_var_http() {
168        std::env::set_var(GSB_URL_ENV_VAR, "http://10.9.8.7:2345");
169        GsbAddr::from_url(None);
170    }
171
172    #[test]
173    pub fn check_ip_port_gsb_url() {
174        let addr = GsbAddr::from_url(Some("tcp://10.9.8.7:2345".parse().unwrap()));
175        let addr = match addr {
176            GsbAddr::Tcp(addr) => addr,
177            _ => panic!("Not a TCP addr"),
178        };
179        assert_eq!(addr, "10.9.8.7:2345");
180    }
181
182    #[cfg(unix)]
183    #[test]
184    pub fn check_unix_gsb_url() {
185        let addr = GsbAddr::from_url(Some("unix:/tmp/śmigły żółw/socket".parse().unwrap()));
186        let path = match addr {
187            GsbAddr::Unix(path) => path,
188            _ => panic!("Not a UNIX addr"),
189        };
190        assert_eq!(path, PathBuf::from("/tmp/śmigły żółw/socket"))
191    }
192
193    #[cfg(not(unix))]
194    #[test]
195    #[should_panic(expected = "Unix sockets not supported on this OS")]
196    pub fn check_unix_gsb_url() {
197        GsbAddr::from_url(Some("unix:/tmp/socket".parse().unwrap()));
198    }
199
200    #[test]
201    #[should_panic(expected = "unimplemented protocol for GSB URL: http")]
202    pub fn panic_http_gsb_url() {
203        GsbAddr::from_url(Some("http://10.9.8.7".parse().unwrap()));
204    }
205
206    #[test]
207    #[should_panic(expected = "need host for GSB URL")]
208    pub fn panic_no_host_gsb_url() {
209        GsbAddr::from_url(Some("tcp:".parse().unwrap()));
210    }
211}