1use std::error::Error;
7use std::fmt;
8use std::net::{ToSocketAddrs, UdpSocket};
9
10pub const DEFAULT_PORT: u16 = 65530;
12
13pub const DEFAULT_BROADCAST_ADDRESS: &str = "255.255.255.255";
15
16#[derive(Debug)]
18pub enum WolError {
19 InvalidMacAddress(String),
21 NetworkError(std::io::Error),
23 Other(String),
25}
26
27impl fmt::Display for WolError {
28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self {
30 WolError::InvalidMacAddress(mac) => write!(f, "Invalid MAC address format: {}", mac),
31 WolError::NetworkError(err) => write!(f, "Network error: {}", err),
32 WolError::Other(msg) => write!(f, "{}", msg),
33 }
34 }
35}
36
37impl Error for WolError {}
38
39impl From<std::io::Error> for WolError {
40 fn from(err: std::io::Error) -> Self {
41 WolError::NetworkError(err)
42 }
43}
44
45impl From<std::num::ParseIntError> for WolError {
46 fn from(_: std::num::ParseIntError) -> Self {
47 WolError::Other("Failed to parse hexadecimal value in MAC address".to_string())
48 }
49}
50
51#[derive(Debug, Clone)]
53pub struct WolOptions {
54 pub broadcast_address: String,
56 pub port: u16,
58 pub retries: u32,
60}
61
62impl Default for WolOptions {
63 fn default() -> Self {
64 WolOptions {
65 broadcast_address: DEFAULT_BROADCAST_ADDRESS.to_string(),
66 port: DEFAULT_PORT,
67 retries: 1,
68 }
69 }
70}
71
72#[derive(Debug)]
74pub struct WolClient {
75 options: WolOptions,
76}
77
78impl WolClient {
79 pub fn new() -> Self {
81 WolClient {
82 options: WolOptions::default(),
83 }
84 }
85
86 pub fn with_options(options: WolOptions) -> Self {
88 WolClient { options }
89 }
90
91 pub fn wake(&self, mac_address: &str) -> Result<(), WolError> {
101 let frame = create_magic_packet(mac_address)?;
102 let socket = self.create_socket()?;
103 let target = format!("{}:{}", self.options.broadcast_address, self.options.port)
104 .to_socket_addrs()?
105 .next()
106 .ok_or_else(|| WolError::Other("Failed to resolve address".to_string()))?;
107
108 for _ in 0..self.options.retries {
109 socket.send_to(&frame, &target)?;
110 }
111
112 Ok(())
113 }
114
115 fn create_socket(&self) -> Result<UdpSocket, std::io::Error> {
116 let socket = UdpSocket::bind("0.0.0.0:0")?;
117 socket.set_broadcast(true)?;
118 Ok(socket)
119 }
120}
121
122fn create_magic_packet(mac_address: &str) -> Result<Vec<u8>, WolError> {
126 let mac_bytes = parse_mac_address(mac_address)?;
127
128 let mut frame: Vec<u8> = Vec::with_capacity(6 + 16 * 6);
130 frame.extend_from_slice(&[0xFF; 6]);
131
132 for _ in 0..16 {
133 frame.extend_from_slice(&mac_bytes);
134 }
135
136 Ok(frame)
137}
138
139fn parse_mac_address(mac_address: &str) -> Result<[u8; 6], WolError> {
141 let mac_parts: Vec<&str> = mac_address.split(|c| c == ':' || c == '-').collect();
142
143 if mac_parts.len() != 6 {
144 return Err(WolError::InvalidMacAddress(mac_address.to_string()));
145 }
146
147 let mut mac_bytes = [0u8; 6];
148 for (i, part) in mac_parts.iter().enumerate() {
149 mac_bytes[i] = u8::from_str_radix(part, 16)
150 .map_err(|_| WolError::InvalidMacAddress(mac_address.to_string()))?;
151 }
152
153 Ok(mac_bytes)
154}