Skip to main content

pito_jobs/
record.rs

1use std::path::Path;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub struct Record {
7    pub v: u32,
8    pub id: String,
9    pub host: String,
10    pub pid: u32,
11    pub pid_start: u64,
12    pub tool: String,
13    #[serde(default)]
14    pub app: Option<String>,
15    pub label: String,
16    #[serde(default)]
17    pub detail: Option<String>,
18    #[serde(default)]
19    pub invoker: Option<String>,
20    #[serde(default)]
21    pub parent: Option<String>,
22    pub state: String,
23    #[serde(default)]
24    pub stage: Option<String>,
25    #[serde(default, skip_serializing_if = "Vec::is_empty")]
26    pub stages: Vec<String>,
27    #[serde(default)]
28    pub stage_note: Option<String>,
29    #[serde(default)]
30    pub progress: Option<Progress>,
31    #[serde(default)]
32    pub rate: Option<f64>,
33    #[serde(default)]
34    pub pgpu: Option<Pgpu>,
35    #[serde(default)]
36    pub device: Option<String>,
37    #[serde(default)]
38    pub out: Option<String>,
39    pub started: u64,
40    pub updated: u64,
41    #[serde(default)]
42    pub ended: Option<u64>,
43    #[serde(default)]
44    pub exit: Option<i32>,
45    #[serde(default)]
46    pub error: Option<String>,
47}
48
49#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
50pub struct Progress {
51    pub done: u64,
52    #[serde(default)]
53    pub total: Option<u64>,
54    pub unit: String,
55}
56
57impl Progress {
58    pub fn fraction(&self) -> Option<f64> {
59        let total = self.total.filter(|total| *total > 0)?;
60        crate::op::clean(Some(self.done as f64 / total as f64))
61    }
62}
63
64#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
65pub struct Pgpu {
66    pub pid: u32,
67    #[serde(rename = "as")]
68    pub as_name: String,
69}
70
71pub fn read_dir(path: impl AsRef<Path>) -> Vec<Record> {
72    let Ok(entries) = std::fs::read_dir(path) else {
73        return Vec::new();
74    };
75    let mut found = Vec::new();
76    for entry in entries.flatten() {
77        let name = entry.file_name();
78        if name.to_string_lossy().starts_with('.') {
79            continue;
80        }
81        let Ok(meta) = entry.metadata() else {
82            continue;
83        };
84        if !meta.is_file() {
85            continue;
86        }
87        let Some(bytes) = crate::util::read_capped(&entry.path()) else {
88            continue;
89        };
90        if let Ok(record) = serde_json::from_slice::<Record>(&bytes) {
91            found.push((name, record));
92        }
93    }
94    found.sort_by(|left, right| left.0.cmp(&right.0));
95    found.into_iter().map(|(_, record)| record).collect()
96}