1use super::printf_impl::Error;
2use std::result::Result;
3#[cfg(feature = "widestring")]
4use widestring::{Utf32Str as wstr, Utf32String as WString};
5
6#[derive(Debug, PartialEq)]
10pub enum Arg<'a> {
11 Str(&'a str),
12 #[cfg(feature = "widestring")]
13 WStr(&'a wstr),
14 String(String),
15 #[cfg(feature = "widestring")]
16 WString(WString),
17 UInt(u64),
18 SInt(i64, u8), Float(f64),
20 USizeRef(&'a mut usize), }
22
23impl<'a> Arg<'a> {
24 pub fn set_count(&mut self, count: usize) -> Result<(), Error> {
25 match self {
26 Arg::USizeRef(p) => **p = count,
27 _ => return Err(Error::BadArgType),
28 }
29 Ok(())
30 }
31
32 #[allow(unused_variables, clippy::ptr_arg)]
35 pub fn as_str<'s>(&'s self, storage: &'s mut String) -> Result<&'s str, Error>
36 where
37 'a: 's,
38 {
39 match self {
40 Arg::Str(s) => Ok(s),
41 Arg::String(s) => Ok(s),
42 #[cfg(feature = "widestring")]
43 Arg::WStr(s) => {
44 storage.clear();
45 storage.extend(s.chars());
46 Ok(storage)
47 }
48 #[cfg(feature = "widestring")]
49 Arg::WString(s) => {
50 storage.clear();
51 storage.extend(s.chars());
52 Ok(storage)
53 }
54 _ => Err(Error::BadArgType),
55 }
56 }
57
58 pub fn as_uint(&self) -> Result<u64, Error> {
60 match *self {
61 Arg::UInt(u) => Ok(u),
62 Arg::SInt(i, _w) => i.try_into().map_err(|_| Error::Overflow),
63 _ => Err(Error::BadArgType),
64 }
65 }
66
67 pub fn as_sint(&self) -> Result<i64, Error> {
69 match *self {
70 Arg::UInt(u) => u.try_into().map_err(|_| Error::Overflow),
71 Arg::SInt(i, _w) => Ok(i),
72 _ => Err(Error::BadArgType),
73 }
74 }
75
76 pub fn as_wrapping_sint(&self) -> Result<(bool, u64), Error> {
81 match *self {
82 Arg::UInt(u) => Ok((false, u)),
83 Arg::SInt(i, w) => {
84 debug_assert!(w > 0);
86 let mask = ((1u64 << (w - 1)) << 1).wrapping_sub(1);
87 let ui = (i as u64) & mask;
88 Ok((i < 0, ui))
89 }
90 _ => Err(Error::BadArgType),
91 }
92 }
93
94 pub fn as_float(&self) -> Result<f64, Error> {
96 #[allow(clippy::cast_precision_loss)]
97 match *self {
98 Arg::Float(f) => Ok(f),
99 Arg::UInt(u) => Ok(u as f64),
100 Arg::SInt(i, _w) => Ok(i as f64),
101 _ => Err(Error::BadArgType),
102 }
103 }
104
105 pub fn as_char(&self) -> Result<char, Error> {
106 let v: u32 = self.as_uint()?.try_into().map_err(|_| Error::Overflow)?;
107 v.try_into().map_err(|_| Error::Overflow)
108 }
109}
110
111pub trait ToArg<'a> {
113 fn to_arg(self) -> Arg<'a>;
114}
115
116impl<'a> ToArg<'a> for &'a str {
117 fn to_arg(self) -> Arg<'a> {
118 Arg::Str(self)
119 }
120}
121
122impl<'a> ToArg<'a> for &'a String {
123 fn to_arg(self) -> Arg<'a> {
124 Arg::Str(self)
125 }
126}
127
128#[cfg(feature = "widestring")]
129impl<'a> ToArg<'a> for &'a wstr {
130 fn to_arg(self) -> Arg<'a> {
131 Arg::WStr(self)
132 }
133}
134
135#[cfg(feature = "widestring")]
136impl<'a> ToArg<'a> for &'a WString {
137 fn to_arg(self) -> Arg<'a> {
138 Arg::WStr(self)
139 }
140}
141
142impl<'a> ToArg<'a> for f32 {
143 fn to_arg(self) -> Arg<'a> {
144 Arg::Float(self.into())
145 }
146}
147
148impl<'a> ToArg<'a> for f64 {
149 fn to_arg(self) -> Arg<'a> {
150 Arg::Float(self)
151 }
152}
153
154impl<'a> ToArg<'a> for char {
155 fn to_arg(self) -> Arg<'a> {
156 Arg::UInt((self as u32).into())
157 }
158}
159
160impl<'a> ToArg<'a> for &'a mut usize {
161 fn to_arg(self) -> Arg<'a> {
162 Arg::USizeRef(self)
163 }
164}
165
166impl<'a, T> ToArg<'a> for &'a *const T {
167 fn to_arg(self) -> Arg<'a> {
168 Arg::UInt((*self) as usize as u64)
169 }
170}
171
172macro_rules! impl_to_arg {
174 ($($t:ty),*) => {
175 $(
176 impl<'a> ToArg<'a> for $t {
177 fn to_arg(self) -> Arg<'a> {
178 Arg::SInt(self as i64, <$t>::BITS as u8)
179 }
180 }
181 )*
182 };
183}
184impl_to_arg!(i8, i16, i32, i64, isize);
185
186macro_rules! impl_to_arg_u {
188 ($($t:ty),*) => {
189 $(
190 impl<'a> ToArg<'a> for $t {
191 fn to_arg(self) -> Arg<'a> {
192 Arg::UInt(self as u64)
193 }
194 }
195 )*
196 };
197}
198impl_to_arg_u!(u8, u16, u32, u64, usize);
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 #[cfg(feature = "widestring")]
204 use widestring::utf32str;
205
206 #[test]
207 fn test_to_arg() {
208 const SIZE_WIDTH: u8 = isize::BITS as u8;
209
210 assert!(matches!("test".to_arg(), Arg::Str("test")));
211 assert!(matches!(String::from("test").to_arg(), Arg::Str(_)));
212 #[cfg(feature = "widestring")]
213 assert!(matches!(utf32str!("test").to_arg(), Arg::WStr(_)));
214 #[cfg(feature = "widestring")]
215 assert!(matches!(WString::from("test").to_arg(), Arg::WStr(_)));
216 assert!(matches!(42f32.to_arg(), Arg::Float(_)));
217 assert!(matches!(42f64.to_arg(), Arg::Float(_)));
218 assert!(matches!('x'.to_arg(), Arg::UInt(120)));
219 let mut usize_val: usize = 0;
220 assert!(matches!((&mut usize_val).to_arg(), Arg::USizeRef(_)));
221 assert!(matches!(42i8.to_arg(), Arg::SInt(42, 8)));
222 assert!(matches!(42i16.to_arg(), Arg::SInt(42, 16)));
223 assert!(matches!(42i32.to_arg(), Arg::SInt(42, 32)));
224 assert!(matches!(42i64.to_arg(), Arg::SInt(42, 64)));
225 assert!(matches!(42isize.to_arg(), Arg::SInt(42, SIZE_WIDTH)));
226
227 assert_eq!((-42i8).to_arg(), Arg::SInt(-42, 8));
228 assert_eq!((-42i16).to_arg(), Arg::SInt(-42, 16));
229 assert_eq!((-42i32).to_arg(), Arg::SInt(-42, 32));
230 assert_eq!((-42i64).to_arg(), Arg::SInt(-42, 64));
231 assert_eq!((-42isize).to_arg(), Arg::SInt(-42, SIZE_WIDTH));
232
233 assert!(matches!(42u8.to_arg(), Arg::UInt(42)));
234 assert!(matches!(42u16.to_arg(), Arg::UInt(42)));
235 assert!(matches!(42u32.to_arg(), Arg::UInt(42)));
236 assert!(matches!(42u64.to_arg(), Arg::UInt(42)));
237 assert!(matches!(42usize.to_arg(), Arg::UInt(42)));
238
239 let ptr = &42f32 as *const f32;
240 assert!(matches!(ptr.to_arg(), Arg::UInt(_)));
241 }
242
243 #[test]
244 fn test_negative_to_arg() {
245 assert_eq!((-1_i8).to_arg().as_sint(), Ok(-1));
246 assert_eq!((-1_i16).to_arg().as_sint(), Ok(-1));
247 assert_eq!((-1_i32).to_arg().as_sint(), Ok(-1));
248 assert_eq!((-1_i64).to_arg().as_sint(), Ok(-1));
249
250 assert_eq!((u64::MAX).to_arg().as_sint(), Err(Error::Overflow));
251 }
252}