Skip to main content

agile_config_client/
lib.rs

1//! `AgileConfig` client for the [`config`] crate.
2//!
3//! This crate talks to an [AgileConfig](https://github.com/dotnetcore/AgileConfig)
4//! cluster over HTTP and WebSocket and exposes configuration through two types:
5//!
6//! - [`Client`] loads published key/value items, optionally caches them on
7//!   disk, and can keep a WebSocket session for live reload notifications.
8//! - [`Source`] implements [`config::AsyncSource`] so the snapshot can be
9//!   composed with other sources (files, environment variables, and so on).
10//!
11//! # Building a client
12//!
13//! Construct [`ClientOptions`] directly or use [`Client::builder`]:
14//!
15//! ```no_run
16//! use agile_config_client::{Client, ClientOptions};
17//!
18//! let from_struct = Client::new(ClientOptions {
19//!     app_id: "app".into(),
20//!     secret: "secret".into(),
21//!     nodes: vec!["http://localhost:5000".into()],
22//!     env: "DEV".into(),
23//!     ..ClientOptions::default()
24//! })?;
25//!
26//! let from_builder = Client::builder()
27//!     .app_id("app")
28//!     .secret("secret")
29//!     .nodes(["http://localhost:5000"])
30//!     .env("DEV")
31//!     .build()?;
32//! # Ok::<(), agile_config_client::Error>(())
33//! ```
34//!
35//! # One-shot load with `config`
36//!
37//! [`Source::collect`][source::Source] (via [`Client::source`]) performs HTTP
38//! (and cache fallback). It does **not** open a WebSocket.
39//!
40//! ```no_run
41//! use agile_config_client::{Client, ClientOptions};
42//!
43//! # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
44//! let client = Client::new(ClientOptions {
45//!     app_id: "app".into(),
46//!     secret: "secret".into(),
47//!     nodes: vec!["http://localhost:5000".into()],
48//!     ..ClientOptions::default()
49//! })?;
50//!
51//! let settings = config::Config::builder()
52//!     .add_async_source(client.source())
53//!     .build()
54//!     .await?;
55//!
56//! let connection = settings.get_string("db.connection")?;
57//! # let _ = connection;
58//! # Ok(())
59//! # }
60//! ```
61//!
62//! Keys that the C# client exposes as `group:key` become dotted paths for the
63//! `config` crate (`db:connection` → `db.connection`).
64//!
65//! # Live updates
66//!
67//! Call [`Client::connect`] to pull configuration and start WebSocket
68//! reconnect/heartbeat. Keep the `Client` alive, then listen with
69//! [`Client::subscribe`]. This crate never rebuilds [`config::Config`] for you.
70//!
71//! ```no_run
72//! use agile_config_client::{Client, ClientOptions};
73//!
74//! # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
75//! let client = Client::new(ClientOptions {
76//!     app_id: "app".into(),
77//!     secret: "secret".into(),
78//!     nodes: vec!["http://localhost:5000".into()],
79//!     ..ClientOptions::default()
80//! })?;
81//! client.connect().await?;
82//!
83//! let mut rx = client.subscribe();
84//! while rx.changed().await.is_ok() {
85//!     let snapshot = client.snapshot();
86//!     // Rebuild `config::Config` or swap application state here.
87//!     let _ = snapshot.get("db:connection");
88//! }
89//! # Ok(())
90//! # }
91//! ```
92//!
93//! Lookups on [`ConfigSnapshot`] are case-sensitive.
94//!
95//! # Features
96//!
97//! | Feature | Default | Purpose |
98//! | --- | --- | --- |
99//! | `cache-encrypt` | off | AES-ECB encryption for the local cache file (C# compatible) |
100
101#![warn(missing_docs)]
102
103mod auth;
104mod cache;
105mod client;
106mod error;
107mod http;
108mod nodes;
109mod options;
110mod protocol;
111mod source;
112mod store;
113mod websocket;
114
115pub use client::Client;
116pub use error::Error;
117pub use options::{CacheOptions, ClientBuilder, ClientOptions};
118pub use protocol::ConfigItem;
119pub use source::Source;
120pub use store::ConfigSnapshot;