use dynamic_config::{dynamic_config, Error, Fetched, Format, RemoteSource};
use embassy_executor::{Executor, Spawner};
use serde::Deserialize;
use static_cell::StaticCell;
#[dynamic_config]
#[derive(Debug, Deserialize)]
struct ServerConfig {
greeting: String,
workers: usize,
}
struct BootDocument;
impl RemoteSource for BootDocument {
fn fetch(&self) -> Result<Fetched, Error> {
Ok(Fetched::new(
r#"{"server": {"greeting": "boot", "workers": 0}}"#.to_owned(),
Format::Json,
))
}
fn describe(&self) -> String {
"boot-document://in-process".to_owned()
}
}
#[embassy_executor::task]
async fn reader() {
let mut changes = ServerConfig::changes();
loop {
let config = changes.changed().await;
println!(
" reader woke: {} ({} workers)",
config.greeting, config.workers
);
}
}
#[embassy_executor::task]
async fn pusher(sink: dynamic_config::RemoteSink) {
embassy_futures::yield_now().await;
println!("two pushes with no turn for the reader in between:");
push(&sink, "first", 1);
push(&sink, "second", 2);
embassy_futures::yield_now().await;
embassy_futures::yield_now().await;
println!("\n ...one wakeup, carrying the *latest* of them.");
println!(" Reloads that land while nothing is awaiting are not queued:");
println!(" waking to the newest configuration is what a reader wants, and");
println!(" a queue would hand it stale ones first.\n");
println!("and one more, with the reader waiting:");
push(&sink, "third", 3);
embassy_futures::yield_now().await;
embassy_futures::yield_now().await;
println!("\nDriven by an executor with no threads, no reactor and no");
println!("allocation in its scheduler. `changes()` is a `Future` over a");
println!("generation counter and a list of wakers — `std`, and nothing else.");
std::process::exit(0);
}
fn push(sink: &dynamic_config::RemoteSink, greeting: &str, workers: usize) {
let document = format!(r#"{{"server": {{"greeting": "{greeting}", "workers": {workers}}}}}"#);
if let Err(error) = sink.apply(Fetched::new(document, Format::Json)) {
println!(" the document did not apply: {error}");
}
}
#[embassy_executor::task]
async fn start(spawner: Spawner, sink: dynamic_config::RemoteSink) {
spawner.spawn(reader().expect("the task pool has room"));
spawner.spawn(pusher(sink).expect("the task pool has room"));
}
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
fn main() {
ServerConfig::set_remote(BootDocument);
ServerConfig::refresh_remote().expect("the boot document is well-formed");
let sink = ServerConfig::remote_sink();
ServerConfig::builder("server")
.init()
.expect("the boot document supplies every field");
let executor = EXECUTOR.init(Executor::new());
executor.run(|spawner| spawner.spawn(start(spawner, sink).expect("the task pool has room")));
}