1use std::borrow::Cow;
47use std::ffi::{c_char, CStr};
48use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
49use std::ptr::{null, null_mut};
50use std::time::{Duration, SystemTime};
51
52use crate::ffi::{
53 http, vtim_dur, vtim_real, VSA_GetPtr, VSA_Port, PF_INET, PF_INET6, VCL_ACL, VCL_BACKEND,
54 VCL_BLOB, VCL_BODY, VCL_BOOL, VCL_DURATION, VCL_ENUM, VCL_HEADER, VCL_HTTP, VCL_INT, VCL_IP,
55 VCL_PROBE, VCL_REAL, VCL_STEVEDORE, VCL_STRANDS, VCL_STRING, VCL_TIME, VCL_VCL,
56};
57use crate::vcl::{from_vcl_probe, into_vcl_probe, CowProbe, Probe, VclError, Workspace};
58
59pub trait IntoVCL<T> {
63 fn into_vcl(self, ws: &mut Workspace) -> Result<T, VclError>;
64}
65
66macro_rules! default_null_ptr {
67 ($ident:ident) => {
68 default_null_ptr!($ident, null);
69 };
70 (mut $ident:ident) => {
71 default_null_ptr!($ident, null_mut);
72 };
73 ($ident:ident, $func:ident) => {
74 impl Default for $ident {
75 fn default() -> Self {
76 $ident($func())
77 }
78 }
79 };
80}
81
82macro_rules! into_vcl_using_from {
83 ($rust_ty:ty, $vcl_ty:ident) => {
84 impl IntoVCL<$vcl_ty> for $rust_ty {
85 fn into_vcl(self, _: &mut Workspace) -> Result<$vcl_ty, VclError> {
86 Ok(self.into())
87 }
88 }
89 };
90}
91
92macro_rules! from_rust_to_vcl {
93 ($rust_ty:ty, $vcl_ty:ident) => {
94 impl From<$rust_ty> for $vcl_ty {
95 fn from(b: $rust_ty) -> Self {
96 Self(b.into())
97 }
98 }
99 };
100}
101
102macro_rules! from_vcl_to_opt_rust {
103 ($vcl_ty:ident, $rust_ty:ty) => {
104 impl From<$vcl_ty> for Option<$rust_ty> {
105 fn from(b: $vcl_ty) -> Self {
106 Some(b.into())
107 }
108 }
109 };
110}
111
112default_null_ptr!(VCL_ACL);
114
115default_null_ptr!(VCL_BACKEND);
117
118default_null_ptr!(VCL_BLOB);
120
121default_null_ptr!(VCL_BODY);
123
124into_vcl_using_from!(bool, VCL_BOOL);
128from_rust_to_vcl!(bool, VCL_BOOL);
129from_vcl_to_opt_rust!(VCL_BOOL, bool);
130impl From<VCL_BOOL> for bool {
131 fn from(b: VCL_BOOL) -> Self {
132 b.0 != 0
133 }
134}
135
136into_vcl_using_from!(Duration, VCL_DURATION);
140from_vcl_to_opt_rust!(VCL_DURATION, Duration);
141impl From<VCL_DURATION> for Duration {
142 fn from(value: VCL_DURATION) -> Self {
143 value.0.into()
144 }
145}
146impl From<Duration> for VCL_DURATION {
147 fn from(value: Duration) -> Self {
148 Self(value.into())
149 }
150}
151
152impl From<vtim_dur> for Duration {
156 fn from(value: vtim_dur) -> Self {
157 Self::from_secs_f64(value.0)
158 }
159}
160impl From<Duration> for vtim_dur {
161 fn from(value: Duration) -> Self {
162 Self(value.as_secs_f64())
163 }
164}
165
166default_null_ptr!(VCL_ENUM);
168default_null_ptr!(VCL_HEADER);
170default_null_ptr!(mut VCL_HTTP);
172impl From<*mut http> for VCL_HTTP {
173 fn from(value: *mut http) -> Self {
175 Self(value)
176 }
177}
178
179into_vcl_using_from!(i64, VCL_INT);
183from_rust_to_vcl!(i64, VCL_INT);
184from_vcl_to_opt_rust!(VCL_INT, i64);
185impl From<VCL_INT> for i64 {
186 fn from(b: VCL_INT) -> Self {
187 b.0
188 }
189}
190
191default_null_ptr!(VCL_IP);
195impl From<VCL_IP> for Option<SocketAddr> {
196 fn from(value: VCL_IP) -> Self {
197 let value = value.0;
198 if value.is_null() {
199 return None;
200 }
201 unsafe {
202 let mut ptr = null();
203 let fam = VSA_GetPtr(value, &raw mut ptr) as u32;
204 let port = VSA_Port(value) as u16;
205
206 match fam {
207 PF_INET => {
208 let buf: &[u8; 4] = std::slice::from_raw_parts(ptr.cast::<u8>(), 4)
209 .try_into()
210 .unwrap();
211 Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::from(*buf)), port))
212 }
213 PF_INET6 => {
214 let buf: &[u8; 16] = std::slice::from_raw_parts(ptr.cast::<u8>(), 16)
215 .try_into()
216 .unwrap();
217 Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(*buf)), port))
218 }
219 _ => None,
220 }
221 }
222 }
223}
224
225default_null_ptr!(VCL_PROBE);
229impl IntoVCL<VCL_PROBE> for CowProbe<'_> {
230 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
231 into_vcl_probe(self, ws)
232 }
233}
234impl IntoVCL<VCL_PROBE> for Probe {
235 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_PROBE, VclError> {
236 into_vcl_probe(self, ws)
237 }
238}
239impl From<VCL_PROBE> for Option<CowProbe<'_>> {
240 fn from(value: VCL_PROBE) -> Self {
241 from_vcl_probe(value)
242 }
243}
244impl From<VCL_PROBE> for Option<Probe> {
245 fn from(value: VCL_PROBE) -> Self {
246 from_vcl_probe(value)
247 }
248}
249
250into_vcl_using_from!(f64, VCL_REAL);
254from_rust_to_vcl!(f64, VCL_REAL);
255from_vcl_to_opt_rust!(VCL_REAL, f64);
256impl From<VCL_REAL> for f64 {
257 fn from(b: VCL_REAL) -> Self {
258 b.0
259 }
260}
261
262default_null_ptr!(VCL_STRING);
266impl IntoVCL<VCL_STRING> for &[u8] {
267 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
268 let with_extra_byte = unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len() + 1) };
273 if (ws.contains(self) && self.ends_with(b"\0"))
274 || (ws.contains(with_extra_byte) && with_extra_byte.last() == Some(&b'\0'))
275 {
276 Ok(VCL_STRING(self.as_ptr().cast::<c_char>()))
277 } else {
278 Ok(VCL_STRING(ws.copy_bytes_with_null(self)?.b))
279 }
280 }
281}
282
283impl IntoVCL<VCL_STRING> for &str {
284 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
285 self.as_bytes().into_vcl(ws)
286 }
287}
288impl IntoVCL<VCL_STRING> for &CStr {
289 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
290 ws.copy_cstr(self)
291 }
292}
293impl IntoVCL<VCL_STRING> for &Cow<'_, str> {
294 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
295 self.as_bytes().into_vcl(ws)
296 }
297}
298impl IntoVCL<VCL_STRING> for String {
299 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
300 self.as_str().into_vcl(ws)
301 }
302}
303impl<T: IntoVCL<VCL_STRING> + AsRef<[u8]>> IntoVCL<VCL_STRING> for Option<T> {
304 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_STRING, VclError> {
305 match self {
306 None => Ok(VCL_STRING(null())),
307 Some(t) => t.as_ref().into_vcl(ws),
308 }
309 }
310}
311impl From<VCL_STRING> for Option<&CStr> {
312 fn from(value: VCL_STRING) -> Self {
313 if value.0.is_null() {
314 None
315 } else {
316 Some(unsafe { CStr::from_ptr(value.0) })
317 }
318 }
319}
320impl From<VCL_STRING> for &CStr {
321 fn from(value: VCL_STRING) -> Self {
322 <Option<&CStr>>::from(value).unwrap_or_default()
324 }
325}
326impl TryFrom<VCL_STRING> for Option<&str> {
327 type Error = VclError;
328 fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
329 Ok(<Option<&CStr>>::from(value).map(CStr::to_str).transpose()?)
330 }
331}
332impl<'a> TryFrom<VCL_STRING> for &'a str {
333 type Error = VclError;
334 fn try_from(value: VCL_STRING) -> Result<Self, Self::Error> {
335 Ok(<Option<&'a str>>::try_from(value)?.unwrap_or(""))
336 }
337}
338
339default_null_ptr!(VCL_STEVEDORE);
341default_null_ptr!(VCL_STRANDS);
343
344impl IntoVCL<VCL_TIME> for SystemTime {
348 fn into_vcl(self, _: &mut Workspace) -> Result<VCL_TIME, VclError> {
349 self.try_into()
350 }
351}
352impl TryFrom<SystemTime> for VCL_TIME {
353 type Error = VclError;
354
355 fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
356 Ok(VCL_TIME(vtim_real(
357 value
358 .duration_since(SystemTime::UNIX_EPOCH)
359 .map_err(|e| VclError::new(e.to_string()))?
360 .as_secs_f64(),
361 )))
362 }
363}
364
365default_null_ptr!(mut VCL_VCL);
367
368#[cfg(not(varnishsys_6))]
369mod version_after_v6 {
370 use std::ffi::c_void;
371 use std::net::SocketAddr;
372 use std::num::NonZeroUsize;
373 use std::ptr;
374 use std::ptr::null;
375
376 use super::IntoVCL;
377 use crate::ffi::{
378 sa_family_t, vsa_suckaddr_len, VSA_BuildFAP, PF_INET, PF_INET6, VCL_IP, VCL_REGEX, VCL_SUB,
379 };
380 use crate::vcl::{VclError, Workspace};
381 default_null_ptr!(VCL_SUB);
382
383 default_null_ptr!(VCL_REGEX);
384
385 impl IntoVCL<VCL_IP> for SocketAddr {
386 fn into_vcl(self, ws: &mut Workspace) -> Result<VCL_IP, VclError> {
387 unsafe {
388 let size = NonZeroUsize::new(vsa_suckaddr_len).unwrap();
391 let p = ws.alloc(size);
392 if p.is_null() {
393 Err(VclError::WsOutOfMemory(size))?;
394 }
395 match self {
396 SocketAddr::V4(sa) => {
397 assert!(!VSA_BuildFAP(
398 p,
399 PF_INET as sa_family_t,
400 sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
401 4,
402 ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
403 2
404 )
405 .is_null());
406 }
407 SocketAddr::V6(sa) => {
408 assert!(!VSA_BuildFAP(
409 p,
410 PF_INET6 as sa_family_t,
411 sa.ip().octets().as_slice().as_ptr().cast::<c_void>(),
412 16,
413 ptr::from_ref::<u16>(&sa.port().to_be()).cast::<c_void>(),
414 2
415 )
416 .is_null());
417 }
418 }
419 Ok(VCL_IP(p.cast()))
420 }
421 }
422 }
423}