1#![deny(missing_docs)]
2#![deny(missing_debug_implementations)]
3#![cfg_attr(test, deny(warnings))]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6use std::{env, fmt};
25use std::collections::HashMap;
26use std::fmt::Formatter;
27
28use serde::Deserialize;
29
30pub const VCAP_SERVICES: &str = "VCAP_SERVICES";
32
33#[derive(Deserialize, Debug)]
36pub struct Service {
37 #[serde(default)]
39 pub name: String,
40 #[serde(default)]
42 pub instance_name: String,
43 #[serde(default)]
45 pub binding_name: String,
46 pub credentials: Credentials,
48 #[serde(default)]
50 pub label: String,
51}
52
53#[derive(Deserialize, Debug, Clone)]
55pub struct Credentials {
56 #[serde(default)]
58 pub uri: String,
59 #[serde(rename(deserialize = "jdbcUrl"))]
61 #[serde(default)]
62 pub jdbc_url: String,
63 #[serde(rename(deserialize = "http_api_uri"))]
65 #[serde(default)]
66 pub api_uri: String,
67 #[serde(rename(deserialize = "licenseKey"))]
69 #[serde(default)]
70 pub license_key: String,
71 #[serde(default)]
73 pub client_secret: String,
74 #[serde(default)]
75 pub client_id: String,
77 #[serde(default)]
78 pub access_token_uri: String,
80 #[serde(default)]
82 pub hostname: String,
83 #[serde(default)]
85 pub username: String,
86 #[serde(default)]
88 pub password: String,
89 #[serde(default)]
91 pub port: i16,
92 #[serde(default)]
94 pub name: String,
95}
96
97pub fn get_service_cred_from_env(service_name: String) -> Result<Vec<Credentials>, CFError> {
99 get_services_from_env()
100 .and_then(|services| get_service_credentials(services, service_name))
101}
102
103pub fn get_services_from_env() -> Result<HashMap<String, Vec<Service>>, CFError> {
105 env::var(VCAP_SERVICES)
106 .map_err(|_| CFError::EnvNotSet)
107 .and_then(|val| serde_json::from_str(&val).map_err(|_| CFError::MalformedJSON))
108}
109
110pub fn get_service_credentials(services: HashMap<String, Vec<Service>>, service_name: String) -> Result<Vec<Credentials>, CFError> {
113 match services.get(&service_name) {
114 Some(services) => Ok(services.iter().map(|service| service.credentials.clone()).collect()),
115 None => Err(CFError::ServiceNotPresent(service_name))
116 }
117}
118
119#[derive(PartialEq, Debug)]
121pub enum CFError {
122 EnvNotSet,
124 MalformedJSON,
126 ServiceNotPresent(String),
128}
129
130impl fmt::Display for CFError {
131 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
132 match *self {
133 CFError::EnvNotSet => write!(f, "environment variable {:?} is not set", VCAP_SERVICES),
134 CFError::MalformedJSON => write!(f, "environment variable {:?} is malformed", VCAP_SERVICES),
135 CFError::ServiceNotPresent(ref s) => write!(f, "service {:?} is not bounded to the application", s)
136 }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use std::collections::HashMap;
143 use std::env;
144
145 use crate::{CFError, Credentials, get_service_cred_from_env, get_service_credentials, get_services_from_env, Service, VCAP_SERVICES};
146
147 #[test]
148 fn test_get_service_cred_from_env() {
149 let json = r#"{
150 "serviceA": [
151 {
152 "name":"service_a",
153 "credentials": {
154 "uri": "example_uri",
155 "port": 8080
156 }
157 }
158 ]
159 }"#;
160 env::set_var(VCAP_SERVICES, json);
161 let creds = get_service_cred_from_env("serviceA".to_string()).unwrap();
162 assert_eq!(1, creds.len());
163 let cred = creds.get(0).unwrap();
164 assert_eq!("example_uri", cred.uri);
165 assert_eq!(8080, cred.port);
166 env::remove_var(VCAP_SERVICES);
167 }
168
169 #[test]
170 fn test_get_services_from_env() {
171 let json = r#"{
172 "serviceA": [
173 {
174 "name":"service_a",
175 "credentials": {
176 "uri": "example_uri",
177 "port": 8080
178 }
179 }
180 ]
181 }"#;
182 env::set_var(VCAP_SERVICES, json);
183 let services = get_services_from_env().unwrap();
184 let service_a = services.get("serviceA").unwrap();
185 assert_eq!(1, service_a.len());
186 let service_details = service_a.get(0).unwrap();
187 assert_eq!("service_a", service_details.name);
188 assert_eq!("example_uri", service_details.credentials.uri);
189 assert_eq!(8080, service_details.credentials.port);
190 env::remove_var(VCAP_SERVICES);
191 }
192
193 #[test]
194 fn test_get_services_from_env_not_set() {
195 env::remove_var(VCAP_SERVICES);
196 let err = get_services_from_env().err().unwrap();
197 assert_eq!(CFError::EnvNotSet, err);
198 }
199
200 #[test]
201 fn test_get_services_from_env_malformed_json() {
202 let json = r#"{
203 "serviceA": [
204 {
205 "name":"service_a",
206 "credentials": {
207 "uri": "example_uri"
208 }
209 }
210 ]"#;
211 env::set_var(VCAP_SERVICES, json);
212 let err = get_services_from_env().err().unwrap();
213 assert_eq!(CFError::MalformedJSON, err);
214 env::remove_var(VCAP_SERVICES);
215 }
216
217 #[test]
218 fn test_get_service_credentials() {
219 let mut services = HashMap::new();
220 let mut service_a: Vec<Service> = Vec::new();
221 let service = Service {
222 name: "service_a".to_string(),
223 instance_name: "".to_string(),
224 binding_name: "".to_string(),
225 credentials: Credentials {
226 uri: "example_uri".to_string(),
227 jdbc_url: "".to_string(),
228 api_uri: "".to_string(),
229 license_key: "".to_string(),
230 client_secret: "".to_string(),
231 client_id: "".to_string(),
232 access_token_uri: "".to_string(),
233 hostname: "".to_string(),
234 username: "".to_string(),
235 password: "".to_string(),
236 port: 0,
237 name: "".to_string(),
238 },
239 label: "".to_string(),
240 };
241 service_a.push(service);
242 services.insert("serviceA".to_string(), service_a);
243 let creds = get_service_credentials(services, "serviceA".to_string()).unwrap();
244 assert_eq!(1, creds.len());
245 let cred = creds.get(0).unwrap();
246 assert_eq!("example_uri", cred.uri);
247 }
248
249 #[test]
250 fn test_get_service_credentials_not_found() {
251 let mut services = HashMap::new();
252 let service_a: Vec<Service> = Vec::new();
253 services.insert("serviceA".to_string(), service_a);
254 let err = get_service_credentials(services, "serviceB".to_string()).err().unwrap();
255 assert_eq!(CFError::ServiceNotPresent("serviceB".to_string()), err)
256 }
257}