1pub mod cpu_miner;
2pub mod error;
3pub mod imports;
4pub mod kaspad;
5pub mod result;
6
7use std::fmt::Display;
8
9use crate::imports::*;
10pub use crate::result::Result;
11pub use cpu_miner::{CpuMiner, CpuMinerConfig, CpuMinerCtl};
12pub use kaspad::{Kaspad, KaspadConfig, KaspadCtl};
13use workflow_core::runtime;
14use workflow_node::process::Event as ProcessEvent;
15use workflow_store::fs::*;
16
17pub static LOCATIONS: &[&str] = &[
18 "bin",
19 "../target/release",
20 "../target/debug",
21 "../../kaspa-cpu-miner/target/debug",
22 "../../kaspa-cpu-miner/target/release",
23 "bin/windows-x64",
24 "bin/linux-ia32",
25 "bin/linux-x64",
26 "bin/linux-arm64",
27 "bin/macos-x64",
28 "bin/macos-aarch64",
29];
30
31pub async fn locate_binaries(root: &str, name: &str) -> Result<Vec<PathBuf>> {
32 if !runtime::is_nw() && !runtime::is_node() && !runtime::is_native() {
35 return Err(Error::Platform);
36 }
37
38 let name = if runtime::is_windows() { name.to_string() + ".exe" } else { name.to_string() };
39
40 let locations = LOCATIONS
41 .iter()
42 .map(|path| PathBuf::from(&root).join(path).join(&name).normalize().map_err(|e| e.into()))
43 .collect::<Result<Vec<_>>>()?;
44
45 let mut list = Vec::new();
46 for path in locations {
47 if exists(&path).await? {
48 list.push(path);
49 }
50 }
51
52 Ok(list)
53}
54
55#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
56pub enum DaemonKind {
57 Kaspad,
58 CpuMiner,
59}
60
61#[derive(Default)]
62pub struct Daemons {
63 pub kaspad: Option<Arc<dyn KaspadCtl + Send + Sync + 'static>>,
64 pub cpu_miner: Option<Arc<dyn CpuMinerCtl + Send + Sync + 'static>>,
66}
67
68impl Daemons {
69 pub fn new() -> Self {
70 Self { kaspad: None, cpu_miner: None }
71 }
72
73 pub fn with_kaspad(mut self, kaspad: Arc<dyn KaspadCtl + Send + Sync + 'static>) -> Self {
74 self.kaspad = Some(kaspad);
75 self
76 }
77
78 pub fn with_cpu_miner(mut self, cpu_miner: Arc<dyn CpuMinerCtl + Send + Sync + 'static>) -> Self {
79 self.cpu_miner = Some(cpu_miner);
80 self
81 }
82
83 pub fn kaspad(&self) -> Arc<dyn KaspadCtl + Send + Sync + 'static> {
84 self.kaspad.as_ref().expect("accessing Daemons::kaspad while kaspad option is None").clone()
85 }
86
87 pub fn try_kaspad(&self) -> Option<Arc<dyn KaspadCtl + Send + Sync + 'static>> {
88 self.kaspad.clone()
89 }
90
91 pub fn cpu_miner(&self) -> Arc<dyn CpuMinerCtl + Send + Sync + 'static> {
92 self.cpu_miner.as_ref().expect("accessing Daemons::cpu_miner while cpu_miner option is None").clone()
93 }
94
95 pub fn try_cpu_miner(&self) -> Option<Arc<dyn CpuMinerCtl + Send + Sync + 'static>> {
96 self.cpu_miner.clone()
97 }
98}
99
100#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
101pub struct DaemonEvent {
102 pub kind: DaemonKind,
103 pub inner: ProcessEvent,
104}
105
106impl DaemonEvent {
107 pub fn new(kind: DaemonKind, inner: ProcessEvent) -> Self {
108 Self { kind, inner }
109 }
110
111 pub fn kind(&self) -> &DaemonKind {
112 &self.kind
113 }
114}
115
116impl From<DaemonEvent> for ProcessEvent {
117 fn from(event: DaemonEvent) -> Self {
118 event.inner
119 }
120}
121
122#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
123pub struct DaemonStatus {
124 pub uptime: Option<u64>,
125}
126
127impl Display for DaemonStatus {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 if let Some(uptime) = self.uptime {
130 write!(f, "running - uptime: {}", format_duration(uptime))?;
131 } else {
132 write!(f, "not running")?;
133 }
134 Ok(())
135 }
136}
137
138fn format_duration(seconds: u64) -> String {
139 let days = seconds / (24 * 60 * 60);
140 let hours = (seconds / (60 * 60)) % 24;
141 let minutes = (seconds / 60) % 60;
142 let seconds = seconds % 60;
143
144 if days > 0 {
145 format!("{0} days {1:02} hours, {2:02} minutes, {3:02} seconds", days, hours, minutes, seconds)
146 } else {
147 format!("{0:02} hours, {1:02} minutes, {2:02} seconds", hours, minutes, seconds)
148 }
149}