1use crate::utils::{slice_from_external_parts, slice_from_external_parts_mut};
13use clap_sys::stream::{clap_istream, clap_ostream};
14use std::error::Error;
15use std::ffi::c_void;
16use std::fmt::{Display, Formatter};
17use std::io::{ErrorKind, Read, Write};
18use std::marker::PhantomData;
19
20#[derive(Copy, Clone, Debug)]
22pub struct StreamError {
23 code: i64,
24}
25
26impl StreamError {
27 pub fn code(&self) -> i64 {
29 self.code
30 }
31}
32
33impl Display for StreamError {
34 #[inline]
35 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
36 write!(f, "CLAP stream error (code: {})", self.code)
37 }
38}
39
40impl Error for StreamError {}
41
42#[repr(C)]
46pub struct InputStream<'a>(clap_istream, PhantomData<(&'a mut clap_istream, *const ())>);
47
48impl<'a> InputStream<'a> {
49 pub fn from_reader<R: Read + Sized + 'a>(reader: &'a mut R) -> Self {
51 Self(
52 clap_istream {
53 ctx: reader as *mut R as *mut _,
54 read: Some(read::<R>),
55 },
56 PhantomData,
57 )
58 }
59
60 #[inline]
66 pub unsafe fn from_raw_mut(raw: &mut clap_istream) -> &mut Self {
67 &mut *(raw as *mut _ as *mut _)
68 }
69
70 #[inline]
72 pub fn as_raw_mut(&mut self) -> &mut clap_istream {
73 &mut self.0
74 }
75}
76
77impl Read for InputStream<'_> {
78 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
79 let ret = if let Some(read) = self.0.read {
80 unsafe { read(&self.0, buf.as_mut_ptr().cast(), buf.len() as u64) }
82 } else {
83 return Ok(0);
84 };
85 match ret {
86 i if i >= 0 => Ok(usize::try_from(i).map_err(std::io::Error::other)?),
87 code => Err(std::io::Error::other(StreamError { code })),
88 }
89 }
90}
91
92#[repr(C)]
96pub struct OutputStream<'a>(clap_ostream, PhantomData<(&'a mut clap_ostream, *const ())>);
97
98impl<'a> OutputStream<'a> {
99 pub fn from_writer<W: Write + Sized + 'a>(writer: &'a mut W) -> Self {
101 Self(
102 clap_ostream {
103 ctx: writer as *mut W as *mut _,
104 write: Some(write::<W>),
105 },
106 PhantomData,
107 )
108 }
109
110 #[inline]
116 pub unsafe fn from_raw_mut(raw: &mut clap_ostream) -> &mut Self {
117 &mut *(raw as *mut _ as *mut _)
118 }
119
120 #[inline]
122 pub fn as_raw_mut(&mut self) -> &mut clap_ostream {
123 &mut self.0
124 }
125}
126
127impl Write for OutputStream<'_> {
128 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
129 let ret = if let Some(write) = self.0.write {
130 unsafe { write(&self.0, buf.as_ptr().cast(), buf.len() as u64) }
132 } else {
133 return Ok(0);
134 };
135
136 match ret {
137 i if i >= 0 => Ok(usize::try_from(i).map_err(std::io::Error::other)?),
138 code => Err(std::io::Error::other(StreamError { code })),
139 }
140 }
141
142 #[inline]
143 fn flush(&mut self) -> std::io::Result<()> {
144 Ok(())
145 }
146}
147
148fn saturating_from_u64(val: u64) -> usize {
150 let val = i64::try_from(val).unwrap_or(i64::MAX);
152
153 usize::try_from(val).unwrap_or(usize::MAX)
155}
156
157#[allow(clippy::missing_safety_doc)]
158unsafe extern "C" fn read<R: Read + Sized>(
159 istream: *const clap_istream,
160 buffer: *mut c_void,
161 size: u64,
162) -> i64 {
163 let reader = &mut *((*istream).ctx as *mut R);
164 let size = saturating_from_u64(size);
165
166 let buffer = slice_from_external_parts_mut(buffer as *mut u8, size);
167
168 match handle_interrupted(|| reader.read(buffer)) {
169 #[allow(clippy::cast_possible_wrap)]
170 Ok(read) => {
171 if read > size {
173 size as i64
175 } else {
176 read as i64
178 }
179 }
180 Err(_) => -1,
181 }
182}
183
184#[allow(clippy::missing_safety_doc)]
185unsafe extern "C" fn write<W: Write + Sized>(
186 ostream: *const clap_ostream,
187 buffer: *const c_void,
188 size: u64,
189) -> i64 {
190 let writer = &mut *((*ostream).ctx as *mut W);
191 let size = saturating_from_u64(size);
192
193 let buffer = slice_from_external_parts(buffer as *const u8, size);
194
195 match handle_interrupted(|| writer.write(buffer)) {
196 #[allow(clippy::cast_possible_wrap)]
197 Ok(written) => {
198 if written > size {
200 size as i64
202 } else {
203 written as i64
205 }
206 }
207 Err(_) => -1,
208 }
209}
210
211fn handle_interrupted<F: FnMut() -> std::io::Result<usize>>(
212 mut handler: F,
213) -> std::io::Result<usize> {
214 const MAX_ATTEMPTS: u8 = 5;
215 let mut attempts = 0u8;
216
217 loop {
218 match handler() {
219 Err(e) if e.kind() == ErrorKind::Interrupted && attempts < MAX_ATTEMPTS => {
220 attempts += 1
221 }
222 res => return res,
223 }
224 }
225}
226
227#[cfg(test)]
228mod test {
229 extern crate static_assertions as sa;
230 use super::*;
231 use std::io::Cursor;
232
233 sa::assert_not_impl_any!(InputStream: Send, Sync);
234 sa::assert_not_impl_any!(OutputStream: Send, Sync);
235
236 #[test]
237 fn input_streams_work() {
238 let src = b"Hello";
239 let mut buf = vec![0; 5];
240 let mut cursor = Cursor::new(src);
241
242 let mut stream = InputStream::from_reader(&mut cursor);
243 let res = stream.read(&mut buf).unwrap();
244 assert_eq!(res, 5);
245 assert_eq!(&buf, b"Hello");
246 }
247
248 #[test]
249 fn output_streams_work() {
250 let mut buf = vec![];
251
252 let mut stream = OutputStream::from_writer(&mut buf);
253 let res = stream.write(b"Hello").unwrap();
254
255 assert_eq!(res, 5);
256 assert_eq!(&buf, b"Hello");
257 }
258}