added docs and streamlined code, changed try_parse

This commit is contained in:
Frederik Palmø 2022-11-15 13:10:48 +01:00
parent 7266b37bd1
commit 154840f9f8
7 changed files with 108 additions and 78 deletions

2
Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]]
name = "eyes"
version = "1.1.2"
version = "1.2.0"

View file

@ -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]

View file

@ -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
View 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");
}
}

View file

@ -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)
}
}

View file

@ -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));
}