extern crate regex;
use crate::vm::{self, thread::Thread, ExternModule};
#[derive(Debug, Userdata, Trace, VmType)]
#[gluon(vm_type = "std.regex.Regex")]
#[gluon(crate_name = "vm")]
#[gluon_trace(skip)]
struct Regex(regex::Regex);
#[derive(Debug, Userdata, Trace, VmType)]
#[gluon(vm_type = "std.regex.Error")]
#[gluon(crate_name = "vm")]
#[gluon_trace(skip)]
struct Error(regex::Error);
fn new(re: &str) -> Result<Regex, Error> {
    match regex::Regex::new(re) {
        Ok(r) => Ok(Regex(r)),
        Err(e) => Err(Error(e)),
    }
}
fn is_match(re: &Regex, text: &str) -> bool {
    let &Regex(ref re) = re;
    re.is_match(text)
}
#[derive(Pushable, VmType)]
#[gluon(vm_type = "std.regex.types.Match")]
#[gluon(crate_name = "vm")]
struct Match<'a> {
    start: usize,
    end: usize,
    text: &'a str,
}
impl<'a> Match<'a> {
    fn new(m: regex::Match<'a>) -> Self {
        Match {
            start: m.start(),
            end: m.end(),
            text: m.as_str(),
        }
    }
}
fn find<'a>(re: &Regex, text: &'a str) -> Option<Match<'a>> {
    let &Regex(ref re) = re;
    re.find(text).map(Match::new)
}
fn captures<'a>(re: &Regex, text: &'a str) -> Option<Vec<Option<Match<'a>>>> {
    let &Regex(ref re) = re;
    re.captures(text)
        .map(|c| (0..c.len()).map(move |i| c.get(i).map(Match::new)))
        .map(|i| i.collect())
}
fn error_to_string(err: &Error) -> String {
    let &Error(ref err) = err;
    err.to_string()
}
mod std {
    pub mod regex {
        pub use crate::std_lib::regex as prim;
    }
}
pub fn load(vm: &Thread) -> vm::Result<ExternModule> {
    vm.register_type::<Regex>("std.regex.Regex", &[])?;
    vm.register_type::<Error>("std.regex.Error", &[])?;
    ExternModule::new(
        vm,
        record! {
            type Error => Error,
            type Regex => Regex,
            type Match => Match,
            new => primitive!(1, std::regex::prim::new),
            is_match => primitive!(2, std::regex::prim::is_match),
            find => primitive!(2, std::regex::prim::find),
            captures => primitive!(2, "std.regex.prim.captures", |x, y| std::regex::prim::captures(x, y)),
            error_to_string => primitive!(1, std::regex::prim::error_to_string)
        },
    )
}