evergreen/osrf/
addr.rs

1use crate::util;
2use gethostname::gethostname;
3use std::fmt;
4use std::process;
5
6const BUS_ADDR_NAMESPACE: &str = "opensrf";
7
8#[derive(Debug, Clone, PartialEq)]
9enum AddressPurpose {
10    Router,
11    Service,
12    Client,
13}
14
15/// Models a bus-level address providing access to indivual components
16/// of each address.
17///
18/// Examples:
19///
20/// ```text
21/// opensrf:router:$username:$domain
22/// opensrf:service:$username:$domain:$service
23/// opensrf:client:$username:$domain:$hostname:$pid:$random
24/// ```
25#[derive(Debug, Clone)]
26pub struct BusAddress {
27    /// Full address string, recompiled as needed.
28    full: String,
29
30    purpose: AddressPurpose,
31    domain: String,
32    username: String,
33
34    /// Only some addresses have content after the $domain.
35    remainder: Option<String>,
36}
37
38impl fmt::Display for BusAddress {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        write!(f, "Address={}", &self.full)
41    }
42}
43
44impl BusAddress {
45    /// Creates a new BusAddress from a bus address string.
46    ///
47    /// ```
48    /// use evergreen::osrf::addr::BusAddress;
49    ///
50    /// let addr =
51    ///   BusAddress::parse_str("opensrf:client:foobar:localhost:12345")
52    ///   .expect("Error creating address from string");
53    ///
54    /// assert!(addr.is_client());
55    /// assert_eq!(addr.username(), "foobar");
56    /// assert_eq!(addr.domain(), "localhost");
57    /// ```
58    pub fn parse_str(full: &str) -> Result<Self, String> {
59        let parts: Vec<&str> = full.split(':').collect();
60
61        // Every address has 4 well-known parts, so we need that many at minimum.
62        if parts.len() < 4 {
63            return Err(format!("BusAddress bad format: {}", full));
64        }
65
66        let purpose = match parts[1] {
67            "router" => AddressPurpose::Router,
68            "service" => AddressPurpose::Service,
69            "client" => AddressPurpose::Client,
70            _ => return Err(format!("Invalid address purpose: {}", parts[1])),
71        };
72
73        let username = parts[2].to_string();
74        let domain = parts[3].to_string();
75        let remainder = match parts.len() > 4 {
76            true => Some(parts[4..].join(":")),
77            _ => None,
78        };
79
80        Ok(BusAddress {
81            full: full.to_string(),
82            purpose,
83            username,
84            domain,
85            remainder,
86        })
87    }
88
89    /// Router address
90    ///
91    /// Send messages here to talk to a Router.
92    ///
93    /// ```
94    /// let addr = evergreen::osrf::addr::BusAddress::for_router("router", "private.localhost");
95    ///
96    /// assert!(addr.is_router());
97    /// assert_eq!(addr.as_str(), "opensrf:router:router:private.localhost");
98    /// ```
99    pub fn for_router(username: &str, domain: &str) -> Self {
100        let full = format!("{}:router:{}:{}", BUS_ADDR_NAMESPACE, username, domain);
101
102        BusAddress {
103            full,
104            purpose: AddressPurpose::Router,
105            domain: domain.to_string(),
106            username: username.to_string(),
107            remainder: None,
108        }
109    }
110
111    /// Service address unqualified by username or domain.
112    ///
113    /// The router will fill in the gaps for username/domain.
114    ///
115    /// ```
116    /// let addr = evergreen::osrf::addr::BusAddress::for_bare_service("opensrf.settings");
117    ///
118    /// assert!(addr.is_service());
119    /// assert_eq!(addr.service(), Some("opensrf.settings"));
120    /// assert_eq!(addr.as_str(), "opensrf:service:_:_:opensrf.settings");
121    /// ```
122    pub fn for_bare_service(service: &str) -> Self {
123        BusAddress::for_service("_", "_", service)
124    }
125
126    pub fn for_service(username: &str, domain: &str, service: &str) -> Self {
127        let full = format!(
128            "{}:service:{}:{}:{}",
129            BUS_ADDR_NAMESPACE, username, domain, service
130        );
131
132        BusAddress {
133            full,
134            purpose: AddressPurpose::Service,
135            domain: domain.to_string(),
136            username: username.to_string(),
137            remainder: Some(service.to_string()),
138        }
139    }
140
141    /// Create a new client address.
142    ///
143    /// ```
144    /// let username = "opensrf";
145    /// let domain = "private.localhost";
146    /// let addr = evergreen::osrf::addr::BusAddress::for_client(username, domain);
147    /// assert_eq!(addr.domain(), domain);
148    /// assert!(addr.is_client());
149    /// ```
150    pub fn for_client(username: &str, domain: &str) -> Self {
151        let remainder = format!(
152            "{}:{}:{}",
153            &gethostname().into_string().unwrap(),
154            process::id(),
155            &util::random_number(6)
156        );
157
158        let full = format!(
159            "{}:client:{}:{}:{}",
160            BUS_ADDR_NAMESPACE, username, domain, remainder
161        );
162
163        BusAddress {
164            full,
165            purpose: AddressPurpose::Client,
166            domain: domain.to_string(),
167            username: username.to_string(),
168            remainder: Some(remainder),
169        }
170    }
171
172    /// Full address string
173    pub fn as_str(&self) -> &str {
174        &self.full
175    }
176    pub fn domain(&self) -> &str {
177        &self.domain
178    }
179    pub fn username(&self) -> &str {
180        &self.username
181    }
182    /// All the stuff after opensrf:$purpose:$username:$domain
183    pub fn remainder(&self) -> Option<&str> {
184        self.remainder.as_deref()
185    }
186
187    fn compile(&mut self) {
188        let purpose = if self.is_service() {
189            "service"
190        } else if self.is_router() {
191            "router"
192        } else {
193            "client"
194        };
195
196        self.full = format!(
197            "{}:{}:{}:{}",
198            BUS_ADDR_NAMESPACE,
199            purpose,
200            self.username(),
201            self.domain()
202        );
203
204        if let Some(r) = self.remainder.as_ref() {
205            self.full += ":";
206            self.full += r;
207        }
208    }
209
210    pub fn set_domain(&mut self, s: &str) {
211        self.domain = s.to_string();
212        self.compile();
213    }
214    pub fn set_username(&mut self, s: &str) {
215        self.username = s.to_string();
216        self.compile();
217    }
218
219    /// Allow the caller to provide the address content after the domain.
220    ///
221    /// ```
222    /// let username = "opensrf";
223    /// let domain = "private.localhost";
224    /// let mut addr = evergreen::osrf::addr::BusAddress::for_client(username, domain);
225    /// assert_eq!(addr.domain(), domain);
226    ///
227    /// let remainder = "HELLO123";
228    /// addr.set_remainder(remainder);
229    /// assert!(addr.is_client());
230    /// assert!(addr.as_str().ends_with(remainder));
231    /// ```
232    pub fn set_remainder(&mut self, s: &str) {
233        self.remainder = Some(s.to_string());
234        self.compile();
235    }
236
237    pub fn service(&self) -> Option<&str> {
238        if self.is_service() {
239            self.remainder.as_deref()
240        } else {
241            None
242        }
243    }
244    pub fn is_client(&self) -> bool {
245        self.purpose == AddressPurpose::Client
246    }
247    pub fn is_service(&self) -> bool {
248        self.purpose == AddressPurpose::Service
249    }
250    pub fn is_router(&self) -> bool {
251        self.purpose == AddressPurpose::Router
252    }
253}