(&self, value: T)
| 45 | } |
| 46 | |
| 47 | pub fn schedule_save(&self, value: T) { |
| 48 | let should_spawn = { |
| 49 | let mut state = self.state.lock().expect("persistence state poisoned"); |
| 50 | state.pending = Some(value); |
| 51 | |
| 52 | if state.task_running { |
| 53 | false |
| 54 | } else { |
| 55 | state.task_running = true; |
| 56 | true |
| 57 | } |
| 58 | }; |
| 59 | |
| 60 | if !should_spawn { |
| 61 | return; |
| 62 | } |
| 63 | |
| 64 | let label = self.label; |
| 65 | let path = self.path.clone(); |
| 66 | let delay = self.delay; |
| 67 | let state = self.state.clone(); |
| 68 | |
| 69 | self.runtime.spawn(async move { |
| 70 | loop { |
| 71 | sleep(delay).await; |
| 72 | |
| 73 | let snapshot = { |
| 74 | let mut state = state.lock().expect("persistence state poisoned"); |
| 75 | state.pending.take() |
| 76 | }; |
| 77 | |
| 78 | let Some(snapshot) = snapshot else { |
| 79 | let mut state = state.lock().expect("persistence state poisoned"); |
| 80 | state.task_running = false; |
| 81 | return; |
| 82 | }; |
| 83 | |
| 84 | if let Some(parent) = path.parent() |
| 85 | && let Err(err) = tokio::fs::create_dir_all(parent).await |
| 86 | { |
| 87 | log::error!("Failed to create {} storage directory: {}", label, err); |
| 88 | } |
| 89 | |
| 90 | match serde_json::to_string(&snapshot) { |
| 91 | Ok(contents) => { |
| 92 | if let Err(err) = tokio::fs::write(&path, contents).await { |
| 93 | log::error!("Failed to save {}: {}", label, err); |
| 94 | } |
| 95 | } |
| 96 | Err(err) => { |
| 97 | log::error!("Failed to serialize {}: {}", label, err); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | let should_continue = { |
| 102 | let mut state = state.lock().expect("persistence state poisoned"); |
| 103 | if state.pending.is_some() { |
| 104 | true |
no outgoing calls
no test coverage detected