<commit_before>use std::path::PathBuf;

pub trait Completer {
    fn completions(&self, start: &str) -> Vec<String>;
}

pub struct BasicCompleter {
    prefixes: Vec<String>,
}

impl BasicCompleter {
    pub fn new<T: Into<String>>(prefixes: Vec<T>) -> BasicCompleter {
        BasicCompleter { prefixes: prefixes.into_iter().map(|s| s.into()).collect() }
    }
}

impl Completer for BasicCompleter {
    fn completions(&self, start: &str) -> Vec<String> {
        self.prefixes.iter().filter(|s| s.starts_with(start)).cloned().collect()
    }
}

pub struct FilenameCompleter {
    working_dir: Option<PathBuf>,
}

impl FilenameCompleter {
    pub fn new<T: Into<PathBuf>>(working_dir: Option<T>) -> Self {
        FilenameCompleter { working_dir: working_dir.map(|p| p.into()) }
    }
}

impl Completer for FilenameCompleter {
    fn completions(&self, mut start: &str) -> Vec<String> {
        // XXX: this function is really bad, TODO rewrite

        let start_owned;
        if start.starts_with("\"") || start.starts_with("'") {
            start = &start[1..];
            if start.len() >= 1 {
                start = &start[..start.len() - 1];
            }
            start_owned = start.into();
        } else {
            start_owned = start.replace("\\ ", " ");
        }

        let full_path;
        let start_path = PathBuf::from(&start_owned[..]);

        if let Some(ref wd) = self.working_dir {
            let mut fp = PathBuf::from(wd);
            fp.push(start_owned.clone());
            full_path = fp;
        } else {
            full_path = PathBuf::from(start_owned.clone());
        }

        if full_path.is_relative() {
            return vec![];
        }

        let p;
        let start_name;
        let completing_dir;
        match full_path.parent() {
            // XXX non-unix separaor
            Some(parent) if start != "" && !start_owned.ends_with("/") => {
                p = PathBuf::from(parent);
                start_name = full_path.file_name().unwrap().to_string_lossy().into_owned();
                completing_dir = false;
            }
            _ => {
                p = full_path.clone();
                start_name = "".into();
                completing_dir = start == "" || start.ends_with("/");
            }
        }

        let read_dir = match p.read_dir() {
            Ok(x) => x,
            Err(_) => return vec![],
        };

        let mut matches = vec![];
        for dir in read_dir {
            let dir = match dir {
                Ok(x) => x,
                Err(_) => continue,
            };
            let file_name = dir.file_name();
            let file_name = file_name.to_string_lossy();

            if start_name == "" || file_name.starts_with(&*start_name) {
                let mut a = start_path.clone();
                if !a.is_absolute() {
                    a = PathBuf::new();
                } else if !completing_dir && !a.pop() {
                    return vec![];
                }
                a.push(dir.file_name());
                let mut s = a.to_string_lossy().into_owned();
                if dir.path().is_dir() {
                    s = s + "/";
                }

                let mut b = PathBuf::from(start_owned.clone());
                if !completing_dir {
                    b.pop();
                }
                b.push(s);

                matches.push(b.to_string_lossy().to_owned().replace(" ", "\\ "));
            }
        }

        matches
    }
}
<commit_msg>Properly handle path ending in ..<commit_after>use std::path::PathBuf;

pub trait Completer {
    fn completions(&self, start: &str) -> Vec<String>;
}

pub struct BasicCompleter {
    prefixes: Vec<String>,
}

impl BasicCompleter {
    pub fn new<T: Into<String>>(prefixes: Vec<T>) -> BasicCompleter {
        BasicCompleter { prefixes: prefixes.into_iter().map(|s| s.into()).collect() }
    }
}

impl Completer for BasicCompleter {
    fn completions(&self, start: &str) -> Vec<String> {
        self.prefixes.iter().filter(|s| s.starts_with(start)).cloned().collect()
    }
}

pub struct FilenameCompleter {
    working_dir: Option<PathBuf>,
}

impl FilenameCompleter {
    pub fn new<T: Into<PathBuf>>(working_dir: Option<T>) -> Self {
        FilenameCompleter { working_dir: working_dir.map(|p| p.into()) }
    }
}

impl Completer for FilenameCompleter {
    fn completions(&self, mut start: &str) -> Vec<String> {
        // XXX: this function is really bad, TODO rewrite

        let start_owned;
        if start.starts_with("\"") || start.starts_with("'") {
            start = &start[1..];
            if start.len() >= 1 {
                start = &start[..start.len() - 1];
            }
            start_owned = start.into();
        } else {
            start_owned = start.replace("\\ ", " ");
        }

        let full_path;
        let start_path = PathBuf::from(&start_owned[..]);

        if let Some(ref wd) = self.working_dir {
            let mut fp = PathBuf::from(wd);
            fp.push(start_owned.clone());
            full_path = fp;
        } else {
            full_path = PathBuf::from(start_owned.clone());
        }

        if full_path.is_relative() {
            return vec![];
        }

        let p;
        let start_name;
        let completing_dir;
        match full_path.parent() {
            // XXX non-unix separaor
            Some(parent) if start != "" && !start_owned.ends_with("/") &&
                            !full_path.ends_with("..") => {
                p = PathBuf::from(parent);
                start_name = full_path.file_name().unwrap().to_string_lossy().into_owned();
                completing_dir = false;
            }
            _ => {
                p = full_path.clone();
                start_name = "".into();
                completing_dir = start == "" || start.ends_with("/") || full_path.ends_with("..");
            }
        }

        let read_dir = match p.read_dir() {
            Ok(x) => x,
            Err(_) => return vec![],
        };

        let mut matches = vec![];
        for dir in read_dir {
            let dir = match dir {
                Ok(x) => x,
                Err(_) => continue,
            };
            let file_name = dir.file_name();
            let file_name = file_name.to_string_lossy();

            if start_name == "" || file_name.starts_with(&*start_name) {
                let mut a = start_path.clone();
                if !a.is_absolute() {
                    a = PathBuf::new();
                } else if !completing_dir && !a.pop() {
                    return vec![];
                }
                a.push(dir.file_name());
                let mut s = a.to_string_lossy().into_owned();
                if dir.path().is_dir() {
                    s = s + "/";
                }

                let mut b = PathBuf::from(start_owned.clone());
                if !completing_dir {
                    b.pop();
                }
                b.push(s);

                matches.push(b.to_string_lossy().to_owned().replace(" ", "\\ "));
            }
        }

        matches
    }
}
<|endoftext|>


<commit_before>use core as _core;

pub trait BitFlags<T>:
    _core::ops::BitOr<Output = Self>
    + _core::ops::BitOrAssign
    + _core::ops::BitXor<Output = Self>
    + _core::ops::BitXorAssign
    + _core::ops::BitAnd<Output = Self>
    + _core::ops::BitAndAssign
    + _core::ops::Sub<Output = Self>
    + _core::ops::SubAssign
    + _core::ops::Not<Output = Self>
    + _core::iter::Extend<Self>
    + _core::iter::FromIterator<Self>
    + _core::fmt::Debug
    + _core::cmp::PartialEq<Self>
    + _core::cmp::Eq
    + _core::cmp::PartialOrd<Self>
    + _core::cmp::Ord
    + _core::hash::Hash
    + _core::fmt::Octal
    + _core::fmt::Binary
    + _core::fmt::LowerHex
    + _core::fmt::UpperHex
    + Clone
    + Copy
{
    /// Returns an empty set of flags.
    fn empty() -> Self;
    /// Returns the set containing all flags.
    fn all() -> Self;
    /// Returns the raw value of the flags currently stored.
    fn bits(&self) -> T;
    /// Convert from underlying bit representation, unless that
    /// representation contains bits that do not correspond to a flag.
    fn from_bits(bits: T) -> core::option::Option<Self>;
    /// Convert from underlying bit representation, dropping any bits
    /// that do not correspond to flags.
    unsafe fn from_bits_truncate(bits: T) -> Self;
    /// Convert from underlying bit representation, preserving all
    /// bits (even those not corresponding to a defined flag).
    ///
    /// # Safety
    ///
    /// The caller of the `bitflags!` macro can chose to allow or
    /// disallow extra bits for their bitflags type.
    ///
    /// The caller of `from_bits_unchecked()` has to ensure that
    /// all bits correspond to a defined flag or that extra bits
    /// are valid for this bitflags type.
    unsafe fn from_bits_unchecked(bits: T) -> Self;
    /// Returns `true` if no flags are currently stored.
    fn is_empty(&self) -> bool;
    /// Returns `true` if all flags are currently set.
    fn is_all(&self) -> bool;
    /// Returns `true` if there are flags common to both `self` and `other`.
    fn intersects(&self, other: Self) -> bool;
    /// Returns `true` all of the flags in `other` are contained within `self`.
    fn contains(&self, other: Self) -> bool;
    /// Inserts the specified flags in-place.
    fn insert(&mut self, other: Self);
    /// Removes the specified flags in-place.
    fn remove(&mut self, other: Self);
    /// Toggles the specified flags in-place.
    fn toggle(&mut self, other: Self);
    /// Inserts or removes the specified flags depending on the passed value.
    fn set(&mut self, other: Self, value: bool);
}
<commit_msg>fix BitFlags trait for old compilers<commit_after>use core as _core;

pub trait BitFlags<T>:
    _core::ops::BitOr<Output = Self>
    + _core::ops::BitOrAssign
    + _core::ops::BitXor<Output = Self>
    + _core::ops::BitXorAssign
    + _core::ops::BitAnd<Output = Self>
    + _core::ops::BitAndAssign
    + _core::ops::Sub<Output = Self>
    + _core::ops::SubAssign
    + _core::ops::Not<Output = Self>
    + _core::iter::Extend<Self>
    + _core::iter::FromIterator<Self>
    + _core::fmt::Debug
    + _core::cmp::PartialEq<Self>
    + _core::cmp::Eq
    + _core::cmp::PartialOrd<Self>
    + _core::cmp::Ord
    + _core::hash::Hash
    + _core::fmt::Octal
    + _core::fmt::Binary
    + _core::fmt::LowerHex
    + _core::fmt::UpperHex
    + Clone
    + Copy
{
    /// Returns an empty set of flags.
    fn empty() -> Self;
    /// Returns the set containing all flags.
    fn all() -> Self;
    /// Returns the raw value of the flags currently stored.
    fn bits(&self) -> T;
    /// Convert from underlying bit representation, unless that
    /// representation contains bits that do not correspond to a flag.
    fn from_bits(bits: T) -> _core::option::Option<Self>;
    /// Convert from underlying bit representation, dropping any bits
    /// that do not correspond to flags.
    unsafe fn from_bits_truncate(bits: T) -> Self;
    /// Convert from underlying bit representation, preserving all
    /// bits (even those not corresponding to a defined flag).
    ///
    /// # Safety
    ///
    /// The caller of the `bitflags!` macro can chose to allow or
    /// disallow extra bits for their bitflags type.
    ///
    /// The caller of `from_bits_unchecked()` has to ensure that
    /// all bits correspond to a defined flag or that extra bits
    /// are valid for this bitflags type.
    unsafe fn from_bits_unchecked(bits: T) -> Self;
    /// Returns `true` if no flags are currently stored.
    fn is_empty(&self) -> bool;
    /// Returns `true` if all flags are currently set.
    fn is_all(&self) -> bool;
    /// Returns `true` if there are flags common to both `self` and `other`.
    fn intersects(&self, other: Self) -> bool;
    /// Returns `true` all of the flags in `other` are contained within `self`.
    fn contains(&self, other: Self) -> bool;
    /// Inserts the specified flags in-place.
    fn insert(&mut self, other: Self);
    /// Removes the specified flags in-place.
    fn remove(&mut self, other: Self);
    /// Toggles the specified flags in-place.
    fn toggle(&mut self, other: Self);
    /// Inserts or removes the specified flags depending on the passed value.
    fn set(&mut self, other: Self, value: bool);
}
<|endoftext|>


<commit_before>#[doc = "Rust wrappers around the raw JS apis"];

import bg = jsapi::bindgen;
import libc::types::os::arch::c95::{size_t, c_uint};
import std::map::{hashmap, str_hash};

export rt;
export cx;
export jsobj;
export methods;
export compartment;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

type rt = @rt_rsrc;

struct rt_rsrc {
    let ptr : *JSRuntime;
    new(p : {ptr: *JSRuntime}) {
        self.ptr = p.ptr;
    }
    drop {
        JS_Finish(self.ptr);
    }
}

fn rt() -> rt {
    @rt_rsrc({ptr: JS_Init(default_heapsize)})
}

impl rt {
    fn cx() -> cx {
        @cx_rsrc({ptr: JS_NewContext(self.ptr, default_stacksize as size_t),
                  rt: self})
    }
}

// ___________________________________________________________________________
// contexts

type cx = @cx_rsrc;

struct cx_rsrc {
    let ptr : *JSContext;
    let rt: rt;
    let classes: hashmap<~str, @JSClass>;

    new(rec : {ptr: *JSContext, rt: rt}) {
        self.ptr = rec.ptr;
        self.rt = rec.rt;
        self.classes = str_hash();
    }
    drop {
        JS_DestroyContext(self.ptr);
    }
}

impl cx {
    fn rooted_obj(obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc({cx: self, cxptr: self.ptr, ptr: obj});
        JS_AddObjectRoot(self.ptr, ptr::addr_of(jsobj.ptr));
        jsobj
    }

    fn set_default_options_and_version() {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
        self.set_version(JSVERSION_LATEST);
    }

    fn set_options(v: c_uint) {
        JS_SetOptions(self.ptr, v);
    }

    fn set_version(v: i32) {
        JS_SetVersion(self.ptr, v);
    }

    fn set_logging_error_reporter() {
        JS_SetErrorReporter(self.ptr, reportError);
    }

    fn set_error_reporter(reportfn: *u8) {
        JS_SetErrorReporter(self.ptr, reportfn);
    }

    fn new_compartment(globclsfn: fn(name_pool) -> JSClass) -> Result<compartment,()> {
        let np = name_pool();
        let globcls = @globclsfn(np);
        let globobj = JS_NewGlobalObject(self.ptr, ptr::to_unsafe_ptr(&*globcls), null());
        result(JS_InitStandardClasses(self.ptr, globobj)).chain(|_ok| {
            let compartment = @{cx: self,
                                name_pool: np,
                                mut global_funcs: ~[],
                                mut global_props: ~[],
                                global_class: globcls,
                                global_obj: self.rooted_obj(globobj),
                                global_protos: str_hash()
                               };
            self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
            Ok(compartment)
        })
    }

    fn evaluate_script(glob: jsobj, bytes: ~[u8], filename: ~str, line_num: uint)
                    -> Result<(),()> {
        vec::as_buf(bytes, |bytes_ptr, bytes_len| {
            str::as_c_str(filename, |filename_cstr| {
                let bytes_ptr = bytes_ptr as *c_char;
                let rval: jsval = JSVAL_NULL;
                #debug["Evaluating script from %s with bytes %?", filename, bytes];
                if JS_EvaluateScript(self.ptr, glob.ptr,
                                     bytes_ptr, bytes_len as c_uint,
                                     filename_cstr, line_num as c_uint,
                                     ptr::addr_of(rval)) == ERR {
                    #debug["...err!"];
                    Err(())
                } else {
                    // we could return the script result but then we'd have
                    // to root it and so forth and, really, who cares?
                    #debug["...ok!"];
                    Ok(())
                }
            })
        })
    }

    fn lookup_class_name(s: ~str) ->  @JSClass {
      option::expect(self.classes.find(s),
           #fmt("Class %s not found in class table", s))
    }

    unsafe fn get_cx_private() -> *() {
        unsafe::reinterpret_cast(JS_GetContextPrivate(self.ptr))
    }

    unsafe fn set_cx_private(data: *()) {
        JS_SetContextPrivate(self.ptr, unsafe::reinterpret_cast(data));
    }

    unsafe fn get_obj_private(obj: *JSObject) -> *() {
        unsafe::reinterpret_cast(JS_GetPrivate(obj))
    }

    unsafe fn set_obj_private(obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, unsafe::reinterpret_cast(data));
    }
}

extern fn reportError(_cx: *JSContext,
                     msg: *c_char,
                     report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        #error["Error at %s:%?: %s\n", fname, lineno, msg];
    }
}

// ___________________________________________________________________________
// compartment

type bare_compartment = {
    cx: cx,
    name_pool: name_pool,
    mut global_funcs: ~[@~[JSFunctionSpec]],
    mut global_props: ~[@~[JSPropertySpec]],
    global_class: @JSClass,
    global_obj: jsobj,
    global_protos: hashmap<~str, jsobj>
};

trait methods {
    fn define_functions(specfn: fn(name_pool) -> ~[JSFunctionSpec]) -> Result<(),()>;
    fn define_properties(specfn: fn() -> ~[JSPropertySpec]) -> Result<(),()>;
    fn define_property(name: ~str, value: jsval, getter: JSPropertyOp,
                       setter: JSStrictPropertyOp, attrs: c_uint) -> Result<(),()>;
    fn new_object(class_name: ~str, proto: *JSObject, parent: *JSObject) -> Result<jsobj, ()>;
    fn new_object_with_proto(class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()>;
    fn register_class(class_fn: fn(bare_compartment) -> JSClass);
    fn get_global_proto(name: ~str) -> jsobj;
    fn stash_global_proto(name: ~str, proto: jsobj);
    fn add_name(name: ~str) -> *c_char;
}

type compartment = @bare_compartment;

impl bare_compartment : methods {
    fn define_functions(specfn: fn(name_pool) -> ~[JSFunctionSpec]) -> Result<(),()> {
        let specvec = @specfn(self.name_pool);
        vec::push(self.global_funcs, specvec);
        vec::as_buf(*specvec, |specs, _len| {
            result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
        })
    }
    fn define_properties(specfn: fn() -> ~[JSPropertySpec]) -> Result<(),()> {
        let specvec = @specfn();
        vec::push(self.global_props, specvec);
        vec::as_buf(*specvec, |specs, _len| {
            result(JS_DefineProperties(self.cx.ptr, self.global_obj.ptr, specs))
        })
    }
    fn define_property(name: ~str, value: jsval, getter: JSPropertyOp, setter: JSStrictPropertyOp,
                       attrs: c_uint)
                    -> Result<(),()> {
        result(JS_DefineProperty(self.cx.ptr, self.global_obj.ptr, self.add_name(name),
                                 value, getter, setter, attrs))
    }
    fn new_object(class_name: ~str, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                  proto, parent));
        result_obj(obj)
    }
    fn new_object_with_proto(class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let proto = option::expect(self.global_protos.find(proto_name),
           #fmt("new_object_with_proto: expected to find %s in the proto \
              table", proto_name));
        let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                  proto.ptr, parent));
        result_obj(obj)
    }
    fn get_global_proto(name: ~str) -> jsobj {
        self.global_protos.get(name)
    }
    fn stash_global_proto(name: ~str, proto: jsobj) {
        if !self.global_protos.insert(name, proto) {
            fail ~"Duplicate global prototype registered; you're gonna have a bad time."
        }
    }
    fn register_class(class_fn: fn(bare_compartment) -> JSClass) {
        let classptr = @class_fn(self);
        if !self.cx.classes.insert(
            unsafe { str::unsafe::from_c_str(classptr.name) },
            classptr) {
            fail ~"Duplicate JSClass registered; you're gonna have a bad time."
        }
    }
    fn add_name(name: ~str) -> *c_char {
        self.name_pool.add(copy name)
    }
}

// ___________________________________________________________________________
// objects

type jsobj = @jsobj_rsrc;

struct jsobj_rsrc {
    let cx : cx;
    let cxptr : *JSContext;
    let ptr : *JSObject;
    new(rec : {cx: cx, cxptr: *JSContext, ptr: *JSObject}) {
        self.cx = rec.cx;
        self.cxptr = rec.cxptr;
        self.ptr = rec.ptr;
    }
    drop {
        JS_RemoveObjectRoot(self.cxptr, ptr::addr_of(self.ptr));
    }
}

// ___________________________________________________________________________
// random utilities

trait to_jsstr {
    fn to_jsstr(cx: cx) -> *JSString;
}

impl ~str : to_jsstr {
    fn to_jsstr(cx: cx) -> *JSString {
        str::as_buf(self, |buf, len| {
            let cbuf = unsafe { unsafe::reinterpret_cast(buf) };
            bg::JS_NewStringCopyN(cx.ptr, cbuf, len as size_t)
        })
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).chain(|comp| {
            comp.define_functions(global::debug_fns);

            let bytes = str::bytes(~"debug(22);");
            cx.evaluate_script(comp.global_obj, bytes, ~"test", 1u)
        });
    }

}
<commit_msg>Fix tests<commit_after>#[doc = "Rust wrappers around the raw JS apis"];

import bg = jsapi::bindgen;
import libc::types::os::arch::c95::{size_t, c_uint};
import std::map::{hashmap, str_hash};

export rt;
export cx;
export jsobj;
export methods;
export compartment;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

type rt = @rt_rsrc;

struct rt_rsrc {
    let ptr : *JSRuntime;
    new(p : {ptr: *JSRuntime}) {
        self.ptr = p.ptr;
    }
    drop {
        JS_Finish(self.ptr);
    }
}

fn rt() -> rt {
    @rt_rsrc({ptr: JS_Init(default_heapsize)})
}

impl rt {
    fn cx() -> cx {
        @cx_rsrc({ptr: JS_NewContext(self.ptr, default_stacksize as size_t),
                  rt: self})
    }
}

// ___________________________________________________________________________
// contexts

type cx = @cx_rsrc;

struct cx_rsrc {
    let ptr : *JSContext;
    let rt: rt;
    let classes: hashmap<~str, @JSClass>;

    new(rec : {ptr: *JSContext, rt: rt}) {
        self.ptr = rec.ptr;
        self.rt = rec.rt;
        self.classes = str_hash();
    }
    drop {
        JS_DestroyContext(self.ptr);
    }
}

impl cx {
    fn rooted_obj(obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc({cx: self, cxptr: self.ptr, ptr: obj});
        JS_AddObjectRoot(self.ptr, ptr::addr_of(jsobj.ptr));
        jsobj
    }

    fn set_default_options_and_version() {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
        self.set_version(JSVERSION_LATEST);
    }

    fn set_options(v: c_uint) {
        JS_SetOptions(self.ptr, v);
    }

    fn set_version(v: i32) {
        JS_SetVersion(self.ptr, v);
    }

    fn set_logging_error_reporter() {
        JS_SetErrorReporter(self.ptr, reportError);
    }

    fn set_error_reporter(reportfn: *u8) {
        JS_SetErrorReporter(self.ptr, reportfn);
    }

    fn new_compartment(globclsfn: fn(name_pool) -> JSClass) -> Result<compartment,()> {
        let np = name_pool();
        let globcls = @globclsfn(np);
        let globobj = JS_NewGlobalObject(self.ptr, ptr::to_unsafe_ptr(&*globcls), null());
        result(JS_InitStandardClasses(self.ptr, globobj)).chain(|_ok| {
            let compartment = @{cx: self,
                                name_pool: np,
                                mut global_funcs: ~[],
                                mut global_props: ~[],
                                global_class: globcls,
                                global_obj: self.rooted_obj(globobj),
                                global_protos: str_hash()
                               };
            self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
            Ok(compartment)
        })
    }

    fn evaluate_script(glob: jsobj, bytes: ~[u8], filename: ~str, line_num: uint)
                    -> Result<(),()> {
        vec::as_buf(bytes, |bytes_ptr, bytes_len| {
            str::as_c_str(filename, |filename_cstr| {
                let bytes_ptr = bytes_ptr as *c_char;
                let rval: jsval = JSVAL_NULL;
                #debug["Evaluating script from %s with bytes %?", filename, bytes];
                if JS_EvaluateScript(self.ptr, glob.ptr,
                                     bytes_ptr, bytes_len as c_uint,
                                     filename_cstr, line_num as c_uint,
                                     ptr::addr_of(rval)) == ERR {
                    #debug["...err!"];
                    Err(())
                } else {
                    // we could return the script result but then we'd have
                    // to root it and so forth and, really, who cares?
                    #debug["...ok!"];
                    Ok(())
                }
            })
        })
    }

    fn lookup_class_name(s: ~str) ->  @JSClass {
      option::expect(self.classes.find(s),
           #fmt("Class %s not found in class table", s))
    }

    unsafe fn get_cx_private() -> *() {
        unsafe::reinterpret_cast(JS_GetContextPrivate(self.ptr))
    }

    unsafe fn set_cx_private(data: *()) {
        JS_SetContextPrivate(self.ptr, unsafe::reinterpret_cast(data));
    }

    unsafe fn get_obj_private(obj: *JSObject) -> *() {
        unsafe::reinterpret_cast(JS_GetPrivate(obj))
    }

    unsafe fn set_obj_private(obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, unsafe::reinterpret_cast(data));
    }
}

extern fn reportError(_cx: *JSContext,
                     msg: *c_char,
                     report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        #error["Error at %s:%?: %s\n", fname, lineno, msg];
    }
}

// ___________________________________________________________________________
// compartment

type bare_compartment = {
    cx: cx,
    name_pool: name_pool,
    mut global_funcs: ~[@~[JSFunctionSpec]],
    mut global_props: ~[@~[JSPropertySpec]],
    global_class: @JSClass,
    global_obj: jsobj,
    global_protos: hashmap<~str, jsobj>
};

trait methods {
    fn define_functions(specfn: fn(name_pool) -> ~[JSFunctionSpec]) -> Result<(),()>;
    fn define_properties(specfn: fn() -> ~[JSPropertySpec]) -> Result<(),()>;
    fn define_property(name: ~str, value: jsval, getter: JSPropertyOp,
                       setter: JSStrictPropertyOp, attrs: c_uint) -> Result<(),()>;
    fn new_object(class_name: ~str, proto: *JSObject, parent: *JSObject) -> Result<jsobj, ()>;
    fn new_object_with_proto(class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()>;
    fn register_class(class_fn: fn(bare_compartment) -> JSClass);
    fn get_global_proto(name: ~str) -> jsobj;
    fn stash_global_proto(name: ~str, proto: jsobj);
    fn add_name(name: ~str) -> *c_char;
}

type compartment = @bare_compartment;

impl bare_compartment : methods {
    fn define_functions(specfn: fn(name_pool) -> ~[JSFunctionSpec]) -> Result<(),()> {
        let specvec = @specfn(self.name_pool);
        vec::push(self.global_funcs, specvec);
        vec::as_buf(*specvec, |specs, _len| {
            result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
        })
    }
    fn define_properties(specfn: fn() -> ~[JSPropertySpec]) -> Result<(),()> {
        let specvec = @specfn();
        vec::push(self.global_props, specvec);
        vec::as_buf(*specvec, |specs, _len| {
            result(JS_DefineProperties(self.cx.ptr, self.global_obj.ptr, specs))
        })
    }
    fn define_property(name: ~str, value: jsval, getter: JSPropertyOp, setter: JSStrictPropertyOp,
                       attrs: c_uint)
                    -> Result<(),()> {
        result(JS_DefineProperty(self.cx.ptr, self.global_obj.ptr, self.add_name(name),
                                 value, getter, setter, attrs))
    }
    fn new_object(class_name: ~str, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                  proto, parent));
        result_obj(obj)
    }
    fn new_object_with_proto(class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let proto = option::expect(self.global_protos.find(proto_name),
           #fmt("new_object_with_proto: expected to find %s in the proto \
              table", proto_name));
        let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                  proto.ptr, parent));
        result_obj(obj)
    }
    fn get_global_proto(name: ~str) -> jsobj {
        self.global_protos.get(name)
    }
    fn stash_global_proto(name: ~str, proto: jsobj) {
        if !self.global_protos.insert(name, proto) {
            fail ~"Duplicate global prototype registered; you're gonna have a bad time."
        }
    }
    fn register_class(class_fn: fn(bare_compartment) -> JSClass) {
        let classptr = @class_fn(self);
        if !self.cx.classes.insert(
            unsafe { str::unsafe::from_c_str(classptr.name) },
            classptr) {
            fail ~"Duplicate JSClass registered; you're gonna have a bad time."
        }
    }
    fn add_name(name: ~str) -> *c_char {
        self.name_pool.add(copy name)
    }
}

// ___________________________________________________________________________
// objects

type jsobj = @jsobj_rsrc;

struct jsobj_rsrc {
    let cx : cx;
    let cxptr : *JSContext;
    let ptr : *JSObject;
    new(rec : {cx: cx, cxptr: *JSContext, ptr: *JSObject}) {
        self.cx = rec.cx;
        self.cxptr = rec.cxptr;
        self.ptr = rec.ptr;
    }
    drop {
        JS_RemoveObjectRoot(self.cxptr, ptr::addr_of(self.ptr));
    }
}

// ___________________________________________________________________________
// random utilities

trait to_jsstr {
    fn to_jsstr(cx: cx) -> *JSString;
}

impl ~str : to_jsstr {
    fn to_jsstr(cx: cx) -> *JSString {
        str::as_buf(self, |buf, len| {
            let cbuf = unsafe { unsafe::reinterpret_cast(buf) };
            bg::JS_NewStringCopyN(cx.ptr, cbuf, len as size_t)
        })
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).chain(|comp| {
            comp.define_functions(global::debug_fns);

            let bytes = str::to_bytes(~"debug(22);");
            cx.evaluate_script(comp.global_obj, bytes, ~"test", 1u)
        });
    }

}
<|endoftext|>


<commit_before>use std::convert;
use std::io;
use std::result;
use std::num::{FromPrimitive, ToPrimitive};
use std::str::Utf8Error;

use byteorder;

const FIXSTR_SIZE   : u8 = 0x1f;
const FIXARRAY_SIZE : u8 = 0x0f;
const FIXMAP_SIZE   : u8 = 0x0f;

#[derive(Debug, PartialEq)]
pub enum Marker {
    PositiveFixnum(u8),
    NegativeFixnum(i8),
    Null,
    True,
    False,
    U8,
    U16,
    U32,
    U64,
    I8,
    I16,
    I32,
    I64,
    F32,
    F64,
    FixedString(u8),
    Str8,
    Str16,
    Str32,
    Bin8,
    Bin16,
    Bin32,
    FixedArray(u8),
    Array16,
    Array32,
    FixedMap(u8),
    Map16,
    Map32,
    FixExt1,
    FixExt2,
    FixExt4,
    FixExt8,
    FixExt16,
    Ext8,
    Ext16,
    Ext32,
    Reserved,
}

// TODO: Consider using own trait without Option packing/unpacking.
impl FromPrimitive for Marker {
    fn from_i64(n: i64) -> Option<Marker> {
        FromPrimitive::from_u64(n as u64)
    }

    fn from_u64(n: u64) -> Option<Marker> {
        match n {
            val @ 0x00 ... 0x7f => Some(Marker::PositiveFixnum(val as u8)),
            val @ 0xe0 ... 0xff => Some(Marker::NegativeFixnum(val as i8)),
            val @ 0x80 ... 0x8f => Some(Marker::FixedMap((val as u8) & FIXMAP_SIZE)),
            val @ 0x90 ... 0x9f => Some(Marker::FixedArray((val as u8) & FIXARRAY_SIZE)),
            val @ 0xa0 ... 0xbf => Some(Marker::FixedString((val as u8) & FIXSTR_SIZE)),
            0xc0 => Some(Marker::Null),
            0xc1 => None, // Marked in MessagePack spec as never used.
            0xc2 => Some(Marker::False),
            0xc3 => Some(Marker::True),
            0xc4 => Some(Marker::Bin8),
            0xc5 => Some(Marker::Bin16),
            0xc6 => Some(Marker::Bin32),
            0xc7 => Some(Marker::Ext8),
            0xc8 => Some(Marker::Ext16),
            0xc9 => Some(Marker::Ext32),
            0xca => Some(Marker::F32),
            0xcb => Some(Marker::F64),
            0xcc => Some(Marker::U8),
            0xcd => Some(Marker::U16),
            0xce => Some(Marker::U32),
            0xcf => Some(Marker::U64),
            0xd0 => Some(Marker::I8),
            0xd1 => Some(Marker::I16),
            0xd2 => Some(Marker::I32),
            0xd3 => Some(Marker::I64),
            0xd4 => Some(Marker::FixExt1),
            0xd5 => Some(Marker::FixExt2),
            0xd6 => Some(Marker::FixExt4),
            0xd7 => Some(Marker::FixExt8),
            0xd8 => Some(Marker::FixExt16),
            0xd9 => Some(Marker::Str8),
            0xda => Some(Marker::Str16),
            0xdb => Some(Marker::Str32),
            0xdc => Some(Marker::Array16),
            0xdd => Some(Marker::Array32),
            0xde => Some(Marker::Map16),
            0xdf => Some(Marker::Map32),
            _ => None,
        }
    }
}

// TODO: Consider using own trait without Option packing/unpacking.
impl ToPrimitive for Marker {
    fn to_i64(&self) -> Option<i64> {
        Some(ToPrimitive::to_u64(self).unwrap() as i64)
    }

    fn to_u64(&self) -> Option<u64> {
        let byte = match *self {
            Marker::PositiveFixnum(val) => val,
            Marker::NegativeFixnum(val) => val as u8,
            Marker::Null                => 0xc0,
            Marker::True                => unimplemented!(),
            Marker::False               => unimplemented!(),
            Marker::U8                  => 0xcc,
            Marker::U16                 => 0xcd,
            Marker::U32                 => 0xce,
            Marker::U64                 => 0xcf,
            Marker::I8                  => 0xd0,
            Marker::I16                 => 0xd1,
            Marker::I32                 => 0xd2,
            Marker::I64                 => 0xd3,
            Marker::F32                 => unimplemented!(),
            Marker::F64                 => unimplemented!(),
            Marker::FixedString(..)     => unimplemented!(),
            Marker::Str8                => unimplemented!(),
            Marker::Str16               => unimplemented!(),
            Marker::Str32               => unimplemented!(),
            Marker::Bin8                => unimplemented!(),
            Marker::Bin16               => unimplemented!(),
            Marker::Bin32               => unimplemented!(),
            Marker::FixedArray(..)      => unimplemented!(),
            Marker::Array16             => unimplemented!(),
            Marker::Array32             => unimplemented!(),
            Marker::FixedMap(..)        => unimplemented!(),
            Marker::Map16               => unimplemented!(),
            Marker::Map32               => unimplemented!(),
            Marker::FixExt1             => unimplemented!(),
            Marker::FixExt2             => unimplemented!(),
            Marker::FixExt4             => unimplemented!(),
            Marker::FixExt8             => unimplemented!(),
            Marker::FixExt16            => unimplemented!(),
            Marker::Ext8                => unimplemented!(),
            Marker::Ext16               => unimplemented!(),
            Marker::Ext32               => unimplemented!(),
            Marker::Reserved            => unimplemented!(),
        };

        Some(byte as u64)
    }
}

/// An error type for reading bytes from the reader.
///
/// This is a thin wrapper over the standard `io::Error` type. Namely, it adds one additional error
/// case: an unexpected EOF.
#[unstable(reason = "remove Debug trait")]
#[derive(Debug)]
pub enum ReadError {
    UnexpectedEOF,
    IO(io::Error),
}

// TODO: This is a hack, because io::Error has PartialEq once.
impl PartialEq for ReadError {
    fn eq(&self, other: &ReadError) -> bool {
        match (self, other) {
            (&ReadError::UnexpectedEOF, &ReadError::UnexpectedEOF) => true,
            (&ReadError::IO(ref lhs),   &ReadError::IO(ref rhs)) => {
                lhs.kind() == rhs.kind()
            }
            _ => false
        }
    }

    fn ne(&self, other: &ReadError) -> bool {
        return !self.eq(other);
    }
}

impl convert::From<io::Error> for ReadError {
    fn from(err: io::Error) -> ReadError { ReadError::IO(err) }
}

impl convert::From<ReadError> for io::Error {
    fn from(err: ReadError) -> io::Error {
        match err {
            ReadError::IO(err) => err,
            ReadError::UnexpectedEOF => io::Error::new(io::ErrorKind::Other, "unexpected EOF")
        }
    }
}

impl convert::From<byteorder::Error> for ReadError {
    fn from(err: byteorder::Error) -> ReadError {
        match err {
            byteorder::Error::UnexpectedEOF => ReadError::UnexpectedEOF,
            byteorder::Error::Io(err) => ReadError::IO(err),
        }
    }
}

#[unstable(reason = "remove Debug trait; incomplete")]
#[derive(Debug, PartialEq)]
pub enum Error {
    /// Marker type error.
    TypeMismatch(Marker),
    /// IO error while reading marker.
    InvalidMarkerRead(ReadError),
    /// IO error while reading data.
    InvalidDataRead(ReadError),
}

#[unstable(reason = "Core? Shit name!")]
#[derive(Debug, PartialEq)]
pub enum DecodeStringError<'a> {
    Core(Error),
    /// The given buffer is too small to accumulate specified amount of bytes.
    BufferSizeTooSmall(u32),
    InvalidDataCopy(&'a [u8], ReadError),
    InvalidUtf8(&'a [u8], Utf8Error),
}

impl<'a> convert::From<Error> for DecodeStringError<'a> {
    fn from(err: Error) -> DecodeStringError<'a> {
        DecodeStringError::Core(err)
    }
}

pub type Result<T> = result::Result<T, Error>;

#[derive(Clone, Debug, PartialEq)]
pub enum Integer {
    /// Every non-negative integer is treated as u64, even if it fits in i64.
    U64(u64),
    /// Every negative integer is treated as i64.
    I64(i64),
}

#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    Null,
    Integer(Integer),
    String(String),
    Array(Vec<Value>),
}

pub mod decode;
pub mod encode;
<commit_msg>[Core] Marker enum is now copyable.<commit_after>use std::convert;
use std::io;
use std::result;
use std::num::{FromPrimitive, ToPrimitive};
use std::str::Utf8Error;

use byteorder;

const FIXSTR_SIZE   : u8 = 0x1f;
const FIXARRAY_SIZE : u8 = 0x0f;
const FIXMAP_SIZE   : u8 = 0x0f;

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Marker {
    PositiveFixnum(u8),
    NegativeFixnum(i8),
    Null,
    True,
    False,
    U8,
    U16,
    U32,
    U64,
    I8,
    I16,
    I32,
    I64,
    F32,
    F64,
    FixedString(u8),
    Str8,
    Str16,
    Str32,
    Bin8,
    Bin16,
    Bin32,
    FixedArray(u8),
    Array16,
    Array32,
    FixedMap(u8),
    Map16,
    Map32,
    FixExt1,
    FixExt2,
    FixExt4,
    FixExt8,
    FixExt16,
    Ext8,
    Ext16,
    Ext32,
    Reserved,
}

// TODO: Consider using own trait without Option packing/unpacking.
impl FromPrimitive for Marker {
    fn from_i64(n: i64) -> Option<Marker> {
        FromPrimitive::from_u64(n as u64)
    }

    fn from_u64(n: u64) -> Option<Marker> {
        match n {
            val @ 0x00 ... 0x7f => Some(Marker::PositiveFixnum(val as u8)),
            val @ 0xe0 ... 0xff => Some(Marker::NegativeFixnum(val as i8)),
            val @ 0x80 ... 0x8f => Some(Marker::FixedMap((val as u8) & FIXMAP_SIZE)),
            val @ 0x90 ... 0x9f => Some(Marker::FixedArray((val as u8) & FIXARRAY_SIZE)),
            val @ 0xa0 ... 0xbf => Some(Marker::FixedString((val as u8) & FIXSTR_SIZE)),
            0xc0 => Some(Marker::Null),
            0xc1 => None, // Marked in MessagePack spec as never used.
            0xc2 => Some(Marker::False),
            0xc3 => Some(Marker::True),
            0xc4 => Some(Marker::Bin8),
            0xc5 => Some(Marker::Bin16),
            0xc6 => Some(Marker::Bin32),
            0xc7 => Some(Marker::Ext8),
            0xc8 => Some(Marker::Ext16),
            0xc9 => Some(Marker::Ext32),
            0xca => Some(Marker::F32),
            0xcb => Some(Marker::F64),
            0xcc => Some(Marker::U8),
            0xcd => Some(Marker::U16),
            0xce => Some(Marker::U32),
            0xcf => Some(Marker::U64),
            0xd0 => Some(Marker::I8),
            0xd1 => Some(Marker::I16),
            0xd2 => Some(Marker::I32),
            0xd3 => Some(Marker::I64),
            0xd4 => Some(Marker::FixExt1),
            0xd5 => Some(Marker::FixExt2),
            0xd6 => Some(Marker::FixExt4),
            0xd7 => Some(Marker::FixExt8),
            0xd8 => Some(Marker::FixExt16),
            0xd9 => Some(Marker::Str8),
            0xda => Some(Marker::Str16),
            0xdb => Some(Marker::Str32),
            0xdc => Some(Marker::Array16),
            0xdd => Some(Marker::Array32),
            0xde => Some(Marker::Map16),
            0xdf => Some(Marker::Map32),
            _ => None,
        }
    }
}

// TODO: Consider using own trait without Option packing/unpacking.
impl ToPrimitive for Marker {
    fn to_i64(&self) -> Option<i64> {
        Some(ToPrimitive::to_u64(self).unwrap() as i64)
    }

    fn to_u64(&self) -> Option<u64> {
        let byte = match *self {
            Marker::PositiveFixnum(val) => val,
            Marker::NegativeFixnum(val) => val as u8,
            Marker::Null                => 0xc0,
            Marker::True                => unimplemented!(),
            Marker::False               => unimplemented!(),
            Marker::U8                  => 0xcc,
            Marker::U16                 => 0xcd,
            Marker::U32                 => 0xce,
            Marker::U64                 => 0xcf,
            Marker::I8                  => 0xd0,
            Marker::I16                 => 0xd1,
            Marker::I32                 => 0xd2,
            Marker::I64                 => 0xd3,
            Marker::F32                 => unimplemented!(),
            Marker::F64                 => unimplemented!(),
            Marker::FixedString(..)     => unimplemented!(),
            Marker::Str8                => unimplemented!(),
            Marker::Str16               => unimplemented!(),
            Marker::Str32               => unimplemented!(),
            Marker::Bin8                => unimplemented!(),
            Marker::Bin16               => unimplemented!(),
            Marker::Bin32               => unimplemented!(),
            Marker::FixedArray(..)      => unimplemented!(),
            Marker::Array16             => unimplemented!(),
            Marker::Array32             => unimplemented!(),
            Marker::FixedMap(..)        => unimplemented!(),
            Marker::Map16               => unimplemented!(),
            Marker::Map32               => unimplemented!(),
            Marker::FixExt1             => unimplemented!(),
            Marker::FixExt2             => unimplemented!(),
            Marker::FixExt4             => unimplemented!(),
            Marker::FixExt8             => unimplemented!(),
            Marker::FixExt16            => unimplemented!(),
            Marker::Ext8                => unimplemented!(),
            Marker::Ext16               => unimplemented!(),
            Marker::Ext32               => unimplemented!(),
            Marker::Reserved            => unimplemented!(),
        };

        Some(byte as u64)
    }
}

/// An error type for reading bytes from the reader.
///
/// This is a thin wrapper over the standard `io::Error` type. Namely, it adds one additional error
/// case: an unexpected EOF.
#[unstable(reason = "remove Debug trait")]
#[derive(Debug)]
pub enum ReadError {
    UnexpectedEOF,
    IO(io::Error),
}

// TODO: This is a hack, because io::Error has PartialEq once.
impl PartialEq for ReadError {
    fn eq(&self, other: &ReadError) -> bool {
        match (self, other) {
            (&ReadError::UnexpectedEOF, &ReadError::UnexpectedEOF) => true,
            (&ReadError::IO(ref lhs),   &ReadError::IO(ref rhs)) => {
                lhs.kind() == rhs.kind()
            }
            _ => false
        }
    }

    fn ne(&self, other: &ReadError) -> bool {
        return !self.eq(other);
    }
}

impl convert::From<io::Error> for ReadError {
    fn from(err: io::Error) -> ReadError { ReadError::IO(err) }
}

impl convert::From<ReadError> for io::Error {
    fn from(err: ReadError) -> io::Error {
        match err {
            ReadError::IO(err) => err,
            ReadError::UnexpectedEOF => io::Error::new(io::ErrorKind::Other, "unexpected EOF")
        }
    }
}

impl convert::From<byteorder::Error> for ReadError {
    fn from(err: byteorder::Error) -> ReadError {
        match err {
            byteorder::Error::UnexpectedEOF => ReadError::UnexpectedEOF,
            byteorder::Error::Io(err) => ReadError::IO(err),
        }
    }
}

#[unstable(reason = "remove Debug trait; incomplete")]
#[derive(Debug, PartialEq)]
pub enum Error {
    /// Marker type error.
    TypeMismatch(Marker),
    /// IO error while reading marker.
    InvalidMarkerRead(ReadError),
    /// IO error while reading data.
    InvalidDataRead(ReadError),
}

#[unstable(reason = "Core? Shit name!")]
#[derive(Debug, PartialEq)]
pub enum DecodeStringError<'a> {
    Core(Error),
    /// The given buffer is too small to accumulate specified amount of bytes.
    BufferSizeTooSmall(u32),
    InvalidDataCopy(&'a [u8], ReadError),
    InvalidUtf8(&'a [u8], Utf8Error),
}

impl<'a> convert::From<Error> for DecodeStringError<'a> {
    fn from(err: Error) -> DecodeStringError<'a> {
        DecodeStringError::Core(err)
    }
}

pub type Result<T> = result::Result<T, Error>;

#[derive(Clone, Debug, PartialEq)]
pub enum Integer {
    /// Every non-negative integer is treated as u64, even if it fits in i64.
    U64(u64),
    /// Every negative integer is treated as i64.
    I64(i64),
}

#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    Null,
    Integer(Integer),
    String(String),
    Array(Vec<Value>),
}

pub mod decode;
pub mod encode;
<|endoftext|>


<commit_before>//! Manage data for algorithms
//!
//! Instantiate from the [`Algorithmia`](../struct.Algorithmia.html) struct
pub use data::dir::{DataDir, DirEntry, DataAcl, ReadAcl};
pub use self::file::DataFile;
pub use self::path::{DataPath, HasDataPath};

pub mod dir;
pub mod file;
pub mod path;

static DATA_BASE_PATH: &'static str = "v1/data";

header! { (XDataType, "X-Data-Type") => [String] }
header! { (XErrorMessage, "X-Error-Message") => [String] }

pub enum DataType {
    File,
    Dir,
}

pub enum DataObject {
    File(DataFile),
    Dir(DataDir),
}

// Shared by results for deleting both files and directories
#[derive(RustcDecodable, Debug)]
pub struct DeletedResult {
    pub deleted: u64,
}

pub fn parse_data_uri(data_uri: &str) -> &str {
    match data_uri {
        p if p.starts_with("data://") => &p[7..],
        p if p.starts_with("/") => &p[1..],
        p => p,
    }
}

<commit_msg>add tests for path helpers<commit_after>//! Manage data for algorithms
//!
//! Instantiate from the [`Algorithmia`](../struct.Algorithmia.html) struct
pub use data::dir::{DataDir, DirEntry, DataAcl, ReadAcl};
pub use self::file::DataFile;
pub use self::path::{DataPath, HasDataPath};

pub mod dir;
pub mod file;
pub mod path;

static DATA_BASE_PATH: &'static str = "v1/data";

header! { (XDataType, "X-Data-Type") => [String] }
header! { (XErrorMessage, "X-Error-Message") => [String] }

pub enum DataType {
    File,
    Dir,
}

pub enum DataObject {
    File(DataFile),
    Dir(DataDir),
}

// Shared by results for deleting both files and directories
#[derive(RustcDecodable, Debug)]
pub struct DeletedResult {
    pub deleted: u64,
}

pub fn parse_data_uri(data_uri: &str) -> &str {
    match data_uri {
        p if p.starts_with("data://") => &p[7..],
        p if p.starts_with("/") => &p[1..],
        p => p,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_protocol() {
        assert_eq!(parse_data_uri("data://foo"), "foo");
        assert_eq!(parse_data_uri("data://foo/"), "foo/");
        assert_eq!(parse_data_uri("data://foo/bar"), "foo/bar");
    }

    #[test]
    fn test_parse_leading_slash() {
        assert_eq!(parse_data_uri("/foo"), "foo");
        assert_eq!(parse_data_uri("/foo/"), "foo/");
        assert_eq!(parse_data_uri("/foo/bar"), "foo/bar");
    }

    #[test]
    fn test_parse_unprefixed() {
        assert_eq!(parse_data_uri("foo"), "foo");
        assert_eq!(parse_data_uri("foo/"), "foo/");
        assert_eq!(parse_data_uri("foo/bar"), "foo/bar");
    }

}<|endoftext|>


<commit_before>generate_error_module!(
    generate_error_types!(RefError, RefErrorKind,
        StoreReadError          => "Store read error",
        StoreWriteError         => "Store write error",
        IOError                 => "IO Error",
        HeaderTypeError         => "Header type error",
        HeaderFieldMissingError => "Header field missing error",
        HeaderFieldWriteError   => "Header field cannot be written",
        HeaderFieldReadError    => "Header field cannot be read",
        HeaderFieldAlreadyExistsError => "Header field already exists, cannot override",
        PathUTF8Error => "Path cannot be converted because of UTF8 Error",
        PathHashingError => "Path cannot be hashed",
        PathCanonicalizationError => "Path cannot be canonicalized",

        TypeConversionError => "Couldn't convert types",
        RefToDisplayError => "Cannot convert Ref to string to show it to user",

        RefNotInStore => "Ref/StoreId does not exist in store",

        RefTargetDoesNotExist       => "Ref Target does not exist",
        RefTargetPermissionError    => "Ref Target permissions insufficient for referencing",
        RefTargetCannotBeHashed     => "Ref Target cannot be hashed (is it a directory?)",
        RefTargetFileCannotBeOpened => "Ref Target File cannot be open()ed",
        RefTargetCannotReadPermissions => "Ref Target: Cannot read permissions"
    );
);

pub use self::error::RefError;
pub use self::error::RefErrorKind;
pub use self::error::MapErrInto;

<commit_msg>Add generic UTF8Error error kind<commit_after>generate_error_module!(
    generate_error_types!(RefError, RefErrorKind,
        StoreReadError          => "Store read error",
        StoreWriteError         => "Store write error",
        IOError                 => "IO Error",
        UTF8Error               => "UTF8 Error",
        HeaderTypeError         => "Header type error",
        HeaderFieldMissingError => "Header field missing error",
        HeaderFieldWriteError   => "Header field cannot be written",
        HeaderFieldReadError    => "Header field cannot be read",
        HeaderFieldAlreadyExistsError => "Header field already exists, cannot override",
        PathUTF8Error => "Path cannot be converted because of UTF8 Error",
        PathHashingError => "Path cannot be hashed",
        PathCanonicalizationError => "Path cannot be canonicalized",

        TypeConversionError => "Couldn't convert types",
        RefToDisplayError => "Cannot convert Ref to string to show it to user",

        RefNotInStore => "Ref/StoreId does not exist in store",

        RefTargetDoesNotExist       => "Ref Target does not exist",
        RefTargetPermissionError    => "Ref Target permissions insufficient for referencing",
        RefTargetCannotBeHashed     => "Ref Target cannot be hashed (is it a directory?)",
        RefTargetFileCannotBeOpened => "Ref Target File cannot be open()ed",
        RefTargetCannotReadPermissions => "Ref Target: Cannot read permissions"
    );
);

pub use self::error::RefError;
pub use self::error::RefErrorKind;
pub use self::error::MapErrInto;

<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)")
                 .unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap();
    let port = cap.name("port").unwrap().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        match doc_type.as_ref() {
            "addr" => {
                serde_json::from_value::<mimir::Addr>(*v)
                    .ok()
                    .and_then(|o| Some(mimir::Place::Addr(o)))
            }
            "street" => {
                serde_json::from_value::<mimir::Street>(*v)
                    .ok()
                    .and_then(|o| Some(mimir::Place::Street(o)))
            }
            "admin" => {
                serde_json::from_value::<mimir::Admin>(*v)
                    .ok()
                    .and_then(|o| Some(mimir::Place::Admin(o)))
            }
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn build_query(q: &String, match_type: &str, coord: &Option<model::Coord>) -> rs_es::query::Query {
    let boost_addr = rs_q::build_term("_type","addr").with_boost(1000).build();
    let boost_match_query = rs_q::build_match(match_type, q.to_string()).with_boost(100).build();

    let mut should_query = vec![
   boost_addr,
   boost_match_query];
   if let &Some(ref c) = coord {
       // if we have coordinate, we boost we result near this coordinate
       let boost_on_proximity = rs_q::build_function_score()
              .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
              .with_boost(300)
              .with_function(
          rs_es::query::functions::Function::build_decay("coord", rs_u::Location::LatLon(c.lat, c.lon), rs_u::Distance::new(3f64, rs_u::DistanceUnit::Kilometer)).build_gauss()).build();
        should_query.push(boost_on_proximity);
   } else {
       // if we don't have coords, we take the field `weight` into account
       let boost_on_weight = rs_q::build_function_score()
              .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
              .with_boost(300)
              .with_query(rs_q::build_match_all().build())
              .with_function(
          rs_es::query::functions::Function::build_field_value_factor("weight")
                              .with_factor(1)
                      .with_modifier(rs_es::query::functions::Modifier::Log1p)
                                                      .build())
              .build();
        should_query.push(boost_on_weight);
   }
}

fn query(q: &str,
         cnx: &str,
         match_type: &str,
	 coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let sub_query = rs_q::build_bool()
                        .with_should(should_query)
                        .build();

    let mut must = vec![rs_q::build_match(match_type, q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(100f64)).build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
                          .with_must(vec![sub_query])
                          .with_filter(filter)
                          .build()
}

fn query(q: &String, cnx: &String, match_type: &str, coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(cnx);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
                                                       .with_indexes(&["munin"])
                                                       .with_query(&query)
                                                       .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
             .hits
             .into_iter()
             .filter_map(|hit| make_place(hit.doc_type, hit.source))
             .collect())
}

fn query_prefix(q: &String, cnx: &String, coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	query(&q, cnx, "name.prefix", coord, shape)
}

fn query_ngram(q: &String, cnx: &String, coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	query(&q, cnx, "name.ngram", coord, shape)
}

pub fn autocomplete(q: String,
                    coord: Option<model::Coord>,
                    cnx: &String,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	// First search with match = "name.prefix".
	// If no result then another search with match = "name.ngram"
	fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
	    shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
	}

    let results = try!(query_prefix(&q, cnx, &coord, make_shape(&shape)));
    if results.is_empty() {
    	query_ngram(&q, cnx, &coord, make_shape(&shape))
    } else {
    	Ok(results)
    }
}
<commit_msg>rust fmt<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)")
                 .unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap();
    let port = cap.name("port").unwrap().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        match doc_type.as_ref() {
            "addr" => {
                serde_json::from_value::<mimir::Addr>(*v)
                    .ok()
                    .and_then(|o| Some(mimir::Place::Addr(o)))
            }
            "street" => {
                serde_json::from_value::<mimir::Street>(*v)
                    .ok()
                    .and_then(|o| Some(mimir::Place::Street(o)))
            }
            "admin" => {
                serde_json::from_value::<mimir::Admin>(*v)
                    .ok()
                    .and_then(|o| Some(mimir::Place::Admin(o)))
            }
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn build_query(q: &String, match_type: &str, coord: &Option<model::Coord>) -> rs_es::query::Query {
    let boost_addr = rs_q::build_term("_type", "addr").with_boost(1000).build();
    let boost_match_query = rs_q::build_match(match_type, q.to_string()).with_boost(100).build();

    let mut should_query = vec![boost_addr, boost_match_query];
    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        use rs_es::query::functions::Function::build_decay;
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(300)
                .with_function(build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(3f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_gauss())
                .build();
        should_query.push(boost_on_proximity);
   } else {
       // if we don't have coords, we take the field `weight` into account
       let boost_on_weight = rs_q::build_function_score()
              .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
              .with_boost(300)
              .with_query(rs_q::build_match_all().build())
              .with_function(
          rs_es::query::functions::Function::build_field_value_factor("weight")
                              .with_factor(1)
                      .with_modifier(rs_es::query::functions::Modifier::Log1p)
                                                      .build())
              .build();
        should_query.push(boost_on_weight);
   }

    let sub_query = rs_q::build_bool()
                        .with_should(should_query)
                        .build();

    let mut must = vec![rs_q::build_match(match_type, q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(100f64)).build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn query(q: &String,
         cnx: &String,
         match_type: &str,
         coord: &Option<model::Coord>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord);

    let mut client = build_rs_client(cnx);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
                                                       .with_indexes(&["munin"])
                                                       .with_query(&query)
                                                       .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
             .hits
             .into_iter()
             .filter_map(|hit| make_place(hit.doc_type, hit.source))
             .collect())
}

fn query_prefix(q: &String, cnx: &String, coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	query(&q, cnx, "name.prefix", coord, shape)
}

fn query_ngram(q: &String, cnx: &String, coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	query(&q, cnx, "name.ngram", coord, shape)
}

pub fn autocomplete(q: String,
                    coord: Option<model::Coord>,
                    cnx: &String,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	// First search with match = "name.prefix".
	// If no result then another search with match = "name.ngram"
	fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
	    shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
	}

    let results = try!(query_prefix(&q, cnx, &coord, make_shape(&shape)));
    if results.is_empty() {
    	query_ngram(&q, cnx, &coord, make_shape(&shape))
    } else {
    	Ok(results)
    }
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;
use mimir::objects::{MimirObject, Admin, Addr, Stop};

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

#[derive(Debug, Eq, PartialEq)]
enum MatchType {
    Prefix,
    Fuzzy,
}

fn build_query(q: &str,
               match_type: MatchType,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    // we order the type of object we want
    // Note: the addresses are boosted more because even if we don't want them first
    // because they are more severely filtered
    let boost_addr = rs_q::build_term("_type", Addr::doc_type()).with_boost(5000).build();
    let boost_admin = rs_q::build_term("_type", Admin::doc_type()).with_boost(3000).build();
    let boost_stop = rs_q::build_term("_type", Stop::doc_type()).with_boost(2000).build();

    let main_match_type = match match_type {
        MatchType::Prefix => "label.prefix",
        MatchType::Fuzzy => "label.ngram",
    };

    let boost_main_match_query = rs_q::build_multi_match(vec![main_match_type.to_string()],
                                                         q.to_string())
        .with_boost(100)
        .build();
    let boost_zipcode_match_query = rs_q::build_multi_match(vec!["zip_codes.prefix".to_string()],
                                                            q.to_string())
        .with_boost(500)
        .build();

    let mut should_query = vec![boost_addr,
                                boost_admin,
                                boost_stop,
                                boost_main_match_query,
                                boost_zipcode_match_query];

    // for fuzzy search we also search by the prefix index (with a greater boost than ngram)
    // to have better results
    if match_type == MatchType::Fuzzy {
        should_query.push(rs_q::build_match("label.prefix", q.to_string())
            .with_boost(1000)
            .build());
    }

    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(300)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    use rs_es::query::{CombinationMinimumShouldMatch, MinimumShouldMatch};
    let min_match = |min, threshold| {
        CombinationMinimumShouldMatch::new(MinimumShouldMatch::from(min),
                                           MinimumShouldMatch::from(threshold))
    };

    let minimum_should_match = match match_type {
        MatchType::Prefix => {
            // when we search [0, 2] fields, we need to match 100% of them
            // when we search [3, 4] fields, we need to match 90% of them
            // when we search [5, +] fields, we only need to match 70% on them
            // this way if the input has more fields than the documents
            // (additional information like the country, the region, ...)
            // they can be ignored
            MinimumShouldMatch::from(vec![min_match(2, 90f64), min_match(4, 70f64)])
        }
        // for fuzzy search we lower our expectation and we accept 50% of token match
        MatchType::Fuzzy => MinimumShouldMatch::from(50f64),
    };

    let mut must = vec![rs_q::build_multi_match(vec![main_match_type.to_string(),
                                                     "zip_codes.prefix".to_string()],
                                                q.to_string())
                            .with_minimum_should_match(minimum_should_match)
                            .build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    // filter to handle house number
    // we either want:
    // * to exactly match the document house_number
    // * or that the document has no house_number
    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn is_existing_index(client: &mut rs_es::Client, index: &String) -> bool {
    !index.is_empty() && client.open_index(&index).is_ok()
}

fn make_indexes<'a>(all_data: bool,
                    pt_dataset_index: &'a Option<String>,
                    client: &mut rs_es::Client)
                    -> Vec<&'a str> {
    if all_data {
        return vec!["munin"];
    }

    let mut result: Vec<&str> = vec![];
    if is_existing_index(client, &"munin_geo_data".to_string()) {
        result.push("munin_geo_data");
    }
    match *pt_dataset_index {
        Some(ref dataset) => {
            if is_existing_index(client, dataset) {
                result.push(dataset);
            }
            result
        }
        _ => result,
    }

}

fn query(q: &str,
         pt_dataset: &Option<&str>,
         all_data: bool,
         cnx: &str,
         match_type: MatchType,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let pt_dataset_index = pt_dataset.map(|d| format!("munin_stop_{}", d));
    let indexes = make_indexes(all_data, &pt_dataset_index, &mut client);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&indexes)
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

pub fn autocomplete(q: &str,
                    pt_dataset: &Option<&str>,
                    all_data: bool,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    // First we try a prety exact match on the prefix.
    // If there are no results then we do a new fuzzy search (matching ngrams)
    let results = try!(query(&q,
                             &pt_dataset,
                             all_data,
                             cnx,
                             MatchType::Prefix,
                             offset,
                             limit,
                             &coord,
                             make_shape(&shape)));
    if results.is_empty() {
        query(&q,
              &pt_dataset,
              all_data,
              cnx,
              MatchType::Fuzzy,
              offset,
              limit,
              &coord,
              make_shape(&shape))
    } else {
        Ok(results)
    }
}
<commit_msg>refactoring<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;
use mimir::objects::{MimirObject, Admin, Addr, Stop};

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

#[derive(Debug, Eq, PartialEq)]
enum MatchType {
    Prefix,
    Fuzzy,
}

fn build_query(q: &str,
               match_type: MatchType,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    // we order the type of object we want
    // Note: the addresses are boosted more because even if we don't want them first
    // because they are more severely filtered
    let boost_addr = rs_q::build_term("_type", Addr::doc_type()).with_boost(5000).build();
    let boost_admin = rs_q::build_term("_type", Admin::doc_type()).with_boost(3000).build();
    let boost_stop = rs_q::build_term("_type", Stop::doc_type()).with_boost(2000).build();

    let main_match_type = match match_type {
        MatchType::Prefix => "label.prefix",
        MatchType::Fuzzy => "label.ngram",
    };

    let boost_main_match_query = rs_q::build_multi_match(vec![main_match_type.to_string()],
                                                         q.to_string())
        .with_boost(100)
        .build();
    let boost_zipcode_match_query = rs_q::build_multi_match(vec!["zip_codes.prefix".to_string()],
                                                            q.to_string())
        .with_boost(500)
        .build();

    let mut should_query = vec![boost_addr,
                                boost_admin,
                                boost_stop,
                                boost_main_match_query,
                                boost_zipcode_match_query];

    // for fuzzy search we also search by the prefix index (with a greater boost than ngram)
    // to have better results
    if match_type == MatchType::Fuzzy {
        should_query.push(rs_q::build_match("label.prefix", q.to_string())
            .with_boost(1000)
            .build());
    }

    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(300)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    use rs_es::query::{CombinationMinimumShouldMatch, MinimumShouldMatch};
    let min_match = |min, threshold| {
        CombinationMinimumShouldMatch::new(MinimumShouldMatch::from(min),
                                           MinimumShouldMatch::from(threshold))
    };

    let minimum_should_match = match match_type {
        MatchType::Prefix => {
            // when we search [0, 2] fields, we need to match 100% of them
            // when we search [3, 4] fields, we need to match 90% of them
            // when we search [5, +] fields, we only need to match 70% on them
            // this way if the input has more fields than the documents
            // (additional information like the country, the region, ...)
            // they can be ignored
            MinimumShouldMatch::from(vec![min_match(2, 90f64), min_match(4, 70f64)])
        }
        // for fuzzy search we lower our expectation and we accept 50% of token match
        MatchType::Fuzzy => MinimumShouldMatch::from(50f64),
    };

    let mut must = vec![rs_q::build_multi_match(vec![main_match_type.to_string(),
                                                     "zip_codes.prefix".to_string()],
                                                q.to_string())
                            .with_minimum_should_match(minimum_should_match)
                            .build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    // filter to handle house number
    // we either want:
    // * to exactly match the document house_number
    // * or that the document has no house_number
    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn is_existing_index(client: &mut rs_es::Client, index: &String) -> bool {
    !index.is_empty() && client.open_index(&index).is_ok()
}

fn make_indexes<'a>(all_data: bool,
                    pt_dataset_index: &'a Option<String>,
                    client: &mut rs_es::Client)
                    -> Vec<&'a str> {
	let mut result: Vec<&str> = vec![];
    if all_data {
        result.push("munin");
    } else {
        if is_existing_index(client, &"munin_geo_data".to_string()) {
            result.push("munin_geo_data");
        }
        match *pt_dataset_index {
            Some(ref dataset) => {
                if is_existing_index(client, dataset) {
                    result.push(dataset);
                }
            },
            _ => {}
        };
    }
	return result;
}

fn query(q: &str,
         pt_dataset: &Option<&str>,
         all_data: bool,
         cnx: &str,
         match_type: MatchType,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let pt_dataset_index = pt_dataset.map(|d| format!("munin_stop_{}", d));
    let indexes = make_indexes(all_data, &pt_dataset_index, &mut client);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&indexes)
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

pub fn autocomplete(q: &str,
                    pt_dataset: &Option<&str>,
                    all_data: bool,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    // First we try a prety exact match on the prefix.
    // If there are no results then we do a new fuzzy search (matching ngrams)
    let results = try!(query(&q,
                             &pt_dataset,
                             all_data,
                             cnx,
                             MatchType::Prefix,
                             offset,
                             limit,
                             &coord,
                             make_shape(&shape)));
    if results.is_empty() {
        query(&q,
              &pt_dataset,
              all_data,
              cnx,
              MatchType::Fuzzy,
              offset,
              limit,
              &coord,
              make_shape(&shape))
    } else {
        Ok(results)
    }
}
<|endoftext|>