1use crate::{
4 bindings::{self, gint64},
5 connection::VipsConnection,
6 error::Error,
7 utils::{new_c_string, path_to_cstring, result_cond},
8 Result,
9};
10use std::{
11 ffi::{c_int, c_void},
12 io::{Read, Seek, SeekFrom, Write},
13 path::Path,
14};
15
16#[derive(Debug, Clone)]
18pub struct VipsTarget {
19 pub(crate) ctx: *mut bindings::VipsTarget,
20}
21
22pub trait Writer: Write + Read + Seek + Send + 'static {}
23impl<T: Write + Read + Seek + Send + 'static> Writer for T {}
24struct TargetContext {
25 writer: Box<dyn Writer>,
26}
27
28impl VipsTarget {
29 pub fn new_to_writer<W: Writer>(output: W) -> Result<VipsTarget> {
31 unsafe {
32 let target = bindings::vips_target_custom_new();
33
34 if target.is_null() {
35 return Err(
36 Error::InitializationError("Cannot create VipsTargetCustom".to_string()),
37 );
38 }
39
40 let context = Box::new(
41 TargetContext {
42 writer: Box::new(output),
43 },
44 );
45
46 let user_data = Box::into_raw(context) as *mut c_void;
47
48 bindings::g_signal_connect_data(
49 target as *mut c_void,
50 c"write".as_ptr(),
51 Some(
52 std::mem::transmute::<
53 unsafe extern "C" fn(
54 target: *mut bindings::VipsTargetCustom,
55 data: *const c_void,
56 length: bindings::gint64,
57 user_data: *mut c_void,
58 ) -> bindings::gint64,
59 unsafe extern "C" fn(),
60 >(on_write),
61 ),
62 user_data,
63 None,
64 0,
65 );
66
67 bindings::g_signal_connect_data(
68 target as *mut c_void,
69 c"read".as_ptr(),
70 Some(
71 std::mem::transmute::<
72 unsafe extern "C" fn(
73 target: *mut bindings::VipsTargetCustom,
74 buffer: *mut c_void,
75 length: gint64,
76 user_data: *mut c_void,
77 ) -> gint64,
78 unsafe extern "C" fn(),
79 >(on_read),
80 ),
81 user_data,
82 None,
83 0,
84 );
85
86 bindings::g_signal_connect_data(
87 target as *mut c_void,
88 c"seek".as_ptr(),
89 Some(
90 std::mem::transmute::<
91 unsafe extern "C" fn(
92 target: *mut bindings::VipsTargetCustom,
93 offset: gint64,
94 whence: c_int,
95 user_data: *mut c_void,
96 ) -> gint64,
97 unsafe extern "C" fn(),
98 >(on_seek),
99 ),
100 user_data,
101 None,
102 0,
103 );
104
105 bindings::g_signal_connect_data(
106 target as *mut c_void,
107 c"end".as_ptr(),
108 Some(
109 std::mem::transmute::<
110 unsafe extern "C" fn(
111 target: *mut bindings::VipsTargetCustom,
112 user_data: *mut c_void,
113 ) -> c_int,
114 unsafe extern "C" fn(),
115 >(on_end),
116 ),
117 user_data,
118 None,
119 0,
120 );
121
122 Ok(
123 VipsTarget {
124 ctx: target as *mut bindings::VipsTarget,
125 },
126 )
127 }
128 }
129
130 pub fn new_to_descriptor(descriptor: i32) -> Result<VipsTarget> {
132 unsafe {
133 let res = bindings::vips_target_new_to_descriptor(descriptor);
134 result_cond(
135 !res.is_null(),
136 VipsTarget {
137 ctx: res,
138 },
139 Error::InitializationError(
140 "Could not initialise VipsTarget from descriptor".to_string(),
141 ),
142 )
143 }
144 }
145
146 pub fn new_to_file<P: AsRef<Path>>(filename: P) -> Result<VipsTarget> {
148 unsafe {
149 let filename_c_str = path_to_cstring(filename)?;
150 let res = bindings::vips_target_new_to_file(filename_c_str.as_ptr());
151 result_cond(
152 !res.is_null(),
153 VipsTarget {
154 ctx: res,
155 },
156 Error::InitializationError("Could not initialise VipsTarget from file".to_string()),
157 )
158 }
159 }
160
161 pub fn new_to_memory() -> Result<VipsTarget> {
163 unsafe {
164 let res = bindings::vips_target_new_to_memory();
165 result_cond(
166 !res.is_null(),
167 VipsTarget {
168 ctx: res,
169 },
170 Error::InitializationError(
171 "Could not initialise VipsTarget from memory".to_string(),
172 ),
173 )
174 }
175 }
176
177 pub fn putc(&mut self, ch: char) -> Result<()> {
179 unsafe {
180 let res = bindings::vips_target_putc(
181 self.ctx,
182 ch as c_int,
183 );
184 result_cond(
185 res != -1,
186 (),
187 Error::OperationError("Could not write to target".to_string()),
188 )
189 }
190 }
191
192 pub fn write(&mut self, buffer: &[u8]) -> Result<()> {
194 unsafe {
195 let res = bindings::vips_target_write(
196 self.ctx,
197 buffer.as_ptr() as *const c_void,
198 buffer.len(),
199 );
200 result_cond(
201 res != -1,
202 (),
203 Error::OperationError("Could not write to target".to_string()),
204 )
205 }
206 }
207
208 pub fn writes(&mut self, text: &str) -> Result<()> {
210 unsafe {
211 let cstr = new_c_string(text)?;
212 let res = bindings::vips_target_writes(
213 self.ctx,
214 cstr.as_ptr(),
215 );
216 result_cond(
217 res != -1,
218 (),
219 Error::OperationError("Could not write to target".to_string()),
220 )
221 }
222 }
223
224 pub fn write_amp(&mut self, text: &str) -> Result<()> {
226 unsafe {
227 let cstr = new_c_string(text)?;
228 let res = bindings::vips_target_write_amp(
229 self.ctx,
230 cstr.as_ptr(),
231 );
232 result_cond(
233 res != -1,
234 (),
235 Error::OperationError("Could not write to target".to_string()),
236 )
237 }
238 }
239
240 pub fn end(self) {
242 unsafe {
243 bindings::vips_target_end(self.ctx);
244 }
245 }
246
247 pub fn get_blob(&self) -> Vec<u8> {
248 unsafe {
249 if self
250 .ctx
251 .is_null()
252 || (*self.ctx)
253 .blob
254 .is_null()
255 {
256 return Vec::new();
257 }
258 let mut size = 0;
259 let bytes = bindings::vips_blob_get(
260 (*self.ctx).blob,
261 &mut size,
262 );
263 let slice = std::slice::from_raw_parts(
264 bytes as *const u8,
265 size,
266 );
267 slice.to_vec()
268 }
269 }
270
271 pub fn filename(&self) -> Option<String> {
273 unsafe { VipsConnection::filename(&mut (*self.ctx).parent_object) }
274 }
275
276 pub fn nick(&self) -> Option<String> {
278 unsafe { VipsConnection::nick(&mut (*self.ctx).parent_object) }
279 }
280}
281
282unsafe extern "C" fn on_read(
283 _: *mut bindings::VipsTargetCustom,
284 buffer: *mut c_void,
285 length: gint64,
286 user_data: *mut c_void,
287) -> gint64 {
288 let ctx = &mut *(user_data as *mut TargetContext);
289
290 let slice = std::slice::from_raw_parts_mut(
291 buffer as *mut u8,
292 length as usize,
293 );
294
295 match ctx
296 .writer
297 .read(slice)
298 {
299 Ok(n) => n as i64,
300 Err(_) => -1,
301 }
302}
303
304unsafe extern "C" fn on_seek(
305 _: *mut bindings::VipsTargetCustom,
306 offset: gint64,
307 whence: c_int,
308 user_data: *mut c_void,
309) -> gint64 {
310 let ctx = &mut *(user_data as *mut TargetContext);
311
312 let pos = match whence {
313 0 => SeekFrom::Start(offset as u64),
314 1 => SeekFrom::Current(offset),
315 2 => SeekFrom::End(offset),
316 _ => return -1,
317 };
318
319 match ctx
320 .writer
321 .seek(pos)
322 {
323 Ok(n) => n as gint64,
324 Err(_) => -1,
325 }
326}
327
328unsafe extern "C" fn on_write(
329 _: *mut bindings::VipsTargetCustom,
330 data: *const c_void,
331 length: gint64,
332 user_data: *mut c_void,
333) -> gint64 {
334 let ctx = &mut *(user_data as *mut TargetContext);
335
336 let slice = std::slice::from_raw_parts(
337 data as *const u8,
338 length as usize,
339 );
340
341 match ctx
342 .writer
343 .write(slice)
344 {
345 Ok(n) => n as gint64,
346 Err(_) => -1,
347 }
348}
349
350unsafe extern "C" fn on_end(_: *mut bindings::VipsTargetCustom, data: *mut c_void) -> i32 {
351 let _ = Box::from_raw(data as *mut TargetContext);
352 0
353}
354
355impl Drop for VipsTarget {
356 fn drop(&mut self) {
357 unsafe {
358 if !self
359 .ctx
360 .is_null()
361 {
362 bindings::g_object_unref(self.ctx as *mut c_void);
363 }
364 }
365 }
366}