summaryrefslogtreecommitdiffstats
path: root/examples/async_source/main.rs
blob: 02f8cd7e63dd69263b17038a0a1f58d7984caf80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use std::{collections::HashMap, error::Error, fmt::Debug};

use config::{AsyncSource, ConfigBuilder, ConfigError, FileFormat, Format, builder::AsyncState, Map};

use async_trait::async_trait;
use futures::{select, FutureExt};
use warp::Filter;

// Example below presents sample configuration server and client.
//
// Server serves simple configuration on HTTP endpoint.
// Client consumes it using custom HTTP AsyncSource built on top of reqwest.

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    select! {
        r = run_server().fuse() => r,
        r = run_client().fuse() => r
    }
}

async fn run_server() -> Result<(), Box<dyn Error>> {
    let service = warp::path("configuration").map(|| r#"{ "value" : 123 }"#);

    println!("Running server on localhost:5001");

    warp::serve(service).bind(([127, 0, 0, 1], 5001)).await;

    Ok(())
}

async fn run_client() -> Result<(), Box<dyn Error>> {
    // Good enough for an example to allow server to start
    tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;

    let config = ConfigBuilder::<AsyncState>::default()
        .add_async_source(HttpSource {
            uri: "http://localhost:5001/configuration".into(),
            format: FileFormat::Json,
        })
        .build()
        .await?;

    println!("Config value is {}", config.get::<String>("value")?);

    Ok(())
}

// Actual implementation of AsyncSource can be found below

#[derive(Debug)]
struct HttpSource<F : Format> {
    uri: String,
    format: F,
}

#[async_trait]
impl<F : Format + Send + Sync + Debug> AsyncSource for HttpSource<F> {
    async fn collect(&self) -> Result<Map<String, config::Value>, ConfigError> {
        reqwest::get(&self.uri)
            .await
            .map_err(|e| ConfigError::Foreign(Box::new(e)))? // error conversion is possible from custom AsyncSource impls
            .text()
            .await
            .map_err(|e| ConfigError::Foreign(Box::new(e)))
            .and_then(|text| {
                self.format
                    .parse(Some(&self.uri), &text)
                    .map_err(|e| ConfigError::Foreign(e))
            })
    }
}