|
1 | 1 | use camino::Utf8PathBuf; |
2 | 2 | use snafu::prelude::*; |
3 | 3 | use tokio::fs::{File, OpenOptions}; |
4 | | -use tokio::io::AsyncWriteExt; |
| 4 | +use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; |
5 | 5 | use tokio::sync::RwLock; |
6 | 6 |
|
| 7 | +use std::io::SeekFrom; |
7 | 8 | use std::sync::Arc; |
8 | 9 |
|
9 | 10 | use crate::database::operation::OperationLog; |
@@ -35,6 +36,11 @@ pub enum LoggerError { |
35 | 36 | path: Utf8PathBuf, |
36 | 37 | source: serde_json::Error, |
37 | 38 | }, |
| 39 | + #[snafu(display("Other IO error with operaitons log {path}"))] |
| 40 | + IO { |
| 41 | + path: Utf8PathBuf, |
| 42 | + source: std::io::Error, |
| 43 | + }, |
38 | 44 | } |
39 | 45 |
|
40 | 46 | #[derive(Clone, Debug)] |
@@ -84,4 +90,39 @@ impl Logger { |
84 | 90 |
|
85 | 91 | Ok(()) |
86 | 92 | } |
| 93 | + |
| 94 | + pub async fn read(&self) -> Result<Vec<OperationLog>, AppStateError> { |
| 95 | + // When in append mode, the cursor may be set to the end of file |
| 96 | + // so start again from the beginning |
| 97 | + let mut s = String::new(); |
| 98 | + { |
| 99 | + let mut handle = self.handle.write().await; |
| 100 | + handle |
| 101 | + .seek(SeekFrom::Start(0)) |
| 102 | + .await |
| 103 | + .context(IOSnafu { |
| 104 | + path: self.path.to_path_buf(), |
| 105 | + }) |
| 106 | + .context(state_error::LoggerSnafu)?; |
| 107 | + |
| 108 | + handle |
| 109 | + .read_to_string(&mut s) |
| 110 | + .await |
| 111 | + .context(ReadSnafu { |
| 112 | + path: self.path.to_path_buf(), |
| 113 | + }) |
| 114 | + .context(state_error::LoggerSnafu)?; |
| 115 | + } |
| 116 | + |
| 117 | + // Now that we have dropped the RwLock, parse the results |
| 118 | + s.lines() |
| 119 | + .map(|entry| { |
| 120 | + serde_json::from_str(entry) |
| 121 | + .context(ParseSnafu { |
| 122 | + path: self.path.to_path_buf(), |
| 123 | + }) |
| 124 | + .context(state_error::LoggerSnafu) |
| 125 | + }) |
| 126 | + .collect() |
| 127 | + } |
87 | 128 | } |
0 commit comments