wip: refactor whole module in lib

This commit is contained in:
Jun Kurihara 2023-11-21 22:46:52 +09:00
commit f98c778a0c
No known key found for this signature in database
GPG key ID: 48ADFD173ED22B03
42 changed files with 943 additions and 531 deletions

View file

@ -0,0 +1,123 @@
/// Server name (hostname or ip address) representation in bytes-based struct
/// for searching hashmap or key list by exact or longest-prefix matching
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct ServerNameBytesExp(pub Vec<u8>); // lowercase ascii bytes
impl From<&[u8]> for ServerNameBytesExp {
fn from(b: &[u8]) -> Self {
Self(b.to_ascii_lowercase())
}
}
impl TryInto<String> for &ServerNameBytesExp {
type Error = anyhow::Error;
fn try_into(self) -> Result<String, Self::Error> {
let s = std::str::from_utf8(&self.0)?;
Ok(s.to_string())
}
}
/// Path name, like "/path/ok", represented in bytes-based struct
/// for searching hashmap or key list by exact or longest-prefix matching
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PathNameBytesExp(pub Vec<u8>); // lowercase ascii bytes
impl PathNameBytesExp {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
pub fn get<I>(&self, index: I) -> Option<&I::Output>
where
I: std::slice::SliceIndex<[u8]>,
{
self.0.get(index)
}
pub fn starts_with(&self, needle: &Self) -> bool {
self.0.starts_with(&needle.0)
}
}
impl AsRef<[u8]> for PathNameBytesExp {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
/// Trait to express names in ascii-lowercased bytes
pub trait BytesName {
type OutputSv: Send + Sync + 'static;
type OutputPath;
fn to_server_name_vec(self) -> Self::OutputSv;
fn to_path_name_vec(self) -> Self::OutputPath;
}
impl<'a, T: Into<std::borrow::Cow<'a, str>>> BytesName for T {
type OutputSv = ServerNameBytesExp;
type OutputPath = PathNameBytesExp;
fn to_server_name_vec(self) -> Self::OutputSv {
let name = self.into().bytes().collect::<Vec<u8>>().to_ascii_lowercase();
ServerNameBytesExp(name)
}
fn to_path_name_vec(self) -> Self::OutputPath {
let name = self.into().bytes().collect::<Vec<u8>>().to_ascii_lowercase();
PathNameBytesExp(name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytes_name_str_works() {
let s = "OK_string";
let bn = s.to_path_name_vec();
let bn_lc = s.to_server_name_vec();
assert_eq!(Vec::from("ok_string".as_bytes()), bn.0);
assert_eq!(Vec::from("ok_string".as_bytes()), bn_lc.0);
}
#[test]
fn from_works() {
let s = "OK_string".to_server_name_vec();
let m = ServerNameBytesExp::from("OK_strinG".as_bytes());
assert_eq!(s, m);
assert_eq!(s.0, "ok_string".as_bytes().to_vec());
assert_eq!(m.0, "ok_string".as_bytes().to_vec());
}
#[test]
fn get_works() {
let s = "OK_str".to_path_name_vec();
let i = s.get(0);
assert_eq!(Some(&"o".as_bytes()[0]), i);
let i = s.get(1);
assert_eq!(Some(&"k".as_bytes()[0]), i);
let i = s.get(2);
assert_eq!(Some(&"_".as_bytes()[0]), i);
let i = s.get(3);
assert_eq!(Some(&"s".as_bytes()[0]), i);
let i = s.get(4);
assert_eq!(Some(&"t".as_bytes()[0]), i);
let i = s.get(5);
assert_eq!(Some(&"r".as_bytes()[0]), i);
let i = s.get(6);
assert_eq!(None, i);
}
#[test]
fn start_with_works() {
let s = "OK_str".to_path_name_vec();
let correct = "OK".to_path_name_vec();
let incorrect = "KO".to_path_name_vec();
assert!(s.starts_with(&correct));
assert!(!s.starts_with(&incorrect));
}
#[test]
fn as_ref_works() {
let s = "OK_str".to_path_name_vec();
assert_eq!(s.as_ref(), "ok_str".as_bytes());
}
}

View file

@ -0,0 +1,5 @@
mod bytes_name;
mod socket_addr;
pub use bytes_name::{BytesName, PathNameBytesExp, ServerNameBytesExp};
pub use socket_addr::ToCanonical;

View file

@ -0,0 +1,60 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
pub trait ToCanonical {
fn to_canonical(&self) -> Self;
}
impl ToCanonical for SocketAddr {
fn to_canonical(&self) -> Self {
match self {
SocketAddr::V4(_) => *self,
SocketAddr::V6(v6) => match v6.ip().to_ipv4() {
Some(mapped) => {
if mapped == Ipv4Addr::new(0, 0, 0, 1) {
*self
} else {
SocketAddr::new(IpAddr::V4(mapped), self.port())
}
}
None => *self,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::Ipv6Addr;
#[test]
fn ipv4_loopback_to_canonical() {
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
assert_eq!(socket.to_canonical(), socket);
}
#[test]
fn ipv6_loopback_to_canonical() {
let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
assert_eq!(socket.to_canonical(), socket);
}
#[test]
fn ipv4_to_canonical() {
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
assert_eq!(socket.to_canonical(), socket);
}
#[test]
fn ipv6_to_canonical() {
let socket = SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0xdead, 0xbeef)),
8080,
);
assert_eq!(socket.to_canonical(), socket);
}
#[test]
fn ipv4_mapped_to_ipv6_to_canonical() {
let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff)), 8080);
assert_eq!(
socket.to_canonical(),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 10, 2, 255)), 8080)
);
}
}