diff --git a/Cargo.toml b/Cargo.toml index 43fc476..e5a5731 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "pawd" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "MIT" repository = "https://lab.themackabu.dev/crates/pawd" description = "Process analysis & watcher daemon" [dependencies] +serde_json = "1.0.108" psutil = { version = "3.2.2", features = ["serde"] } -serde = "1.0.192" -serde_derive = "1.0.192" +serde = { version = "1.0.192", features = ["derive"] } [dev-dependencies] -assert_matches = "1.5.0" \ No newline at end of file +assert_matches = "1.5.0" diff --git a/src/lib.rs b/src/lib.rs index 08a4f2e..611890c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,110 +1,118 @@ mod structs; use psutil::process::{MemoryInfo, Process}; use std::io::{self, Read}; use std::process::{Command, Stdio}; use std::thread::{sleep, spawn}; use std::time::{Duration, Instant}; use structs::{PawDone, PawInfo, PawProcess, PawResult}; /// Represents a process watcher that monitors the resource usage of a child process. #[derive(Debug, Clone)] pub struct Paw { command: String, arguments: Vec, duration: Duration, } impl Paw { /// Creates a new `Paw` instance with the specified command, arguments, and duration. pub fn new>(command: &str, arguments: &[T], duration: u64) -> Paw { Paw { command: command.to_string(), arguments: arguments.iter().map(|s| s.as_ref().to_string()).collect(), duration: Duration::from_millis(duration), } } /// Watches the process and calls the provided callback with the result at regular intervals. /// /// # Arguments /// /// * `callback` - A callback function that takes a `PawResult` as its parameter. /// /// # Returns /// /// Returns a `Result` containing a `PawDone` indicating the completion status. pub fn watch(&self, callback: F) -> Result> { let mut child = Command::new(&self.command).args(&self.arguments).stdout(Stdio::piped()).spawn()?; let done: PawDone; let pid = child.id(); let start_time = Instant::now(); let stdout_handle = child.stdout.take().unwrap(); let stdout_thread = spawn(move || { let mut buffer = String::new(); let mut reader = io::BufReader::new(stdout_handle); reader.read_to_string(&mut buffer).unwrap(); buffer }); loop { let uptime = start_time.elapsed().as_millis(); let mut memory_usage: Option = None; let mut cpu_percent: Option = None; if let Ok(mut process) = Process::new(pid) { memory_usage = process.memory_info().ok(); cpu_percent = process.cpu_percent().ok(); } let result = PawResult { info: PawInfo { memory_usage, cpu_percent, uptime }, process: PawProcess { cmd: self.command.clone(), args: self.arguments.clone(), }, }; callback(result); sleep(self.duration); if let Some(status) = child.try_wait()? { done = PawDone { stdout: stdout_thread.join().unwrap(), code: status.code(), }; break; } } Ok(done) } } +impl PawResult { + pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap() } +} + +impl PawDone { + pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap() } +} + #[cfg(test)] mod tests { use super::*; use assert_matches::assert_matches; #[test] fn watch() { let paw = Paw::new("node", &["tests/test.js"], 500); let callback = move |result: PawResult| { assert_matches!( result, PawResult { info: PawInfo { .. }, process: PawProcess { .. } } ); }; match paw.watch(callback) { Ok(result) => assert_matches!(result, PawDone { .. }), Err(error) => panic!("{error}"), } } } diff --git a/src/structs.rs b/src/structs.rs index 3a67bc7..d838b27 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,27 +1,27 @@ use psutil::process::MemoryInfo; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct PawResult { pub info: PawInfo, pub process: PawProcess, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct PawProcess { pub cmd: String, pub args: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct PawInfo { pub uptime: u128, pub memory_usage: Option, pub cpu_percent: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct PawDone { pub stdout: String, pub code: Option, }