123 lines
3.2 KiB
Rust
123 lines
3.2 KiB
Rust
// src/mqtt/config.rs
|
|
#![allow(dead_code)]
|
|
|
|
use embassy_net::{IpAddress, Ipv4Address, Ipv6Address};
|
|
|
|
// Compile-time values injected by build.rs
|
|
const BROKER_IP: &str = env!("BROKER_IP");
|
|
const BROKER_PORT: &str = env!("BROKER_PORT");
|
|
|
|
pub fn mqtt_broker_endpoint() -> (IpAddress, u16) {
|
|
(parse_ip(BROKER_IP), parse_port(BROKER_PORT))
|
|
}
|
|
|
|
fn parse_port(s: &str) -> u16 {
|
|
let p: u16 = s
|
|
.parse()
|
|
.unwrap_or_else(|_| panic!("BROKER_PORT must be a valid u16 (1..=65535)"));
|
|
assert!(p != 0, "BROKER_PORT cannot be 0");
|
|
p
|
|
}
|
|
|
|
fn parse_ip(s: &str) -> IpAddress {
|
|
if s.contains(':') {
|
|
IpAddress::Ipv6(parse_ipv6(s))
|
|
} else {
|
|
IpAddress::Ipv4(parse_ipv4(s))
|
|
}
|
|
}
|
|
|
|
fn parse_ipv4(s: &str) -> Ipv4Address {
|
|
let mut it = s.split('.');
|
|
let a = parse_octet(it.next(), 1);
|
|
let b = parse_octet(it.next(), 2);
|
|
let c = parse_octet(it.next(), 3);
|
|
let d = parse_octet(it.next(), 4);
|
|
assert!(it.next().is_none(), "Too many IPv4 octets");
|
|
Ipv4Address::new(a, b, c, d)
|
|
}
|
|
|
|
fn parse_octet(part: Option<&str>, idx: usize) -> u8 {
|
|
let p = part.unwrap_or_else(|| panic!("IPv4 missing octet {}", idx));
|
|
let v: u16 = p
|
|
.parse()
|
|
.unwrap_or_else(|_| panic!("Invalid IPv4 octet {}: {}", idx, p));
|
|
assert!(v <= 255, "IPv4 octet {} out of range: {}", idx, v);
|
|
v as u8
|
|
}
|
|
|
|
// Minimal IPv6 parser with '::' compression. Does not handle IPv4-embedded IPv6.
|
|
fn parse_ipv6(s: &str) -> Ipv6Address {
|
|
assert!(
|
|
!s.contains('.'),
|
|
"IPv4-embedded IPv6 like ::ffff:192.0.2.1 not supported; \
|
|
use pure hex IPv6"
|
|
);
|
|
|
|
let has_double = s.contains("::");
|
|
let (left_s, right_s) = if has_double {
|
|
let mut sp = s.splitn(2, "::");
|
|
(sp.next().unwrap_or(""), sp.next().unwrap_or(""))
|
|
} else {
|
|
(s, "")
|
|
};
|
|
|
|
let mut left = [0u16; 8];
|
|
let mut right = [0u16; 8];
|
|
let mut ll = 0usize;
|
|
let mut rl = 0usize;
|
|
|
|
if !left_s.is_empty() {
|
|
for part in left_s.split(':') {
|
|
left[ll] = parse_group(part);
|
|
ll += 1;
|
|
assert!(ll <= 8, "Too many IPv6 groups on the left");
|
|
}
|
|
}
|
|
|
|
if !right_s.is_empty() {
|
|
for part in right_s.split(':') {
|
|
right[rl] = parse_group(part);
|
|
rl += 1;
|
|
assert!(rl <= 8, "Too many IPv6 groups on the right");
|
|
}
|
|
}
|
|
|
|
let zeros = if has_double {
|
|
assert!(ll + rl < 8, "Invalid IPv6 '::' usage");
|
|
8 - (ll + rl)
|
|
} else {
|
|
assert!(ll == 8, "IPv6 must have 8 groups without '::'");
|
|
0
|
|
};
|
|
|
|
let mut g = [0u16; 8];
|
|
let mut idx = 0usize;
|
|
|
|
for i in 0..ll {
|
|
g[idx] = left[i];
|
|
idx += 1;
|
|
}
|
|
for _ in 0..zeros {
|
|
g[idx] = 0;
|
|
idx += 1;
|
|
}
|
|
for i in 0..rl {
|
|
g[idx] = right[i];
|
|
idx += 1;
|
|
}
|
|
assert!(idx == 8, "IPv6 did not resolve to 8 groups");
|
|
|
|
Ipv6Address::new(g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7])
|
|
}
|
|
|
|
fn parse_group(part: &str) -> u16 {
|
|
assert!(
|
|
!part.is_empty(),
|
|
"Empty IPv6 group (use '::' instead for compression)"
|
|
);
|
|
assert!(part.len() <= 4, "IPv6 group too long: {}", part);
|
|
u16::from_str_radix(part, 16)
|
|
.unwrap_or_else(|_| panic!("Invalid IPv6 hex group: {}", part))
|
|
}
|