1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use std::path::{Path, PathBuf};
4use std::sync::{
5 Arc,
6 atomic::{AtomicBool, AtomicU32},
7};
8use time::OffsetDateTime;
9use tokio::fs as tokio_fs;
10
11#[derive(Clone, Debug, Serialize, Deserialize)]
13pub enum ProcessKind {
14 Node,
15 Sidecar,
16}
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
20pub enum ProcessGroup {
21 Validators1,
22 Validators2,
23 Validators3,
24}
25
26#[derive(Clone, Debug, Serialize, Deserialize)]
28pub enum ProcessStatus {
29 Running,
30 Stopped,
31 Exited,
32 Unknown,
33 Skipped,
34}
35
36#[derive(Clone, Debug, Serialize, Deserialize)]
38pub struct ProcessRecord {
39 pub id: String,
40 pub node_id: u32,
41 pub kind: ProcessKind,
42 pub group: ProcessGroup,
43 pub command: String,
44 pub args: Vec<String>,
45 pub cwd: String,
46 pub pid: Option<u32>,
47 #[serde(skip)]
48 pub pid_handle: Option<Arc<AtomicU32>>,
49 #[serde(skip)]
50 pub shutdown_handle: Option<Arc<AtomicBool>>,
51 pub stdout_path: String,
52 pub stderr_path: String,
53 #[serde(with = "time::serde::rfc3339::option")]
54 pub started_at: Option<OffsetDateTime>,
55 #[serde(with = "time::serde::rfc3339::option")]
56 pub stopped_at: Option<OffsetDateTime>,
57 pub exit_code: Option<i32>,
58 pub exit_signal: Option<i32>,
59 pub last_status: ProcessStatus,
60}
61
62#[derive(Clone, Debug, Serialize, Deserialize)]
64pub struct State {
65 #[serde(with = "time::serde::rfc3339")]
66 pub created_at: OffsetDateTime,
67 #[serde(with = "time::serde::rfc3339")]
68 pub updated_at: OffsetDateTime,
69 pub last_block_height: Option<u64>,
70 pub processes: Vec<ProcessRecord>,
71 #[serde(skip)]
72 path: PathBuf,
73}
74
75impl State {
76 pub async fn new(path: PathBuf) -> Result<Self> {
77 let now = OffsetDateTime::now_utc();
78 let state = Self {
79 created_at: now,
80 updated_at: now,
81 last_block_height: None,
82 processes: Vec::new(),
83 path,
84 };
85 state.persist().await?;
86 Ok(state)
87 }
88
89 pub async fn touch(&mut self) -> Result<()> {
90 self.updated_at = OffsetDateTime::now_utc();
91 self.persist().await
92 }
93
94 async fn persist(&self) -> Result<()> {
95 ensure_parent(&self.path).await?;
96 let contents = serde_json::to_string_pretty(self)?;
97 tokio_fs::write(&self.path, contents).await?;
98 Ok(())
99 }
100}
101
102async fn ensure_parent(path: &Path) -> Result<()> {
103 if let Some(parent) = path.parent() {
104 tokio_fs::create_dir_all(parent).await?;
105 }
106 Ok(())
107}