Skip to content

Commit 74a0a80

Browse files
Merge pull request #207 from szarykott/async_source
Add AsyncSource with tests, docs and examples
2 parents 11602b0 + 33c6432 commit 74a0a80

File tree

9 files changed

+516
-38
lines changed

9 files changed

+516
-38
lines changed

.github/workflows/msrv.yml

+11-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
matrix:
1111
rust:
12-
- 1.44.0
12+
- 1.46.0
1313
- stable
1414
- beta
1515
- nightly
@@ -44,7 +44,7 @@ jobs:
4444
strategy:
4545
matrix:
4646
rust:
47-
- 1.44.0
47+
- 1.46.0
4848
- stable
4949
- beta
5050
- nightly
@@ -59,10 +59,18 @@ jobs:
5959
override: true
6060

6161
- name: Run cargo test
62-
if: matrix.rust != 'nightly'
62+
if: matrix.rust != 'nightly' && matrix.rust != '1.46.0'
63+
uses: actions-rs/cargo@v1
64+
with:
65+
command: test
66+
67+
- name: Run cargo test (nightly)
68+
if: matrix.rust == '1.46.0'
69+
continue-on-error: true
6370
uses: actions-rs/cargo@v1
6471
with:
6572
command: test
73+
args: --tests
6674

6775
- name: Run cargo test (nightly)
6876
if: matrix.rust == 'nightly'

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ini = ["rust-ini"]
2323
json5 = ["json5_rs"]
2424

2525
[dependencies]
26+
async-trait = "0.1.50"
2627
lazy_static = "1.0"
2728
serde = "1.0.8"
2829
nom = "6"
@@ -39,3 +40,7 @@ json5_rs = { version = "0.3", optional = true, package = "json5" }
3940
serde_derive = "1.0.8"
4041
float-cmp = "0.8"
4142
chrono = { version = "0.4", features = ["serde"] }
43+
tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util", "time"]}
44+
warp = "0.3.1"
45+
futures = "0.3.15"
46+
reqwest = "0.11.3"

examples/async_source/main.rs

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

0 commit comments

Comments
 (0)