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, Acl, BackendRef, CowProbe, Probe,
64 VclError, 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);
125
126from_vcl_to_opt_rust!(VCL_ACL, Acl);
127impl From<VCL_ACL> for Acl {
128 fn from(value: VCL_ACL) -> Acl {
129 assert!(!value.0.is_null());
130 Acl { raw: value }
131 }
132}
133
134impl IntoVCL<VCL_ACL> for Acl {
135 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_ACL, VclError> {
136 unsafe {
137 assert!(!self.vcl_ptr().0.is_null());
138 Ok(self.vcl_ptr())
139 }
140 }
141}
142
143default_null_ptr!(VCL_BLOB);
145impl From<VCL_BLOB> for &[u8] {
146 fn from(value: VCL_BLOB) -> Self {
147 if value.0.is_null() {
148 return &[];
149 }
150
151 unsafe {
152 let blob = &*value.0;
153 if blob.blob.is_null() || blob.len == 0 {
154 &[]
155 } else {
156 std::slice::from_raw_parts(blob.blob.cast::<u8>(), blob.len)
157 }
158 }
159 }
160}
161from_vcl_to_opt_rust!(VCL_BLOB, &[u8]);
162
163default_null_ptr!(VCL_BODY);
165
166into_vcl_using_from!(bool, VCL_BOOL);
170from_rust_to_vcl!(bool, VCL_BOOL);
171from_vcl_to_opt_rust!(VCL_BOOL, bool);
172impl From<VCL_BOOL> for bool {
173 fn from(b: VCL_BOOL) -> Self {
174 b.0 != 0
175 }
176}
177
178into_vcl_using_from!(Duration, VCL_DURATION);
182from_vcl_to_opt_rust!(VCL_DURATION, Duration);
183impl From<VCL_DURATION> for Duration {
184 fn from(value: VCL_DURATION) -> Self {
185 value.0.into()
186 }
187}
188impl From<Duration> for VCL_DURATION {
189 fn from(value: Duration) -> Self {
190 Self(value.into())
191 }
192}
193
194impl From<vtim_dur> for Duration {
198 fn from(value: vtim_dur) -> Self {
199 Self::from_secs_f64(value.0)
200 }
201}
202impl From<Duration> for vtim_dur {
203 fn from(value: Duration) -> Self {
204 Self(value.as_secs_f64())
205 }
206}
207
208default_null_ptr!(VCL_ENUM);
210default_null_ptr!(VCL_HEADER);
212default_null_ptr!(mut VCL_HTTP);
214impl From<*mut http> for VCL_HTTP {
215 fn from(value: *mut http) -> Self {
217 Self(value)
218 }
219}
220
221into_vcl_using_from!(i64, VCL_INT);
225from_rust_to_vcl!(i64, VCL_INT);
226from_vcl_to_opt_rust!(VCL_INT, i64);
227impl From<VCL_INT> for i64 {
228 fn from(b: VCL_INT) -> Self {
229 b.0
230 }
231}
232
233default_null_ptr!(VCL_IP);
237impl From<VCL_IP> for Option<SocketAddr> {
238 fn from(value: VCL_IP) -> Self {
239 let value = value.0;
240 if value.is_null() {
241 return None;
242 }
243 unsafe {
244 let mut ptr = null();
245 let fam = VSA_GetPtr(value, &raw mut ptr) as u32;
246 let port = VSA_Port(value) as u16;
247
248 match fam {
249 PF_INET => {
250 let buf: &[u8; 4] = std::slice::from_raw_parts(ptr.cast::<u8>(), 4)
251 .try_into()
252 .expect("IPv4 address bytes slice must always be 4 bytes");
253 Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(*buf)), port))
254 }
255 PF_INET6 => {
256 let buf: &[u8; 16] = std::slice::from_raw_parts(ptr.cast::<u8>(), 16)
257 .try_into()
258 .expect("IPv6 address bytes slice must always be 16 bytes");
259 Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(*buf)), port))
260 }
261 _ => None,
262 }
263 }
264 }
265}
266
267default_null_ptr!(VCL_PROBE);
271impl IntoVCL<VCL_PROBE> for CowProbe<'_> {
272 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
273 into_vcl_probe(self, ws)
274 }
275}
276impl IntoVCL<VCL_PROBE> for Probe {
277 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
278 into_vcl_probe(self, ws)
279 }
280}
281impl From<VCL_PROBE> for Option<CowProbe<'_>> {
282 fn from(value: VCL_PROBE) -> Self {
283 from_vcl_probe(value)
284 }
285}
286impl From<VCL_PROBE> for Option<Probe> {
287 fn from(value: VCL_PROBE) -> Self {
288 from_vcl_probe(value)
289 }
290}
291
292into_vcl_using_from!(f64, VCL_REAL);
296from_rust_to_vcl!(f64, VCL_REAL);
297from_vcl_to_opt_rust!(VCL_REAL, f64);
298impl From<VCL_REAL> for f64 {
299 fn from(b: VCL_REAL) -> Self {
300 b.0
301 }
302}
303
304default_null_ptr!(VCL_STRING);
308impl IntoVCL<VCL_STRING> for &str {
309 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
310 Ok(VCL_STRING(ws.copy_bytes_with_null(self.as_bytes())?.b))
311 }
312}
313impl IntoVCL<VCL_STRING> for &CStr {
314 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
315 ws.copy_cstr(self)
316 }
317}
318impl IntoVCL<VCL_STRING> for &Cow<'_, str> {
319 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
320 Ok(VCL_STRING(ws.copy_bytes_with_null(self.as_bytes())?.b))
321 }
322}
323impl IntoVCL<VCL_STRING> for String {
324 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
325 self.as_str().into_vcl(ws)
326 }
327}
328impl<T: IntoVCL<VCL_STRING>> IntoVCL<VCL_STRING> for Option<T> {
329 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
330 match self {
331 None => Ok(VCL_STRING(null())),
332 Some(t) => t.into_vcl(ws),
333 }
334 }
335}
336impl From<VCL_STRING> for Option<&CStr> {
337 fn from(value: VCL_STRING) -> Self {
338 if value.0.is_null() {
339 None
340 } else {
341 Some(unsafe { CStr::from_ptr(value.0) })
342 }
343 }
344}
345impl From<VCL_STRING> for &CStr {
346 fn from(value: VCL_STRING) -> Self {
347 <Option<&CStr>>::from(value).unwrap_or_default()
349 }
350}
351impl TryFrom<VCL_STRING> for Option<&str> {
352 type Error = VclError;
353 fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
354 Ok(<Option<&CStr>>::from(value).map(CStr::to_str).transpose()?)
355 }
356}
357impl<'a> TryFrom<VCL_STRING> for &'a str {
358 type Error = VclError;
359 fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
360 Ok(<Option<&'a str>>::try_from(value)?.unwrap_or(""))
361 }
362}
363
364default_null_ptr!(VCL_STEVEDORE);
366default_null_ptr!(VCL_STRANDS);
368
369impl From<VCL_TIME> for SystemTime {
373 fn from(value: VCL_TIME) -> Self {
374 let secs = value.0 .0;
376
377 if !secs.is_finite() {
379 return UNIX_EPOCH;
380 }
381
382 if secs >= 0.0 {
383 Duration::try_from_secs_f64(secs)
384 .ok()
385 .and_then(|dur| UNIX_EPOCH.checked_add(dur))
386 .unwrap_or(UNIX_EPOCH)
387 } else {
388 Duration::try_from_secs_f64(-secs)
390 .ok()
391 .and_then(|dur| UNIX_EPOCH.checked_sub(dur))
392 .unwrap_or(UNIX_EPOCH)
393 }
394 }
395}
396
397impl IntoVCL<VCL_TIME> for SystemTime {
398 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_TIME, VclError> {
399 self.try_into()
400 }
401}
402
403impl TryFrom<SystemTime> for VCL_TIME {
404 type Error = VclError;
405
406 fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
407 Ok(VCL_TIME(vtim_real(
408 value
409 .duration_since(SystemTime::UNIX_EPOCH)
410 .map_err(|e| VclError::new(e.to_string()))?
411 .as_secs_f64(),
412 )))
413 }
414}
415
416default_null_ptr!(mut VCL_VCL);
418
419default_null_ptr!(VCL_BACKEND);
421
422use std::ffi::c_void;
423use std::num::NonZeroUsize;
424use std::ptr;
425
426impl IntoVCL<VCL_BACKEND> for BackendRef {
427 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_BACKEND, VclError> {
428 unsafe { Ok(self.vcl_ptr()) }
429 }
430}
431
432impl IntoVCL<VCL_BACKEND> for Option<BackendRef> {
433 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_BACKEND, VclError> {
434 unsafe { Ok(self.map_or(VCL_BACKEND(null()), |b: BackendRef| b.vcl_ptr())) }
435 }
436}
437
438impl From<VCL_BACKEND> for Option<BackendRef> {
439 fn from(value: VCL_BACKEND) -> Self {
440 unsafe { BackendRef::new(value) }
441 }
442}
443
444default_null_ptr!(VCL_SUB);
446impl From<VCL_SUB> for Subroutine {
447 fn from(value: VCL_SUB) -> Self {
448 assert!(!value.0.is_null(), "VCL_SUB must not be null");
449 Subroutine(value)
450 }
451}
452
453impl IntoVCL<VCL_SUB> for Subroutine {
454 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_SUB, VclError> {
455 Ok(self.vcl_ptr())
456 }
457}
458
459default_null_ptr!(VCL_REGEX);
460
461unsafe fn write_ip_to_ptr(ip: SocketAddr, p: *mut c_void) {
462 match ip {
463 SocketAddr::V4(sa) => {
464 assert!(!VSA_BuildFAP(
465 p,
466 PF_INET as sa_family_t,
467 sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
468 4,
469 ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
470 2
471 )
472 .is_null());
473 }
474 SocketAddr::V6(sa) => {
475 assert!(!VSA_BuildFAP(
476 p,
477 PF_INET6 as sa_family_t,
478 sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
479 16,
480 ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
481 2
482 )
483 .is_null());
484 }
485 }
486}
487
488pub(crate) unsafe fn write_ip_to_buf(ip: SocketAddr, buf: &mut [u8]) {
489 assert_eq!(buf.len(), vsa_suckaddr_len);
490 write_ip_to_ptr(ip, buf.as_mut_ptr().cast::<c_void>());
491}
492impl IntoVCL<VCL_IP> for SocketAddr {
493 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_IP, VclError> {
494 unsafe {
495 let size =
498 NonZeroUsize::new(vsa_suckaddr_len).expect("vsa_suckaddr_len must be non-zero");
499 let p = ws.alloc(size);
500 if p.is_null() {
501 Err(VclError::WsOutOfMemory(size))?;
502 }
503
504 let buf = std::slice::from_raw_parts_mut(p.cast::<u8>(), vsa_suckaddr_len);
505 write_ip_to_buf(self, buf);
506
507 Ok(VCL_IP(p.cast()))
508 }
509 }
510}