added docs and streamlined code, changed try_parse
This commit is contained in:
		
							parent
							
								
									7266b37bd1
								
							
						
					
					
						commit
						154840f9f8
					
				
					 7 changed files with 108 additions and 78 deletions
				
			
		
							
								
								
									
										2
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -4,4 +4,4 @@ version = 3
 | 
			
		|||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "eyes"
 | 
			
		||||
version = "1.1.2"
 | 
			
		||||
version = "1.2.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "eyes"
 | 
			
		||||
version = "1.1.2"
 | 
			
		||||
version = "1.2.0"
 | 
			
		||||
edition = "2018"
 | 
			
		||||
description = "A simpler way to parse using human-readable templates"
 | 
			
		||||
license="BSD-3-Clause"
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +9,5 @@ repository = "https://git.palmoe.dk/vodofrede/eyes/"
 | 
			
		|||
readme = "README.md"
 | 
			
		||||
keywords = ["parsing", "parse", "simple", "no-deps"]
 | 
			
		||||
categories = ["encoding", "parsing", "text-processing"]
 | 
			
		||||
exclude = [".gitignore", "src/main.rs"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										49
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								README.md
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,53 +1,44 @@
 | 
			
		|||
# Eyes
 | 
			
		||||
# eyes
 | 
			
		||||
 | 
			
		||||
## A simpler way to parse using human-readable templates
 | 
			
		||||
A simpler way to parse using human-readable templates.
 | 
			
		||||
 | 
			
		||||
Eyes was made for the primary purpose of parsing challenge inputs for [Advent of Code](https://adventofcode.com) challenges.
 | 
			
		||||
The crate's primary purpose is parsing challenge inputs for [Advent of Code](https://adventofcode.com) challenges. It currently provides limited functionality, but more options may be added provided they are useful additions for parsing slightly more complicated formats.
 | 
			
		||||
 | 
			
		||||
It currently provides limited functionality, but more options may be added provided they are useful additions for parsing slightly more complicated formats.
 | 
			
		||||
This crate does not have any dependencies, as I wanted to keep it simple to and lightweight in design.
 | 
			
		||||
 | 
			
		||||
Eyes does not have any dependencies, as I wanted to keep it simple to and lightweight in design. Good performance is not guaranteed, as the library isn't well tested yet.
 | 
			
		||||
## Syntax
 | 
			
		||||
 | 
			
		||||
I was told this functionality is similar to `scanf` from C.
 | 
			
		||||
The only special characters in templates are curly brackets ('{}'). These act as stand-ins for where the extracted values are in the input strings.
 | 
			
		||||
 | 
			
		||||
### Examples:
 | 
			
		||||
## Examples:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
# #[macro_use] extern crate eyes;
 | 
			
		||||
# fn main() {
 | 
			
		||||
let input = "#lol @ 338,7643: 20.2x24.5";
 | 
			
		||||
let template = "#{} @ {},{}: {}x{}";
 | 
			
		||||
 | 
			
		||||
println!("input: '{}'", input);
 | 
			
		||||
println!("pattern: '{}'", template);
 | 
			
		||||
 | 
			
		||||
let (id, x, y, w, h) = parse!(input, template, String, isize, isize, f64, f64);
 | 
			
		||||
 | 
			
		||||
println!("id: {:?}", id);
 | 
			
		||||
println!("x: {:?}", x);
 | 
			
		||||
println!("y: {:?}", y);
 | 
			
		||||
println!("w: {:?}", w);
 | 
			
		||||
println!("h: {:?}", h);
 | 
			
		||||
let (id, x, y, w, h) = eyes::parse!(input, template, String, isize, isize, f64, f64);
 | 
			
		||||
 | 
			
		||||
assert_eq!((id.as_str(), x, y, w, h), ("lol", 338, 7643, 20.2, 24.5));
 | 
			
		||||
 | 
			
		||||
# }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Eyes** will try to expand its captures, so that the following example also works as expected:
 | 
			
		||||
**eyes** will try to expand capture groups, so that the following example also works as expected:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
# #[macro_use] extern crate eyes;
 | 
			
		||||
# fn main() {
 | 
			
		||||
let input = "turn off 660,55 through 986,197";
 | 
			
		||||
let template = "{} {},{} through {},{}";
 | 
			
		||||
 | 
			
		||||
println!("input: '{}'", input);
 | 
			
		||||
println!("pattern: '{}'", template);
 | 
			
		||||
 | 
			
		||||
let (op, x1, y1, x2, y2) = try_parse!(input, template, String, usize, usize, usize, usize);
 | 
			
		||||
 | 
			
		||||
println!("op: {:?}", op);
 | 
			
		||||
println!("p1: {:?}", (&x1, &y1));
 | 
			
		||||
println!("p2: {:?}", (&x2, &y2));
 | 
			
		||||
let (op, x1, y1, x2, y2) = eyes::parse!(input, template, String, usize, usize, usize, usize);
 | 
			
		||||
 | 
			
		||||
assert_eq!(
 | 
			
		||||
    (op.unwrap().as_str(), x1, y1, x2, y2),
 | 
			
		||||
    ("turn off", Ok(660), Ok(55), Ok(986), Ok(197))
 | 
			
		||||
    (op.as_str(), x1, y1, x2, y2),
 | 
			
		||||
    ("turn off", 660, 55, 986, 197)
 | 
			
		||||
);
 | 
			
		||||
# }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Notice that "turn off" is captured correctly, even though it contains a space.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										7
									
								
								src/bin/sandbox.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/bin/sandbox.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
fn main() {
 | 
			
		||||
    if let Some((a, b, c)) = eyes::try_parse!("1 2,3", "{} {},{}", u8, u8, u8) {
 | 
			
		||||
        assert!(a == 1 && b == 2 && c == 3);
 | 
			
		||||
    } else {
 | 
			
		||||
        unreachable!("This should not happen, as the pattern is matchable to the input");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										109
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								src/lib.rs
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,8 +1,28 @@
 | 
			
		|||
pub struct Parser<'a> {
 | 
			
		||||
#![allow(clippy::needless_question_mark)]
 | 
			
		||||
#![warn(clippy::all, clippy::cargo)]
 | 
			
		||||
#![deny(missing_docs, unsafe_code)]
 | 
			
		||||
#![doc = include_str!("../README.md")]
 | 
			
		||||
 | 
			
		||||
/// A list of captures, created by calling [`Captures::new()`] with the input and template strings.
 | 
			
		||||
///
 | 
			
		||||
/// An easier way to use this struct is with the [`eyes::parse`] and [`eyes::try_parse`] macros, which allow for automatic type conversion of captures.
 | 
			
		||||
pub struct Captures<'a> {
 | 
			
		||||
    captures: Vec<&'a str>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Parser<'a> {
 | 
			
		||||
impl<'a> Captures<'a> {
 | 
			
		||||
    /// Create a new list of captures from input and template strings.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The input and template strings must live as long as the list of captures itself, as the captures list borrows from them.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// Basic usage:
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// # use eyes::Captures;
 | 
			
		||||
    /// let captures = Captures::new("haystack|needle|haystack", "haystack|{}|haystack");
 | 
			
		||||
    /// assert_eq!(captures.unwrap().to_inner()[0], "needle");
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn new(input: &'a str, template: &'a str) -> Option<Self> {
 | 
			
		||||
        // find all patterns in the template
 | 
			
		||||
        let patterns = template
 | 
			
		||||
| 
						 | 
				
			
			@ -56,42 +76,71 @@ impl<'a> Parser<'a> {
 | 
			
		|||
        Some(Self { captures })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn captures(&self) -> Vec<&'a str> {
 | 
			
		||||
    /// Get the internal representation of the captures as an owned value, which allow usage of standard [`Vec`] methods.
 | 
			
		||||
    pub fn to_inner(&self) -> Vec<&'a str> {
 | 
			
		||||
        self.captures.to_owned()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the internal representation of the captures as a reference, which allow usage of standard [`Vec`] methods.
 | 
			
		||||
    pub fn as_inner(&self) -> &Vec<&'a str> {
 | 
			
		||||
        &self.captures
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! parse {
 | 
			
		||||
    ($input: expr, $pattern: tt, $($type:ty),*) => {
 | 
			
		||||
        {
 | 
			
		||||
            let parser = $crate::Parser::new($input, $pattern).unwrap();
 | 
			
		||||
            let captures = parser.captures();
 | 
			
		||||
            let mut iter = captures.iter();
 | 
			
		||||
 | 
			
		||||
            ($(iter.next().unwrap().parse::<$type>().unwrap()),*)
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Parse an input and template, and convert the captures to the specified types.
 | 
			
		||||
/// This version returns an option, indicating whether the input matched the template by returning None in the negative case.
 | 
			
		||||
///
 | 
			
		||||
/// # Examples
 | 
			
		||||
///
 | 
			
		||||
/// Basic usage:
 | 
			
		||||
/// ```
 | 
			
		||||
/// # #[macro_use] extern crate eyes;
 | 
			
		||||
/// if let Some((a, b, c)) = eyes::try_parse!("1 2,3", "{} {},{}", u8, u8, u8) {
 | 
			
		||||
///     assert!(a == 1 && b == 2 && c == 3);
 | 
			
		||||
/// } else {
 | 
			
		||||
///     unreachable!("This should not happen, as the pattern is matchable to the input");
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! try_parse {
 | 
			
		||||
    ($input: expr, $pattern: tt, $($type:ty),*) => {
 | 
			
		||||
        {
 | 
			
		||||
            if let Some(parser) = $crate::Parser::new($input, $pattern) {
 | 
			
		||||
                let captures = parser.captures();
 | 
			
		||||
                let mut iter = captures.iter();
 | 
			
		||||
 | 
			
		||||
                Some(($(iter.next().unwrap().parse::<$type>()),*))
 | 
			
		||||
            } else {
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[allow(clippy::all)]
 | 
			
		||||
            $crate::Captures::new($input, $pattern)
 | 
			
		||||
                .and_then(|c| {
 | 
			
		||||
                    let mut iter = c.as_inner().iter();
 | 
			
		||||
 | 
			
		||||
                    Some(($(iter.next()?.parse::<$type>().ok()?),*))
 | 
			
		||||
                })
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Parse an input and template, and convert the captures to the specified types.
 | 
			
		||||
///
 | 
			
		||||
/// ## Panics
 | 
			
		||||
///
 | 
			
		||||
/// This macro unwraps the parse result, causing a panic in any of the following cases:
 | 
			
		||||
/// - The template does not match the input.
 | 
			
		||||
/// - The capture could not be converted to the specified type.
 | 
			
		||||
///
 | 
			
		||||
/// # Examples
 | 
			
		||||
///
 | 
			
		||||
/// Basic usage:
 | 
			
		||||
/// ```
 | 
			
		||||
/// # #[macro_use] extern crate eyes;
 | 
			
		||||
/// # fn main() {
 | 
			
		||||
/// let (a, b, c) = eyes::parse!("1 2,3", "{} {},{}", u8, u8, u8);
 | 
			
		||||
/// assert!(a == 1 && b == 2 && c == 3);
 | 
			
		||||
/// # }
 | 
			
		||||
/// ```
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! parse {
 | 
			
		||||
    ($input: expr, $pattern: tt, $($type:ty),*) => {
 | 
			
		||||
        try_parse!($input, $pattern, $($type),*).unwrap()
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
| 
						 | 
				
			
			@ -132,8 +181,8 @@ mod tests {
 | 
			
		|||
        println!("p2: {:?}", (&x2, &y2));
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            (op.unwrap().as_str(), x1, y1, x2, y2),
 | 
			
		||||
            ("turn off", Ok(660), Ok(55), Ok(986), Ok(197))
 | 
			
		||||
            (op.as_str(), x1, y1, x2, y2),
 | 
			
		||||
            ("turn off", 660, 55, 986, 197)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +200,7 @@ mod tests {
 | 
			
		|||
        println!("b: {:?}", b);
 | 
			
		||||
        println!("c: {:?}", c);
 | 
			
		||||
 | 
			
		||||
        assert_eq!((a, b, c), (Ok(775), Ok(785), Ok(361)));
 | 
			
		||||
        assert_eq!((a, b, c), (775, 785, 361));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +216,7 @@ mod tests {
 | 
			
		|||
        println!("a: {:?}", a);
 | 
			
		||||
        println!("b: {:?}", b);
 | 
			
		||||
 | 
			
		||||
        assert_eq!((a, b), (Ok(1), Ok(1)))
 | 
			
		||||
        assert_eq!((a, b), (1, 1))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +231,6 @@ mod tests {
 | 
			
		|||
 | 
			
		||||
        println!("a: {:?}", a);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(a, Ok(3240955))
 | 
			
		||||
        assert_eq!(a, 3240955)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/main.rs
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,16 +0,0 @@
 | 
			
		|||
use eyes::*;
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let input = "turn off 660,55 through 986,197";
 | 
			
		||||
    let template = "{} {},{} through {},{}";
 | 
			
		||||
 | 
			
		||||
    println!("input: '{}'", input);
 | 
			
		||||
    println!("pattern: '{}'", template);
 | 
			
		||||
 | 
			
		||||
    let (op, x1, y1, x2, y2) =
 | 
			
		||||
        try_parse!(input, template, String, usize, usize, usize, usize).unwrap();
 | 
			
		||||
 | 
			
		||||
    println!("op: {:?}", op);
 | 
			
		||||
    println!("p1: {:?}", (x1, y1));
 | 
			
		||||
    println!("p2: {:?}", (x2, y2));
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in a new issue