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}