cljrs-value 0.1.6

Runtime Value type and persistent collections for clojurust
Documentation
use crate::{PersistentVector, Value};
use cljrs_gc::{GcPtr, GcVisitor, MarkVisitor, Trace};
use regex::{Captures, Regex};
use std::sync::Mutex;

#[derive(Debug, Clone)]
pub enum MatchPhase {
    New,
    Matching(usize),
    Complete,
}

#[derive(Debug, Clone)]
struct MatcherState {
    phase: MatchPhase,
    last_match: Option<MatchResult>,
}

#[derive(Debug)]
pub struct Matcher {
    pub pattern: GcPtr<Regex>,
    haystack: GcPtr<String>,
    state: Mutex<MatcherState>,
    match_all: bool,
}

#[derive(Debug, Clone)]
pub struct MatchResult {
    pub full: String,
    pub groups: Vec<Option<String>>,
}

impl Clone for Matcher {
    fn clone(&self) -> Matcher {
        let state = self.state.lock().unwrap().clone();
        Matcher {
            pattern: self.pattern.clone(),
            haystack: self.haystack.clone(),
            state: Mutex::new(state.clone()),
            match_all: self.match_all,
        }
    }
}

impl Trace for Matcher {
    fn trace(&self, visitor: &mut MarkVisitor) {
        visitor.visit(&self.pattern);
        visitor.visit(&self.haystack);
    }
}

impl Matcher {
    pub fn new(pattern: Regex, source: String, match_all: bool) -> Self {
        Self {
            pattern: GcPtr::new(pattern),
            haystack: GcPtr::new(source),
            state: Mutex::new(MatcherState {
                phase: MatchPhase::New,
                last_match: None,
            }),
            match_all,
        }
    }

    pub fn next(&self) -> MatchPhase {
        let mut state = self.state.lock().unwrap();
        match state.phase {
            MatchPhase::New => match self.pattern.get().captures(self.haystack.get()) {
                Some(cap) => {
                    if !self.match_all || cap.len() == self.haystack.get().len() {
                        let match_ = cap.get_match();
                        *state = MatcherState {
                            phase: MatchPhase::Matching(match_.end()),
                            last_match: Some(MatchResult::new(cap)),
                        }
                    }
                }
                None => {
                    *state = MatcherState {
                        phase: MatchPhase::Complete,
                        last_match: None,
                    }
                }
            },
            MatchPhase::Matching(n) => {
                match self.pattern.get().captures_at(self.haystack.get(), n) {
                    Some(cap) => {
                        *state = MatcherState {
                            phase: MatchPhase::Matching(cap.get_match().end()),
                            last_match: Some(MatchResult::new(cap)),
                        }
                    }
                    None => {
                        *state = MatcherState {
                            phase: MatchPhase::Complete,
                            last_match: None,
                        };
                    }
                }
            }
            MatchPhase::Complete => {}
        }
        state.phase.clone()
    }

    pub fn capture(&self) -> Option<MatchResult> {
        let state = self.state.lock().unwrap();
        state.last_match.clone()
    }

    pub fn phase(&self) -> MatchPhase {
        self.state.lock().unwrap().phase.clone()
    }
}

impl MatchResult {
    pub fn new(cap: Captures) -> Self {
        Self {
            full: cap.get_match().as_str().to_string(),
            groups: cap
                .iter()
                .map(|g| g.map(|e| e.as_str().to_string()))
                .collect(),
        }
    }

    pub fn to_value(&self) -> Value {
        if self.groups.len() == 1 || self.groups.iter().skip(1).all(|g| g.is_none()) {
            Value::Str(GcPtr::new(self.full.to_string()))
        } else {
            let groups: Vec<Value> = self
                .groups
                .iter()
                .map(|g| match g {
                    Some(m) => Value::Str(GcPtr::new(m.to_string())),
                    None => Value::Nil,
                })
                .collect();
            Value::Vector(GcPtr::new(PersistentVector::from_iter(groups)))
        }
    }
}