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}