A Rust crate with a sscanf (inverse of format!()) Macro based on Regex [![Tests](https://github.com/mich101mich/sscanf/actions/workflows/test.yml/badge.svg)](https://github.com/mich101mich/sscanf/actions/workflows/test.yml) [![Crates.io](https://img.shields.io/crates/v/sscanf.svg)](https://crates.io/crates/sscanf) [![Documentation](https://docs.rs/sscanf/badge.svg)](https://docs.rs/sscanf/) [![Dependency status](https://deps.rs/repo/github/mich101mich/sscanf/status.svg)](https://deps.rs/repo/github/mich101mich/sscanf) `sscanf` is originally a C-function that takes a string, a format string with placeholders and several variables (in the Rust version replaced with types). It then parses the input string, writing the values behind the placeholders into the variables (Rust: returns a tuple). `sscanf` can be thought of as reversing a call to `format!()`: ```rust // format: takes format string and values, returns String let msg = format!("Hello {}{}!", "World", 5); assert_eq!(msg, "Hello World5!"); // sscanf: takes string, format string and types, returns tuple let parsed = sscanf::sscanf!(msg, "Hello {}{}!", str, usize); // parsed is Result<(&str, usize), ...> assert_eq!(parsed.unwrap(), ("World", 5)); // alternative syntax: let parsed2 = sscanf::sscanf!(msg, "Hello {str}{usize}!"); assert_eq!(parsed2.unwrap(), ("World", 5)); ``` `sscanf!()` takes a format string like `format!()`, but doesn't write the values into the placeholders (`{}`), but extracts the values at those `{}` into the return tuple. If matching the format string failed, an Error is returned: ```rust let msg = "Text that doesn't match the format string"; let parsed = sscanf::sscanf!(msg, "Hello {str}{usize}!"); assert!(matches!(parsed, Err(sscanf::Error::MatchFailed))); ``` **Types in Placeholders:** The types can either be given as a separate parameter after the format string, or directly inside of the `{}` placeholder. The first allows for autocomplete while typing, syntax highlighting and better compiler errors generated by sscanf in case that the wrong types are given. The second imitates the [Rust format!() behavior since 1.58](https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html#captured-identifiers-in-format-strings). This option gives worse compiler errors when using stable Rust, but is otherwise identical to the first option. More examples of the capabilities of `sscanf`: ```rust use sscanf::sscanf; use std::num::NonZeroUsize; let input = ""; let parsed = sscanf!(input, ""); assert_eq!(parsed.unwrap(), (3, -6, 6)); let input = "Move to N36E21"; let parsed = sscanf!(input, "Move to {char}{usize}{char}{usize}"); assert_eq!(parsed.unwrap(), ('N', 36, 'E', 21)); let input = "Escape literal { } as {{ and }}"; let parsed = sscanf!(input, "Escape literal {{ }} as {{{{ and }}}}"); assert_eq!(parsed.unwrap(), ()); let input = "Indexing types: N36E21"; let parsed = sscanf!(input, "Indexing types: {1}{0}{1}{0}", NonZeroUsize, char); // output is in the order of the placeholders assert_eq!(parsed.unwrap(), ('N', NonZeroUsize::new(36).unwrap(), 'E', NonZeroUsize::new(21).unwrap())); let input = "A Sentence with Spaces. Another Sentence."; // str and String do the same, but String clones from the input string // to take ownership instead of borrowing. let (a, b) = sscanf!(input, "{String}. {str}.").unwrap(); assert_eq!(a, "A Sentence with Spaces"); assert_eq!(b, "Another Sentence"); // Number format options let input = "ab01 127 101010 1Z"; let parsed = sscanf!(input, "{usize:x} {i32:o} {u8:b} {u32:r36}"); let (a, b, c, d) = parsed.unwrap(); assert_eq!(a, 0xab01); // Hexadecimal assert_eq!(b, 0o127); // Octal assert_eq!(c, 0b101010); // Binary assert_eq!(d, 71); // any radix (r36 = Radix 36) assert_eq!(d, u32::from_str_radix("1Z", 36).unwrap()); let input = "color: #D4AF37"; // Number types take their size into account, and hexadecimal u8 can // have at most 2 digits => only possible match is 2 digits each. let (r, g, b) = sscanf!(input, "color: #{u8:x}{u8:x}{u8:x}").unwrap(); assert_eq!((r, g, b), (0xD4, 0xAF, 0x37)); ``` The input in this case is a `&'static str`, but it can be `String`, `&str`, `&String`, ... Basically anything with [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html). and without taking ownership. See [here](https://docs.rs/sscanf/latest/sscanf/macro.sscanf.html#examples) for a few examples of possible inputs. The parsing part of this macro has very few limitations, since it replaces the `{}` with a Regular Expression ([`regex`](https://docs.rs/regex)) that corresponds to that type. For example: - `char` is just one character (regex `"."`) - `str` is any sequence of characters (regex `".+?"`) - Numbers are any sequence of digits (regex `"[-+]?\d+"`) And so on. The actual implementation for numbers tries to take the size of the type into account and some other details, but that is the gist of the parsing. This means that any sequence of replacements is possible as long as the Regex finds a combination that works. In the `char, usize, char, usize` example above it manages to assign the `N` and `E` to the `char`s because they cannot be matched by the `usize`s. # Format Options All options are inside `'{'` `'}'` and after a `:`, so either as `{: