streambed_storage/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub mod args;
4
5use rand::RngCore;
6use serde::{de::DeserializeOwned, Serialize};
7use std::{error::Error, path::Path};
8use streambed::{decrypt_buf, encrypt_struct, secret_store};
9use tokio::{
10    fs,
11    io::{AsyncReadExt, AsyncWriteExt},
12};
13
14/// Loads and deserializes a structure described by T, if the file is present.
15/// If there are IO issues outside of the file not being there, they will be returned
16/// as an error. Beyond IO, state is attempted to be decrypted and deserialized when present.
17/// Any issues there cause the default representation of the structure to be returned. The default
18/// structure is also returned where there is no file present in the first place.
19pub async fn load_struct<T, D, DE>(
20    state_storage_path: &Path,
21    ss: &impl secret_store::SecretStore,
22    secret_path: &str,
23    deserialize: D,
24) -> Result<T, Box<dyn Error>>
25where
26    T: Default + DeserializeOwned,
27    D: FnOnce(&[u8]) -> Result<T, DE>,
28{
29    if let Ok(mut f) = fs::File::open(state_storage_path).await {
30        let mut buf = vec![];
31        f.read_to_end(&mut buf).await?;
32        Ok(decrypt_buf(ss, secret_path, &mut buf, deserialize)
33            .await
34            .unwrap_or_default())
35    } else {
36        Ok(T::default())
37    }
38}
39
40/// Saves an encrypted structure described by T. Any IO errors are returned.
41pub async fn save_struct<T, U, F, S, SE>(
42    state_storage_path: &Path,
43    ss: &impl secret_store::SecretStore,
44    secret_path: &str,
45    serialize: S,
46    rng: F,
47    state: &T,
48) -> Result<(), Box<dyn Error>>
49where
50    T: Serialize,
51    S: FnOnce(&T) -> Result<Vec<u8>, SE>,
52    F: FnOnce() -> U,
53    U: RngCore,
54{
55    if let Some(buf) = encrypt_struct(ss, secret_path, serialize, rng, state).await {
56        let mut f = fs::File::create(state_storage_path).await?;
57        f.write_all(&buf).await?;
58    }
59    Ok(())
60}