1#![no_std]
96
97#[cfg(feature = "regex")]
98extern crate regex;
99
100#[cfg(any(test, doctest, feature = "std"))]
101extern crate std;
102
103#[macro_use]
104extern crate alloc;
105
106pub mod parse;
107
108#[macro_export]
109macro_rules! scan_fmt_help {
110 ( wrap $res:expr, [hex $arg:tt] ) => {
111 match $res.next() {
112 Some(item) => $arg::from_str_radix(&item, 16).ok(),
113 _ => None,
114 }
115 };
116 ( wrap $res:expr , $($arg1:tt)::* ) => {
117 match $res.next() {
118 Some(item) => item.parse::<$($arg1)::*>().ok(),
119 _ => None,
120 }
121 };
122 ( no_wrap $err:ident, $res:expr, [hex $arg:tt] ) => {
123 match $res.next() {
124 Some(item) => {
125 let ret = $arg::from_str_radix(&item, 16);
126 if ret.is_err() {
127 $err = "from_str_radix hex";
128 }
129 ret.unwrap_or(0)
130 }
131 _ => {
132 $err = "internal hex";
133 0
134 }
135 }
136 };
137 ( no_wrap $err:ident, $res:expr , $($arg1:tt)::* ) => {{
138 let mut err = "0".parse::<$($arg1)::*>(); if err.is_err() {
142 err = "0.0.0.0".parse::<$($arg1)::*>(); }
144 let err = err.unwrap();
145 match $res.next() {
146 Some(item) => {
147 let ret = item.parse::<$($arg1)::*>();
148 if(item == "") {
149 $err = "match::none";
150 } else if ret.is_err() {
151 $err = concat!("parse::", stringify!($($arg1)::*));
152 }
153 ret.unwrap_or(err)
154 }
155 _ => {
156 $err = concat!("internal ", stringify!($($arg1)::*));
157 err
158 }
159 }
160 }};
161}
162
163#[macro_export]
164macro_rules! scan_fmt_some {
165 ( $instr:expr, $fmt:expr, $($($args:tt)::*),* ) => {
166 {
167 let mut res = $crate::parse::scan( $instr, $fmt ) ;
168 ($($crate::scan_fmt_help!(wrap res,$($args)::*)),*)
169 }
170 };
171}
172
173#[macro_export]
174macro_rules! scan_fmt {
175 ( $instr:expr, $fmt:expr, $($($args:tt)::*),* ) => {
176 {
177 let mut err = "" ;
178 let mut res = $crate::parse::scan( $instr, $fmt ) ;
179 let result = ($($crate::scan_fmt_help!(no_wrap err,res,$($args)::*)),*) ;
180 if err == "" {
181 Ok(result)
182 } else {
183 Err($crate::parse::ScanError(err.into()))
184 }
185 }
186 };
187}
188
189#[cfg(feature = "std")]
190pub use std_features::*;
191
192#[cfg(feature = "std")]
193mod std_features {
194 use std::string::String;
195
196 pub fn get_input_unwrap() -> String {
197 let mut input = String::new();
198 std::io::stdin().read_line(&mut input).unwrap();
199 input
200 }
201
202 #[macro_export]
205 macro_rules! scanln_fmt {
206 ($($arg:tt)*) => {{ scan_fmt!(&$crate::get_input_unwrap(), $($arg)*) }}
207 }
208
209 #[macro_export]
212 macro_rules! scanln_fmt_some {
213 ($($arg:tt)*) => {{ scan_fmt_some!(&$crate::get_input_unwrap(), $($arg)*) }}
214 }
215}
216
217#[cfg(test)]
218use alloc::string::{String, ToString};
219#[cfg(test)]
220use parse::ScanError;
221
222#[cfg(test)]
223macro_rules! assert_flt_eq {
224 ($t:ident, $v1:expr, $v2:expr) => {{
225 assert!(($v1 - $v2).abs() <= 2.0 * std::$t::EPSILON);
226 }};
227}
228
229#[cfg(test)]
230fn ret_scan_all() -> Result<(), ScanError> {
231 let (a, b) = scan_fmt!("1.2 e","{f} {x}",f32,[hex u32])?;
232 assert_flt_eq!(f32, a, 1.2);
233 assert_eq!(b, 14);
234 Ok(())
235}
236
237#[test]
238fn test_scan_all() {
239 if let Ok(a) = scan_fmt!("hi1 3", "{} {d}", std::string::String, u32) {
240 assert_eq!(a, ("hi1".to_string(), 3));
241 } else {
242 assert!(false, "error 0");
243 }
244 if let Ok((a, b, c)) = scan_fmt!("hi1 0xf -3","{} {x} {d}",String,[hex u32],i8) {
245 assert_eq!(a, "hi1");
246 assert_eq!(b, 0xf);
247 assert_eq!(c, -3);
248 } else {
249 assert!(false, "error 1");
250 }
251 let a = scan_fmt!("hi1 f", "{} {d}", String, i32);
252 assert!(a.is_err());
253 let a = ret_scan_all();
254 std::println!("{:?}", a);
255 assert!(a.is_ok());
256}
257
258#[test]
259fn test_plus_sign() {
260 let a = scan_fmt_some!("+42", "{d}", i32);
261 assert_eq!(a, Some(42));
262 let a = scan_fmt_some!("+42.0", "{f}", f64);
263 assert_flt_eq!(f64, a.unwrap(), 42.0);
264}
265
266#[test]
267fn test_hex() {
268 let (a, b, c) =
269 scan_fmt_some!("DEV 0xab 0x1234", "{} {x} {x}", std::string::String, [hex u32], [hex u64]);
270 assert_eq!(a, Some("DEV".into()));
271 assert_eq!(b, Some(0xab));
272 assert_eq!(c, Some(0x1234));
273}
274
275#[test]
276fn test_limited_data_range() {
277 let (a, b, c) = scan_fmt_some!(
278 "test{\t 1e9 \n bye 257} hi 22.7e-1",
279 "test{{ {} bye {d}}} hi {f}",
280 f64,
281 u8,
282 f32
283 );
284 assert_flt_eq!(f64, a.unwrap(), 1e9);
285 assert_eq!(b, None); assert_flt_eq!(f32, c.unwrap(), 2.27);
287}
288
289#[test]
290fn test_too_many_outputs() {
291 let (a, b, c, d) = scan_fmt_some!("a_aa bb_b c", "{} {s} {}", String, String, String, String);
292 assert_eq!(a.unwrap(), "a_aa");
293 assert_eq!(b.unwrap(), "bb_b");
294 assert_eq!(c.unwrap(), "c");
295 assert_eq!(d, None);
296}
297
298#[test]
299fn test_skip_assign() {
300 let (a, b) = scan_fmt_some!("1 2 3, 4 5, 6 7", "{[^,]},{*[^,]},{[^,]}", String, String);
301 assert_eq!(a.unwrap(), "1 2 3");
302 assert_eq!(b.unwrap(), "6 7");
303 let a = scan_fmt!("1 2 3, 4 5, 6 7", "{[^,]},{*[^,]},{[^,]}", String, String).unwrap();
304 assert_eq!(a.0, "1 2 3");
305 assert_eq!(a.1, "6 7");
306}
307
308#[test]
309fn test_width_specifier() {
310 let a = scan_fmt!("123ab71 2.1234",
311 "{1d}{2d}{3x}{4d}{3f}",
312 u8, u8, [hex u16], u16, f32)
313 .unwrap();
314 assert_eq!(a.0, 1);
315 assert_eq!(a.1, 23);
316 assert_eq!(a.2, 0xab7);
317 assert_eq!(a.3, 1);
318 assert_flt_eq!(f32, a.4, 2.1);
319}
320
321#[test]
322fn test_err_equals() {
323 let a = scan_fmt!("hi 123", "hi {d", u8);
324 assert_eq!(a, Err(parse::ScanError("internal u8".to_string())));
325}
326
327#[test]
328fn test_no_post_match_regex() {
329 let a = scan_fmt!("74in", "{d}{/in/}", u8, String);
330 assert_eq!(a, Ok((74, String::from("in"))));
331 let a = scan_fmt!("74in", "{d}{/cm/}", u8, String);
332 assert_eq!(a, Err(parse::ScanError("match::none".to_string())));
333}
334
335#[test]
336fn test_no_post_match() {
337 let a = scan_fmt!("17in", "{d}in", u8);
338 assert_eq!(a, Ok(17u8));
339
340 let a = scan_fmt!("17in", "{d}cm", u8);
341 assert_eq!(a, Err(parse::ScanError("match::none".to_string())));
342}
343
344#[test]
345fn test_match_end() {
346 let a = scan_fmt!("17in", "{d}in{e}", u8);
347 assert_eq!(a, Ok(17u8));
348 let a = scan_fmt!("17ink", "{d}in{e}", u8);
349 assert_eq!(a, Err(parse::ScanError("match::none".to_string())));
350}
351
352#[test]
353fn test_ip_addr() {
354 let a = scan_fmt!("x 185.187.165.163 y", "x {} y", std::net::IpAddr);
355 assert_eq!(
356 a.unwrap(),
357 std::net::IpAddr::V4(std::net::Ipv4Addr::new(185, 187, 165, 163))
358 );
359}