use std::borrow::Cow;
use std::ffi::{c_char, c_void, CStr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::num::NonZeroUsize;
use std::ptr;
use std::ptr::{null, null_mut};
use std::time::{Duration, SystemTime};
use crate::ffi::{
sa_family_t, vsa_suckaddr_len, vtim_dur, vtim_real, VSA_BuildFAP, VSA_GetPtr, VSA_Port,
PF_INET, PF_INET6, VCL_ACL, VCL_BACKEND, VCL_BLOB, VCL_BODY, VCL_BOOL, VCL_DURATION, VCL_ENUM,
VCL_HEADER, VCL_HTTP, VCL_INT, VCL_IP, VCL_PROBE, VCL_REAL, VCL_REGEX, VCL_STEVEDORE,
VCL_STRANDS, VCL_STRING, VCL_SUB, VCL_TIME, VCL_VCL,
};
use crate::vcl::{from_vcl_probe, into_vcl_probe, CowProbe, Probe, VclError, Workspace};
pub trait IntoVCL<T> {
fn into_vcl(self, ws: &mut Workspace) -> Result<T, VclError>;
}
macro_rules! default_null_ptr {
($ident:ident) => {
default_null_ptr!($ident, null);
};
(mut $ident:ident) => {
default_null_ptr!($ident, null_mut);
};
($ident:ident, $func:ident) => {
impl Default for $ident {
fn default() -> Self {
$ident($func())
}
}
};
}
macro_rules! into_vcl_using_from {
($rust_ty:ty, $vcl_ty:ident) => {
impl IntoVCL<$vcl_ty> for $rust_ty {
fn into_vcl(self, _: &mut Workspace) -> Result<$vcl_ty, VclError> {
Ok(self.into())
}
}
};
}
macro_rules! from_rust_to_vcl {
($rust_ty:ty, $vcl_ty:ident) => {
impl From<$rust_ty> for $vcl_ty {
fn from(b: $rust_ty) -> Self {
Self(b.into())
}
}
};
}
macro_rules! from_vcl_to_opt_rust {
($vcl_ty:ident, $rust_ty:ty) => {
impl From<$vcl_ty> for Option<$rust_ty> {
fn from(b: $vcl_ty) -> Self {
Some(b.into())
}
}
};
}
default_null_ptr!(VCL_ACL);
default_null_ptr!(VCL_BACKEND);
default_null_ptr!(VCL_BLOB);
default_null_ptr!(VCL_BODY);
into_vcl_using_from!(bool, VCL_BOOL);
from_rust_to_vcl!(bool, VCL_BOOL);
from_vcl_to_opt_rust!(VCL_BOOL, bool);
impl From<VCL_BOOL> for bool {
fn from(b: VCL_BOOL) -> Self {
b.0 != 0
}
}
into_vcl_using_from!(Duration, VCL_DURATION);
from_vcl_to_opt_rust!(VCL_DURATION, Duration);
impl From<VCL_DURATION> for Duration {
fn from(value: VCL_DURATION) -> Self {
value.0.into()
}
}
impl From<Duration> for VCL_DURATION {
fn from(value: Duration) -> Self {
Self(value.into())
}
}
impl From<vtim_dur> for Duration {
fn from(value: vtim_dur) -> Self {
Self::from_secs_f64(value.0)
}
}
impl From<Duration> for vtim_dur {
fn from(value: Duration) -> Self {
Self(value.as_secs_f64())
}
}
default_null_ptr!(VCL_ENUM);
default_null_ptr!(VCL_HEADER);
default_null_ptr!(mut VCL_HTTP);
into_vcl_using_from!(i64, VCL_INT);
from_rust_to_vcl!(i64, VCL_INT);
from_vcl_to_opt_rust!(VCL_INT, i64);
impl From<VCL_INT> for i64 {
fn from(b: VCL_INT) -> Self {
b.0
}
}
default_null_ptr!(VCL_IP);
impl IntoVCL<VCL_IP> for SocketAddr {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_IP, VclError> {
unsafe {
let size = NonZeroUsize::new(vsa_suckaddr_len).unwrap();
let p = ws.alloc(size);
if p.is_null() {
Err(VclError::WsOutOfMemory(size))?;
}
match self {
SocketAddr::V4(sa) => {
assert!(!VSA_BuildFAP(
p,
PF_INET as sa_family_t,
sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
4,
ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
2
)
.is_null());
}
SocketAddr::V6(sa) => {
assert!(!VSA_BuildFAP(
p,
PF_INET6 as sa_family_t,
sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
16,
ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
2
)
.is_null());
}
}
Ok(VCL_IP(p.cast()))
}
}
}
impl From<VCL_IP> for Option<SocketAddr> {
fn from(value: VCL_IP) -> Self {
let value = value.0;
if value.is_null() {
return None;
}
unsafe {
let mut ptr = null();
let fam = VSA_GetPtr(value, &mut ptr) as u32;
let port = VSA_Port(value) as u16;
match fam {
PF_INET => {
let buf: &[u8; 4] = std::slice::from_raw_parts(ptr.cast::<u8>(), 4)
.try_into()
.unwrap();
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(*buf)), port))
}
PF_INET6 => {
let buf: &[u8; 16] = std::slice::from_raw_parts(ptr.cast::<u8>(), 16)
.try_into()
.unwrap();
Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(*buf)), port))
}
_ => None,
}
}
}
}
default_null_ptr!(VCL_PROBE);
impl<'a> IntoVCL<VCL_PROBE> for CowProbe<'a> {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
into_vcl_probe(self, ws)
}
}
impl IntoVCL<VCL_PROBE> for Probe {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
into_vcl_probe(self, ws)
}
}
impl<'a> From<VCL_PROBE> for Option<CowProbe<'a>> {
fn from(value: VCL_PROBE) -> Self {
from_vcl_probe(value)
}
}
impl From<VCL_PROBE> for Option<Probe> {
fn from(value: VCL_PROBE) -> Self {
from_vcl_probe(value)
}
}
into_vcl_using_from!(f64, VCL_REAL);
from_rust_to_vcl!(f64, VCL_REAL);
from_vcl_to_opt_rust!(VCL_REAL, f64);
impl From<VCL_REAL> for f64 {
fn from(b: VCL_REAL) -> Self {
b.0
}
}
default_null_ptr!(VCL_REGEX);
default_null_ptr!(VCL_STRING);
impl IntoVCL<VCL_STRING> for &[u8] {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
if ws.contains(self) {
Ok(VCL_STRING(self.as_ptr().cast::<c_char>()))
} else {
Ok(VCL_STRING(ws.copy_bytes_with_null(self)?.b))
}
}
}
impl IntoVCL<VCL_STRING> for &str {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
self.as_bytes().into_vcl(ws)
}
}
impl IntoVCL<VCL_STRING> for &CStr {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
ws.copy_cstr(self)
}
}
impl IntoVCL<VCL_STRING> for &Cow<'_, str> {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
self.as_bytes().into_vcl(ws)
}
}
impl IntoVCL<VCL_STRING> for String {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
self.as_str().into_vcl(ws)
}
}
impl<T: IntoVCL<VCL_STRING> + AsRef<[u8]>> IntoVCL<VCL_STRING> for Option<T> {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
match self {
None => Ok(VCL_STRING(null())),
Some(t) => t.as_ref().into_vcl(ws),
}
}
}
impl From<VCL_STRING> for Option<&CStr> {
fn from(value: VCL_STRING) -> Self {
if value.0.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(value.0) })
}
}
}
impl From<VCL_STRING> for &CStr {
fn from(value: VCL_STRING) -> Self {
<Option<&CStr>>::from(value).unwrap_or_default()
}
}
impl<'a> TryFrom<VCL_STRING> for Option<&'a str> {
type Error = VclError;
fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
Ok(<Option<&CStr>>::from(value).map(CStr::to_str).transpose()?)
}
}
impl<'a> TryFrom<VCL_STRING> for &'a str {
type Error = VclError;
fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
Ok(<Option<&'a str>>::try_from(value)?.unwrap_or(""))
}
}
default_null_ptr!(VCL_STEVEDORE);
default_null_ptr!(VCL_STRANDS);
default_null_ptr!(VCL_SUB);
impl IntoVCL<VCL_TIME> for SystemTime {
fn into_vcl(self, _: &mut Workspace) -> Result<VCL_TIME, VclError> {
self.try_into()
}
}
impl TryFrom<SystemTime> for VCL_TIME {
type Error = VclError;
fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
Ok(VCL_TIME(vtim_real(
value
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|e| VclError::new(e.to_string()))?
.as_secs_f64(),
)))
}
}
default_null_ptr!(mut VCL_VCL);