use std::net::{
SocketAddr,
SocketAddrV4,
SocketAddrV6,
AddrParseError
};
use liter::column::{
Affinity,
Column
};
use rusqlite::Result as SqlResult;
use rusqlite::types::{
FromSql,
FromSqlResult,
FromSqlError,
ToSql,
ToSqlOutput,
ValueRef
};
use serde::{Serialize, Deserialize};
const DUMMY_PREFIX: &str = "dummy_addr/";
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Address {
V4UdpChaCha20(SocketAddrV4),
V6UdpChaCha20(SocketAddrV6),
Dummy(usize)
}
impl std::fmt::Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
Self::V4UdpChaCha20(addr) =>
write!(f, "IPv4-UDP-ChaCha20/{addr}"),
Self::V6UdpChaCha20(addr) =>
write!(f, "IPv6-UDP-ChaCha20/{addr}"),
Self::Dummy(addr) => write!(f, "{DUMMY_PREFIX}{addr}")
}
}
}
impl std::str::FromStr for Address {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match SocketAddr::from_str(s) {
Ok(SocketAddr::V4(addr)) => Ok(Self::V4UdpChaCha20(addr)),
Ok(SocketAddr::V6(addr)) => Ok(Self::V6UdpChaCha20(addr)),
Err(e) => Err(e)
}
}
}
impl Column for Address {
const AFFINITY: Affinity = Affinity::Text;
}
impl FromSql for Address {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
let val_as_str = value.as_str()?;
#[cfg(test)]
if let Some(dummy_addr) = val_as_str.strip_prefix(DUMMY_PREFIX) {
return dummy_addr.parse()
.map(Self::Dummy)
.map_err(Into::into)
.map_err(FromSqlError::Other)
}
val_as_str.parse()
.map_err(Into::into)
.map_err(FromSqlError::Other)
}
}
impl ToSql for Address {
fn to_sql(&self) -> SqlResult<ToSqlOutput<'_>> {
let as_string = match &self {
Self::V4UdpChaCha20(addr) => addr.to_string(),
Self::V6UdpChaCha20(addr) => addr.to_string(),
Self::Dummy(addr) => format!("{DUMMY_PREFIX}{addr}")
};
Ok(ToSqlOutput::Owned(as_string.into()))
}
}
liter::types::impl_from_to_sql_2!(Address);