#![doc = include_str!("../README.md")]
use std::collections::HashSet;
use std::fmt::Display;
use std::net::SocketAddr;
mod platform;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub const IS_OS_SUPPORTED: bool = cfg!(any(
target_os = "windows",
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
));
#[derive(Eq, PartialEq, Hash, Debug, Clone)]
pub struct Listener {
pub process: Process,
pub socket: SocketAddr,
pub protocol: Protocol,
}
#[derive(Eq, PartialEq, Hash, Debug, Clone)]
pub struct Process {
pub pid: u32,
pub name: String,
pub path: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Protocol {
TCP,
UDP,
}
#[doc = include_str!("../examples/get_all.rs")]
pub fn get_all() -> Result<HashSet<Listener>> {
platform::get_all()
}
#[doc = include_str!("../examples/get_process_by_port.rs")]
pub fn get_process_by_port(port: u16, protocol: Protocol) -> Result<Process> {
if port == 0 {
return Err("Port can't be 0".into());
}
platform::get_process_by_port(port, protocol)
}
impl Listener {
fn new(pid: u32, name: String, path: String, socket: SocketAddr, protocol: Protocol) -> Self {
let process = Process::new(pid, name, path);
Self {
process,
socket,
protocol,
}
}
}
impl Process {
fn new(pid: u32, name: String, path: String) -> Self {
Self { pid, name, path }
}
}
impl Display for Listener {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Listener {
process,
socket,
protocol,
} = self;
let process = process.to_string();
write!(f, "{process:<55} Socket: {socket:<30} Protocol: {protocol}",)
}
}
impl Display for Process {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Process { pid, name, .. } = self;
write!(f, "PID: {pid:<10} Process name: {name}")
}
}
impl Display for Protocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Protocol::TCP => write!(f, "TCP"),
Protocol::UDP => write!(f, "UDP"),
}
}
}
#[cfg(test)]
mod tests {
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use crate::{Listener, Process, Protocol};
#[test]
fn test_v4_listener_to_string() {
let listener = Listener::new(
455,
"rapportd".to_string(),
"path/to/rapportd".to_string(),
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 51189),
Protocol::TCP,
);
assert_eq!(
listener.to_string(),
"PID: 455 Process name: rapportd Socket: 0.0.0.0:51189 Protocol: TCP"
);
}
#[test]
fn test_v6_listener_to_string() {
let listener = Listener::new(
160,
"mysqld".to_string(),
"path/to/mysqld".to_string(),
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 3306),
Protocol::UDP,
);
assert_eq!(
listener.to_string(),
"PID: 160 Process name: mysqld Socket: [::]:3306 Protocol: UDP"
);
}
#[test]
fn test_process_to_string() {
let process = Process::new(
611,
"Microsoft SharePoint".to_string(),
"path/to/sharepoint".to_string(),
);
assert_eq!(
process.to_string(),
"PID: 611 Process name: Microsoft SharePoint"
);
}
}