use std::borrow::Cow;
use std::ffi::CStr;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::ptr::{null, null_mut};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::ffi::{
http, 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, subroutine::Subroutine, BackendRef, 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_BLOB);
impl From<VCL_BLOB> for &[u8] {
fn from(value: VCL_BLOB) -> Self {
if value.0.is_null() {
return &[];
}
unsafe {
let blob = &*value.0;
if blob.blob.is_null() || blob.len == 0 {
&[]
} else {
std::slice::from_raw_parts(blob.blob.cast::<u8>(), blob.len)
}
}
}
}
from_vcl_to_opt_rust!(VCL_BLOB, &[u8]);
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);
impl From<*mut http> for VCL_HTTP {
fn from(value: *mut http) -> Self {
Self(value)
}
}
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 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, &raw 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()
.expect("IPv4 address bytes slice must always be 4 bytes");
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()
.expect("IPv6 address bytes slice must always be 16 bytes");
Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(*buf)), port))
}
_ => None,
}
}
}
}
default_null_ptr!(VCL_PROBE);
impl IntoVCL<VCL_PROBE> for CowProbe<'_> {
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 From<VCL_PROBE> for Option<CowProbe<'_>> {
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_STRING);
impl IntoVCL<VCL_STRING> for &str {
fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
Ok(VCL_STRING(ws.copy_bytes_with_null(self.as_bytes())?.b))
}
}
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> {
Ok(VCL_STRING(ws.copy_bytes_with_null(self.as_bytes())?.b))
}
}
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>> 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.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 TryFrom<VCL_STRING> for Option<&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);
impl From<VCL_TIME> for SystemTime {
fn from(value: VCL_TIME) -> Self {
let secs = value.0 .0;
if !secs.is_finite() {
return UNIX_EPOCH;
}
if secs >= 0.0 {
Duration::try_from_secs_f64(secs)
.ok()
.and_then(|dur| UNIX_EPOCH.checked_add(dur))
.unwrap_or(UNIX_EPOCH)
} else {
Duration::try_from_secs_f64(-secs)
.ok()
.and_then(|dur| UNIX_EPOCH.checked_sub(dur))
.unwrap_or(UNIX_EPOCH)
}
}
}
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);
default_null_ptr!(VCL_BACKEND);
use std::ffi::c_void;
use std::num::NonZeroUsize;
use std::ptr;
impl IntoVCL<VCL_BACKEND> for BackendRef {
fn into_vcl(self, _: &mut Workspace) -> Result<VCL_BACKEND, VclError> {
unsafe { Ok(self.vcl_ptr()) }
}
}
impl IntoVCL<VCL_BACKEND> for Option<BackendRef> {
fn into_vcl(self, _: &mut Workspace) -> Result<VCL_BACKEND, VclError> {
unsafe { Ok(self.map_or(VCL_BACKEND(null()), |b: BackendRef| b.vcl_ptr())) }
}
}
impl From<VCL_BACKEND> for Option<BackendRef> {
fn from(value: VCL_BACKEND) -> Self {
unsafe { BackendRef::new(value) }
}
}
default_null_ptr!(VCL_SUB);
impl From<VCL_SUB> for Subroutine {
fn from(value: VCL_SUB) -> Self {
assert!(!value.0.is_null(), "VCL_SUB must not be null");
Subroutine(value)
}
}
impl IntoVCL<VCL_SUB> for Subroutine {
fn into_vcl(self, _: &mut Workspace) -> Result<VCL_SUB, VclError> {
Ok(self.vcl_ptr())
}
}
default_null_ptr!(VCL_REGEX);
unsafe fn write_ip_to_ptr(ip: SocketAddr, p: *mut c_void) {
match ip {
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());
}
}
}
pub(crate) unsafe fn write_ip_to_buf(ip: SocketAddr, buf: &mut [u8]) {
assert_eq!(buf.len(), vsa_suckaddr_len);
write_ip_to_ptr(ip, buf.as_mut_ptr().cast::<c_void>());
}
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).expect("vsa_suckaddr_len must be non-zero");
let p = ws.alloc(size);
if p.is_null() {
Err(VclError::WsOutOfMemory(size))?;
}
let buf = std::slice::from_raw_parts_mut(p.cast::<u8>(), vsa_suckaddr_len);
write_ip_to_buf(self, buf);
Ok(VCL_IP(p.cast()))
}
}
}