i suggested the Banger. copilot worked it good
This commit is contained in:
+13
-25
@@ -8,7 +8,7 @@ use anyhow::Result;
|
||||
use clap::{ArgAction, Args, Parser, Subcommand};
|
||||
use tracing::debug;
|
||||
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
|
||||
use wakey_core::{DeviceFilters, DeviceQuery, InterfaceSummary, WakeResult};
|
||||
use wakey_core::{InterfaceSummary, InventoryQuery, InventoryQueryBuilder, Query, WakeResult};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "wakey")]
|
||||
@@ -141,7 +141,10 @@ pub async fn run(cli: Cli) -> Result<()> {
|
||||
Command::Inventory(args) => {
|
||||
let as_json = args.json;
|
||||
let query = inventory_args_to_query(args);
|
||||
let selected_name = query.name.clone();
|
||||
let selected_name = query.iter().find_map(|term| match term {
|
||||
Query::Text(text) => Some(text.clone()),
|
||||
_ => None,
|
||||
});
|
||||
debug!(?query, json = as_json, "dispatching inventory command");
|
||||
let status = wakey::inventory(query).await?;
|
||||
if as_json {
|
||||
@@ -207,29 +210,14 @@ pub async fn run(cli: Cli) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inventory_args_to_query(args: InventoryArgs) -> DeviceQuery {
|
||||
if let Some(query) = args.query.as_ref()
|
||||
&& args.name.is_none()
|
||||
&& args.ips.is_empty()
|
||||
&& args.devs.is_empty()
|
||||
&& args.nuds.is_empty()
|
||||
&& args.macs.is_empty()
|
||||
{
|
||||
return DeviceQuery {
|
||||
name: Some(query.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
DeviceQuery {
|
||||
name: args.name.or(args.query),
|
||||
filter: DeviceFilters {
|
||||
ips: args.ips,
|
||||
devs: args.devs,
|
||||
nuds: args.nuds,
|
||||
macs: args.macs,
|
||||
},
|
||||
}
|
||||
fn inventory_args_to_query(args: InventoryArgs) -> InventoryQuery {
|
||||
InventoryQueryBuilder::new()
|
||||
.maybe_text(args.name.or(args.query))
|
||||
.ips(args.ips)
|
||||
.interfaces(args.devs)
|
||||
.neighbor_states(args.nuds)
|
||||
.macs(args.macs)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn validate_wake_args(args: &WakeArgs) -> Result<()> {
|
||||
|
||||
+9
-6
@@ -3,7 +3,7 @@ pub mod utils;
|
||||
|
||||
pub use service::{
|
||||
broadcast_wake_targets, get_interface_summaries, get_interface_summary, get_ips, get_leases,
|
||||
inventory, leases_without_state, merge_devices, query_to_device_query, resolve_devices,
|
||||
inventory, leases_without_state, merge_devices, query_to_inventory_query, resolve_devices,
|
||||
resolve_query, resolve_selector, resolve_wake_targets, wake_explicit, wake_from_query,
|
||||
wake_targets,
|
||||
};
|
||||
@@ -20,8 +20,8 @@ mod tests {
|
||||
async fn resolve_query_parses_ip() {
|
||||
let query = resolve_query("192.168.1.10").await.expect("resolve query");
|
||||
assert_eq!(
|
||||
query.filter.ips,
|
||||
vec![IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10))]
|
||||
query,
|
||||
vec![Query::Ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)))]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,13 +30,16 @@ mod tests {
|
||||
let query = resolve_query("aa:bb:cc:dd:ee:ff")
|
||||
.await
|
||||
.expect("resolve query");
|
||||
assert_eq!(query.filter.macs.len(), 1);
|
||||
assert_eq!(
|
||||
query,
|
||||
vec![Query::Mac("aa:bb:cc:dd:ee:ff".parse().expect("mac"))]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_query_parses_nud() {
|
||||
let query = resolve_query("reachable").await.expect("resolve query");
|
||||
assert_eq!(query.filter.nuds, vec![NeighborState::Reachable]);
|
||||
assert_eq!(query, vec![Query::NeighborState(NeighborState::Reachable)]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -80,7 +83,7 @@ mod tests {
|
||||
},
|
||||
nud_state: None,
|
||||
}];
|
||||
let devices = merge_devices(neighbors, leases, &wakey_core::DeviceQuery::default());
|
||||
let devices = merge_devices(neighbors, leases, &wakey_core::InventoryQuery::default());
|
||||
assert_eq!(devices.len(), 1);
|
||||
assert_eq!(devices[0].presence, Presence::Online);
|
||||
assert_eq!(devices[0].names, vec!["pc".to_string()]);
|
||||
|
||||
+87
-18
@@ -1,6 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use wakey_core::{
|
||||
Device, DeviceInventory, DeviceQuery, DhcpLease, DhcpLeaseWithState, NeighborEntry, Presence,
|
||||
Device, DeviceInventory, DhcpLease, DhcpLeaseWithState, InventoryQuery, NeighborEntry,
|
||||
Presence, Query,
|
||||
};
|
||||
|
||||
use crate::service::leases::get_leases;
|
||||
@@ -17,7 +18,7 @@ pub async fn resolve_devices(input: impl Into<String>) -> Result<Vec<Device>> {
|
||||
/// This is the current center of gravity for the service layer. Higher-level
|
||||
/// status and wake flows should prefer deriving from inventory rather than
|
||||
/// directly from raw Linux source rows.
|
||||
pub async fn inventory(query: DeviceQuery) -> Result<DeviceInventory> {
|
||||
pub async fn inventory(query: InventoryQuery) -> Result<DeviceInventory> {
|
||||
let neighbors = wakey_linux::devices::query_neighbors(&query).await?;
|
||||
let leases = get_leases(wakey_core::LeaseQuery {
|
||||
include_state: false,
|
||||
@@ -35,7 +36,7 @@ pub async fn inventory(query: DeviceQuery) -> Result<DeviceInventory> {
|
||||
pub fn merge_devices(
|
||||
neighbors: Vec<NeighborEntry>,
|
||||
leases: Vec<DhcpLeaseWithState>,
|
||||
query: &DeviceQuery,
|
||||
query: &InventoryQuery,
|
||||
) -> Vec<Device> {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -58,34 +59,45 @@ pub fn merge_devices(
|
||||
.map(|(neighbors, leases)| Device::from_parts(neighbors, leases))
|
||||
.collect();
|
||||
|
||||
if let Some(name) = &query.name {
|
||||
devices.retain(|device| device.names.iter().any(|n| n == name));
|
||||
let mut texts: Vec<&str> = Vec::new();
|
||||
let mut devs: Vec<&str> = Vec::new();
|
||||
let mut ips = Vec::new();
|
||||
let mut macs = Vec::new();
|
||||
let mut nuds = Vec::new();
|
||||
|
||||
for term in query {
|
||||
match term {
|
||||
Query::Text(v) => texts.push(v.as_str()),
|
||||
Query::Interface(v) => devs.push(v.as_str()),
|
||||
Query::Ip(v) => ips.push(*v),
|
||||
Query::Mac(v) => macs.push(*v),
|
||||
Query::NeighborState(v) => nuds.push(*v),
|
||||
}
|
||||
if !query.filter.devs.is_empty() {
|
||||
}
|
||||
|
||||
if !texts.is_empty() {
|
||||
devices.retain(|device| device.names.iter().any(|n| texts.iter().any(|t| n == t)));
|
||||
}
|
||||
if !devs.is_empty() {
|
||||
devices.retain(|device| {
|
||||
device
|
||||
.interfaces
|
||||
.iter()
|
||||
.any(|iface| query.filter.devs.contains(iface))
|
||||
.any(|iface| devs.iter().any(|d| iface == d))
|
||||
});
|
||||
}
|
||||
if !query.filter.ips.is_empty() {
|
||||
devices.retain(|device| device.ips.iter().any(|ip| query.filter.ips.contains(ip)));
|
||||
if !ips.is_empty() {
|
||||
devices.retain(|device| device.ips.iter().any(|ip| ips.contains(ip)));
|
||||
}
|
||||
if !query.filter.macs.is_empty() {
|
||||
devices.retain(|device| {
|
||||
device
|
||||
.macs
|
||||
.iter()
|
||||
.any(|mac| query.filter.macs.contains(mac))
|
||||
});
|
||||
if !macs.is_empty() {
|
||||
devices.retain(|device| device.macs.iter().any(|mac| macs.contains(mac)));
|
||||
}
|
||||
if !query.filter.nuds.is_empty() {
|
||||
if !nuds.is_empty() {
|
||||
devices.retain(|device| {
|
||||
device
|
||||
.neighbors
|
||||
.iter()
|
||||
.any(|neighbor| query.filter.nuds.contains(&neighbor.state))
|
||||
.any(|neighbor| nuds.contains(&neighbor.state))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,3 +117,60 @@ const fn presence_rank(presence: Presence) -> u8 {
|
||||
Presence::Offline => 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use wakey_core::{DhcpLease, InventoryQueryBuilder, NeighborState};
|
||||
|
||||
fn sample_neighbors() -> Vec<NeighborEntry> {
|
||||
vec![NeighborEntry {
|
||||
ip: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)),
|
||||
dev: Some("br-lan".to_string()),
|
||||
mac: Some("aa:bb:cc:dd:ee:ff".parse().expect("mac")),
|
||||
state: NeighborState::Reachable,
|
||||
}]
|
||||
}
|
||||
|
||||
fn sample_leases() -> Vec<DhcpLeaseWithState> {
|
||||
vec![DhcpLeaseWithState {
|
||||
lease_line: DhcpLease {
|
||||
expires_epoch: 1,
|
||||
ip: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)),
|
||||
mac: "aa:bb:cc:dd:ee:ff".parse().expect("mac"),
|
||||
name: Some("pc".to_string()),
|
||||
},
|
||||
nud_state: None,
|
||||
}]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_devices_applies_and_across_categories() {
|
||||
let query = InventoryQueryBuilder::new()
|
||||
.maybe_text(Some("pc".to_string()))
|
||||
.interfaces(vec!["br-lan".to_string()])
|
||||
.neighbor_states(vec![NeighborState::Reachable])
|
||||
.build();
|
||||
|
||||
let out = merge_devices(sample_neighbors(), sample_leases(), &query);
|
||||
assert_eq!(out.len(), 1);
|
||||
|
||||
let no_match_query = InventoryQueryBuilder::new()
|
||||
.maybe_text(Some("pc".to_string()))
|
||||
.interfaces(vec!["eth9".to_string()])
|
||||
.build();
|
||||
let out = merge_devices(sample_neighbors(), sample_leases(), &no_match_query);
|
||||
assert!(out.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_devices_allows_or_within_same_category() {
|
||||
let query = InventoryQueryBuilder::new()
|
||||
.neighbor_states(vec![NeighborState::Stale, NeighborState::Reachable])
|
||||
.build();
|
||||
|
||||
let out = merge_devices(sample_neighbors(), sample_leases(), &query);
|
||||
assert_eq!(out.len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ pub mod wake;
|
||||
pub use interfaces::{get_interface_summaries, get_interface_summary, get_ips};
|
||||
pub use inventory::{inventory, merge_devices, resolve_devices};
|
||||
pub use leases::{get_leases, leases_without_state};
|
||||
pub use query::{query_to_device_query, resolve_query, resolve_selector};
|
||||
pub use query::{query_to_inventory_query, resolve_query, resolve_selector};
|
||||
pub use wake::{
|
||||
broadcast_wake_targets, resolve_wake_targets, wake_explicit, wake_from_query, wake_targets,
|
||||
};
|
||||
|
||||
+8
-41
@@ -1,12 +1,12 @@
|
||||
use anyhow::Result;
|
||||
use wakey_core::{DeviceFilters, DeviceQuery, Query, QueryInput};
|
||||
use wakey_core::{InventoryQuery, Query, QueryInput};
|
||||
|
||||
/// Resolve free-form user input into the legacy `DeviceQuery` filter shape.
|
||||
/// Resolve free-form user input into an `InventoryQuery` filter shape.
|
||||
///
|
||||
/// This is the compatibility entrypoint used by CLI and HTTP paths that still
|
||||
/// speak in terms of `DeviceQuery`.
|
||||
pub async fn resolve_query(input: impl Into<String>) -> Result<DeviceQuery> {
|
||||
query_to_device_query(resolve_selector(input).await?)
|
||||
/// speak in terms of query/filter payloads.
|
||||
pub async fn resolve_query(input: impl Into<String>) -> Result<InventoryQuery> {
|
||||
query_to_inventory_query(resolve_selector(input).await?)
|
||||
}
|
||||
|
||||
/// Classify one piece of free-form user input into a typed selector.
|
||||
@@ -25,43 +25,10 @@ pub async fn resolve_selector(input: impl Into<String>) -> Result<Query> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert the newer selector-oriented `Query` model into a `DeviceQuery`.
|
||||
/// Convert the newer selector-oriented `Query` model into an `InventoryQuery`.
|
||||
///
|
||||
/// This keeps the old filter-based service and HTTP surfaces working while the
|
||||
/// internals migrate toward selector- and device-oriented APIs.
|
||||
pub fn query_to_device_query(query: Query) -> Result<DeviceQuery> {
|
||||
Ok(match query {
|
||||
Query::Ip(ip_addr) => DeviceQuery {
|
||||
filter: DeviceFilters {
|
||||
ips: vec![ip_addr],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Query::Mac(mac_addr) => DeviceQuery {
|
||||
filter: DeviceFilters {
|
||||
macs: vec![mac_addr],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Query::Interface(dev) => DeviceQuery {
|
||||
filter: DeviceFilters {
|
||||
devs: vec![dev],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Query::NeighborState(state) => DeviceQuery {
|
||||
filter: DeviceFilters {
|
||||
nuds: vec![state],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Query::Text(name) => DeviceQuery {
|
||||
name: Some(name),
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
pub fn query_to_inventory_query(query: Query) -> Result<InventoryQuery> {
|
||||
Ok(vec![query])
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use wakey::inventory;
|
||||
use wakey_core::DeviceQuery;
|
||||
use wakey_core::InventoryQuery;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "runs against live router data; use on-device or via scripts/test_remote.ps1"]
|
||||
async fn inventory_real_router_prints_device_inventory() -> anyhow::Result<()> {
|
||||
let inventory = inventory(DeviceQuery::default()).await?;
|
||||
let inventory = inventory(InventoryQuery::default()).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&inventory)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use wakey::{broadcast_wake_targets, get_interface_summaries, inventory, resolve_query};
|
||||
use wakey_core::{DeviceFilters, DeviceQuery};
|
||||
use wakey_core::{InventoryQuery, Query};
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "runs against live router data; use on-device or via scripts/test_remote.ps1"]
|
||||
@@ -18,7 +18,7 @@ async fn interfaces_real_router_prints_interface_summaries() -> anyhow::Result<(
|
||||
#[tokio::test]
|
||||
#[ignore = "runs against live router data; use on-device or via scripts/test_remote.ps1"]
|
||||
async fn inventory_real_router_default_query_returns_rows_or_empty_cleanly() -> anyhow::Result<()> {
|
||||
let inv = inventory(DeviceQuery::default()).await?;
|
||||
let inv = inventory(InventoryQuery::default()).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&inv)?);
|
||||
Ok(())
|
||||
}
|
||||
@@ -32,14 +32,7 @@ async fn inventory_real_router_for_interface_filter_succeeds() -> anyhow::Result
|
||||
.map(|iface| iface.ifname.clone())
|
||||
.expect("expected at least one interface");
|
||||
|
||||
let inv = inventory(DeviceQuery {
|
||||
name: None,
|
||||
filter: DeviceFilters {
|
||||
devs: vec![first.clone()],
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
let inv = inventory(vec![Query::Interface(first.clone())]).await?;
|
||||
|
||||
println!("filtered dev: {first}");
|
||||
println!("{}", serde_json::to_string_pretty(&inv)?);
|
||||
|
||||
@@ -51,7 +51,7 @@ async fn dispatch_devs(req: DevsRequest) -> Result<CommandResult> {
|
||||
}
|
||||
|
||||
async fn dispatch_inventory(req: InventoryRequest) -> Result<CommandResult> {
|
||||
let inventory = wakey::inventory(req.into_device_query()).await?;
|
||||
let inventory = wakey::inventory(req.into_inventory_query()).await?;
|
||||
debug!(
|
||||
rows = inventory.devices.len(),
|
||||
"dispatched inventory command"
|
||||
|
||||
+13
-27
@@ -4,7 +4,8 @@ use std::fmt;
|
||||
use std::net::IpAddr;
|
||||
use wakey_core::parse::mac;
|
||||
use wakey_core::{
|
||||
DeviceFilters, DeviceInventory, DeviceQuery, DhcpLeaseWithState, InterfaceSummary, WakeResult,
|
||||
DeviceInventory, DhcpLeaseWithState, InterfaceSummary, InventoryQuery, InventoryQueryBuilder,
|
||||
WakeResult,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -95,43 +96,28 @@ pub struct InventoryRequest {
|
||||
}
|
||||
|
||||
impl InventoryRequest {
|
||||
pub fn into_device_query(self) -> DeviceQuery {
|
||||
into_device_query(
|
||||
pub fn into_inventory_query(self) -> InventoryQuery {
|
||||
into_inventory_query(
|
||||
self.query, self.name, self.ips, self.devs, self.nuds, self.macs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn into_device_query(
|
||||
fn into_inventory_query(
|
||||
query: Option<String>,
|
||||
name: Option<String>,
|
||||
ips: Vec<IpAddr>,
|
||||
devs: Vec<String>,
|
||||
nuds: Vec<wakey_core::NeighborState>,
|
||||
macs: Vec<MacAddr>,
|
||||
) -> DeviceQuery {
|
||||
if let Some(q) = query.as_ref()
|
||||
&& name.is_none()
|
||||
&& ips.is_empty()
|
||||
&& devs.is_empty()
|
||||
&& nuds.is_empty()
|
||||
&& macs.is_empty()
|
||||
{
|
||||
return DeviceQuery {
|
||||
name: Some(q.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
DeviceQuery {
|
||||
name: name.or(query),
|
||||
filter: DeviceFilters {
|
||||
ips,
|
||||
devs,
|
||||
nuds,
|
||||
macs,
|
||||
},
|
||||
}
|
||||
) -> InventoryQuery {
|
||||
InventoryQueryBuilder::new()
|
||||
.maybe_text(name.or(query))
|
||||
.ips(ips)
|
||||
.interfaces(devs)
|
||||
.neighbor_states(nuds)
|
||||
.macs(macs)
|
||||
.build()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -9,5 +9,5 @@ pub use device::{Device, DeviceId, DeviceInventory, Presence};
|
||||
pub use dhcp::{DhcpLease, DhcpLeaseWithState, LeaseQuery};
|
||||
pub use interface::{InterfaceAddr, InterfaceSummary};
|
||||
pub use neighbor::{NeighborEntry, NeighborParseError, NeighborState, parse_neighbor_line};
|
||||
pub use query::{DeviceFilters, DeviceQuery, NamePath, Query, QueryInput};
|
||||
pub use query::{InventoryQuery, InventoryQueryBuilder, NamePath, Query, QueryInput};
|
||||
pub use wake::{WakeResult, WakeStatus, WakeTarget, WakeTargetResult};
|
||||
|
||||
@@ -1,37 +1,53 @@
|
||||
use macaddr::MacAddr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{DisplayFromStr, OneOrMany, serde_as};
|
||||
use serde::Deserialize;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::model::NeighborState;
|
||||
|
||||
/// Legacy-compatible query shape used by HTTP and service adapters.
|
||||
///
|
||||
/// `name` carries free-form text selection, while `filter` carries explicit
|
||||
/// machine-readable filters such as IPs, MACs, interfaces, and neighbor states.
|
||||
#[derive(Debug, Default, Clone, Hash, Deserialize, Serialize)]
|
||||
pub struct DeviceQuery {
|
||||
pub name: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub filter: DeviceFilters,
|
||||
/// Canonical inventory query represented as an AND of selector terms.
|
||||
pub type InventoryQuery = Vec<Query>;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct InventoryQueryBuilder {
|
||||
terms: InventoryQuery,
|
||||
}
|
||||
|
||||
/// Explicit device filters for source- and service-level queries.
|
||||
#[serde_as]
|
||||
#[derive(Debug, Default, Clone, Hash, Serialize, Deserialize)]
|
||||
pub struct DeviceFilters {
|
||||
#[serde_as(as = "OneOrMany<_>")]
|
||||
#[serde(default)]
|
||||
pub ips: Vec<IpAddr>,
|
||||
#[serde_as(as = "OneOrMany<_>")]
|
||||
#[serde(default)]
|
||||
pub devs: Vec<String>,
|
||||
#[serde_as(as = "OneOrMany<_>")]
|
||||
#[serde(default)]
|
||||
pub nuds: Vec<NeighborState>,
|
||||
#[serde_as(as = "OneOrMany<DisplayFromStr>")]
|
||||
#[serde(default)]
|
||||
pub macs: Vec<MacAddr>,
|
||||
impl InventoryQueryBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn maybe_text(mut self, text: Option<String>) -> Self {
|
||||
if let Some(text) = text {
|
||||
self.terms.push(Query::Text(text));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ips(mut self, values: impl IntoIterator<Item = IpAddr>) -> Self {
|
||||
self.terms.extend(values.into_iter().map(Query::Ip));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn interfaces(mut self, values: impl IntoIterator<Item = String>) -> Self {
|
||||
self.terms.extend(values.into_iter().map(Query::Interface));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn neighbor_states(mut self, values: impl IntoIterator<Item = NeighborState>) -> Self {
|
||||
self.terms
|
||||
.extend(values.into_iter().map(Query::NeighborState));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn macs(mut self, values: impl IntoIterator<Item = MacAddr>) -> Self {
|
||||
self.terms.extend(values.into_iter().map(Query::Mac));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> InventoryQuery {
|
||||
self.terms
|
||||
}
|
||||
}
|
||||
|
||||
/// Path helper for routes that receive a single `{name}` segment.
|
||||
@@ -51,7 +67,7 @@ pub enum QueryInput {
|
||||
}
|
||||
|
||||
/// Higher-level typed selector used by the service layer.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Query {
|
||||
Text(String),
|
||||
Ip(IpAddr),
|
||||
|
||||
@@ -3,7 +3,7 @@ use futures::future::try_join_all;
|
||||
use lda_ipjs::subcommands::neighbor;
|
||||
use std::collections::HashSet;
|
||||
use std::net::IpAddr;
|
||||
use wakey_core::{DeviceQuery, NeighborEntry, NeighborState};
|
||||
use wakey_core::{InventoryQuery, NeighborEntry, NeighborState, Query};
|
||||
|
||||
/// Resolve a hostname through the local resolver and return all reported IPs.
|
||||
pub async fn get_ips(machine_name: &str) -> Result<impl Iterator<Item = IpAddr>> {
|
||||
@@ -106,16 +106,25 @@ pub async fn get_neighbors(
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience wrapper around [`get_neighbors`] using a `DeviceQuery` filter.
|
||||
pub async fn query_neighbors(query: &DeviceQuery) -> Result<Vec<NeighborEntry>> {
|
||||
get_neighbors(
|
||||
query.name.as_slice(),
|
||||
&query.filter.ips,
|
||||
&query.filter.devs,
|
||||
&query.filter.nuds,
|
||||
&query.filter.macs,
|
||||
)
|
||||
.await
|
||||
/// Convenience wrapper around [`get_neighbors`] using an `InventoryQuery` filter.
|
||||
pub async fn query_neighbors(query: &InventoryQuery) -> Result<Vec<NeighborEntry>> {
|
||||
let mut names: Vec<&str> = Vec::new();
|
||||
let mut ips = Vec::new();
|
||||
let mut devs: Vec<&str> = Vec::new();
|
||||
let mut nuds = Vec::new();
|
||||
let mut macs = Vec::new();
|
||||
|
||||
for term in query {
|
||||
match term {
|
||||
Query::Text(v) => names.push(v.as_str()),
|
||||
Query::Ip(v) => ips.push(*v),
|
||||
Query::Interface(v) => devs.push(v.as_str()),
|
||||
Query::NeighborState(v) => nuds.push(*v),
|
||||
Query::Mac(v) => macs.push(*v),
|
||||
}
|
||||
}
|
||||
|
||||
get_neighbors(&names, &ips, &devs, &nuds, &macs).await
|
||||
}
|
||||
|
||||
fn to_ipjs_state(value: NeighborState) -> lda_ipjs::subcommands::neighbor::NUDState {
|
||||
|
||||
Reference in New Issue
Block a user