rs_vips/
connection.rs

1use crate::{
2    bindings,
3    error::Error,
4    region::VipsBlob,
5    utils::{self, vips_source_result, vips_target_result},
6    Result,
7};
8use std::{
9    borrow::Cow,
10    ffi::{c_void, CStr, CString},
11    ptr::null_mut,
12};
13
14#[derive(Debug, Clone)]
15pub struct VipsConnection {
16    pub(crate) ctx: *mut bindings::VipsConnection,
17}
18
19#[derive(Debug, Clone)]
20pub struct VipsSource {
21    pub(crate) ctx: *mut bindings::VipsSource,
22}
23
24#[derive(Debug, Clone)]
25pub struct VipsTarget {
26    pub(crate) ctx: *mut bindings::VipsTarget,
27}
28
29impl VipsConnection {
30    pub fn connection_filename(&self) -> Option<String> {
31        unsafe {
32            let result = bindings::vips_connection_filename(self.ctx);
33            if result.is_null() {
34                None
35            } else {
36                let cstr = CStr::from_ptr(result);
37                match cstr.to_string_lossy() {
38                    Cow::Borrowed(slice) => Some(slice.to_string()),
39                    Cow::Owned(string) => Some(string),
40                }
41            }
42        }
43    }
44
45    pub fn connection_nick(&self) -> Option<String> {
46        unsafe {
47            let result = bindings::vips_connection_nick(self.ctx);
48            if result.is_null() {
49                None
50            } else {
51                let cstr = CStr::from_ptr(result);
52                match cstr.to_string_lossy() {
53                    Cow::Borrowed(slice) => Some(slice.to_string()),
54                    Cow::Owned(string) => Some(string),
55                }
56            }
57        }
58    }
59}
60
61impl VipsSource {
62    /// Create an source attached to a file descriptor. descriptor is closed with close() when source is finalized.
63    pub fn new_from_descriptor(descriptor: i32) -> Result<Self> {
64        unsafe {
65            let res = bindings::vips_source_new_from_descriptor(descriptor);
66            vips_source_result(
67                res,
68                Error::InitializationError(
69                    "Could not initialise VipsSource from descriptor".to_string(),
70                ),
71            )
72        }
73    }
74
75    /// Create a source attached to a file.
76    pub fn new_from_file(filename: &str) -> Result<Self> {
77        unsafe {
78            let f = utils::new_c_string(filename)?;
79            let res = bindings::vips_source_new_from_file(f.as_ptr());
80            vips_source_result(
81                res,
82                Error::InitializationError("Could not initialise VipsSource from file".to_string()),
83            )
84        }
85    }
86
87    // Create a source attached to an area of memory.
88    // not sure if it this is safe
89    // should test before making it public
90    fn new_from_blob(blob: VipsBlob) -> Result<Self> {
91        unsafe {
92            let res = bindings::vips_source_new_from_blob(blob.ctx);
93            vips_source_result(
94                res,
95                Error::InitializationError("Could not initialise VipsSource from blob".to_string()),
96            )
97        }
98    }
99
100    /// Create a source attached to an area of memory. You must not free data while the source is active.
101    pub fn new_from_memory(buffer: &[u8]) -> Result<Self> {
102        unsafe {
103            let res = bindings::vips_source_new_from_memory(
104                buffer.as_ptr() as *const c_void,
105                buffer.len() as u64,
106            );
107            vips_source_result(
108                res,
109                Error::InitializationError(
110                    "Could not initialise VipsSource from memory".to_string(),
111                ),
112            )
113        }
114    }
115
116    /// Create a source from an option string.
117    pub fn new_from_options(option_str: &str) -> Result<Self> {
118        unsafe {
119            let options = utils::new_c_string(option_str)?;
120            let res = bindings::vips_source_new_from_options(options.as_ptr());
121            vips_source_result(
122                res,
123                Error::InitializationError(
124                    "Could not initialise VipsSource from options".to_string(),
125                ),
126            )
127        }
128    }
129
130    /// Minimise the source. As many resources as can be safely removed are removed.
131    pub fn minimise(&mut self) {
132        unsafe {
133            bindings::vips_source_minimise(self.ctx);
134        }
135    }
136
137    /// Restore the source after minimisation. This is called at the start of every source method, so loaders should not usually need this.
138    pub fn unminimise(&mut self) -> Result<()> {
139        unsafe {
140            let result = bindings::vips_source_unminimise(self.ctx);
141            utils::result(
142                result,
143                (),
144                Error::OperationError("Error on vips unminimise".to_string()),
145            )
146        }
147    }
148
149    /// Signal the end of header read and the start of the pixel decode phase. After this, you can no longer seek on this source.
150    pub fn decode(&mut self) -> Result<()> {
151        unsafe {
152            let result = bindings::vips_source_decode(self.ctx);
153            utils::result(
154                result,
155                (),
156                Error::OperationError("Error on vips decode".to_string()),
157            )
158        }
159    }
160
161    /// Read up to length bytes from source and store the bytes in buffer.
162    pub fn read(&mut self, length: u64) -> Result<Vec<u8>> {
163        unsafe {
164            let bytes: *mut c_void = null_mut();
165            let result = bindings::vips_source_read(
166                self.ctx,
167                bytes,
168                length,
169            );
170            if result != -1 {
171                let buffer = Vec::from_raw_parts(
172                    bytes as *mut u8,
173                    result as usize,
174                    result as usize,
175                );
176                Ok(buffer)
177            } else {
178                Err(Error::OperationError("Error on vips read".to_string()))
179            }
180        }
181    }
182
183    /// Whether the source can be efficiently mapped into memory.
184    pub fn is_mappable(&self) -> bool {
185        unsafe { bindings::vips_source_is_mappable(self.ctx) == 1 }
186    }
187
188    /// Move the file read position.
189    pub fn seek(&mut self, offset: i64, whence: i32) -> Result<i64> {
190        unsafe {
191            let result = bindings::vips_source_seek(
192                self.ctx,
193                offset,
194                whence,
195            );
196            if result == -1 {
197                Err(Error::OperationError("Error on vips seek".to_string()))
198            } else {
199                Ok(result)
200            }
201        }
202    }
203
204    /// Rewind the source to the start.
205    pub fn rewind(&mut self) -> Result<()> {
206        unsafe {
207            let result = bindings::vips_source_rewind(self.ctx);
208            if result == -1 {
209                Err(Error::OperationError("Error on vips rewind".to_string()))
210            } else {
211                Ok(())
212            }
213        }
214    }
215
216    /// Return the length in bytes of the source.
217    pub fn length(&self) -> Result<i64> {
218        unsafe {
219            let result = bindings::vips_source_length(self.ctx);
220            if result == -1 {
221                Err(Error::OperationError("Error on vips length".to_string()))
222            } else {
223                Ok(result)
224            }
225        }
226    }
227}
228
229impl<'a> VipsSource {
230    /// Map the source entirely into memory and return a pointer to the start.
231    pub fn map(&'a self) -> Result<&'a [u8]> {
232        unsafe {
233            let length: *mut u64 = null_mut();
234            let result = bindings::vips_source_map(
235                self.ctx,
236                length,
237            );
238            if length.is_null() {
239                Err(Error::OperationError("Error on vips map".to_string()))
240            } else {
241                let size = (*length)
242                    .try_into()
243                    .map_err(|_| Error::OperationError("Can't get size of array".to_string()))?;
244                Ok(
245                    std::slice::from_raw_parts(
246                        result as *mut u8,
247                        size,
248                    ),
249                )
250            }
251        }
252    }
253}
254
255impl VipsTarget {
256    /// Create a target attached to a file descriptor. descriptor is kept open until the target is finalized.
257    pub fn new_to_descriptor(descriptor: i32) -> Result<Self> {
258        unsafe {
259            let res = bindings::vips_target_new_to_descriptor(descriptor);
260            vips_target_result(
261                res,
262                Error::InitializationError(
263                    "Could not initialise VipsTarget from descriptor".to_string(),
264                ),
265            )
266        }
267    }
268
269    /// Create a target attached to a file.
270    pub fn new_to_file(filename: &str) -> Result<Self> {
271        unsafe {
272            let f = utils::new_c_string(filename)?;
273            let res = bindings::vips_target_new_to_file(f.as_ptr());
274            vips_target_result(
275                res,
276                Error::InitializationError("Could not initialise VipsTarget from file".to_string()),
277            )
278        }
279    }
280
281    /// Create a target which will write to a memory area. Read from blob to get memory.
282    pub fn new_to_memory() -> Result<Self> {
283        unsafe {
284            let res = bindings::vips_target_new_to_memory();
285            vips_target_result(
286                res,
287                Error::InitializationError(
288                    "Could not initialise VipsTarget from memory".to_string(),
289                ),
290            )
291        }
292    }
293
294    /// Call this at the end of write to make the target do any cleaning up.
295    pub fn end(self) {
296        unsafe {
297            bindings::vips_target_end(self.ctx);
298        }
299    }
300
301    /// Write a single character ch to target.
302    pub fn putc(&mut self, ch: char) -> Result<()> {
303        unsafe {
304            let res = bindings::vips_target_putc(
305                self.ctx,
306                ch as i32,
307            );
308            if res == -1 {
309                Err(Error::OperationError("Could not write to buffer".to_string()))
310            } else {
311                Ok(())
312            }
313        }
314    }
315
316    /// Write buffer to the output.
317    pub fn write(&mut self, buffer: &[u8]) -> Result<()> {
318        unsafe {
319            let res = bindings::vips_target_write(
320                self.ctx,
321                buffer.as_ptr() as *const c_void,
322                buffer.len() as u64,
323            );
324            if res == -1 {
325                Err(Error::OperationError("Could not write to buffer".to_string()))
326            } else {
327                Ok(())
328            }
329        }
330    }
331
332    /// Write str to target.
333    pub fn write_amp(&mut self, text: &str) -> Result<()> {
334        unsafe {
335            let cstr = CString::new(text)
336                .map_err(|_| Error::OperationError("Cannot initialize C string".to_string()))?;
337            let res = bindings::vips_target_write_amp(
338                self.ctx,
339                cstr.as_ptr(),
340            );
341            if res == -1 {
342                Err(Error::OperationError("Could not write to buffer".to_string()))
343            } else {
344                Ok(())
345            }
346        }
347    }
348
349    /// Write a null-terminated string to target.
350    pub fn writes(&mut self, text: &str) -> Result<()> {
351        unsafe {
352            let cstr = CString::new(text)
353                .map_err(|_| Error::OperationError("Cannot initialize C string".to_string()))?;
354            let res = bindings::vips_target_writes(
355                self.ctx,
356                cstr.as_ptr(),
357            );
358            if res == -1 {
359                Err(Error::OperationError("Could not write to buffer".to_string()))
360            } else {
361                Ok(())
362            }
363        }
364    }
365
366    pub fn get_blob(&self) -> VipsBlob {
367        unsafe {
368            VipsBlob {
369                ctx: (*self.ctx).blob,
370            }
371        }
372    }
373}
374
375impl Drop for VipsConnection {
376    fn drop(&mut self) {
377        unsafe {
378            if !self
379                .ctx
380                .is_null()
381            {
382                bindings::g_object_unref(self.ctx as *mut c_void);
383            }
384        }
385    }
386}
387
388impl Drop for VipsSource {
389    fn drop(&mut self) {
390        unsafe {
391            if !self
392                .ctx
393                .is_null()
394            {
395                bindings::g_object_unref(self.ctx as *mut c_void);
396            }
397        }
398    }
399}
400
401impl Drop for VipsTarget {
402    fn drop(&mut self) {
403        unsafe {
404            if !self
405                .ctx
406                .is_null()
407            {
408                bindings::g_object_unref(self.ctx as *mut c_void);
409            }
410        }
411    }
412}
413
414impl From<*mut bindings::VipsSource> for VipsSource {
415    fn from(value: *mut bindings::VipsSource) -> Self {
416        Self {
417            ctx: value,
418        }
419    }
420}
421
422impl From<*mut bindings::VipsTarget> for VipsTarget {
423    fn from(value: *mut bindings::VipsTarget) -> Self {
424        Self {
425            ctx: value,
426        }
427    }
428}