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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
use std::sync::Arc; use std::time::{Duration, SystemTime}; use arc_swap::ArcSwap; use yaml_rust::{Yaml, YamlLoader}; use crate::infograph::docs::Doc; use crate::infograph::yaml::hash_to_doc; use crate::platform::Platform; pub struct Config { filename: String, tx: tokio::sync::broadcast::Sender<()>, config: ArcSwap<(Doc, Option<SystemTime>)>, } pub type ChangeNotifier = tokio::sync::broadcast::Receiver<()>; pub struct Handle { config: Arc<(Doc, Option<SystemTime>)>, } impl Config { pub fn new(file: &str) -> Self { let (tx, _) = tokio::sync::broadcast::channel(1); Config { filename: file.to_owned(), config: ArcSwap::new(Arc::new((Doc::empty(), None))), tx, } } pub fn notifier(&self) -> ChangeNotifier { self.tx.subscribe() } pub fn current(&self) -> Handle { Handle { config: self.config.load_full().clone(), } } fn last_modified(&self) -> Option<SystemTime> { std::fs::metadata(&self.filename) .ok() .and_then(|meta| meta.modified().ok()) } pub fn load(&self) -> anyhow::Result<()> { log::info!("Loading config file {}...", &self.filename); let config_data = match std::fs::read_to_string(&self.filename) { Ok(data) => data, Err(error) => { return Err(anyhow::anyhow!( "Cannot load config file {}: {}", &self.filename, error )); } }; let last_modified = std::fs::metadata(&self.filename) .ok() .and_then(|metadata| metadata.modified().ok()); self.load_from_string(config_data.as_str(), last_modified) } pub fn load_from_string( &self, data: &str, last_modified: Option<SystemTime>, ) -> anyhow::Result<()> { let docs = match YamlLoader::load_from_str(data) { Ok(docs) => docs, Err(error) => { return Err(anyhow::anyhow!( "Cannot parse config file {}: {}", &self.filename, error )); } }; let doc = if let Some(Yaml::Hash(map)) = docs.get(0) { hash_to_doc(map)? } else { Doc::empty() }; self.config.store(Arc::new((doc, last_modified))); Ok(()) } } impl Handle { pub fn config(&self) -> &Doc { &self.config.0 } } pub fn install(platform: Arc<Platform>) { let config = Arc::new(Config::new("settings.yml")); platform.register::<Config>(config.clone()); if let Err(error) = config.load() { log::error!("{}", error); } tokio::spawn(async move { while platform.is_running() { tokio::time::delay_for(Duration::from_secs(2)).await; let last_modified = config.last_modified(); let last_loaded = config.config.load().1; if last_modified.is_some() && (last_loaded.is_none() || last_modified > last_loaded) { match config.load() { Ok(_) => { log::info!("System configuration was re-loaded."); if let Err(_) = config.tx.clone().send(()) { log::error!("Failed to broadcast system config change..."); } } Err(error) => log::error!("{}", error), } } } }); }