1use std::borrow::Cow;
50use std::ffi::CStr;
51use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
52use std::ptr::{null, null_mut};
53use std::time::{Duration, SystemTime, UNIX_EPOCH};
54
55use crate::ffi::{
56 http, sa_family_t, vsa_suckaddr_len, vtim_dur, vtim_real, VSA_BuildFAP, VSA_GetPtr, VSA_Port,
57 PF_INET, PF_INET6, VCL_ACL, VCL_BACKEND, VCL_BLOB, VCL_BODY, VCL_BOOL, VCL_DURATION, VCL_ENUM,
58 VCL_HEADER, VCL_HTTP, VCL_INT, VCL_IP, VCL_PROBE, VCL_REAL, VCL_REGEX, VCL_STEVEDORE,
59 VCL_STRANDS, VCL_STRING, VCL_SUB, VCL_TIME, VCL_VCL,
60};
61
62use crate::vcl::{
63 from_vcl_probe, into_vcl_probe, subroutine::Subroutine, BackendRef, CowProbe, Probe, VclError,
64 Workspace,
65};
66
67pub trait IntoVCL<T> {
71 fn into_vcl(self, ws: &mut Workspace) -> Result<T, VclError>;
72}
73
74macro_rules! default_null_ptr {
75 ($ident:ident) => {
76 default_null_ptr!($ident, null);
77 };
78 (mut $ident:ident) => {
79 default_null_ptr!($ident, null_mut);
80 };
81 ($ident:ident, $func:ident) => {
82 impl Default for $ident {
83 fn default() -> Self {
84 $ident($func())
85 }
86 }
87 };
88}
89
90macro_rules! into_vcl_using_from {
91 ($rust_ty:ty, $vcl_ty:ident) => {
92 impl IntoVCL<$vcl_ty> for $rust_ty {
93 fn into_vcl(self, _: &mut Workspace) -> Result<$vcl_ty, VclError> {
94 Ok(self.into())
95 }
96 }
97 };
98}
99
100macro_rules! from_rust_to_vcl {
101 ($rust_ty:ty, $vcl_ty:ident) => {
102 impl From<$rust_ty> for $vcl_ty {
103 fn from(b: $rust_ty) -> Self {
104 Self(b.into())
105 }
106 }
107 };
108}
109
110macro_rules! from_vcl_to_opt_rust {
111 ($vcl_ty:ident, $rust_ty:ty) => {
112 impl From<$vcl_ty> for Option<$rust_ty> {
113 fn from(b: $vcl_ty) -> Self {
114 Some(b.into())
115 }
116 }
117 };
118}
119
120default_null_ptr!(VCL_ACL);
122
123default_null_ptr!(VCL_BLOB);
125impl From<VCL_BLOB> for &[u8] {
126 fn from(value: VCL_BLOB) -> Self {
127 if value.0.is_null() {
128 return &[];
129 }
130
131 unsafe {
132 let blob = &*value.0;
133 if blob.blob.is_null() || blob.len == 0 {
134 &[]
135 } else {
136 std::slice::from_raw_parts(blob.blob.cast::<u8>(), blob.len)
137 }
138 }
139 }
140}
141from_vcl_to_opt_rust!(VCL_BLOB, &[u8]);
142
143default_null_ptr!(VCL_BODY);
145
146into_vcl_using_from!(bool, VCL_BOOL);
150from_rust_to_vcl!(bool, VCL_BOOL);
151from_vcl_to_opt_rust!(VCL_BOOL, bool);
152impl From<VCL_BOOL> for bool {
153 fn from(b: VCL_BOOL) -> Self {
154 b.0 != 0
155 }
156}
157
158into_vcl_using_from!(Duration, VCL_DURATION);
162from_vcl_to_opt_rust!(VCL_DURATION, Duration);
163impl From<VCL_DURATION> for Duration {
164 fn from(value: VCL_DURATION) -> Self {
165 value.0.into()
166 }
167}
168impl From<Duration> for VCL_DURATION {
169 fn from(value: Duration) -> Self {
170 Self(value.into())
171 }
172}
173
174impl From<vtim_dur> for Duration {
178 fn from(value: vtim_dur) -> Self {
179 Self::from_secs_f64(value.0)
180 }
181}
182impl From<Duration> for vtim_dur {
183 fn from(value: Duration) -> Self {
184 Self(value.as_secs_f64())
185 }
186}
187
188default_null_ptr!(VCL_ENUM);
190default_null_ptr!(VCL_HEADER);
192default_null_ptr!(mut VCL_HTTP);
194impl From<*mut http> for VCL_HTTP {
195 fn from(value: *mut http) -> Self {
197 Self(value)
198 }
199}
200
201into_vcl_using_from!(i64, VCL_INT);
205from_rust_to_vcl!(i64, VCL_INT);
206from_vcl_to_opt_rust!(VCL_INT, i64);
207impl From<VCL_INT> for i64 {
208 fn from(b: VCL_INT) -> Self {
209 b.0
210 }
211}
212
213default_null_ptr!(VCL_IP);
217impl From<VCL_IP> for Option<SocketAddr> {
218 fn from(value: VCL_IP) -> Self {
219 let value = value.0;
220 if value.is_null() {
221 return None;
222 }
223 unsafe {
224 let mut ptr = null();
225 let fam = VSA_GetPtr(value, &raw mut ptr) as u32;
226 let port = VSA_Port(value) as u16;
227
228 match fam {
229 PF_INET => {
230 let buf: &[u8; 4] = std::slice::from_raw_parts(ptr.cast::<u8>(), 4)
231 .try_into()
232 .expect("IPv4 address bytes slice must always be 4 bytes");
233 Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(*buf)), port))
234 }
235 PF_INET6 => {
236 let buf: &[u8; 16] = std::slice::from_raw_parts(ptr.cast::<u8>(), 16)
237 .try_into()
238 .expect("IPv6 address bytes slice must always be 16 bytes");
239 Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(*buf)), port))
240 }
241 _ => None,
242 }
243 }
244 }
245}
246
247default_null_ptr!(VCL_PROBE);
251impl IntoVCL<VCL_PROBE> for CowProbe<'_> {
252 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
253 into_vcl_probe(self, ws)
254 }
255}
256impl IntoVCL<VCL_PROBE> for Probe {
257 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
258 into_vcl_probe(self, ws)
259 }
260}
261impl From<VCL_PROBE> for Option<CowProbe<'_>> {
262 fn from(value: VCL_PROBE) -> Self {
263 from_vcl_probe(value)
264 }
265}
266impl From<VCL_PROBE> for Option<Probe> {
267 fn from(value: VCL_PROBE) -> Self {
268 from_vcl_probe(value)
269 }
270}
271
272into_vcl_using_from!(f64, VCL_REAL);
276from_rust_to_vcl!(f64, VCL_REAL);
277from_vcl_to_opt_rust!(VCL_REAL, f64);
278impl From<VCL_REAL> for f64 {
279 fn from(b: VCL_REAL) -> Self {
280 b.0
281 }
282}
283
284default_null_ptr!(VCL_STRING);
288impl IntoVCL<VCL_STRING> for &str {
289 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
290 Ok(VCL_STRING(ws.copy_bytes_with_null(self.as_bytes())?.b))
291 }
292}
293impl IntoVCL<VCL_STRING> for &CStr {
294 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
295 ws.copy_cstr(self)
296 }
297}
298impl IntoVCL<VCL_STRING> for &Cow<'_, str> {
299 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
300 Ok(VCL_STRING(ws.copy_bytes_with_null(self.as_bytes())?.b))
301 }
302}
303impl IntoVCL<VCL_STRING> for String {
304 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
305 self.as_str().into_vcl(ws)
306 }
307}
308impl<T: IntoVCL<VCL_STRING>> IntoVCL<VCL_STRING> for Option<T> {
309 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
310 match self {
311 None => Ok(VCL_STRING(null())),
312 Some(t) => t.into_vcl(ws),
313 }
314 }
315}
316impl From<VCL_STRING> for Option<&CStr> {
317 fn from(value: VCL_STRING) -> Self {
318 if value.0.is_null() {
319 None
320 } else {
321 Some(unsafe { CStr::from_ptr(value.0) })
322 }
323 }
324}
325impl From<VCL_STRING> for &CStr {
326 fn from(value: VCL_STRING) -> Self {
327 <Option<&CStr>>::from(value).unwrap_or_default()
329 }
330}
331impl TryFrom<VCL_STRING> for Option<&str> {
332 type Error = VclError;
333 fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
334 Ok(<Option<&CStr>>::from(value).map(CStr::to_str).transpose()?)
335 }
336}
337impl<'a> TryFrom<VCL_STRING> for &'a str {
338 type Error = VclError;
339 fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
340 Ok(<Option<&'a str>>::try_from(value)?.unwrap_or(""))
341 }
342}
343
344default_null_ptr!(VCL_STEVEDORE);
346default_null_ptr!(VCL_STRANDS);
348
349impl From<VCL_TIME> for SystemTime {
353 fn from(value: VCL_TIME) -> Self {
354 let secs = value.0 .0;
356
357 if !secs.is_finite() {
359 return UNIX_EPOCH;
360 }
361
362 if secs >= 0.0 {
363 Duration::try_from_secs_f64(secs)
364 .ok()
365 .and_then(|dur| UNIX_EPOCH.checked_add(dur))
366 .unwrap_or(UNIX_EPOCH)
367 } else {
368 Duration::try_from_secs_f64(-secs)
370 .ok()
371 .and_then(|dur| UNIX_EPOCH.checked_sub(dur))
372 .unwrap_or(UNIX_EPOCH)
373 }
374 }
375}
376
377impl IntoVCL<VCL_TIME> for SystemTime {
378 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_TIME, VclError> {
379 self.try_into()
380 }
381}
382
383impl TryFrom<SystemTime> for VCL_TIME {
384 type Error = VclError;
385
386 fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
387 Ok(VCL_TIME(vtim_real(
388 value
389 .duration_since(SystemTime::UNIX_EPOCH)
390 .map_err(|e| VclError::new(e.to_string()))?
391 .as_secs_f64(),
392 )))
393 }
394}
395
396default_null_ptr!(mut VCL_VCL);
398
399default_null_ptr!(VCL_BACKEND);
401
402use std::ffi::c_void;
403use std::num::NonZeroUsize;
404use std::ptr;
405
406impl IntoVCL<VCL_BACKEND> for BackendRef {
407 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_BACKEND, VclError> {
408 unsafe { Ok(self.vcl_ptr()) }
409 }
410}
411
412impl IntoVCL<VCL_BACKEND> for Option<BackendRef> {
413 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_BACKEND, VclError> {
414 unsafe { Ok(self.map_or(VCL_BACKEND(null()), |b: BackendRef| b.vcl_ptr())) }
415 }
416}
417
418impl From<VCL_BACKEND> for Option<BackendRef> {
419 fn from(value: VCL_BACKEND) -> Self {
420 unsafe { BackendRef::new(value) }
421 }
422}
423
424default_null_ptr!(VCL_SUB);
426impl From<VCL_SUB> for Subroutine {
427 fn from(value: VCL_SUB) -> Self {
428 assert!(!value.0.is_null(), "VCL_SUB must not be null");
429 Subroutine(value)
430 }
431}
432
433impl IntoVCL<VCL_SUB> for Subroutine {
434 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_SUB, VclError> {
435 Ok(self.vcl_ptr())
436 }
437}
438
439default_null_ptr!(VCL_REGEX);
440
441unsafe fn write_ip_to_ptr(ip: SocketAddr, p: *mut c_void) {
442 match ip {
443 SocketAddr::V4(sa) => {
444 assert!(!VSA_BuildFAP(
445 p,
446 PF_INET as sa_family_t,
447 sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
448 4,
449 ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
450 2
451 )
452 .is_null());
453 }
454 SocketAddr::V6(sa) => {
455 assert!(!VSA_BuildFAP(
456 p,
457 PF_INET6 as sa_family_t,
458 sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
459 16,
460 ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
461 2
462 )
463 .is_null());
464 }
465 }
466}
467
468pub(crate) unsafe fn write_ip_to_buf(ip: SocketAddr, buf: &mut [u8]) {
469 assert_eq!(buf.len(), vsa_suckaddr_len);
470 write_ip_to_ptr(ip, buf.as_mut_ptr().cast::<c_void>());
471}
472impl IntoVCL<VCL_IP> for SocketAddr {
473 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_IP, VclError> {
474 unsafe {
475 let size =
478 NonZeroUsize::new(vsa_suckaddr_len).expect("vsa_suckaddr_len must be non-zero");
479 let p = ws.alloc(size);
480 if p.is_null() {
481 Err(VclError::WsOutOfMemory(size))?;
482 }
483
484 let buf = std::slice::from_raw_parts_mut(p.cast::<u8>(), vsa_suckaddr_len);
485 write_ip_to_buf(self, buf);
486
487 Ok(VCL_IP(p.cast()))
488 }
489 }
490}