I am trying to create a global configuration that can be used by axum handlers, and that is being updated periodically (in real life from s3, in the example below I kept it simple).
I have created the code below with the help of CoPilot. It seems to work but the code feels weird with all the cloning in the main function, but this is the only way I could make to compile.
Is there any other more elegant way to do it?
use std::sync::{Arc};
use axum::{extract::State, routing::get, Router};
use chrono::Local;
use serde::{Deserialize, Serialize};
use tokio::{net::TcpListener, spawn};
use tokio_schedule::{every, Job};
use tokio::sync::RwLock;
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Config1 {
time: String
}
struct ServiceState {
config1: Config1
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Hello, world!");
let service_state = ServiceState {
config1: read_config().await
};
let state_arc = Arc::new(RwLock::new(service_state));
let state_arc_clone = Arc::clone(&state_arc);
let every_30_seconds = every(3).seconds().perform(move || {
let arc_clone = Arc::clone(&state_arc_clone);
update_config(arc_clone)
});
spawn(every_30_seconds);
let listener = TcpListener::bind("0.0.0.0:3000").await?;
let app = Router::new()
.route("/config", get(get_config))
.with_state(state_arc);
axum::serve(listener, app).await?;
Ok(())
}
async fn get_config(State(service_state): State<Arc<RwLock<ServiceState>>>) -> axum::Json<Config1> {
let state = service_state.read().await;
axum::Json(state.config1.clone())
}
async fn update_config(state_arc : Arc<RwLock<ServiceState>>) {
let new_config = read_config().await;
state_arc.write().await.config1 = new_config;
}
async fn read_config() -> Config1 {
let local_time = Local::now();
Config1 {
time: local_time.to_string()
}
}