1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use std::path::{Path, PathBuf};
4use std::sync::{
5 atomic::{AtomicBool, AtomicU32},
6 Arc,
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 processes: Vec<ProcessRecord>,
70 #[serde(skip)]
71 path: PathBuf,
72}
73
74impl State {
75 pub async fn new(path: PathBuf) -> Result<Self> {
76 let now = OffsetDateTime::now_utc();
77 let state = Self {
78 created_at: now,
79 updated_at: now,
80 processes: Vec::new(),
81 path,
82 };
83 state.persist().await?;
84 Ok(state)
85 }
86
87 pub async fn touch(&mut self) -> Result<()> {
88 self.updated_at = OffsetDateTime::now_utc();
89 self.persist().await
90 }
91
92 async fn persist(&self) -> Result<()> {
93 ensure_parent(&self.path).await?;
94 let contents = serde_json::to_string_pretty(self)?;
95 tokio_fs::write(&self.path, contents).await?;
96 Ok(())
97 }
98}
99
100async fn ensure_parent(path: &Path) -> Result<()> {
101 if let Some(parent) = path.parent() {
102 tokio_fs::create_dir_all(parent).await?;
103 }
104 Ok(())
105}