1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
// Copyright 2015 Will Lentz. // Licensed under the MIT license. //! This crate provides a simple sscanf()-like interface to extract //! data from strings and stdin. //! //! To use this crate, do: //! //! ```ignore //! #[macro_use] extern crate scan_fmt; //! ``` //! //! Example to read from a string: //! //! ```rust //! # #[macro_use] extern crate scan_fmt; //! # fn main() { //! let (a,b,c) = scan_fmt!( "hello 12 345 bye", // input string //! "hello {} {d} {}", // format //! u8, i32, String); // type of a-c Options //! assert_eq!( a.unwrap(), 12 ) ; //! assert_eq!( b.unwrap(), 345 ) ; //! assert_eq!( c.unwrap(), "bye" ) ; //! # } //! ``` //! //! Special format_string tokens: //! <pre class="rust"> //! {{ = escape for '{' //! }} = escape for '}' //! {} = return any value (until next whitespace) //! {d} = return base-10 decimal //! {x} = return hex (0xab or ab) //! {f} = return float //! {*d} = "*" as the first character means "match but don't return" //! {[...]} = return pattern. //! ^ inverts if it is the first character //! - is for ranges. For a literal - put it at the start or end. //! To add a literal ] do "[]abc]" //! Examples: //! {[0-9ab]} = match 0-9 or a or b //! {[^,.]} = match anything but , or . //! {/.../} = return regex inside of `//`. //! If there is a single capture group inside of the slashes then //! that group will make up the pattern. //! Examples: //! {/[0-9ab]/} = same as {[0-9]ab}, above //! {/a+/} = matches at least one `a`, greedily //! {/jj(a*)jj/} = matches any number of `a`s, but only if //! they're surrounded by two `j`s //! </pre> //! //! Example to read from stdin: //! //! ```rust //! # #[macro_use] extern crate scan_fmt; //! # fn main() { //! let (a,b) = scanln_fmt!( "{}-{}", // format //! u16, u8); // type of a&b Options //! match (a,b) { //! (Some(aa),Some(bb)) => println!("Got {} and {}",aa,bb), //! _ => println!("input error") //! } //! # } //! ``` //! //! ## LIMITATIONS: //! There are no compile-time checks to make sure the format //! strings matches the number of return arguments. Extra //! return values will be None. //! //! Like sscanf(), whitespace (including \n) is largely ignored. //! //! Conversion to output values is done using parse::<T>(). extern crate regex; pub mod parse; /// (a,+) = scan_fmt!( input_string, format_string, types,+ ) #[macro_export] macro_rules! scan_fmt { ( $instr:expr, $fmt:expr, $arg1:ty, $($arg2:ty),* ) => { { let mut res = $crate::parse::scan( $instr, $fmt ) ; ( match res.next() { Some(item) => item.parse::<$arg1>().ok(), _ => None } $( , match res.next() { Some(item) => item.parse::<$arg2>().ok(), _ => None } )* ) } }; ( $instr:expr, $fmt:expr, $arg1:ty ) => { { let mut res = $crate::parse::scan( $instr, $fmt ) ; ( match res.next() { Some(item) => item.parse::<$arg1>().ok(), _ => None } ) } }; } pub fn get_input_unwrap() -> String { let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); input } /// (a,+) = scanln_fmt!( format_string, types,+ ) /// <p>Same as scan_fmt!(), but reads input string from stdin.</p> #[macro_export] macro_rules! scanln_fmt { ($($arg:tt)*) => {{ scan_fmt!(&$crate::get_input_unwrap(), $($arg)*) }} } #[cfg(test)] macro_rules! assert_flt_eq { ($t:ident, $v1:expr, $v2:expr) => {{ assert!( ($v1 - $v2).abs() <= 2.0*std::$t::EPSILON ); }}; } #[test] fn test_plus_sign() { let a = scan_fmt!("+42", "{d}", i32); assert_eq!(a, Some(42)); let a = scan_fmt!("+42.0", "{f}", f64); assert_flt_eq!(f64, a.unwrap(), 42.0); } #[test] fn test_limited_data_range() { let (a, b, c) = scan_fmt!( "test{\t 1e9 \n bye 257} hi 22.7e-1", "test{{ {} bye {d}}} hi {f}", f64, u8, f32 ); assert_flt_eq!(f64, a.unwrap(), 1e9); assert_eq!(b, None); // 257 doesn't fit into a u8 assert_flt_eq!(f32, c.unwrap(), 2.27); } #[test] fn test_too_many_outputs() { let (a, b, c, d) = scan_fmt!("a_aa bb_b c", "{} {s} {}", String, String, String, String); assert_eq!(a.unwrap(), "a_aa"); assert_eq!(b.unwrap(), "bb_b"); assert_eq!(c.unwrap(), "c"); assert_eq!(d, None); } #[test] fn test_skip_assign() { let (a, b) = scan_fmt!("1 2 3, 4 5, 6 7", "{[^,]},{*[^,]},{[^,]}", String, String); assert_eq!(a.unwrap(), "1 2 3"); assert_eq!(b.unwrap(), "6 7"); }