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
//! # bird-machine
//!
//! Compile your regular expressions at compile time.
//!
//! **Almost none of this has actually been implemented yet. Do not use this yet.**
//!
//! ## Example: find a date
//!
//! ```rust
//! use bird_machine::{bird_machine, Machine};
//!
//! #[bird_machine(r"^\d{4}-\d{2}-\d{2}$")]
//! struct Date;
//!
//! assert!(Date::is_match("2014-01-01"));
//! ```
//!
//! ## Example: iterating over capture groups
//!
//! ```rust
//! use bird_machine::{bird_machine, Machine};
//!
//! #[bird_machine(r"(\d{4})-(\d{2})-(\d{2})")]
//! struct Date<'a>(&'a str, &'a str, &'a str);
//! let input = "2012-03-14, 2013-01-01 and 2014-07-05";
//! let match_info = Date::captures_iter(input)
//!     .map(|x: Date| format!("Month: {} Day: {} Year: {}", x.1, x.2, x.0));
//! let expected = [
//!     "Month: 03 Day: 14 Year: 2012",
//!     "Month: 01 Day: 01 Year: 2013",
//!     "Month: 07 Day: 05 Year: 2014",
//! ];
//! for (actual, expected) in match_info.zip(expected) {
//!     assert_eq!(actual, expected);
//! }
//! ```
//!
//! ## Example: replacement with named capture groups
//!
//! ```rust
//! use bird_machine::{bird_machine, Machine};
//!
//! #[bird_machine(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})")]
//! struct Date<'a> {
//!     y: &'a str,
//!     m: &'a str,
//!     d: &'a str,
//! }
//! let before = "2012-03-14, 2013-01-01 and 2014-07-05";
//! let after = Date::replace_all(before, "$m/$d/$y");
//! assert_eq!(after, "03/14/2012, 01/01/2013 and 07/05/2014");
//! ```
//!
//! ## Example: compile-time rejection of invalid regular expressions
//!
//! ```rust,compile_fail
//! use bird_machine::bird_machine;
//!
//! #[bird_machine(r"(oops i left this group open")]
//! struct Bad;
//! ```
use std::borrow::Cow;

pub use regex::Match;

pub use bird_machine_macros::bird_machine;

pub trait Machine<'t>: Sized {
    const ORIGINAL_REGEX: &'static str;

    fn captures(text: &'t str) -> Option<Self>;

    // rust smdh why can this not just return impl Iterator
    type CaptureIterator: Iterator<Item = Self>;
    fn captures_iter(text: &'t str) -> Self::CaptureIterator;

    fn find(text: &'t str) -> Option<Match<'t>>;
    fn find_at(text: &'t str, start: usize) -> Option<Match<'t>>;

    // once again i am asking why trait methods can't return impl Iterator
    type FindIterator: Iterator<Item = Match<'t>>;
    fn find_iter(text: &'t str) -> Self::FindIterator;

    fn is_match(text: &'t str) -> bool;
    fn is_match_at(text: &'t str, start: usize) -> bool;

    fn replace(text: &'t str, rep: impl Replacer<'t, Self>) -> Cow<'t, str>;
    fn replace_all(text: &'t str, rep: impl Replacer<'t, Self>) -> Cow<'t, str>;
    fn replacen(text: &'t str, limit: usize, rep: impl Replacer<'t, Self>) -> Cow<'t, str>;

    type SplitIterator: Iterator<Item = &'t str>;
    fn split(text: &'t str) -> Self::SplitIterator;
    type SplitNIterator: Iterator<Item = &'t str>;
    fn splitn(text: &'t str, limit: usize) -> Self::SplitNIterator;
}

pub trait Replacer<'t, M: Machine<'t>> {
    fn replace_append(&mut self, r#match: &M, dst: &mut String);
}

impl<'t, M: Machine<'t>, S: AsRef<str>, F: FnMut(&M) -> S> Replacer<'t, M> for F {
    fn replace_append(&mut self, r#match: &M, dst: &mut String) {
        let replaced_with = self(r#match);
        let replaced_with: &str = replaced_with.as_ref();
        dst.push_str(replaced_with);
    }
}