#hot-reload #reload #value #define #async-trait #partial-eq #source #sized #reload-v #reloader-error-v

hot_reload

Trait and service definition of periodic hot reloader and notifier for config-file, KVS, etc

10 releases

Uses new Rust 2024

0.1.9 Apr 1, 2025
0.1.8 Nov 5, 2024
0.1.6 Jul 16, 2024
0.1.5 Jan 23, 2024
0.1.4 Jul 21, 2023

#394 in Filesystem

Download history 48/week @ 2025-01-15 52/week @ 2025-01-22 236/week @ 2025-01-29 36/week @ 2025-02-05 44/week @ 2025-02-12 48/week @ 2025-02-19 47/week @ 2025-02-26 47/week @ 2025-03-05 270/week @ 2025-03-12 51/week @ 2025-03-19 243/week @ 2025-03-26 240/week @ 2025-04-02 137/week @ 2025-04-09 64/week @ 2025-04-16 103/week @ 2025-04-23 192/week @ 2025-04-30

516 downloads per month

MIT license

11KB
140 lines

Periodic hot reloader and notifier for files, KVS, etc. for Rust

hot_reload hot_reload License: MIT

This provides a Rust trait definition and service library for hot-reloading your files, KVS, etc. by periodically checking the system.

Reload Trait Definition

To use this library, you need to prepare your own struct implementing reloader::Reload trait, defined as follows:

#[async_trait]
/// Trait defining the responsibility of reloaders to periodically load the target value `V` from `Source`.
/// Source could be a file, a KVS, whatever if you can implement `Reload<V>` with `Reload<V>::Source`.
pub trait Reload<V>
where
  V: Eq + PartialEq
{
  type Source;
  async fn new(src: &Self::Source) -> Result<Self, ReloaderError<V>>
  where
    Self: Sized;
  async fn reload(&self) -> Result<Option<V>, ReloaderError<V>>;
}

This trait defines the source type (file, KVS, etc) and reloaded object type V. The following is an example of periodic-reloading a config-file through a given file path string.

pub struct ConfigReloader {
  pub config_path: PathBuf,
}

#[async_trait]
impl Reload<ServerConfig> for ConfigReloader {
  type Source = String;
  async fn new(source: &Self::Source) -> Result<Self, ReloaderError<ServerConfig>> {
    Ok(Self {
      config_path: PathBuf::from(source),
    })
  }

  async fn reload(&self) -> Result<Option<ServerConfig>, ReloaderError<ServerConfig>> {
    let config_str = std::fs::read_to_string(&self.config_path).context("Failed to read config file")?;
    let config: ServerConfig = config_object_from_str(config_str);

    Ok(Some(config))
  }
}

Usage

use hot_reload::*;

let (reloader, rx) = ReloaderService::new(source, 10, false).await.unwrap();
tokio::spawn(async move { reloader_service.start().await });
loop {
  tokio::select! {
    // Add main logic of the event loop with up-to-date value
    _ = something.happened() => {
      // ...
    }
    // immediately update if watcher detects the change
    _ = rx.changed()  => {
      if rx.borrow().is_none() {
        break;
      }
      let value = rx.borrow().clone();
      info!("Received value via watcher");
      info!("value: {:?}", value.unwrap().clone());
    }
    else => break
    }
  }
}

Dependencies

~2.5–8.5MB
~67K SLoC