Skip to main content

bird_machine/
lib.rs

1//! # bird-machine
2//!
3//! Compile your regular expressions at compile time.
4//!
5//! **Almost none of this has actually been implemented yet. Do not use this yet.**
6//!
7//! ## Example: find a date
8//!
9//! ```rust
10//! use bird_machine::{bird_machine, Machine};
11//!
12//! #[bird_machine(r"^\d{4}-\d{2}-\d{2}$")]
13//! struct Date;
14//!
15//! assert!(Date::is_match("2014-01-01"));
16//! ```
17//!
18//! ## Example: iterating over capture groups
19//!
20//! ```rust
21//! use bird_machine::{bird_machine, Machine};
22//!
23//! #[bird_machine(r"(\d{4})-(\d{2})-(\d{2})")]
24//! struct Date<'a>(&'a str, &'a str, &'a str);
25//! let input = "2012-03-14, 2013-01-01 and 2014-07-05";
26//! let match_info = Date::captures_iter(input)
27//!     .map(|x: Date| format!("Month: {} Day: {} Year: {}", x.1, x.2, x.0));
28//! let expected = [
29//!     "Month: 03 Day: 14 Year: 2012",
30//!     "Month: 01 Day: 01 Year: 2013",
31//!     "Month: 07 Day: 05 Year: 2014",
32//! ];
33//! for (actual, expected) in match_info.zip(expected) {
34//!     assert_eq!(actual, expected);
35//! }
36//! ```
37//!
38//! ## Example: replacement with named capture groups
39//!
40//! ```rust
41//! use bird_machine::{bird_machine, Machine};
42//!
43//! #[bird_machine(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})")]
44//! struct Date<'a> {
45//!     y: &'a str,
46//!     m: &'a str,
47//!     d: &'a str,
48//! }
49//! let before = "2012-03-14, 2013-01-01 and 2014-07-05";
50//! let after = Date::replace_all(before, "$m/$d/$y");
51//! assert_eq!(after, "03/14/2012, 01/01/2013 and 07/05/2014");
52//! ```
53//!
54//! ## Example: compile-time rejection of invalid regular expressions
55//!
56//! ```rust,compile_fail
57//! use bird_machine::bird_machine;
58//!
59//! #[bird_machine(r"(oops i left this group open")]
60//! struct Bad;
61//! ```
62use std::borrow::Cow;
63
64pub use regex::Match;
65
66pub use bird_machine_macros::bird_machine;
67
68pub trait Machine<'t>: Sized {
69    const ORIGINAL_REGEX: &'static str;
70
71    fn captures(text: &'t str) -> Option<Self>;
72
73    // rust smdh why can this not just return impl Iterator
74    type CaptureIterator: Iterator<Item = Self>;
75    fn captures_iter(text: &'t str) -> Self::CaptureIterator;
76
77    fn find(text: &'t str) -> Option<Match<'t>>;
78    fn find_at(text: &'t str, start: usize) -> Option<Match<'t>>;
79
80    // once again i am asking why trait methods can't return impl Iterator
81    type FindIterator: Iterator<Item = Match<'t>>;
82    fn find_iter(text: &'t str) -> Self::FindIterator;
83
84    fn is_match(text: &'t str) -> bool;
85    fn is_match_at(text: &'t str, start: usize) -> bool;
86
87    fn replace(text: &'t str, rep: impl Replacer<'t, Self>) -> Cow<'t, str>;
88    fn replace_all(text: &'t str, rep: impl Replacer<'t, Self>) -> Cow<'t, str>;
89    fn replacen(text: &'t str, limit: usize, rep: impl Replacer<'t, Self>) -> Cow<'t, str>;
90
91    type SplitIterator: Iterator<Item = &'t str>;
92    fn split(text: &'t str) -> Self::SplitIterator;
93    type SplitNIterator: Iterator<Item = &'t str>;
94    fn splitn(text: &'t str, limit: usize) -> Self::SplitNIterator;
95}
96
97pub trait Replacer<'t, M: Machine<'t>> {
98    fn replace_append(&mut self, r#match: &M, dst: &mut String);
99}
100
101impl<'t, M: Machine<'t>, S: AsRef<str>, F: FnMut(&M) -> S> Replacer<'t, M> for F {
102    fn replace_append(&mut self, r#match: &M, dst: &mut String) {
103        let replaced_with = self(r#match);
104        let replaced_with: &str = replaced_with.as_ref();
105        dst.push_str(replaced_with);
106    }
107}