jocker_lib/
common.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt::Display,
4};
5
6use pueue_lib::TaskStatus;
7use serde::{Deserialize, Serialize};
8use url::Url;
9
10use crate::{
11    command::cargo::BinaryPackage,
12    config::ConfigProcess,
13    error::{Error, InnerError, Result},
14    Pid,
15};
16
17pub const JOCKER: &str = "jocker";
18pub(crate) const MAX_RECURSION_LEVEL: u8 = 10;
19pub const JOCKER_ENV_STACK: &str = "JOCKER_STACK";
20
21#[expect(async_fn_in_trait)]
22pub trait Exec<T> {
23    async fn exec(&self) -> Result<T>;
24}
25
26#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
27pub struct Binary {
28    pub name: String,
29    pub id: Url,
30}
31
32impl Binary {
33    pub fn name(&self) -> &str {
34        &self.name
35    }
36}
37
38impl From<BinaryPackage> for Binary {
39    fn from(value: BinaryPackage) -> Self {
40        Self {
41            name: value.name,
42            id: value.id,
43        }
44    }
45}
46
47#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
48pub struct Process {
49    pub name: String,
50    pub binary: String,
51    pub state: ProcessState,
52    pub pid: Option<Pid>,
53    pub args: Vec<String>,
54    pub cargo_args: Vec<String>,
55    pub env: HashMap<String, String>,
56}
57
58impl Process {
59    pub fn new(name: &str, binary: &str) -> Process {
60        Self {
61            name: name.to_string(),
62            binary: binary.to_string(),
63            state: ProcessState::Stopped,
64            pid: None,
65            args: Vec::new(),
66            cargo_args: Vec::new(),
67            env: HashMap::new(),
68        }
69    }
70
71    pub fn name(&self) -> &str {
72        &self.name
73    }
74
75    pub fn binary(&self) -> &str {
76        &self.binary
77    }
78
79    pub fn pid(&self) -> &Option<Pid> {
80        &self.pid
81    }
82
83    pub fn args(&self) -> &[String] {
84        self.args.as_slice()
85    }
86
87    pub fn cargo_args(&self) -> &[String] {
88        self.cargo_args.as_slice()
89    }
90}
91
92impl From<(String, ConfigProcess)> for Process {
93    fn from(value: (String, ConfigProcess)) -> Self {
94        Self {
95            binary: value.1.binary.unwrap_or(value.0.clone()),
96            name: value.0,
97            args: value.1.args,
98            cargo_args: value.1.cargo_args,
99            env: value.1.env,
100            ..Default::default()
101        }
102    }
103}
104
105impl Ord for Process {
106    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
107        match self.name.cmp(&other.name) {
108            core::cmp::Ordering::Equal => {}
109            ord => return ord,
110        }
111        match self.binary.cmp(&other.binary) {
112            core::cmp::Ordering::Equal => {}
113            ord => return ord,
114        }
115        match self.state.cmp(&other.state) {
116            core::cmp::Ordering::Equal => {}
117            ord => return ord,
118        }
119        match self.pid.cmp(&other.pid) {
120            core::cmp::Ordering::Equal => {}
121            ord => return ord,
122        }
123        self.args.cmp(&other.args)
124    }
125}
126
127impl PartialOrd for Process {
128    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
129        Some(self.cmp(other))
130    }
131}
132
133#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Ord, PartialOrd, Serialize)]
134pub enum ProcessState {
135    Stopped,
136    Building,
137    Running,
138    Unknown,
139}
140
141impl Default for ProcessState {
142    fn default() -> Self {
143        Self::Stopped
144    }
145}
146
147impl Display for ProcessState {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        let str = match self {
150            ProcessState::Stopped => "stopped",
151            ProcessState::Building => "building",
152            ProcessState::Running => "running",
153            ProcessState::Unknown => "unknown",
154        };
155        write!(f, "{str}")
156    }
157}
158
159impl From<TaskStatus> for ProcessState {
160    fn from(value: TaskStatus) -> Self {
161        match value {
162            TaskStatus::Running { .. } => Self::Running,
163            TaskStatus::Paused { .. } | TaskStatus::Done { .. } => Self::Stopped,
164            _ => Self::Unknown,
165        }
166    }
167}
168
169impl TryFrom<String> for ProcessState {
170    type Error = Error;
171
172    fn try_from(value: String) -> std::prelude::v1::Result<Self, Self::Error> {
173        Ok(match value.as_str() {
174            "stopped" => Self::Stopped,
175            "building" => Self::Building,
176            "running" => Self::Running,
177            "unknown" => Self::Unknown,
178            _ => Err(Error::new(InnerError::Parse(value)))?,
179        })
180    }
181}
182
183#[derive(Debug, Clone, Deserialize, Serialize)]
184pub struct Stack {
185    pub name: String,
186    pub processes: HashSet<String>,
187    pub inherited_processes: HashSet<String>,
188}
189
190impl Stack {
191    pub fn get_all_processes(&self) -> HashSet<&String> {
192        self.processes
193            .iter()
194            .chain(self.inherited_processes.iter())
195            .collect()
196    }
197}