mac explicit
This commit is contained in:
+31
@@ -113,6 +113,30 @@ pub async fn wake_from_query(input: impl Into<String>) -> Result<WakeResult> {
|
|||||||
wake_targets(targets).await
|
wake_targets(targets).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn broadcast_wake_targets(mac: macaddr::MacAddr) -> Result<Vec<WakeTarget>> {
|
||||||
|
Ok(get_interface_summaries()
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|iface| iface.addrs.into_iter())
|
||||||
|
.filter_map(|addr| addr.broadcast)
|
||||||
|
.map(|ip| WakeTarget {
|
||||||
|
ip: Some(std::net::IpAddr::V4(ip)),
|
||||||
|
mac: Some(mac),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wake_explicit(mac: macaddr::MacAddr, ip: Option<std::net::IpAddr>) -> Result<WakeResult> {
|
||||||
|
let targets = match ip {
|
||||||
|
Some(ip) => vec![WakeTarget {
|
||||||
|
ip: Some(ip),
|
||||||
|
mac: Some(mac),
|
||||||
|
}],
|
||||||
|
None => broadcast_wake_targets(mac).await?,
|
||||||
|
};
|
||||||
|
wake_targets(targets).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn list_interfaces() -> Result<Vec<String>> {
|
pub async fn list_interfaces() -> Result<Vec<String>> {
|
||||||
Ok(wakey_linux::devices::devs_sorted().await)
|
Ok(wakey_linux::devices::devs_sorted().await)
|
||||||
}
|
}
|
||||||
@@ -121,6 +145,13 @@ pub async fn get_interface_summaries() -> Result<Vec<InterfaceSummary>> {
|
|||||||
wakey_linux::devices::list_interface_summaries().await
|
wakey_linux::devices::list_interface_summaries().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_interface_summary(name: &str) -> Result<Option<InterfaceSummary>> {
|
||||||
|
Ok(get_interface_summaries()
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.find(|iface| iface.ifname == name))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_ips(name: impl AsRef<str>) -> Result<Vec<std::net::IpAddr>> {
|
pub async fn get_ips(name: impl AsRef<str>) -> Result<Vec<std::net::IpAddr>> {
|
||||||
Ok(wakey_linux::devices::get_ips(name.as_ref())
|
Ok(wakey_linux::devices::get_ips(name.as_ref())
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
+113
-4
@@ -40,7 +40,11 @@ struct LeasesArgs {
|
|||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
struct WakeArgs {
|
struct WakeArgs {
|
||||||
query: String,
|
query: Option<String>,
|
||||||
|
#[arg(long)]
|
||||||
|
mac: Option<macaddr::MacAddr>,
|
||||||
|
#[arg(long)]
|
||||||
|
ip: Option<IpAddr>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
}
|
}
|
||||||
@@ -64,6 +68,9 @@ struct StatusArgs {
|
|||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
struct DevsArgs {
|
struct DevsArgs {
|
||||||
|
dev: Option<String>,
|
||||||
|
#[arg(long)]
|
||||||
|
up: bool,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
}
|
}
|
||||||
@@ -149,6 +156,36 @@ fn render_wake_table(result: &WakeResult) -> Table {
|
|||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_wake_args(args: &WakeArgs) -> anyhow::Result<()> {
|
||||||
|
let has_query = args.query.is_some();
|
||||||
|
let has_mac = args.mac.is_some();
|
||||||
|
let has_ip = args.ip.is_some();
|
||||||
|
|
||||||
|
if has_ip && !has_mac {
|
||||||
|
anyhow::bail!("`wakey wake --ip` needs `--mac`");
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_query && (has_mac || has_ip) {
|
||||||
|
anyhow::bail!("query mode and explicit `--mac/--ip` mode are mutually exclusive");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_query && !has_mac {
|
||||||
|
anyhow::bail!("provide either a query or `--mac`");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_wake(args: WakeArgs) -> anyhow::Result<WakeResult> {
|
||||||
|
validate_wake_args(&args)?;
|
||||||
|
|
||||||
|
match (args.query, args.mac, args.ip) {
|
||||||
|
(Some(query), None, None) => wakey::wake_from_query(query).await,
|
||||||
|
(None, Some(mac), ip) => wakey::wake_explicit(mac, ip).await,
|
||||||
|
_ => unreachable!("wake args validated before dispatch"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_devs_table(devs: &[InterfaceSummary]) -> Table {
|
fn render_devs_table(devs: &[InterfaceSummary]) -> Table {
|
||||||
let mut table = base_table();
|
let mut table = base_table();
|
||||||
table.set_header([
|
table.set_header([
|
||||||
@@ -198,6 +235,16 @@ fn render_devs_table(devs: &[InterfaceSummary]) -> Table {
|
|||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filter_interface_summaries(mut devs: Vec<InterfaceSummary>, args: &DevsArgs) -> Vec<InterfaceSummary> {
|
||||||
|
if args.up {
|
||||||
|
devs.retain(|dev| dev.operstate == "up");
|
||||||
|
}
|
||||||
|
if let Some(name) = &args.dev {
|
||||||
|
devs.retain(|dev| &dev.ifname == name);
|
||||||
|
}
|
||||||
|
devs
|
||||||
|
}
|
||||||
|
|
||||||
fn format_epoch_local(epoch: u64) -> String {
|
fn format_epoch_local(epoch: u64) -> String {
|
||||||
match DateTime::<Utc>::from_timestamp(epoch as i64, 0) {
|
match DateTime::<Utc>::from_timestamp(epoch as i64, 0) {
|
||||||
Some(dt) => dt
|
Some(dt) => dt
|
||||||
@@ -258,15 +305,24 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Wake(args) => {
|
Command::Wake(args) => {
|
||||||
let result = wakey::wake_from_query(args.query).await?;
|
let as_json = args.json;
|
||||||
if args.json {
|
let result = run_wake(args).await?;
|
||||||
|
if as_json {
|
||||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||||
} else {
|
} else {
|
||||||
println!("{}", render_wake_table(&result));
|
println!("{}", render_wake_table(&result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Devs(args) => {
|
Command::Devs(args) => {
|
||||||
let devs = wakey::get_interface_summaries().await?;
|
let devs = if let Some(name) = &args.dev {
|
||||||
|
wakey::get_interface_summary(name)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
wakey::get_interface_summaries().await?
|
||||||
|
};
|
||||||
|
let devs = filter_interface_summaries(devs, &args);
|
||||||
if args.json {
|
if args.json {
|
||||||
println!("{}", serde_json::to_string_pretty(&devs)?);
|
println!("{}", serde_json::to_string_pretty(&devs)?);
|
||||||
} else {
|
} else {
|
||||||
@@ -276,3 +332,56 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::WakeArgs;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wake_rejects_ip_without_mac() {
|
||||||
|
let err = super::validate_wake_args(&WakeArgs {
|
||||||
|
query: None,
|
||||||
|
mac: None,
|
||||||
|
ip: Some("192.168.1.10".parse().expect("ip")),
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
.expect_err("ip-only wake should be rejected");
|
||||||
|
|
||||||
|
assert!(err.to_string().contains("--ip"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wake_rejects_mixed_query_and_explicit_mode() {
|
||||||
|
let err = super::validate_wake_args(&WakeArgs {
|
||||||
|
query: Some("pc".into()),
|
||||||
|
mac: Some("aa:bb:cc:dd:ee:ff".parse().expect("mac")),
|
||||||
|
ip: None,
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
.expect_err("mixed wake mode should be rejected");
|
||||||
|
|
||||||
|
assert!(err.to_string().contains("mutually exclusive"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wake_accepts_query_mode() {
|
||||||
|
super::validate_wake_args(&WakeArgs {
|
||||||
|
query: Some("pc".into()),
|
||||||
|
mac: None,
|
||||||
|
ip: None,
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
.expect("query mode should be accepted");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wake_accepts_manual_mac_mode() {
|
||||||
|
super::validate_wake_args(&WakeArgs {
|
||||||
|
query: None,
|
||||||
|
mac: Some("aa:bb:cc:dd:ee:ff".parse().expect("mac")),
|
||||||
|
ip: None,
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
.expect("manual mac mode should be accepted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user