tuwunel_admin/debug/
dump_pdus.rs1use std::path::Path;
2
3use futures::{Stream, TryFutureExt, TryStreamExt, future::try_join3};
4use tokio::{
5 fs::{self, OpenOptions},
6 io::AsyncWriteExt,
7};
8use tuwunel_core::Result;
9
10use crate::admin_command;
11
12#[admin_command]
13pub(super) async fn dump_pdus(&self, dir: String) -> Result {
14 let dir_path = Path::new(&dir);
15 fs::create_dir_all(dir_path).await?;
16
17 let normal = dumper(dir_path, "normal", self.services.timeline.pdus_raw());
18 let outlier = dumper(dir_path, "outliers", self.services.timeline.outlier_pdus_raw());
19 let retained = dumper(dir_path, "retaineds", self.services.retention.retained_pdus_raw());
20 try_join3(normal, outlier, retained).await?;
21
22 Ok(())
23}
24
25async fn dumper<'a, S>(dir: &Path, name: &str, stream: S) -> Result
26where
27 S: Stream<Item = Result<&'a [u8]>> + Send,
28{
29 let mut fopts = OpenOptions::new();
30 fopts.write(true);
31 fopts.create(true);
32 fopts.truncate(true);
33
34 let path = dir.join(name);
35 let file = fopts.open(path).await?;
36 stream
37 .try_fold(file, async |mut file, data| {
38 file.write_all(data).await?;
39
40 Ok(file)
41 })
42 .and_then(async |mut file| Ok(file.shutdown().await?))
43 .await
44}