the start of Greatness.

This commit is contained in:
lda
2025-10-21 01:06:12 +07:00 Unverified
parent 1307cec52b
commit d7c34ee9f5
11 changed files with 206 additions and 14 deletions
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "lda-ipjs"
description = "ip -j show schemas"
version = "0.0.1"
edition = "2024"
[dependencies]
macaddr = { version = "1.0.1", features = ["serde", "serde_std"] }
strum = { version = "0.27.2", features = ["derive", "strum_macros"] }
serde_json = "1.0.143"
serde_with = { version = "3.14.0", features = ["json"] }
serde = { version = "1.0.219", features = ["derive"] }
+19
View File
@@ -0,0 +1,19 @@
//! low
//!
//! # lda-ipj
//!
//! this package will represent all my needs with the all the subcommands of ip -j.
//!
//! ## what i need
//!
//! ```console
//! ip -j neigh
//! ```
//!
//! i also need to see devices and idk MAYBE maybe not MAYBE UHHHHHH maybe broadcast
//!
pub mod neighbor;
pub mod utils;
// i want a generalized way to build and call
+72
View File
@@ -0,0 +1,72 @@
//! ```
//! ip -j n s
//! ```
//!
//! yes. this is a real call.
use crate::utils::serialize::mac::{des_opm, ser_opm};
use std::{borrow::Cow, net::IpAddr};
use macaddr::MacAddr;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct NeighborInput {
/// supports only the last item (ignore), `to` keyword is optional
to: IpAddr,
/// supports only one item (it complains)
dev: String, // im all for simplicity
/// takes multiple, has to have `nud` before bro or it will think you `to`
nud: Vec<NUDState>,
}
// as input this must be lowercase. as output it is uppercase
#[derive(Debug, PartialEq, Eq, EnumString, Display, Clone, Copy, Hash, Serialize, Deserialize)]
#[strum(serialize_all = "lowercase", ascii_case_insensitive)]
#[serde(rename_all = "UPPERCASE")]
pub enum NUDState {
/// the neighbour entry is valid forever and can
/// be only be removed administratively.
Permanent,
/// the neighbour entry is valid. No attempts to
/// validate this entry will be made but it can
/// be removed when its lifetime expires.
Noarp,
/// the neighbour entry is valid until the
/// reachability timeout expires.
Reachable,
/// the neighbour entry is valid but suspicious.
/// This option to ip neigh does not change the
/// neighbour state if it was valid and the
/// address is not changed by this command.
Stale,
/// this is a pseudo state used when initially
/// creating a neighbour entry or after trying to
/// remove it before it becomes free to do so.
None,
/// the neighbour entry has not (yet) been
/// validated/resolved.
Incomplete,
/// neighbor entry validation is currently
/// delayed.
Delay,
/// neighbor is being probed.
Probe,
/// max number of probes exceeded without
/// success, neighbor validation has ultimately
/// failed.
Failed,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct NeighborItem {
dst: IpAddr,
dev: String,
#[serde(deserialize_with = "des_opm", serialize_with = "ser_opm")]
lladdr: Option<MacAddr>,
state: Vec<NUDState>,
}
+1
View File
@@ -0,0 +1 @@
pub mod serialize;
+44
View File
@@ -0,0 +1,44 @@
use macaddr::MacAddr;
use serde::{self, Deserialize, Deserializer, de::Error as DeError};
use serde::{Serialize, Serializer, de};
pub fn serialize_macs<S>(macs: &[MacAddr], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let strings: Vec<String> = macs.iter().map(|m| m.to_string()).collect();
serde::Serialize::serialize(&strings, serializer)
}
/// Serialize a MacAddr as a string
pub fn serialize_mac<S>(mac: &MacAddr, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&mac.to_string())
}
/// Deserialize a MacAddr from a string
pub fn deserialize_mac<'de, D>(deserializer: D) -> Result<MacAddr, D::Error>
where
D: Deserializer<'de>,
{
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
s.parse::<MacAddr>().map_err(DeError::custom)
}
/// serialize an [`Option<MacAddr>`]
pub fn ser_opm<S: Serializer>(bro: &Option<MacAddr>, ser: S) -> Result<S::Ok, S::Error> {
Option::<String>::serialize(&bro.as_ref().map(ToString::to_string), ser)
}
/// deserialize an [`Option<MacAddr>`]
pub fn des_opm<'de, D>(des: D) -> Result<Option<MacAddr>, D::Error>
where
D: serde::Deserializer<'de>,
{
Option::<&str>::deserialize(des)?
.map(str::parse)
.transpose()
.map_err(de::Error::custom)
}
+2
View File
@@ -0,0 +1,2 @@
pub mod mac;
pub mod vec;
+37
View File
@@ -0,0 +1,37 @@
use serde::Deserialize;
use serde::de;
#[derive(Deserialize)]
#[serde(untagged)]
enum OneOrMany<T> {
One(T),
Many(Vec<T>),
}
pub fn vec_from_strs<'de, D, T>(des: D) -> Result<Vec<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: std::str::FromStr,
T::Err: std::fmt::Display,
{
let raw: OneOrMany<String> = OneOrMany::<String>::deserialize(des)?;
let mut out = Vec::new();
match raw {
OneOrMany::One(s) => {
let t = s.trim();
if !t.is_empty() {
out.push(t.parse().map_err(de::Error::custom)?);
}
}
OneOrMany::Many(vs) => {
for s in vs {
let t = s.trim();
if t.is_empty() {
continue;
}
out.push(t.parse().map_err(de::Error::custom)?);
}
}
}
Ok(out)
}