1 use std::{error::Error, fmt::Debug};
2 
3 use config::{
4     builder::AsyncState, AsyncSource, ConfigBuilder, ConfigError, FileFormat, Format, Map,
5 };
6 
7 use async_trait::async_trait;
8 use futures::{select, FutureExt};
9 use warp::Filter;
10 
11 // Example below presents sample configuration server and client.
12 //
13 // Server serves simple configuration on HTTP endpoint.
14 // Client consumes it using custom HTTP AsyncSource built on top of reqwest.
15 
16 #[tokio::main]
main() -> Result<(), Box<dyn Error>>17 async fn main() -> Result<(), Box<dyn Error>> {
18     select! {
19         r = run_server().fuse() => r,
20         r = run_client().fuse() => r
21     }
22 }
23 
run_server() -> Result<(), Box<dyn Error>>24 async fn run_server() -> Result<(), Box<dyn Error>> {
25     let service = warp::path("configuration").map(|| r#"{ "value" : 123 }"#);
26 
27     println!("Running server on localhost:5001");
28 
29     warp::serve(service).bind(([127, 0, 0, 1], 5001)).await;
30 
31     Ok(())
32 }
33 
run_client() -> Result<(), Box<dyn Error>>34 async fn run_client() -> Result<(), Box<dyn Error>> {
35     // Good enough for an example to allow server to start
36     tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
37 
38     let config = ConfigBuilder::<AsyncState>::default()
39         .add_async_source(HttpSource {
40             uri: "http://localhost:5001/configuration".into(),
41             format: FileFormat::Json,
42         })
43         .build()
44         .await?;
45 
46     println!("Config value is {}", config.get::<String>("value")?);
47 
48     Ok(())
49 }
50 
51 // Actual implementation of AsyncSource can be found below
52 
53 #[derive(Debug)]
54 struct HttpSource<F: Format> {
55     uri: String,
56     format: F,
57 }
58 
59 #[async_trait]
60 impl<F: Format + Send + Sync + Debug> AsyncSource for HttpSource<F> {
collect(&self) -> Result<Map<String, config::Value>, ConfigError>61     async fn collect(&self) -> Result<Map<String, config::Value>, ConfigError> {
62         reqwest::get(&self.uri)
63             .await
64             .map_err(|e| ConfigError::Foreign(Box::new(e)))? // error conversion is possible from custom AsyncSource impls
65             .text()
66             .await
67             .map_err(|e| ConfigError::Foreign(Box::new(e)))
68             .and_then(|text| {
69                 self.format
70                     .parse(Some(&self.uri), &text)
71                     .map_err(|e| ConfigError::Foreign(e))
72             })
73     }
74 }
75