rebecca-windows 0.3.0

Windows-specific adapters for Rebecca discovery and host capabilities.
Documentation
use rebecca_core::error::Result;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ActiveProcess {
    pub process_id: u32,
    pub executable_name: String,
}

pub fn active_processes() -> Result<Vec<ActiveProcess>> {
    platform::active_processes()
}

#[cfg(windows)]
mod platform {
    use super::ActiveProcess;
    use rebecca_core::error::{RebeccaError, Result};
    use windows::Win32::Foundation::{CloseHandle, HANDLE};
    use windows::Win32::System::Diagnostics::ToolHelp::{
        CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW,
        TH32CS_SNAPPROCESS,
    };

    pub fn active_processes() -> Result<Vec<ActiveProcess>> {
        let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }
            .map_err(|err| RebeccaError::ApplicationDiscoveryFailed(err.to_string()))?;
        let snapshot = SnapshotHandle(snapshot);

        let mut entry = PROCESSENTRY32W {
            dwSize: std::mem::size_of::<PROCESSENTRY32W>() as u32,
            ..PROCESSENTRY32W::default()
        };
        let mut processes = Vec::new();

        if unsafe { Process32FirstW(snapshot.0, &mut entry) }.is_err() {
            return Ok(processes);
        }

        loop {
            if let Some(executable_name) = process_name(&entry) {
                processes.push(ActiveProcess {
                    process_id: entry.th32ProcessID,
                    executable_name,
                });
            }

            if unsafe { Process32NextW(snapshot.0, &mut entry) }.is_err() {
                break;
            }
        }

        Ok(processes)
    }

    struct SnapshotHandle(HANDLE);

    impl Drop for SnapshotHandle {
        fn drop(&mut self) {
            let _ = unsafe { CloseHandle(self.0) };
        }
    }

    fn process_name(entry: &PROCESSENTRY32W) -> Option<String> {
        let end = entry
            .szExeFile
            .iter()
            .position(|character| *character == 0)
            .unwrap_or(entry.szExeFile.len());
        if end == 0 {
            return None;
        }

        Some(String::from_utf16_lossy(&entry.szExeFile[..end]))
    }
}

#[cfg(not(windows))]
mod platform {
    use super::ActiveProcess;
    use rebecca_core::error::{RebeccaError, Result};

    pub fn active_processes() -> Result<Vec<ActiveProcess>> {
        Err(RebeccaError::PlatformUnavailable(
            "Windows process diagnostics are not available on this platform".to_string(),
        ))
    }
}