1use error::{Error, Result};
11use std::convert::TryFrom;
12use std::fmt;
13use std::io::{Read, Write};
14use std::path::PathBuf;
15use std::time::Duration;
16use toml;
17
18#[derive(Clone, Debug, PartialEq)]
20pub enum RuntimeEnvironment {
21 Prod,
23 Stage,
25 Test,
27 Dev,
29}
30
31impl fmt::Display for RuntimeEnvironment {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 write!(
34 f,
35 "{}",
36 match *self {
37 RuntimeEnvironment::Prod => "-prod",
38 RuntimeEnvironment::Stage => "-stage",
39 RuntimeEnvironment::Test => "-test",
40 RuntimeEnvironment::Dev => "-dev",
41 }
42 )
43 }
44}
45
46impl<'a> TryFrom<&'a str> for RuntimeEnvironment {
47 type Error = Error;
48
49 fn try_from(env: &str) -> Result<Self> {
50 match env {
51 "prod" => Ok(RuntimeEnvironment::Prod),
52 "stage" => Ok(RuntimeEnvironment::Stage),
53 "test" => Ok(RuntimeEnvironment::Test),
54 "dev" => Ok(RuntimeEnvironment::Dev),
55 _ => Err("invalid runtime enviroment".into()),
56 }
57 }
58}
59
60#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
62pub enum EchoIndex {
63 MslCheckout,
65 ProxyApi,
67 ProxyEcsb,
69 ProxyBanner,
71 ProxyDC2,
73}
74
75impl Default for EchoIndex {
76 fn default() -> Self {
77 EchoIndex::MslCheckout
78 }
79}
80
81impl fmt::Display for EchoIndex {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 write!(
84 f,
85 "{}",
86 match *self {
87 EchoIndex::MslCheckout => "mobile-service-layer-checkout",
88 EchoIndex::ProxyApi => "proxy-api",
89 EchoIndex::ProxyEcsb => "proxy-ecsb",
90 EchoIndex::ProxyBanner => "proxy-banner",
91 EchoIndex::ProxyDC2 => "proxy-dc2",
92 }
93 )
94 }
95}
96
97#[derive(Clone, Debug, Default, Deserialize, Getters, PartialEq, Serialize, Setters)]
99pub struct Config {
100 #[get = "pub"]
102 #[set = "pub"]
103 common: Common,
104 #[get = "pub"]
106 #[set = "pub"]
107 index: Vec<Index>,
108 #[get = "pub"]
110 #[set = "pub"]
111 prod: Environment,
112 #[get = "pub"]
114 #[set = "pub"]
115 stage: Environment,
116 #[get = "pub"]
118 #[set = "pub"]
119 test: Environment,
120 #[get = "pub"]
122 #[set = "pub"]
123 dev: Environment,
124}
125
126#[derive(Clone, Debug, Default, Deserialize, Getters, PartialEq, Serialize, Setters)]
128pub struct Common {
129 #[get = "pub"]
131 #[set = "pub"]
132 base_db_path: PathBuf,
133 #[set = "pub"]
135 search_prefix: Option<String>,
136 #[set = "pub"]
138 search_affix: Option<String>,
139 #[set = "pub"]
141 search_suffix: Option<String>,
142 #[set = "pub"]
144 db_prefix: Option<String>,
145 #[set = "pub"]
147 db_suffix: Option<String>,
148}
149
150impl Common {
151 pub fn search_prefix(&self) -> &str {
152 if let Some(ref prefix) = self.search_prefix {
153 prefix
154 } else {
155 "http://echo"
156 }
157 }
158
159 pub fn search_affix(&self) -> &str {
160 if let Some(ref affix) = self.search_affix {
161 affix
162 } else {
163 ".kroger.com/elastic"
164 }
165 }
166
167 pub fn search_suffix(&self) -> &str {
168 if let Some(ref suffix) = self.search_suffix {
169 suffix
170 } else {
171 "/_search"
172 }
173 }
174
175 pub fn scroll_affix(&self) -> &str {
176 "/scroll"
177 }
178
179 pub fn db_prefix(&self) -> &str {
180 if let Some(ref prefix) = self.search_prefix {
181 prefix
182 } else {
183 "ordmon"
184 }
185 }
186
187 pub fn db_suffix(&self) -> &str {
188 if let Some(ref suffix) = self.search_suffix {
189 suffix
190 } else {
191 ".db"
192 }
193 }
194}
195
196#[derive(Clone, Debug, Default, Deserialize, Getters, PartialEq, Serialize, Setters)]
198pub struct Index {
199 #[get = "pub"]
201 #[set = "pub"]
202 idx_prefix: EchoIndex,
203 #[get = "pub"]
205 #[set = "pub"]
206 fields: Vec<Field>,
207}
208
209#[derive(Clone, Debug, Default, Deserialize, Getters, PartialEq, Serialize, Setters)]
211pub struct Field {
212 #[get = "pub"]
214 #[set = "pub"]
215 name: String,
216 #[get = "pub"]
218 #[set = "pub"]
219 data_type: SqliteDataType,
220 #[get = "pub"]
222 #[set = "pub"]
223 primary_key: Option<bool>,
224 #[get = "pub"]
226 #[set = "pub"]
227 not_null: Option<bool>,
228 #[get = "pub"]
230 #[set = "pub"]
231 unique: Option<bool>,
232}
233
234#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
236pub enum SqliteDataType {
237 Integer,
239 Text,
241}
242
243impl Default for SqliteDataType {
244 fn default() -> Self {
245 SqliteDataType::Integer
246 }
247}
248
249impl fmt::Display for SqliteDataType {
250 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
251 write!(
252 f,
253 "{}",
254 match *self {
255 SqliteDataType::Integer => "INTEGER",
256 SqliteDataType::Text => "TEXT",
257 }
258 )
259 }
260}
261
262#[derive(Clone, Debug, Default, Deserialize, Getters, PartialEq, Serialize, Setters)]
264pub struct Environment {
265 #[get = "pub"]
267 #[set = "pub"]
268 echo_env_affix: Option<String>,
269 #[get = "pub"]
271 #[set = "pub"]
272 verbose: bool,
273 #[get = "pub"]
275 #[set = "pub"]
276 duration: Duration,
277}
278
279pub fn read_toml<R>(reader: &mut R) -> Result<Config>
281where
282 R: Read,
283{
284 let mut toml_str = String::new();
285 let bytes_read = reader.read_to_string(&mut toml_str)?;
286
287 if bytes_read > 0 {
288 Ok(toml::from_str(&toml_str)?)
289 } else {
290 Err("Unable to read any bytes from the reader".into())
291 }
292}
293
294pub fn write_toml<W>(config: &Config, writer: &mut W) -> Result<()>
296where
297 W: Write,
298{
299 let toml = toml::to_string(&config)?;
300 writer.write_all(toml.as_bytes())?;
301 Ok(())
302}
303
304pub fn echo_url(
305 config: &Config,
306 runtime_env: &RuntimeEnvironment,
307 echo_idx: &EchoIndex,
308 scroll: bool,
309) -> String {
310 let common = config.common();
311 let env = match *runtime_env {
312 RuntimeEnvironment::Prod => config.prod(),
313 RuntimeEnvironment::Stage => config.stage(),
314 RuntimeEnvironment::Test => config.test(),
315 RuntimeEnvironment::Dev => config.dev(),
316 };
317 let echo_affix = if let Some(ref affix) = env.echo_env_affix() {
318 affix.to_string()
319 } else {
320 runtime_env.to_string()
321 };
322
323 let index_str = echo_idx.to_string();
324
325 if scroll {
326 format!(
327 "{}{}{}/{}{}{}?scroll=1m",
328 common.search_prefix(),
329 echo_affix,
330 common.search_affix(),
331 index_str,
332 runtime_env,
333 common.search_suffix()
334 )
335 } else {
336 format!(
337 "{}{}{}/{}{}{}",
338 common.search_prefix(),
339 echo_affix,
340 common.search_affix(),
341 index_str,
342 runtime_env,
343 common.search_suffix()
344 )
345 }
346}
347
348pub fn scroll_url(config: &Config, runtime_env: &RuntimeEnvironment) -> String {
349 let common = config.common();
350 let env = match *runtime_env {
351 RuntimeEnvironment::Prod => config.prod(),
352 RuntimeEnvironment::Stage => config.stage(),
353 RuntimeEnvironment::Test => config.test(),
354 RuntimeEnvironment::Dev => config.dev(),
355 };
356 let echo_affix = if let Some(ref affix) = env.echo_env_affix() {
357 affix.to_string()
358 } else {
359 runtime_env.to_string()
360 };
361 format!(
362 "{}{}{}{}{}",
363 common.search_prefix(),
364 echo_affix,
365 common.search_affix(),
366 common.search_suffix(),
367 common.scroll_affix()
368 )
369}
370
371pub fn db_path(config: &Config, runtime_env: &RuntimeEnvironment) -> PathBuf {
372 let common = config.common();
373 let mut database_path = common.base_db_path().clone();
374 let filename = format!(
375 "{}{}{}",
376 common.db_prefix(),
377 runtime_env,
378 common.db_suffix()
379 );
380 database_path.push(filename);
381 database_path
382}
383
384#[cfg(test)]
385mod test {
386 use super::{
387 Common, Config, EchoIndex, Environment, Field, Index, RuntimeEnvironment, SqliteDataType,
388 };
389 use error::Result;
390 use std::convert::TryFrom;
391 use std::path::PathBuf;
392 use std::time::Duration;
393 use toml;
394
395 const TEST_TOML: &str = r#"[common]
396base_db_path = "/Users/kon8116/projects/kon8116/atters"
397
398[[index]]
399idx_prefix = "MslCheckout"
400
401[[index.fields]]
402name = "id"
403data_type = "Integer"
404primary_key = true
405
406[[index.fields]]
407name = "correlation_id"
408data_type = "Text"
409not_null = true
410
411[[index]]
412idx_prefix = "ProxyApi"
413
414[[index.fields]]
415name = "id"
416data_type = "Integer"
417primary_key = true
418
419[[index.fields]]
420name = "correlation_id"
421data_type = "Text"
422not_null = true
423
424[prod]
425echo_env_affix = "-digital"
426verbose = false
427
428[prod.duration]
429secs = 900
430nanos = 0
431
432[stage]
433verbose = false
434
435[stage.duration]
436secs = 900
437nanos = 0
438
439[test]
440verbose = true
441
442[test.duration]
443secs = 900
444nanos = 0
445
446[dev]
447verbose = true
448
449[dev.duration]
450secs = 900
451nanos = 0
452"#;
453
454 fn setup_config() -> Config {
455 let mut config: Config = Default::default();
456 let mut common: Common = Default::default();
457 let mut msl_checkout_index: Index = Default::default();
458 let mut proxy_api_index: Index = Default::default();
459 let mut prod: Environment = Default::default();
460 let mut stage: Environment = Default::default();
461 let mut test: Environment = Default::default();
462 let mut dev: Environment = Default::default();
463
464 common.set_base_db_path(PathBuf::from("/Users/kon8116/projects/kon8116/atters"));
465
466 let mut id: Field = Default::default();
467 id.set_name("id".to_string());
468 id.set_primary_key(Some(true));
469
470 let mut correlation_id: Field = Default::default();
471 correlation_id.set_name("correlation_id".to_string());
472 correlation_id.set_data_type(SqliteDataType::Text);
473 correlation_id.set_not_null(Some(true));
474
475 msl_checkout_index.set_fields(vec![id.clone(), correlation_id.clone()]);
476
477 proxy_api_index.set_idx_prefix(EchoIndex::ProxyApi);
478 proxy_api_index.set_fields(vec![id, correlation_id]);
479
480 prod.set_echo_env_affix(Some("-digital".to_string()));
481 prod.set_verbose(false);
482 prod.set_duration(Duration::from_millis(900000));
483
484 stage.set_verbose(false);
485 stage.set_duration(Duration::from_millis(900000));
486
487 test.set_verbose(true);
488 test.set_duration(Duration::from_millis(900000));
489
490 dev.set_verbose(true);
491 dev.set_duration(Duration::from_millis(900000));
492
493 config.set_common(common);
494 config.set_index(vec![msl_checkout_index, proxy_api_index]);
495 config.set_prod(prod);
496 config.set_stage(stage);
497 config.set_test(test);
498 config.set_dev(dev);
499
500 config
501 }
502
503 #[test]
504 fn serialize() {
505 let config = setup_config();
506 let toml = toml::to_string(&config).expect("Unable to serialize to TOML");
507 assert_eq!(TEST_TOML, toml);
508 }
509
510 #[test]
511 fn deserialize() {
512 let config: Config = toml::from_str(TEST_TOML).expect("Unable to deserialize TOML");
513 let common = config.common();
514 let prod = config.prod();
515 let stage = config.stage();
516 let test = config.test();
517 let dev = config.dev();
518
519 assert_eq!(
520 common.base_db_path(),
521 &PathBuf::from("/Users/kon8116/projects/kon8116/atters")
522 );
523 assert_eq!(common.search_prefix(), "http://echo".to_string());
524 assert_eq!(common.search_affix(), ".kroger.com/elastic".to_string());
525 assert_eq!(common.search_suffix(), "/_search".to_string());
526 assert_eq!(common.db_prefix(), "ordmon".to_string());
527 assert_eq!(common.db_suffix(), ".db".to_string());
528 assert_eq!(*prod.echo_env_affix(), Some("-digital".to_string()));
535 assert!(!prod.verbose());
536 assert_eq!(*prod.duration(), Duration::from_millis(900000));
537 assert!(!stage.verbose());
538 assert_eq!(*stage.duration(), Duration::from_millis(900000));
539 assert!(test.verbose());
540 assert_eq!(*test.duration(), Duration::from_millis(900000));
541 assert!(dev.verbose());
542 assert_eq!(*dev.duration(), Duration::from_millis(900000));
543 }
544
545 #[test]
546 fn convert() {
547 let mut runtime_env: RuntimeEnvironment =
548 TryFrom::try_from("prod").expect("invalid runtime env");
549 assert_eq!(runtime_env, RuntimeEnvironment::Prod);
550 runtime_env = TryFrom::try_from("stage").expect("invalid runtime env");
551 assert_eq!(runtime_env, RuntimeEnvironment::Stage);
552 runtime_env = TryFrom::try_from("test").expect("invalid runtime env");
553 assert_eq!(runtime_env, RuntimeEnvironment::Test);
554 runtime_env = TryFrom::try_from("dev").expect("invalid runtime env");
555 assert_eq!(runtime_env, RuntimeEnvironment::Dev);
556 let err_runtime_env: Result<RuntimeEnvironment> = TryFrom::try_from("blah");
557 match err_runtime_env {
558 Ok(_) => assert!(false, "This shouldn't be a valid env"),
559 Err(_) => assert!(true),
560 }
561 }
562
563 #[test]
564 fn echo_url() {
565 let config: Config = toml::from_str(TEST_TOML).expect("Unable to deserialize TOML");
566 assert_eq!(
567 super::echo_url(
568 &config,
569 &RuntimeEnvironment::Prod,
570 &EchoIndex::MslCheckout,
571 false
572 ),
573 "http://echo-digital.kroger.com/elastic/mobile-service-layer-checkout-prod/_search"
574 );
575 assert_eq!(
576 super::echo_url(&config, &RuntimeEnvironment::Prod, &EchoIndex::MslCheckout, true),
577 "http://echo-digital.kroger.com/elastic/mobile-service-layer-checkout-prod/_search?scroll=1m"
578 );
579 assert_eq!(
580 super::echo_url(
581 &config,
582 &RuntimeEnvironment::Stage,
583 &EchoIndex::MslCheckout,
584 false
585 ),
586 "http://echo-stage.kroger.com/elastic/mobile-service-layer-checkout-stage/_search"
587 );
588 assert_eq!(
589 super::echo_url(
590 &config,
591 &RuntimeEnvironment::Test,
592 &EchoIndex::MslCheckout,
593 false
594 ),
595 "http://echo-test.kroger.com/elastic/mobile-service-layer-checkout-test/_search"
596 );
597 assert_eq!(
598 super::echo_url(
599 &config,
600 &RuntimeEnvironment::Dev,
601 &EchoIndex::MslCheckout,
602 false
603 ),
604 "http://echo-dev.kroger.com/elastic/mobile-service-layer-checkout-dev/_search"
605 );
606 assert_eq!(
607 super::echo_url(
608 &config,
609 &RuntimeEnvironment::Prod,
610 &EchoIndex::ProxyApi,
611 false
612 ),
613 "http://echo-digital.kroger.com/elastic/proxy-api-prod/_search"
614 );
615 assert_eq!(
616 super::echo_url(
617 &config,
618 &RuntimeEnvironment::Stage,
619 &EchoIndex::ProxyApi,
620 false
621 ),
622 "http://echo-stage.kroger.com/elastic/proxy-api-stage/_search"
623 );
624 assert_eq!(
625 super::echo_url(
626 &config,
627 &RuntimeEnvironment::Test,
628 &EchoIndex::ProxyApi,
629 false
630 ),
631 "http://echo-test.kroger.com/elastic/proxy-api-test/_search"
632 );
633 assert_eq!(
634 super::echo_url(
635 &config,
636 &RuntimeEnvironment::Dev,
637 &EchoIndex::ProxyApi,
638 false
639 ),
640 "http://echo-dev.kroger.com/elastic/proxy-api-dev/_search"
641 );
642 }
643
644 #[test]
645 fn scroll_url() {
646 let config: Config = toml::from_str(TEST_TOML).expect("Unable to deserialize TOML");
647 assert_eq!(
648 super::scroll_url(&config, &RuntimeEnvironment::Prod),
649 "http://echo-digital.kroger.com/elastic/_search/scroll"
650 );
651 assert_eq!(
652 super::scroll_url(&config, &RuntimeEnvironment::Stage),
653 "http://echo-stage.kroger.com/elastic/_search/scroll"
654 );
655 assert_eq!(
656 super::scroll_url(&config, &RuntimeEnvironment::Test),
657 "http://echo-test.kroger.com/elastic/_search/scroll"
658 );
659 assert_eq!(
660 super::scroll_url(&config, &RuntimeEnvironment::Dev),
661 "http://echo-dev.kroger.com/elastic/_search/scroll"
662 );
663 }
664
665 #[test]
666 fn db_path() {
667 let config: Config = toml::from_str(TEST_TOML).expect("Unable to deserialize TOML");
668 assert_eq!(
669 super::db_path(&config, &RuntimeEnvironment::Prod),
670 PathBuf::from("/Users/kon8116/projects/kon8116/atters/ordmon-prod.db")
671 );
672 assert_eq!(
673 super::db_path(&config, &RuntimeEnvironment::Stage),
674 PathBuf::from("/Users/kon8116/projects/kon8116/atters/ordmon-stage.db")
675 );
676 assert_eq!(
677 super::db_path(&config, &RuntimeEnvironment::Test),
678 PathBuf::from("/Users/kon8116/projects/kon8116/atters/ordmon-test.db")
679 );
680 assert_eq!(
681 super::db_path(&config, &RuntimeEnvironment::Dev),
682 PathBuf::from("/Users/kon8116/projects/kon8116/atters/ordmon-dev.db")
683 );
684 }
685}