<commit_before>use {Event, WindowBuilder};
use libc;
use std::{mem, ptr};
use std::sync::atomics::AtomicBool;

mod events;
mod ffi;

pub struct Window {
    display: *mut ffi::Display,
    window: ffi::Window,
    im: ffi::XIM,
    ic: ffi::XIC,
    context: ffi::GLXContext,
    is_closed: AtomicBool,
    wm_delete_window: ffi::Atom,
}

pub struct MonitorID(uint);

pub fn get_available_monitors() -> Vec<MonitorID> {
    unimplemented!()
}

pub fn get_primary_monitor() -> MonitorID {
    unimplemented!()
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("<Unknown>".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

impl Window {
    pub fn new(builder: WindowBuilder) -> Result<Window, String> {
        // TODO: temporary
        let dimensions = builder.dimensions;

        // calling XOpenDisplay
        let display = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            if display.is_null() {
                return Err(format!("XOpenDisplay failed"));
            }
            display
        };

        // TODO: set error handler?

        // getting the FBConfig
        let fb_config = unsafe {
            static VISUAL_ATTRIBUTES: [libc::c_int, ..23] = [
                ffi::GLX_X_RENDERABLE,  1,
                ffi::GLX_DRAWABLE_TYPE, ffi::GLX_WINDOW_BIT,
                ffi::GLX_RENDER_TYPE,   ffi::GLX_RGBA_BIT,
                ffi::GLX_X_VISUAL_TYPE, ffi::GLX_TRUE_COLOR,
                ffi::GLX_RED_SIZE,      8,
                ffi::GLX_GREEN_SIZE,    8,
                ffi::GLX_BLUE_SIZE,     8,
                ffi::GLX_ALPHA_SIZE,    8,
                ffi::GLX_DEPTH_SIZE,    24,
                ffi::GLX_STENCIL_SIZE,  8,
                ffi::GLX_DOUBLEBUFFER,  1,
                0
            ];

            let mut num_fb: libc::c_int = mem::uninitialized();

            let fb = ffi::glXChooseFBConfig(display, ffi::XDefaultScreen(display),
                VISUAL_ATTRIBUTES.as_ptr(), &mut num_fb);
            if fb.is_null() {
                return Err(format!("glXChooseFBConfig failed"));
            }
            let preferred_fb = *fb;     // TODO: choose more wisely
            ffi::XFree(fb as *const libc::c_void);
            preferred_fb
        };

        // getting the visual infos
        let visual_infos = unsafe {
            let vi = ffi::glXGetVisualFromFBConfig(display, fb_config);
            if vi.is_null() {
                return Err(format!("glXChooseVisual failed"));
            }
            let vi_copy = *vi;
            ffi::XFree(vi as *const libc::c_void);
            vi_copy
        };

        // getting the root window
        let root = unsafe { ffi::XDefaultRootWindow(display) };

        // creating the color map
        let cmap = unsafe {
            let cmap = ffi::XCreateColormap(display, root,
                visual_infos.visual, ffi::AllocNone);
            // TODO: error checking?
            cmap
        };

        // creating
        let mut set_win_attr = {
            let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
            swa.colormap = cmap;
            swa.event_mask = ffi::ExposureMask | ffi::ResizeRedirectMask |
                ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask |
                ffi::KeyPressMask | ffi::KeyReleaseMask | ffi::ButtonPressMask |
                ffi::ButtonReleaseMask | ffi::KeymapStateMask;
            swa
        };

        // finally creating the window
        let window = unsafe {
            let dimensions = dimensions.unwrap_or((800, 600));

            let win = ffi::XCreateWindow(display, root, 50, 50, dimensions.val0() as libc::c_uint,
                dimensions.val1() as libc::c_uint, 0, visual_infos.depth, ffi::InputOutput,
                visual_infos.visual, ffi::CWColormap | ffi::CWEventMask,
                &mut set_win_attr);
            win
        };

        // creating window, step 2
        let wm_delete_window = unsafe {
            use std::c_str::ToCStr;

            ffi::XMapWindow(display, window);
            let mut wm_delete_window = ffi::XInternAtom(display,
                "WM_DELETE_WINDOW".to_c_str().as_ptr() as *const libc::c_char, 0);
            ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1);
            ffi::XStoreName(display, window, mem::transmute(builder.title.as_slice().as_ptr()));
            ffi::XFlush(display);

            wm_delete_window
        };

        // getting the pointer to glXCreateContextAttribs
        let create_context_attribs = unsafe {
            // creating the dummy context
            let dummy_context =
                ffi::glXCreateContext(display, &visual_infos, ptr::null(), 1);
            ffi::glXMakeCurrent(display, window, dummy_context);

            // getting the pointer
            let fn_ptr = {
                let mut addr = unsafe { ffi::glXGetProcAddress(b"glXCreateContextAttribs".as_ptr()
                    as *const u8) } as *const ();

                if addr.is_null() {
                    addr = unsafe { ffi::glXGetProcAddress(b"glXCreateContextAttribsARB".as_ptr()
                        as *const u8) } as *const ();
                }

                addr.to_option().map(|addr| {
                    let addr: extern "system" fn(*mut ffi::Display, ffi::GLXFBConfig, ffi::GLXContext,
                        ffi::Bool, *const libc::c_int) -> ffi::GLXContext = mem::transmute(addr);
                    addr
                })
            };

            // cleaning up
            ffi::glXMakeCurrent(ptr::mut_null(), 0, ptr::null());
            ffi::glXDestroyContext(display, dummy_context);

            fn_ptr
        };

        // creating IM
        let im = unsafe {
            let im = ffi::XOpenIM(display, ptr::null(), ptr::mut_null(), ptr::mut_null());
            if im.is_null() {
                return Err(format!("XOpenIM failed"));
            }
            im
        };

        // creating input context
        let ic = unsafe {
            use std::c_str::ToCStr;

            let ic = ffi::XCreateIC(im, "inputStyle".to_c_str().as_ptr(),
                ffi::XIMPreeditNothing | ffi::XIMStatusNothing, "clientWindow".to_c_str().as_ptr(),
                window, ptr::null());
            if ic.is_null() {
                return Err(format!("XCreateIC failed"));
            }
            ffi::XSetICFocus(ic);
            ic
        };

        // creating GL context
        let context = unsafe {
            let mut attributes = Vec::new();

            if builder.gl_version.is_some() {
                let version = builder.gl_version.as_ref().unwrap();
                attributes.push(ffi::GLX_CONTEXT_MAJOR_VERSION);
                attributes.push(version.val0() as libc::c_int);
                attributes.push(ffi::GLX_CONTEXT_MINOR_VERSION);
                attributes.push(version.val1() as libc::c_int);
            }

            attributes.push(0);

            let context = if create_context_attribs.is_some() {
                let create_context_attribs = create_context_attribs.unwrap();
                create_context_attribs(display, fb_config, ptr::null(), 1,
                    attributes.as_ptr())
            } else {
                ffi::glXCreateNewContext(display, fb_config, ffi::GLX_RGBA_TYPE, ptr::null(), 1)
            };

            if context.is_null() {
                return Err(format!("GL context creation failed"));
            }

            context
        };

        // returning
        Ok(Window{
            display: display,
            window: window,
            im: im,
            ic: ic,
            context: context,
            is_closed: AtomicBool::new(false),
            wm_delete_window: wm_delete_window,
        })
    }

    pub fn is_closed(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.is_closed.load(Relaxed)
    }

    pub fn set_title(&self, title: &str) {
        unsafe {
            ffi::XStoreName(self.display, self.window,
                mem::transmute(title.as_slice().as_ptr()));
        }
    }

    fn get_geometry(&self) -> Option<(int, int, uint, uint)> {
        unsafe {
            use std::mem;

            let mut root: ffi::Window = mem::uninitialized();
            let mut x: libc::c_int = mem::uninitialized();
            let mut y: libc::c_int = mem::uninitialized();
            let mut width: libc::c_uint = mem::uninitialized();
            let mut height: libc::c_uint = mem::uninitialized();
            let mut border: libc::c_uint = mem::uninitialized();
            let mut depth: libc::c_uint = mem::uninitialized();

            if ffi::XGetGeometry(self.display, self.window,
                &mut root, &mut x, &mut y, &mut width, &mut height,
                &mut border, &mut depth) == 0
            {
                return None;
            }

            Some((x as int, y as int, width as uint, height as uint))
        }
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        self.get_geometry().map(|(x, y, _, _)| (x, y))
    }

    pub fn set_position(&self, x: uint, y: uint) {
        unsafe { ffi::XMoveWindow(self.display, self.window, x as libc::c_int, y as libc::c_int) }
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        self.get_geometry().map(|(_, _, w, h)| (w, h))
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        unimplemented!()
    }

    pub fn set_inner_size(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn poll_events(&self) -> Vec<Event> {
        use std::mem;

        let mut events = Vec::new();

        loop {
            use std::num::Bounded;

            let mut xev = unsafe { mem::uninitialized() };
            let res = unsafe { ffi::XCheckMaskEvent(self.display, Bounded::max_value(), &mut xev) };

            if res == 0 {
                let res = unsafe { ffi::XCheckTypedEvent(self.display, ffi::ClientMessage, &mut xev) };

                if res == 0 {
                    break
                }
            }

            match xev.type_ {
                ffi::KeymapNotify => {
                    unsafe { ffi::XRefreshKeyboardMapping(&xev) }
                },

                ffi::ClientMessage => {
                    use Closed;
                    use std::sync::atomics::Relaxed;

                    let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };

                    if client_msg.l[0] == self.wm_delete_window as libc::c_long {
                        self.is_closed.store(true, Relaxed);
                        events.push(Closed);
                    }
                },

                ffi::ResizeRequest => {
                    use Resized;
                    let rs_event: &ffi::XResizeRequestEvent = unsafe { mem::transmute(&xev) };
                    events.push(Resized(rs_event.width as uint, rs_event.height as uint));
                },

                ffi::MotionNotify => {
                    use CursorPositionChanged;
                    let event: &ffi::XMotionEvent = unsafe { mem::transmute(&xev) };
                    events.push(CursorPositionChanged(event.x as uint, event.y as uint));
                },

                ffi::KeyPress | ffi::KeyRelease => {
                    use {Pressed, Released, ReceivedCharacter};
                    let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&xev) };

                    if event.type_ == ffi::KeyPress {
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.window) };
                    }

                    let keysym = unsafe { ffi::XKeycodeToKeysym(self.display, event.keycode as ffi::KeyCode, 0) };

                    let written = unsafe {
                        use std::str;

                        let mut buffer: [u8, ..16] = [mem::uninitialized(), ..16];
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        let count = ffi::Xutf8LookupString(self.ic, mem::transmute(raw_ev),
                            mem::transmute(buffer.as_mut_ptr()),
                            buffer.len() as libc::c_int, ptr::mut_null(), ptr::mut_null());

                        str::from_utf8(buffer.as_slice().slice_to(count as uint)).unwrap_or("").to_string()
                    };

                    for chr in written.as_slice().chars() {
                        events.push(ReceivedCharacter(chr));
                    }

                    match events::keycode_to_element(keysym as libc::c_uint) {
                        Some(elem) if xev.type_ == ffi::KeyPress => {
                            events.push(Pressed(elem));
                        },
                        Some(elem) if xev.type_ == ffi::KeyRelease => {
                            events.push(Released(elem));
                        },
                        _ => ()
                    }
                    //
                },

                ffi::ButtonPress | ffi::ButtonRelease => {
                    use {Pressed, Released};
                    use events;
                    let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) };

                    let elem = match event.button {
                        ffi::Button1 => Some(events::Button1),
                        ffi::Button2 => Some(events::Button2),
                        ffi::Button3 => Some(events::Button3),
                        ffi::Button4 => Some(events::Button4),
                        ffi::Button5 => Some(events::Button5),
                        _ => None
                    };

                    if elem.is_some() {
                        let elem = elem.unwrap();

                        if xev.type_ == ffi::ButtonPress {
                            events.push(Pressed(elem));
                        } else if xev.type_ == ffi::ButtonRelease {
                            events.push(Released(elem));
                        }
                    }
                },

                _ => ()
            }
        }

        events
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::mem;

        loop {
            // this will block until an event arrives, but doesn't remove
            //  it from the queue
            let mut xev = unsafe { mem::uninitialized() };
            unsafe { ffi::XPeekEvent(self.display, &mut xev) };

            // calling poll_events()
            let ev = self.poll_events();
            if ev.len() >= 1 {
                return ev;
            }
        }
    }

    pub unsafe fn make_current(&self) {
        let res = ffi::glXMakeCurrent(self.display, self.window, self.context);
        if res == 0 {
            fail!("glXMakeCurrent failed");
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;
        use std::mem;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::glXGetProcAddress(mem::transmute(s)) as *const ();
                if !p.is_null() { return p; }
                println!("{}", p);
                p
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe { ffi::glXSwapBuffers(self.display, self.window) }
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe { ffi::glXDestroyContext(self.display, self.context); }
        unsafe { ffi::XDestroyIC(self.ic); }
        unsafe { ffi::XCloseIM(self.im); }
        unsafe { ffi::XDestroyWindow(self.display, self.window); }
        unsafe { ffi::XCloseDisplay(self.display); }
    }
}
<commit_msg>Add some error detection<commit_after>use {Event, WindowBuilder};
use libc;
use std::{mem, ptr};
use std::sync::atomics::AtomicBool;

mod events;
mod ffi;

pub struct Window {
    display: *mut ffi::Display,
    window: ffi::Window,
    im: ffi::XIM,
    ic: ffi::XIC,
    context: ffi::GLXContext,
    is_closed: AtomicBool,
    wm_delete_window: ffi::Atom,
}

pub struct MonitorID(uint);

pub fn get_available_monitors() -> Vec<MonitorID> {
    unimplemented!()
}

pub fn get_primary_monitor() -> MonitorID {
    unimplemented!()
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("<Unknown>".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

impl Window {
    pub fn new(builder: WindowBuilder) -> Result<Window, String> {
        // TODO: temporary
        let dimensions = builder.dimensions;

        // calling XOpenDisplay
        let display = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            if display.is_null() {
                return Err(format!("XOpenDisplay failed"));
            }
            display
        };

        // TODO: set error handler?

        // getting the FBConfig
        let fb_config = unsafe {
            static VISUAL_ATTRIBUTES: [libc::c_int, ..23] = [
                ffi::GLX_X_RENDERABLE,  1,
                ffi::GLX_DRAWABLE_TYPE, ffi::GLX_WINDOW_BIT,
                ffi::GLX_RENDER_TYPE,   ffi::GLX_RGBA_BIT,
                ffi::GLX_X_VISUAL_TYPE, ffi::GLX_TRUE_COLOR,
                ffi::GLX_RED_SIZE,      8,
                ffi::GLX_GREEN_SIZE,    8,
                ffi::GLX_BLUE_SIZE,     8,
                ffi::GLX_ALPHA_SIZE,    8,
                ffi::GLX_DEPTH_SIZE,    24,
                ffi::GLX_STENCIL_SIZE,  8,
                ffi::GLX_DOUBLEBUFFER,  1,
                0
            ];

            let mut num_fb: libc::c_int = mem::uninitialized();

            let fb = ffi::glXChooseFBConfig(display, ffi::XDefaultScreen(display),
                VISUAL_ATTRIBUTES.as_ptr(), &mut num_fb);
            if fb.is_null() {
                return Err(format!("glXChooseFBConfig failed"));
            }
            let preferred_fb = *fb;     // TODO: choose more wisely
            ffi::XFree(fb as *const libc::c_void);
            preferred_fb
        };

        // getting the visual infos
        let visual_infos = unsafe {
            let vi = ffi::glXGetVisualFromFBConfig(display, fb_config);
            if vi.is_null() {
                return Err(format!("glXChooseVisual failed"));
            }
            let vi_copy = *vi;
            ffi::XFree(vi as *const libc::c_void);
            vi_copy
        };

        // getting the root window
        let root = unsafe { ffi::XDefaultRootWindow(display) };

        // creating the color map
        let cmap = unsafe {
            let cmap = ffi::XCreateColormap(display, root,
                visual_infos.visual, ffi::AllocNone);
            // TODO: error checking?
            cmap
        };

        // creating
        let mut set_win_attr = {
            let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
            swa.colormap = cmap;
            swa.event_mask = ffi::ExposureMask | ffi::ResizeRedirectMask |
                ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask |
                ffi::KeyPressMask | ffi::KeyReleaseMask | ffi::ButtonPressMask |
                ffi::ButtonReleaseMask | ffi::KeymapStateMask;
            swa
        };

        // finally creating the window
        let window = unsafe {
            let dimensions = dimensions.unwrap_or((800, 600));

            let win = ffi::XCreateWindow(display, root, 50, 50, dimensions.val0() as libc::c_uint,
                dimensions.val1() as libc::c_uint, 0, visual_infos.depth, ffi::InputOutput,
                visual_infos.visual, ffi::CWColormap | ffi::CWEventMask,
                &mut set_win_attr);
            win
        };

        // creating window, step 2
        let wm_delete_window = unsafe {
            use std::c_str::ToCStr;

            ffi::XMapWindow(display, window);
            let mut wm_delete_window = ffi::XInternAtom(display,
                "WM_DELETE_WINDOW".to_c_str().as_ptr() as *const libc::c_char, 0);
            ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1);
            ffi::XStoreName(display, window, mem::transmute(builder.title.as_slice().as_ptr()));
            ffi::XFlush(display);

            wm_delete_window
        };

        // getting the pointer to glXCreateContextAttribs
        let create_context_attribs = unsafe {
            // creating the dummy context
            let dummy_context =
                ffi::glXCreateContext(display, &visual_infos, ptr::null(), 1);
            if dummy_context.is_null() {
                return Err(format!("glXCreateContext failed"));
            }

            if ffi::glXMakeCurrent(display, window, dummy_context) == 0 {
                return Err(format!("glXMakeCurrent with dummy context failed"));
            }

            // getting the pointer
            let fn_ptr = {
                let mut addr = unsafe { ffi::glXGetProcAddress(b"glXCreateContextAttribs".as_ptr()
                    as *const u8) } as *const ();

                if addr.is_null() {
                    addr = unsafe { ffi::glXGetProcAddress(b"glXCreateContextAttribsARB".as_ptr()
                        as *const u8) } as *const ();
                }

                addr.to_option().map(|addr| {
                    let addr: extern "system" fn(*mut ffi::Display, ffi::GLXFBConfig, ffi::GLXContext,
                        ffi::Bool, *const libc::c_int) -> ffi::GLXContext = mem::transmute(addr);
                    addr
                })
            };

            // cleaning up
            ffi::glXMakeCurrent(ptr::mut_null(), 0, ptr::null());
            ffi::glXDestroyContext(display, dummy_context);

            fn_ptr
        };

        // creating IM
        let im = unsafe {
            let im = ffi::XOpenIM(display, ptr::null(), ptr::mut_null(), ptr::mut_null());
            if im.is_null() {
                return Err(format!("XOpenIM failed"));
            }
            im
        };

        // creating input context
        let ic = unsafe {
            use std::c_str::ToCStr;

            let ic = ffi::XCreateIC(im, "inputStyle".to_c_str().as_ptr(),
                ffi::XIMPreeditNothing | ffi::XIMStatusNothing, "clientWindow".to_c_str().as_ptr(),
                window, ptr::null());
            if ic.is_null() {
                return Err(format!("XCreateIC failed"));
            }
            ffi::XSetICFocus(ic);
            ic
        };

        // creating GL context
        let context = unsafe {
            let mut attributes = Vec::new();

            if builder.gl_version.is_some() {
                let version = builder.gl_version.as_ref().unwrap();
                attributes.push(ffi::GLX_CONTEXT_MAJOR_VERSION);
                attributes.push(version.val0() as libc::c_int);
                attributes.push(ffi::GLX_CONTEXT_MINOR_VERSION);
                attributes.push(version.val1() as libc::c_int);
            }

            attributes.push(0);

            let context = if create_context_attribs.is_some() {
                let create_context_attribs = create_context_attribs.unwrap();
                create_context_attribs(display, fb_config, ptr::null(), 1,
                    attributes.as_ptr())
            } else {
                ffi::glXCreateNewContext(display, fb_config, ffi::GLX_RGBA_TYPE, ptr::null(), 1)
            };

            if context.is_null() {
                return Err(format!("GL context creation failed"));
            }

            context
        };

        // returning
        Ok(Window{
            display: display,
            window: window,
            im: im,
            ic: ic,
            context: context,
            is_closed: AtomicBool::new(false),
            wm_delete_window: wm_delete_window,
        })
    }

    pub fn is_closed(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.is_closed.load(Relaxed)
    }

    pub fn set_title(&self, title: &str) {
        unsafe {
            ffi::XStoreName(self.display, self.window,
                mem::transmute(title.as_slice().as_ptr()));
        }
    }

    fn get_geometry(&self) -> Option<(int, int, uint, uint)> {
        unsafe {
            use std::mem;

            let mut root: ffi::Window = mem::uninitialized();
            let mut x: libc::c_int = mem::uninitialized();
            let mut y: libc::c_int = mem::uninitialized();
            let mut width: libc::c_uint = mem::uninitialized();
            let mut height: libc::c_uint = mem::uninitialized();
            let mut border: libc::c_uint = mem::uninitialized();
            let mut depth: libc::c_uint = mem::uninitialized();

            if ffi::XGetGeometry(self.display, self.window,
                &mut root, &mut x, &mut y, &mut width, &mut height,
                &mut border, &mut depth) == 0
            {
                return None;
            }

            Some((x as int, y as int, width as uint, height as uint))
        }
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        self.get_geometry().map(|(x, y, _, _)| (x, y))
    }

    pub fn set_position(&self, x: uint, y: uint) {
        unsafe { ffi::XMoveWindow(self.display, self.window, x as libc::c_int, y as libc::c_int) }
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        self.get_geometry().map(|(_, _, w, h)| (w, h))
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        unimplemented!()
    }

    pub fn set_inner_size(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn poll_events(&self) -> Vec<Event> {
        use std::mem;

        let mut events = Vec::new();

        loop {
            use std::num::Bounded;

            let mut xev = unsafe { mem::uninitialized() };
            let res = unsafe { ffi::XCheckMaskEvent(self.display, Bounded::max_value(), &mut xev) };

            if res == 0 {
                let res = unsafe { ffi::XCheckTypedEvent(self.display, ffi::ClientMessage, &mut xev) };

                if res == 0 {
                    break
                }
            }

            match xev.type_ {
                ffi::KeymapNotify => {
                    unsafe { ffi::XRefreshKeyboardMapping(&xev) }
                },

                ffi::ClientMessage => {
                    use Closed;
                    use std::sync::atomics::Relaxed;

                    let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };

                    if client_msg.l[0] == self.wm_delete_window as libc::c_long {
                        self.is_closed.store(true, Relaxed);
                        events.push(Closed);
                    }
                },

                ffi::ResizeRequest => {
                    use Resized;
                    let rs_event: &ffi::XResizeRequestEvent = unsafe { mem::transmute(&xev) };
                    events.push(Resized(rs_event.width as uint, rs_event.height as uint));
                },

                ffi::MotionNotify => {
                    use CursorPositionChanged;
                    let event: &ffi::XMotionEvent = unsafe { mem::transmute(&xev) };
                    events.push(CursorPositionChanged(event.x as uint, event.y as uint));
                },

                ffi::KeyPress | ffi::KeyRelease => {
                    use {Pressed, Released, ReceivedCharacter};
                    let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&xev) };

                    if event.type_ == ffi::KeyPress {
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.window) };
                    }

                    let keysym = unsafe { ffi::XKeycodeToKeysym(self.display, event.keycode as ffi::KeyCode, 0) };

                    let written = unsafe {
                        use std::str;

                        let mut buffer: [u8, ..16] = [mem::uninitialized(), ..16];
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        let count = ffi::Xutf8LookupString(self.ic, mem::transmute(raw_ev),
                            mem::transmute(buffer.as_mut_ptr()),
                            buffer.len() as libc::c_int, ptr::mut_null(), ptr::mut_null());

                        str::from_utf8(buffer.as_slice().slice_to(count as uint)).unwrap_or("").to_string()
                    };

                    for chr in written.as_slice().chars() {
                        events.push(ReceivedCharacter(chr));
                    }

                    match events::keycode_to_element(keysym as libc::c_uint) {
                        Some(elem) if xev.type_ == ffi::KeyPress => {
                            events.push(Pressed(elem));
                        },
                        Some(elem) if xev.type_ == ffi::KeyRelease => {
                            events.push(Released(elem));
                        },
                        _ => ()
                    }
                    //
                },

                ffi::ButtonPress | ffi::ButtonRelease => {
                    use {Pressed, Released};
                    use events;
                    let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) };

                    let elem = match event.button {
                        ffi::Button1 => Some(events::Button1),
                        ffi::Button2 => Some(events::Button2),
                        ffi::Button3 => Some(events::Button3),
                        ffi::Button4 => Some(events::Button4),
                        ffi::Button5 => Some(events::Button5),
                        _ => None
                    };

                    if elem.is_some() {
                        let elem = elem.unwrap();

                        if xev.type_ == ffi::ButtonPress {
                            events.push(Pressed(elem));
                        } else if xev.type_ == ffi::ButtonRelease {
                            events.push(Released(elem));
                        }
                    }
                },

                _ => ()
            }
        }

        events
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::mem;

        loop {
            // this will block until an event arrives, but doesn't remove
            //  it from the queue
            let mut xev = unsafe { mem::uninitialized() };
            unsafe { ffi::XPeekEvent(self.display, &mut xev) };

            // calling poll_events()
            let ev = self.poll_events();
            if ev.len() >= 1 {
                return ev;
            }
        }
    }

    pub unsafe fn make_current(&self) {
        let res = ffi::glXMakeCurrent(self.display, self.window, self.context);
        if res == 0 {
            fail!("glXMakeCurrent failed");
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;
        use std::mem;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::glXGetProcAddress(mem::transmute(s)) as *const ();
                if !p.is_null() { return p; }
                println!("{}", p);
                p
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe { ffi::glXSwapBuffers(self.display, self.window) }
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe { ffi::glXDestroyContext(self.display, self.context); }
        unsafe { ffi::XDestroyIC(self.ic); }
        unsafe { ffi::XCloseIM(self.im); }
        unsafe { ffi::XDestroyWindow(self.display, self.window); }
        unsafe { ffi::XCloseDisplay(self.display); }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Maciej Piechotka
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#[comment = "Wrapper for libxml2's DOM parser."];
#[crate_id = "github.com/uzytkownik/xml-rs#xml:0.1"];
#[crate_type = "lib"];
#[license = "MIT"];

extern mod extra;

mod ffi;

/**
 * An XML node that contains text.
 */
pub trait TextNode {
    fn content(&self) -> ~str;
}

/**
 * A CDATA section.
 */
pub struct CData<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * An XML comment.
 */
pub struct Comment<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * An XML document.
 */
pub struct Document {
    priv doc: *ffi::xmlDoc
}

/**
 * An XML element
 */
pub struct Element<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * Iterator over children of XML element
 */
pub struct ElementChildrenIterator<'r> {
    priv cur: Option<&'r ffi::xmlNode>
}

/**
 * Text inside an XML.
 */
pub struct Text<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * Possible children of an element.
 */
pub enum ElementChildren<'r> {
    ElementElementChild(Element<'r>),
    TextElementChild(Text<'r>),
    CDataElementChild(CData<'r>),
    CommentElementChild(Comment<'r>)
}

impl Document {
    /**
     * Find the root element, if it exists.
     */
    pub fn get_root_element<'r>(&'r self) -> Option<Element<'r>> {
        unsafe {
            ptr_to_option(ffi::xmlDocGetRootElement(self.doc)).map(|elem| Element{node: &*elem})
        }
    }
}

#[unsafe_destructor]
impl Drop for Document {
    fn drop(&mut self) {
        unsafe {
            ffi::xmlFreeDoc(self.doc);
        }
    }
}

impl<'r> Comment<'r> {
    /// Get comment contents.
    pub fn comment(&self) -> ~str {
        unsafe {
            std::str::raw::from_c_str(self.node.content as *i8)
        }
    }
}

impl<'r> Element<'r> {
    /**
     * Gets the name of the element.
     */
    pub fn name(&self) -> ~str {
        unsafe {std::str::raw::from_c_str(self.node.name as *i8)}
    }
    /**
     * Iterate over children
     */
    pub fn children_iter(&self) -> ElementChildrenIterator<'r> {
        ElementChildrenIterator {
            cur: ptr_to_option(self.node.children).map(|cur| unsafe {&*cur})
        }
    }
}

impl<'r> Clone for ElementChildrenIterator<'r> {
    fn clone(&self) -> ElementChildrenIterator<'r> {
        ElementChildrenIterator{cur: self.cur}
    }
}

impl<'r> Iterator<ElementChildren<'r>> for ElementChildrenIterator<'r> {
     fn next(&mut self) -> Option<ElementChildren<'r>> {
        self.cur.and_then(|cur| {
            self.cur = unsafe {ptr_to_option(cur.next).map(|next| &*next)};
            match cur._type {
                ffi::ElementNode => Some(ElementElementChild(Element {node: cur})),
                ffi::TextNode => Some(TextElementChild(Text {node: cur})),
                ffi::CDataSectionNode => Some(CDataElementChild(CData {node: cur})),
                ffi::CommentNode => Some(CommentElementChild(Comment {node: cur})),
                t => {
                    error!("Unsupported type {}", t.to_str());
                    self.next()
                }
            }
        })
    }
}

impl<'r> ElementChildren<'r> {
    /// Check if children is an element.
    pub fn is_element(self) -> bool {
        match (self) {
            ElementElementChild(_) => true,
            _ => false
        }
    }
    /// Check if children is text.
    pub fn is_text(self) -> bool {
        match (self) {
            TextElementChild(_) => true,
            _ => false
        }
    }
    /// Check if children is CDATA section
    pub fn is_cdata(self) -> bool {
        match (self) {
            CDataElementChild(_) => true,
            _ => false
        }
    }
    /// Check if children is a comment.
    pub fn is_comment(self) -> bool {
        match (self) {
            CommentElementChild(_) => true,
            _ => false
        }
    }
    /// Get element if it is an element.
    pub fn get_element(self) -> Option<Element<'r>> {
        match (self) {
            ElementElementChild(e) => Some(e),
            _ => None
        }
    }
    /// Get text if it is text.
    pub fn get_text(self) -> Option<Text<'r>> {
        match (self) {
            TextElementChild(t) => Some(t),
            _ => None
        }
    }
    /// Get cdata section if it is cdata section.
    pub fn get_cdata(self) -> Option<CData<'r>> {
        match (self) {
            CDataElementChild(cd) => Some(cd),
            _ => None
        }
    }
    // Get comment if it is a comment.
    pub fn get_comment(self) -> Option<Comment<'r>> {
    match (self) {
            CommentElementChild(c) => Some(c),
            _ => None
        }
    }
}

impl<'r> TextNode for CData<'r> {
    fn content(&self) -> ~str {
        unsafe {
            std::str::raw::from_c_str(self.node.content as *i8)
        }
    }
}

impl<'r> TextNode for Text<'r> {
    fn content(&self) -> ~str {
        unsafe {
            std::str::raw::from_c_str(self.node.content as *i8)
        }
    }
}

/**
 * Reads the buffer treating it as if it came from url.
 *
 * It returns the document if buffer contained valid XML.
 */
pub fn read_memory_with_url(buffer: &[u8], url: Option<&str>) -> Option<Document> {
    unsafe {
        use std::libc::c_int;
        use std::ptr::null;
        ffi::xmlCheckVersion(ffi::xmlVersion);
        let doc = ffi::xmlReadMemory(buffer.as_ptr(), buffer.len() as c_int,
                                     url.map(|x| x.as_ptr()).unwrap_or(null()) as *i8,
                                     null(), 64 | 32 /* No errors or warnings for now */);
        ptr_to_option(doc).map(|doc| Document {doc: doc})
    }
}

/**
 * Reads the buffer and return the document if it contains valid XML.
 */
pub fn read_memory(buffer: &[u8]) -> Option<Document> {
    read_memory_with_url(buffer, None)
}

fn ptr_to_option<T>(ptr: *T) -> Option<*T> {
    if (std::ptr::is_not_null(ptr)) {
        Some(ptr)
    } else {
        None
    }
}
<commit_msg>Rename ElementChildren to ElementChild as it denotes a single child<commit_after>/*
 * Copyright (C) 2014 Maciej Piechotka
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#[comment = "Wrapper for libxml2's DOM parser."];
#[crate_id = "github.com/uzytkownik/xml-rs#xml:0.1"];
#[crate_type = "lib"];
#[license = "MIT"];

extern mod extra;

mod ffi;

/**
 * An XML node that contains text.
 */
pub trait TextNode {
    fn content(&self) -> ~str;
}

/**
 * A CDATA section.
 */
pub struct CData<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * An XML comment.
 */
pub struct Comment<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * An XML document.
 */
pub struct Document {
    priv doc: *ffi::xmlDoc
}

/**
 * An XML element
 */
pub struct Element<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * Iterator over children of XML element
 */
pub struct ElementChildrenIterator<'r> {
    priv cur: Option<&'r ffi::xmlNode>
}

/**
 * Text inside an XML.
 */
pub struct Text<'r> {
    priv node: &'r ffi::xmlNode
}

/**
 * Possible children of an element.
 */
pub enum ElementChild<'r> {
    ElementElementChild(Element<'r>),
    TextElementChild(Text<'r>),
    CDataElementChild(CData<'r>),
    CommentElementChild(Comment<'r>)
}

impl Document {
    /**
     * Find the root element, if it exists.
     */
    pub fn get_root_element<'r>(&'r self) -> Option<Element<'r>> {
        unsafe {
            ptr_to_option(ffi::xmlDocGetRootElement(self.doc)).map(|elem| Element{node: &*elem})
        }
    }
}

#[unsafe_destructor]
impl Drop for Document {
    fn drop(&mut self) {
        unsafe {
            ffi::xmlFreeDoc(self.doc);
        }
    }
}

impl<'r> Comment<'r> {
    /// Get comment contents.
    pub fn comment(&self) -> ~str {
        unsafe {
            std::str::raw::from_c_str(self.node.content as *i8)
        }
    }
}

impl<'r> Element<'r> {
    /**
     * Gets the name of the element.
     */
    pub fn name(&self) -> ~str {
        unsafe {std::str::raw::from_c_str(self.node.name as *i8)}
    }
    /**
     * Iterate over children
     */
    pub fn children_iter(&self) -> ElementChildrenIterator<'r> {
        ElementChildrenIterator {
            cur: ptr_to_option(self.node.children).map(|cur| unsafe {&*cur})
        }
    }
}

impl<'r> Clone for ElementChildrenIterator<'r> {
    fn clone(&self) -> ElementChildrenIterator<'r> {
        ElementChildrenIterator{cur: self.cur}
    }
}

impl<'r> Iterator<ElementChild<'r>> for ElementChildrenIterator<'r> {
     fn next(&mut self) -> Option<ElementChild<'r>> {
        self.cur.and_then(|cur| {
            self.cur = unsafe {ptr_to_option(cur.next).map(|next| &*next)};
            match cur._type {
                ffi::ElementNode => Some(ElementElementChild(Element {node: cur})),
                ffi::TextNode => Some(TextElementChild(Text {node: cur})),
                ffi::CDataSectionNode => Some(CDataElementChild(CData {node: cur})),
                ffi::CommentNode => Some(CommentElementChild(Comment {node: cur})),
                t => {
                    error!("Unsupported type {}", t.to_str());
                    self.next()
                }
            }
        })
    }
}

impl<'r> ElementChild<'r> {
    /// Check if child is an element.
    pub fn is_element(self) -> bool {
        match (self) {
            ElementElementChild(_) => true,
            _ => false
        }
    }
    /// Check if child is text.
    pub fn is_text(self) -> bool {
        match (self) {
            TextElementChild(_) => true,
            _ => false
        }
    }
    /// Check if child is CDATA section
    pub fn is_cdata(self) -> bool {
        match (self) {
            CDataElementChild(_) => true,
            _ => false
        }
    }
    /// Check if child is a comment.
    pub fn is_comment(self) -> bool {
        match (self) {
            CommentElementChild(_) => true,
            _ => false
        }
    }
    /// Get element if it is an element.
    pub fn get_element(self) -> Option<Element<'r>> {
        match (self) {
            ElementElementChild(e) => Some(e),
            _ => None
        }
    }
    /// Get text if it is text.
    pub fn get_text(self) -> Option<Text<'r>> {
        match (self) {
            TextElementChild(t) => Some(t),
            _ => None
        }
    }
    /// Get cdata section if it is cdata section.
    pub fn get_cdata(self) -> Option<CData<'r>> {
        match (self) {
            CDataElementChild(cd) => Some(cd),
            _ => None
        }
    }
    // Get comment if it is a comment.
    pub fn get_comment(self) -> Option<Comment<'r>> {
    match (self) {
            CommentElementChild(c) => Some(c),
            _ => None
        }
    }
}

impl<'r> TextNode for CData<'r> {
    fn content(&self) -> ~str {
        unsafe {
            std::str::raw::from_c_str(self.node.content as *i8)
        }
    }
}

impl<'r> TextNode for Text<'r> {
    fn content(&self) -> ~str {
        unsafe {
            std::str::raw::from_c_str(self.node.content as *i8)
        }
    }
}

/**
 * Reads the buffer treating it as if it came from url.
 *
 * It returns the document if buffer contained valid XML.
 */
pub fn read_memory_with_url(buffer: &[u8], url: Option<&str>) -> Option<Document> {
    unsafe {
        use std::libc::c_int;
        use std::ptr::null;
        ffi::xmlCheckVersion(ffi::xmlVersion);
        let doc = ffi::xmlReadMemory(buffer.as_ptr(), buffer.len() as c_int,
                                     url.map(|x| x.as_ptr()).unwrap_or(null()) as *i8,
                                     null(), 64 | 32 /* No errors or warnings for now */);
        ptr_to_option(doc).map(|doc| Document {doc: doc})
    }
}

/**
 * Reads the buffer and return the document if it contains valid XML.
 */
pub fn read_memory(buffer: &[u8]) -> Option<Document> {
    read_memory_with_url(buffer, None)
}

fn ptr_to_option<T>(ptr: *T) -> Option<*T> {
    if (std::ptr::is_not_null(ptr)) {
        Some(ptr)
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use super::{Result, StorageKey, StorageValue, Snapshot, Fork, Iter};

pub struct BaseIndex<T> {
    prefix: Vec<u8>,
    view: T,
}

// pub struct BaseIndexIter<'a> {
//     prefix: &'a [u8],
//     iter: Iter<'a>,
// }

impl<T> BaseIndex<T> {
    pub fn new(prefix: Vec<u8>, view: T) -> Self {
        BaseIndex {
            prefix: prefix,
            view: view
        }
    }

    fn prefixed_key<K: StorageKey>(&self, key: &K) -> Vec<u8> {
        let mut v = Vec::with_capacity(self.prefix.len() + K::size());
        v.extend_from_slice(&self.prefix);
        key.write(&mut v);
        v
    }
}

impl<T> BaseIndex<T> where T: AsRef<Snapshot> {
    pub fn get<K, V>(&self, key: &K) -> Result<Option<V>> where K: StorageKey,
                                                                V: StorageValue {
        Ok(self.view.as_ref().get(&self.prefixed_key(key))?.map(StorageValue::deserialize))
    }

    pub fn contains<K>(&self, key: &K) -> Result<bool> where K: StorageKey {
        self.view.as_ref().contains(&self.prefixed_key(key))
    }
}

impl<T> BaseIndex<T> where T: AsMut<Fork> {
    pub fn put<K, V>(&mut self, key: &K, value: V) where K: StorageKey,
                                                         V: StorageValue {
        let key = self.prefixed_key(key);
        self.view.as_mut().put(key, value.serialize());
    }

    pub fn delete<K>(&mut self, key: &K) where K: StorageKey {
        let key = self.prefixed_key(key);
        self.view.as_mut().delete(key);
    }
}
<commit_msg>Implement BaseIndexIter<commit_after>use std::marker::PhantomData;

use super::{Result, StorageKey, StorageValue, Snapshot, Fork, Iter};

pub struct BaseIndex<T> {
    prefix: Vec<u8>,
    view: T,
}

pub struct BaseIndexIter<'a, K, V> {
    prefix: &'a [u8],
    iter: Iter<'a>,
    stopped: bool,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
}

impl<T> BaseIndex<T> {
    pub fn new(prefix: Vec<u8>, view: T) -> Self {
        BaseIndex {
            prefix: prefix,
            view: view
        }
    }

    fn prefixed_key<K: StorageKey>(&self, key: &K) -> Vec<u8> {
        let mut v = Vec::with_capacity(self.prefix.len() + K::size());
        v.extend_from_slice(&self.prefix);
        key.write(&mut v);
        v
    }
}

impl<T> BaseIndex<T> where T: AsRef<Snapshot> {
    pub fn get<K, V>(&self, key: &K) -> Result<Option<V>> where K: StorageKey,
                                                                V: StorageValue {
        Ok(self.view.as_ref().get(&self.prefixed_key(key))?.map(StorageValue::deserialize))
    }

    pub fn contains<K>(&self, key: &K) -> Result<bool> where K: StorageKey {
        self.view.as_ref().contains(&self.prefixed_key(key))
    }

    pub fn iter<K, V>(&self) -> BaseIndexIter<K, V> where K: StorageKey,
                                                          V: StorageValue {
        BaseIndexIter {
            prefix: &self.prefix,
            iter: self.view.as_ref().iter(&self.prefix),
            stopped: false,
            _k: PhantomData,
            _v: PhantomData
        }
    }
}

impl<T> BaseIndex<T> where T: AsMut<Fork> {
    pub fn put<K, V>(&mut self, key: &K, value: V) where K: StorageKey,
                                                         V: StorageValue {
        let key = self.prefixed_key(key);
        self.view.as_mut().put(key, value.serialize());
    }

    pub fn delete<K>(&mut self, key: &K) where K: StorageKey {
        let key = self.prefixed_key(key);
        self.view.as_mut().delete(key);
    }
}

impl<'a, K, V> Iterator for BaseIndexIter<'a, K, V> where K: StorageKey,
                                                          V: StorageValue, {
    type Item = (K, V);

    fn next(&mut self) -> Option<Self::Item> {
        if self.stopped {
            return None
        }
        if let Some((ref k, ref v)) = self.iter.next() {
            if k.starts_with(self.prefix) {
                // FIXME: possible unneeded coping here (v.to_vec)
                return Some((K::read(k), V::deserialize(v.to_vec())))
            }
        }
        self.stopped = true;
        None
    }
}
<|endoftext|>


<commit_before>//! Interfaces and implementations for directed graphs.
//!
//! This module provides a set of interfaces and implementations of software
//! components for graph representations.
//!
//! These types are designed to maximize composability and ergonomics;
//! currently we have a single graph implementation
//! ([AdjacencyList](struct.AdjacencyList.html), which uses the adjacency-list
//! pattern and integer-based IDs) and two sets of edge/node implementations
//! ([basic](basic/) and [weighted](weighted/)).
//!
//! We would like a graph representation that gives us both fast traversal and
//! fast analysis, and whose API allows for efficient *and* ergonomic use.
//! Naturally there is no best fit, but we can achieve a good fit using
//! a variant of the adjacency-list pattern.
//!
//! Our implementation is based on that of the `petgraph` crate.  Here,
//! however, each node contains space-optimized vectors of outgoing and
//! incoming edges.
//!
//!

#[macro_use]
mod common;
pub mod basic;
pub mod weighted;
pub mod iter;
pub mod interface;
pub mod transform;
pub mod visit;

pub use self::common::{EdgeIndex,NodeIndex,AdjacencyList};
pub use self::weighted::{Edge as WeightedEdge, Node as WeightedNode};
pub use self::interface::{Id, Edge, Node, Graph, ConcreteGraph};
pub use self::interface::{DirectedGraph, DirectedNode, DirectedEdge};
pub use self::transform::{Build, Builder, Target};

/// Simple data-bearing directed graph.  `N` determines the type of data
/// attached to nodes, `E` the type attached to edges, and `Ix` the integer
/// type used to identify nodes and edges.
pub type WeightedGraph<N, E, Ix = common::DefaultIndexType> = AdjacencyList<WeightedNode<N, EdgeIndex<Ix>>, WeightedEdge<E, NodeIndex<Ix>>>;

/// Unweighted directed graph.  `Ix` determines the integer type used to
/// identify nodes and edges.
pub type BasicGraph<Ix = common::DefaultIndexType> = AdjacencyList<basic::Node<EdgeIndex<Ix>>, basic::Edge<NodeIndex<Ix>>>;
<commit_msg>util/graph: add `pub use` statements for recent trait additions<commit_after>//! Interfaces and implementations for directed graphs.
//!
//! This module provides a set of interfaces and implementations of software
//! components for graph representations.
//!
//! These types are designed to maximize composability and ergonomics;
//! currently we have a single graph implementation
//! ([AdjacencyList](struct.AdjacencyList.html), which uses the adjacency-list
//! pattern and integer-based IDs) and two sets of edge/node implementations
//! ([basic](basic/) and [weighted](weighted/)).
//!
//! We would like a graph representation that gives us both fast traversal and
//! fast analysis, and whose API allows for efficient *and* ergonomic use.
//! Naturally there is no best fit, but we can achieve a good fit using
//! a variant of the adjacency-list pattern.
//!
//! Our implementation is based on that of the `petgraph` crate.  Here,
//! however, each node contains space-optimized vectors of outgoing and
//! incoming edges.
//!
//!

#[macro_use]
mod common;
pub mod basic;
pub mod weighted;
pub mod iter;
pub mod interface;
pub mod transform;
pub mod visit;

pub use self::common::{EdgeIndex,NodeIndex,AdjacencyList};
pub use self::weighted::{Edge as WeightedEdge, Node as WeightedNode};
pub use self::interface::{Id, Edge, Node, Graph, ConcreteGraph, ConcreteGraphMut};
pub use self::interface::{DirectedGraph, DirectedEdge, DirectedNode, DirectedNodeMut};
pub use self::transform::{Build, Builder, Target};

/// Simple data-bearing directed graph.  `N` determines the type of data
/// attached to nodes, `E` the type attached to edges, and `Ix` the integer
/// type used to identify nodes and edges.
pub type WeightedGraph<N, E, Ix = common::DefaultIndexType> = AdjacencyList<WeightedNode<N, EdgeIndex<Ix>>, WeightedEdge<E, NodeIndex<Ix>>>;

/// Unweighted directed graph.  `Ix` determines the integer type used to
/// identify nodes and edges.
pub type BasicGraph<Ix = common::DefaultIndexType> = AdjacencyList<basic::Node<EdgeIndex<Ix>>, basic::Edge<NodeIndex<Ix>>>;
<|endoftext|>


<commit_before>use std::str::from_utf8;

use error::OperationError;

/// Gets the position in a list from a signed integer
/// Redis uses this logic to start counting from the end using negative numbers.
///
/// When the absolute number is bigger than the len, an error is produced
/// returning true if the value was greater than the last index and false
/// if the value is lower than the first index.
///
/// # Examples
/// ```
/// use database::dbutil::normalize_position;
///
/// assert_eq!(normalize_position(0, 10), Ok(0));
/// assert_eq!(normalize_position(-1, 10), Ok(9));
/// assert_eq!(normalize_position(10, 10), Err(true));
/// assert_eq!(normalize_position(-11, 10), Err(false));
/// assert_eq!(normalize_position(-100, 0), Err(false));
/// assert_eq!(normalize_position(100, 0), Err(true));
/// ```
pub fn normalize_position(position: i64, _len: usize) -> Result<usize, bool> {
    let len = _len as i64;
    let mut pos = position;
    if pos < 0 {
        pos += len;
    }
    if pos < 0 {
        return Err(false);
    }
    if pos >= len {
        return Err(true);
    }
    return Ok(pos as usize);
}

/// Creates an ASCII representation of a number
///
/// # Examples
/// ```
/// use database::dbutil::usize_to_vec;
///
/// assert_eq!(usize_to_vec(200), vec!['2' as u8, '0' as u8, '0' as u8]);
/// ```
pub fn usize_to_vec(i: usize) -> Vec<u8> {
    format!("{}", i).into_bytes()
}

/// Parses an ASCII representation of a number
///
/// # Examples
/// ```
/// use database::dbutil::vec_to_usize;
///
/// assert_eq!(vec_to_usize(&vec!['2' as u8, '0' as u8, '0' as u8]).unwrap(), 200);
/// assert!(vec_to_usize(&vec!['a' as u8]).is_err());
/// ```
pub fn vec_to_usize(data: &Vec<u8>) -> Result<usize, OperationError> {
    let res = try!(from_utf8(&data));
    Ok(try!(res.parse::<usize>()))
}
<commit_msg>Strings starting with 0 should not turn into numbers<commit_after>use std::str::from_utf8;

use error::OperationError;

/// Gets the position in a list from a signed integer
/// Redis uses this logic to start counting from the end using negative numbers.
///
/// When the absolute number is bigger than the len, an error is produced
/// returning true if the value was greater than the last index and false
/// if the value is lower than the first index.
///
/// # Examples
/// ```
/// use database::dbutil::normalize_position;
///
/// assert_eq!(normalize_position(0, 10), Ok(0));
/// assert_eq!(normalize_position(-1, 10), Ok(9));
/// assert_eq!(normalize_position(10, 10), Err(true));
/// assert_eq!(normalize_position(-11, 10), Err(false));
/// assert_eq!(normalize_position(-100, 0), Err(false));
/// assert_eq!(normalize_position(100, 0), Err(true));
/// ```
pub fn normalize_position(position: i64, _len: usize) -> Result<usize, bool> {
    let len = _len as i64;
    let mut pos = position;
    if pos < 0 {
        pos += len;
    }
    if pos < 0 {
        return Err(false);
    }
    if pos >= len {
        return Err(true);
    }
    return Ok(pos as usize);
}

/// Creates an ASCII representation of a number
///
/// # Examples
/// ```
/// use database::dbutil::usize_to_vec;
///
/// assert_eq!(usize_to_vec(200), vec!['2' as u8, '0' as u8, '0' as u8]);
/// ```
pub fn usize_to_vec(i: usize) -> Vec<u8> {
    format!("{}", i).into_bytes()
}

/// Parses an ASCII representation of a number
///
/// # Examples
/// ```
/// use database::dbutil::vec_to_usize;
///
/// assert_eq!(vec_to_usize(&vec!['2' as u8, '0' as u8, '0' as u8]).unwrap(), 200);
/// assert!(vec_to_usize(&vec!['a' as u8]).is_err());
/// assert!(vec_to_usize(&b"01".to_vec()).is_err());
/// assert!(vec_to_usize(&b"0".to_vec()).is_ok());
/// assert!(vec_to_usize(&b"".to_vec()).is_err());
/// ```
pub fn vec_to_usize(data: &Vec<u8>) -> Result<usize, OperationError> {
    // "01" should not be transformed into 1
    if data.len() == 0 || (data.len() > 1 && data[0] as char == '0') {
        return Err(OperationError::ValueError);
    }
    let res = try!(from_utf8(&data));
    Ok(try!(res.parse::<usize>()))
}
<|endoftext|>


<commit_before>//! Pass simple commands directly through to `docker-compose`.

use std::ops::Deref;

use args;
use command_runner::{Command, CommandRunner};
#[cfg(test)]
use command_runner::TestCommandRunner;
use errors::*;
use pod::Pod;
use project::{PodOrService, Project};

/// Pass simple commands directly through to `docker-compose`.
pub trait CommandCompose {
    /// Pass simple commands directly through to `docker-compose`.
    fn compose<CR, F>(&self,
                      runner: &CR,
                      command: &str,
                      act_on: &args::ActOn,
                      matching: F,
                      opts: &args::ToArgs)
                      -> Result<()>
        where CR: CommandRunner,
              F: Fn(&Pod) -> bool;
}

impl CommandCompose for Project {
    fn compose<CR, F>(&self,
                      runner: &CR,
                      command: &str,
                      act_on: &args::ActOn,
                      matching: F,
                      opts: &args::ToArgs)
                      -> Result<()>
        where CR: CommandRunner,
              F: Fn(&Pod) -> bool
    {

        let names = match *act_on {
            args::ActOn::Named(ref names) => names.to_owned(),
            args::ActOn::All => {
                let mut pods: Vec<_> = self.pods().collect();
                // Sort so that placeholders come before other pod types,
                // which is important for the `up` command.
                pods.sort_by_key(|p| (p.pod_type(), p.name()));
                pods.iter().map(|p| p.name().to_owned()).collect()
            }
        };

        for name in names.deref() {
            let target = self.current_target();
            match try!(self.pod_or_service_or_err(name)) {
                PodOrService::Pod(pod) => {
                    if pod.enabled_in(target) && matching(pod) {
                        try!(runner.build("docker-compose")
                            .args(&try!(pod.compose_args(self, target)))
                            .arg(command)
                            .args(&opts.to_args())
                            .exec());
                    }
                }
                PodOrService::Service(pod, service_name) => {
                    if pod.enabled_in(target) && matching(pod) {
                        try!(runner.build("docker-compose")
                            .args(&try!(pod.compose_args(self, target)))
                            .arg(command)
                            .args(&opts.to_args())
                            .arg(service_name)
                            .exec());
                    }
                }
            }
        }

        Ok(())
    }
}

#[test]
fn runs_docker_compose_on_all_pods() {
    use env_logger;
    let _ = env_logger::init();
    let proj = Project::from_example("rails_hello").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();

    let opts = args::opts::Empty;
    proj.compose(&runner, "stop", &args::ActOn::All, |_| true, &opts).unwrap();
    assert_ran!(runner, {
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("db.yml"),
         "stop"],
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("frontend.yml"),
         "stop"],
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("rake.yml"),
         "stop"]
    });

    proj.remove_test_output().unwrap();
}

#[test]
fn runs_docker_compose_on_named_pods_and_services() {
    use env_logger;
    let _ = env_logger::init();
    let proj = Project::from_example("rails_hello").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();

    let act_on = args::ActOn::Named(vec!("db".to_owned(), "web".to_owned()));
    let opts = args::opts::Empty;
    proj.compose(&runner, "stop", &act_on, |_| true, &opts).unwrap();
    assert_ran!(runner, {
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("db.yml"),
         "stop"],
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("frontend.yml"),
         "stop",
         "web"]
    });

    proj.remove_test_output().unwrap();
}
<commit_msg>Refactor `CommandCompose` into reusable pieces (#34)<commit_after>//! Pass simple commands directly through to `docker-compose`.

use args;
use command_runner::{Command, CommandRunner};
#[cfg(test)]
use command_runner::TestCommandRunner;
use errors::*;
use pod::Pod;
use project::{PodOrService, Project};

/// Pass simple commands directly through to `docker-compose`.
pub trait CommandCompose {
    /// Pass simple commands directly through to `docker-compose`.
    fn compose<CR, F>(&self,
                      runner: &CR,
                      command: &str,
                      act_on: &args::ActOn,
                      matching: F,
                      opts: &args::ToArgs)
                      -> Result<()>
        where CR: CommandRunner,
              F: Fn(&Pod) -> bool;

    /// Run a `docker-compose` command on a single pod.  If the pod is
    /// disabled, this does nothing.
    fn compose_pod<CR>(&self,
                       runner: &CR,
                       command: &str,
                       pod: &Pod,
                       opts: &args::ToArgs)
                       -> Result<()>
        where CR: CommandRunner;

    /// Run a `docker-compose` command on a single service.  If the pod is
    /// disabled, this does nothing.
    fn compose_service<CR>(&self,
                           runner: &CR,
                           command: &str,
                           pod: &Pod,
                           service_name: &str,
                           opts: &args::ToArgs)
                           -> Result<()>
        where CR: CommandRunner;
}

impl CommandCompose for Project {
    fn compose<CR, F>(&self,
                      runner: &CR,
                      command: &str,
                      act_on: &args::ActOn,
                      matching: F,
                      opts: &args::ToArgs)
                      -> Result<()>
        where CR: CommandRunner,
              F: Fn(&Pod) -> bool
    {
        for pod_or_service in act_on.pods_or_services(self) {
            match try!(pod_or_service) {
                PodOrService::Pod(pod) => {
                    if matching(pod) {
                        try!(self.compose_pod(runner, command, pod, opts));
                    }
                }
                PodOrService::Service(pod, service_name) => {
                    if matching(pod) {
                        try!(self.compose_service(runner,
                                                  command,
                                                  pod,
                                                  service_name,
                                                  opts));
                    }
                }
            }
        }

        Ok(())
    }

    fn compose_pod<CR>(&self,
                       runner: &CR,
                       command: &str,
                       pod: &Pod,
                       opts: &args::ToArgs)
                       -> Result<()>
        where CR: CommandRunner
    {
        let target = self.current_target();
        if pod.enabled_in(target) {
            try!(runner.build("docker-compose")
                 .args(&try!(pod.compose_args(self, target)))
                 .arg(command)
                 .args(&opts.to_args())
                 .exec());
        }
        Ok(())
    }

    fn compose_service<CR>(&self,
                           runner: &CR,
                           command: &str,
                           pod: &Pod,
                           service_name: &str,
                           opts: &args::ToArgs)
                           -> Result<()>
        where CR: CommandRunner
    {
        let target = self.current_target();
        if pod.enabled_in(target) {
            try!(runner.build("docker-compose")
                 .args(&try!(pod.compose_args(self, target)))
                 .arg(command)
                 .args(&opts.to_args())
                 .arg(service_name)
                 .exec());
        }
        Ok(())
    }
}

#[test]
fn runs_docker_compose_on_all_pods() {
    use env_logger;
    let _ = env_logger::init();
    let proj = Project::from_example("rails_hello").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();

    let opts = args::opts::Empty;
    proj.compose(&runner, "stop", &args::ActOn::All, |_| true, &opts).unwrap();
    assert_ran!(runner, {
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("db.yml"),
         "stop"],
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("frontend.yml"),
         "stop"],
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("rake.yml"),
         "stop"]
    });

    proj.remove_test_output().unwrap();
}

#[test]
fn runs_docker_compose_on_named_pods_and_services() {
    use env_logger;
    let _ = env_logger::init();
    let proj = Project::from_example("rails_hello").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();

    let act_on = args::ActOn::Named(vec!("db".to_owned(), "web".to_owned()));
    let opts = args::opts::Empty;
    proj.compose(&runner, "stop", &act_on, |_| true, &opts).unwrap();
    assert_ran!(runner, {
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("db.yml"),
         "stop"],
        ["docker-compose",
         "-p",
         "rails_hello",
         "-f",
         proj.output_dir().join("pods").join("frontend.yml"),
         "stop",
         "web"]
    });

    proj.remove_test_output().unwrap();
}
<|endoftext|>


<commit_before>mod init;
mod config;
mod ignore;
mod sweep;
mod burn;
mod start;
mod end;
mod destroy;

use self::init::Init;
use self::config::Config;
use self::ignore::Ignore;
use self::sweep::Sweep;
use self::burn::Burn;
use self::start::Start;
use self::end::End;
use self::destroy::Destroy;

use constant::*;
use lib::io::*;
use lib::setting::*;
use std::process;

trait Command {
    fn validation(&self) -> bool;
    fn validate(&self) {
        fn message(subject: String) -> String {
            format!("{} does not exist. Please use \"init\" command", subject)
        }
        fn message_for_dir(dir_name: &'static str) -> String {
            message(format!("\"{}\" directory", dir_name))
        }
        fn message_for_file(file_name: &'static str) -> String {
            message(format!("\"{}\" file", file_name))
        }

        if !working_dir_exists() {
            print_with_warning(0, message_for_dir(WORKING_DIR_NAME));
        }
        if !storage_dir_exists() {
            print_with_warning(0, message_for_dir(STORAGE_DIR_NAME));
        }
        if !config_file_exists() {
            print_with_warning(0, message_for_file(CONFIG_FILE_NAME));
        }
        if !ignore_file_exists() {
            print_with_warning(0, message_for_file(IGNORE_FILE_NAME));
        }
    }

    fn help_message(&self) -> &'static str;
    fn help(&self) {
        println!("{}", self.help_message());
        process::exit(1);
    }

    fn main(&self);

    fn exec(&self, help: bool) {
        if self.validation() {
            self.validate();
        }

        if help {
            self.help();
        } else {
            self.main();
        }
    }
}

pub fn execute(args: Vec<String>) {
    let help = args.iter().any(|a| *a == "-h" || *a == "--help");

    let mut args = args.into_iter();

    let command: Box<Command> = match args.next() {
        Some(first) => match first.as_ref() {
            "init"    => Box::new(Init   ),
            "config"  => Box::new(Config ),
            "ignore"  => Box::new(Ignore { command: args.next(), paths: args.collect() }),
            "sweep"   => Box::new(Sweep  ),
            "burn"    => Box::new(Burn   ),
            "start"   => Box::new(Start  ),
            "end"     => Box::new(End    ),
            "destroy" => Box::new(Destroy),
            _         => return print_usage(),
        },
        None => return print_usage(),
    };

    command.exec(help);
}

pub fn print_usage() {
    println!(
r#"Usage:
    {} <command> [--help|-h]

Command:
    init    # Register current directory.
    config  # Set config's parameters.
    ignore  # Set ignored files.
    sweep   # Sweep files in current directory.
    burn    # Burn sweeped files.
    start   # Start "{}".
    end     # End "{}".
    destroy # Destroy "{}"."#,
        ME,
        ME,
        ME,
        ME,
    );
    process::exit(1)
}
<commit_msg>[Refactoring] Specify that no value returns<commit_after>mod init;
mod config;
mod ignore;
mod sweep;
mod burn;
mod start;
mod end;
mod destroy;

use self::init::Init;
use self::config::Config;
use self::ignore::Ignore;
use self::sweep::Sweep;
use self::burn::Burn;
use self::start::Start;
use self::end::End;
use self::destroy::Destroy;

use constant::*;
use lib::io::*;
use lib::setting::*;
use std::process;

trait Command {
    fn validation(&self) -> bool;
    fn validate(&self) {
        fn message(subject: String) -> String {
            format!("{} does not exist. Please use \"init\" command", subject)
        }
        fn message_for_dir(dir_name: &'static str) -> String {
            message(format!("\"{}\" directory", dir_name))
        }
        fn message_for_file(file_name: &'static str) -> String {
            message(format!("\"{}\" file", file_name))
        }

        if !working_dir_exists() {
            print_with_warning(0, message_for_dir(WORKING_DIR_NAME));
        }
        if !storage_dir_exists() {
            print_with_warning(0, message_for_dir(STORAGE_DIR_NAME));
        }
        if !config_file_exists() {
            print_with_warning(0, message_for_file(CONFIG_FILE_NAME));
        }
        if !ignore_file_exists() {
            print_with_warning(0, message_for_file(IGNORE_FILE_NAME));
        }
    }

    fn help_message(&self) -> &'static str;
    fn help(&self) -> ! {
        println!("{}", self.help_message());
        process::exit(1)
    }

    fn main(&self);

    fn exec(&self, help: bool) {
        if self.validation() {
            self.validate();
        }

        if help {
            self.help();
        } else {
            self.main();
        }
    }
}

pub fn execute(args: Vec<String>) {
    let help = args.iter().any(|a| *a == "-h" || *a == "--help");

    let mut args = args.into_iter();

    let command: Box<Command> = match args.next() {
        Some(first) => match first.as_ref() {
            "init"    => Box::new(Init   ),
            "config"  => Box::new(Config ),
            "ignore"  => Box::new(Ignore { command: args.next(), paths: args.collect() }),
            "sweep"   => Box::new(Sweep  ),
            "burn"    => Box::new(Burn   ),
            "start"   => Box::new(Start  ),
            "end"     => Box::new(End    ),
            "destroy" => Box::new(Destroy),
            _         => return print_usage(),
        },
        None => return print_usage(),
    };

    command.exec(help);
}

pub fn print_usage() -> ! {
    println!(
r#"Usage:
    {} <command> [--help|-h]

Command:
    init    # Register current directory.
    config  # Set config's parameters.
    ignore  # Set ignored files.
    sweep   # Sweep files in current directory.
    burn    # Burn sweeped files.
    start   # Start "{}".
    end     # End "{}".
    destroy # Destroy "{}"."#,
        ME,
        ME,
        ME,
        ME,
    );
    process::exit(1)
}
<|endoftext|>


<commit_before>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The reference loader implementation for the `gltf` crate.
//!
//! # Examples
//!
//! ### Importing some `glTF` 2.0
//!
//! ```rust
//! use gltf_importer::import;
//! # #[allow(unused_variables)]
//! let path = "path/to/asset.gltf";
//! # let path = "../examples/Box.gltf";
//! match import(path) {
//!     Ok((gltf, _)) => println!("{:#?}", gltf.as_json()),
//!     Err(err) => println!("error: {:?}", err),
//! }
//! ```

extern crate base64;
extern crate gltf;
extern crate gltf_utils;

use gltf::json::{self, validation};
use std::{fmt, fs, io};

use gltf::Gltf;
use gltf_utils::Source;
use std::error::Error as StdError;
use std::path::Path;

/// Contains parameters for import configuration.
pub mod config;

pub use self::config::Config;
pub use self::config::ValidationStrategy;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A loaded glTF buffer is not of the required length.
    BufferLength(json::Path),

    /// Base 64 decoding error.
    Base64Decoding(base64::DecodeError),

    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(std::io::Error),

    /// `gltf` crate error.
    Gltf(gltf::Error),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// Buffer data returned from `import`.
#[derive(Clone, Debug)]
pub struct Buffers(Vec<Vec<u8>>);

impl Source for Buffers {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.0[buffer.index()]
    }
}

impl Buffers {
    /// Obtain the contents of a loaded buffer.
    pub fn buffer(&self, buffer: &gltf::Buffer) -> Option<&[u8]> {
        self.0.get(buffer.index()).map(Vec::as_slice)
    }

    /// Obtain the contents of a loaded buffer view.
    pub fn view(&self, view: &gltf::buffer::View) -> Option<&[u8]> {
        self.buffer(&view.buffer())
            .map(|data| {
                let begin = view.offset();
                let end = begin + view.length();
                &data[begin..end]
            })
    }

    /// Take the loaded buffer data.
    pub fn take(self) -> Vec<Vec<u8>> {
        self.0
    }
}

fn next_multiple_of_4(mut x: usize) -> usize {
    while x % 4 != 0 {
        x += 1;
    }
    x
}

fn import_impl(path: &Path, config: Config) -> Result<(Gltf, Buffers), Error> {
    let data = read_to_end(path)?;
    if data.starts_with(b"glTF") {
        import_binary(&data, &config, path)
    } else {
        import_standard(&data, &config, path)
    }
}

/// Imports glTF 2.0 with custom configuration.
pub fn import_with_config<P>(path: P, config: Config) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), config)
}

/// Imports glTF 2.0 with default configuration.
pub fn import<P>(path: P) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), Default::default())
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    let file = fs::File::open(path)?;
    let mut reader = io::BufReader::new(file);
    let mut buffer = vec![];
    let _ = reader.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn parse_data_uri(uri: &str) -> Result<Vec<u8>, Error> {
    let encoded = uri.split(",").nth(1).unwrap();
    let decoded = base64::decode(&encoded)?;
    Ok(decoded)
}

fn load_external_buffers(
    base_path: &Path,
    gltf: &Gltf,
    mut bin: Option<Vec<u8>>,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut buffers = vec![];
    for (index, buffer) in gltf.buffers().enumerate() {
        let uri = buffer.uri();
        let data = if uri == "#bin" {
            Ok(bin.take().unwrap())
        } else if uri.starts_with("data:") {
            Ok(parse_data_uri(uri)?)
        } else {
            let path = base_path.parent().unwrap_or(Path::new("./")).join(uri);
            read_to_end(&path)
        }?;
        let buffer_length_including_padding = next_multiple_of_4(buffer.length());
        if data.len() != buffer_length_including_padding {
            let path = json::Path::new().field("buffers").index(index);
            return Err(Error::BufferLength(path));
        }
        buffers.push(data);
    }
    Ok(buffers)
}

fn validate_standard(
    unvalidated: gltf::Unvalidated,
    config: &Config,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    Ok(match config.validation_strategy {
        ValidationStrategy::Skip => unsafe { unvalidated.skip_validation() },
        ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
        ValidationStrategy::Complete => unvalidated.validate_completely()?,
    })
}

fn validate_binary(
    unvalidated: gltf::Unvalidated,
    config: &Config,
    has_bin: bool,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    use json::validation::Error as Reason;

    if config.validation_strategy == ValidationStrategy::Skip {
        return Ok(unsafe { unvalidated.skip_validation() });
    }

    let mut errs = vec![];
    {
        let json = unvalidated.as_json();
        for (index, buffer) in json.buffers.iter().enumerate() {
            let path = || {
                json::Path::new()
                    .field("buffers")
                    .index(index)
                    .field("uri")
            };
            match index {
                0 if has_bin => if buffer.uri.is_some() {
                    errs.push((path(), Reason::Missing));
                },
                _ if buffer.uri.is_none() => {
                    errs.push((path(), Reason::Missing));
                },
                _ => {},
            }
        }
    }

    if errs.is_empty() {
        Ok(match config.validation_strategy {
            ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
            ValidationStrategy::Complete => unvalidated.validate_completely()?,
            ValidationStrategy::Skip => unreachable!(),
        })
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let unvalidated = Gltf::from_slice(data)?;
    let gltf = validate_standard(unvalidated, &config)?;
    let bin = None;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let gltf::Glb { header: _, json, bin } = gltf::Glb::from_slice(data)?;
    let unvalidated = Gltf::from_slice(json)?;
    let bin = bin.map(|x| x.to_vec());
    let gltf = validate_binary(unvalidated, &config, bin.is_some())?;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

impl From<gltf::Error> for Error {
    fn from(err: gltf::Error) -> Error {
        match err {
            gltf::Error::Validation(errs) => Error::Validation(errs),
            _ => Error::Gltf(err),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        Error::Base64Decoding(err)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            Base64Decoding(_) => "Base 64 decoding failed",
            BufferLength(_) => "Loaded buffer does not match required length",
            ExtensionDisabled(_) => "Asset requires a disabled extension",
            ExtensionUnsupported(_) => "Assets requires an unsupported extension",
            IncompatibleVersion(_) => "Asset is not glTF version 2.0",
            Io(_) => "I/O error",
            Gltf(_) => "Error from gltf crate",
            MalformedJson(_) => "Malformed .gltf / .glb JSON",
            Validation(_) => "Asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match *self {
            MalformedJson(ref err) => Some(err),
            Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<commit_msg>Allow buffer lengths to not be multiples of 4<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The reference loader implementation for the `gltf` crate.
//!
//! # Examples
//!
//! ### Importing some `glTF` 2.0
//!
//! ```rust
//! use gltf_importer::import;
//! # #[allow(unused_variables)]
//! let path = "path/to/asset.gltf";
//! # let path = "../examples/Box.gltf";
//! match import(path) {
//!     Ok((gltf, _)) => println!("{:#?}", gltf.as_json()),
//!     Err(err) => println!("error: {:?}", err),
//! }
//! ```

extern crate base64;
extern crate gltf;
extern crate gltf_utils;

use gltf::json::{self, validation};
use std::{fmt, fs, io};

use gltf::Gltf;
use gltf_utils::Source;
use std::error::Error as StdError;
use std::path::Path;

/// Contains parameters for import configuration.
pub mod config;

pub use self::config::Config;
pub use self::config::ValidationStrategy;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A loaded glTF buffer is not of the required length.
    BufferLength(json::Path),

    /// Base 64 decoding error.
    Base64Decoding(base64::DecodeError),

    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(std::io::Error),

    /// `gltf` crate error.
    Gltf(gltf::Error),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// Buffer data returned from `import`.
#[derive(Clone, Debug)]
pub struct Buffers(Vec<Vec<u8>>);

impl Source for Buffers {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.0[buffer.index()]
    }
}

impl Buffers {
    /// Obtain the contents of a loaded buffer.
    pub fn buffer(&self, buffer: &gltf::Buffer) -> Option<&[u8]> {
        self.0.get(buffer.index()).map(Vec::as_slice)
    }

    /// Obtain the contents of a loaded buffer view.
    pub fn view(&self, view: &gltf::buffer::View) -> Option<&[u8]> {
        self.buffer(&view.buffer())
            .map(|data| {
                let begin = view.offset();
                let end = begin + view.length();
                &data[begin..end]
            })
    }

    /// Take the loaded buffer data.
    pub fn take(self) -> Vec<Vec<u8>> {
        self.0
    }
}

fn import_impl(path: &Path, config: Config) -> Result<(Gltf, Buffers), Error> {
    let data = read_to_end(path)?;
    if data.starts_with(b"glTF") {
        import_binary(&data, &config, path)
    } else {
        import_standard(&data, &config, path)
    }
}

/// Imports glTF 2.0 with custom configuration.
pub fn import_with_config<P>(path: P, config: Config) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), config)
}

/// Imports glTF 2.0 with default configuration.
pub fn import<P>(path: P) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), Default::default())
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    let file = fs::File::open(path)?;
    let mut reader = io::BufReader::new(file);
    let mut buffer = vec![];
    let _ = reader.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn parse_data_uri(uri: &str) -> Result<Vec<u8>, Error> {
    let encoded = uri.split(",").nth(1).unwrap();
    let decoded = base64::decode(&encoded)?;
    Ok(decoded)
}

fn load_external_buffers(
    base_path: &Path,
    gltf: &Gltf,
    mut bin: Option<Vec<u8>>,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut buffers = vec![];
    for (index, buffer) in gltf.buffers().enumerate() {
        let uri = buffer.uri();
        let data = if uri == "#bin" {
            Ok(bin.take().unwrap())
        } else if uri.starts_with("data:") {
            Ok(parse_data_uri(uri)?)
        } else {
            let path = base_path.parent().unwrap_or(Path::new("./")).join(uri);
            read_to_end(&path)
        }?;
        if data.len() != buffer.length() {
            let path = json::Path::new().field("buffers").index(index);
            return Err(Error::BufferLength(path));
        }
        buffers.push(data);
    }
    Ok(buffers)
}

fn validate_standard(
    unvalidated: gltf::Unvalidated,
    config: &Config,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    Ok(match config.validation_strategy {
        ValidationStrategy::Skip => unsafe { unvalidated.skip_validation() },
        ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
        ValidationStrategy::Complete => unvalidated.validate_completely()?,
    })
}

fn validate_binary(
    unvalidated: gltf::Unvalidated,
    config: &Config,
    has_bin: bool,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    use json::validation::Error as Reason;

    if config.validation_strategy == ValidationStrategy::Skip {
        return Ok(unsafe { unvalidated.skip_validation() });
    }

    let mut errs = vec![];
    {
        let json = unvalidated.as_json();
        for (index, buffer) in json.buffers.iter().enumerate() {
            let path = || {
                json::Path::new()
                    .field("buffers")
                    .index(index)
                    .field("uri")
            };
            match index {
                0 if has_bin => if buffer.uri.is_some() {
                    errs.push((path(), Reason::Missing));
                },
                _ if buffer.uri.is_none() => {
                    errs.push((path(), Reason::Missing));
                },
                _ => {},
            }
        }
    }

    if errs.is_empty() {
        Ok(match config.validation_strategy {
            ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
            ValidationStrategy::Complete => unvalidated.validate_completely()?,
            ValidationStrategy::Skip => unreachable!(),
        })
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let unvalidated = Gltf::from_slice(data)?;
    let gltf = validate_standard(unvalidated, &config)?;
    let bin = None;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let gltf::Glb { header: _, json, bin } = gltf::Glb::from_slice(data)?;
    let unvalidated = Gltf::from_slice(json)?;
    let bin = bin.map(|x| x.to_vec());
    let gltf = validate_binary(unvalidated, &config, bin.is_some())?;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

impl From<gltf::Error> for Error {
    fn from(err: gltf::Error) -> Error {
        match err {
            gltf::Error::Validation(errs) => Error::Validation(errs),
            _ => Error::Gltf(err),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        Error::Base64Decoding(err)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            Base64Decoding(_) => "Base 64 decoding failed",
            BufferLength(_) => "Loaded buffer does not match required length",
            ExtensionDisabled(_) => "Asset requires a disabled extension",
            ExtensionUnsupported(_) => "Assets requires an unsupported extension",
            IncompatibleVersion(_) => "Asset is not glTF version 2.0",
            Io(_) => "I/O error",
            Gltf(_) => "Error from gltf crate",
            MalformedJson(_) => "Malformed .gltf / .glb JSON",
            Validation(_) => "Asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match *self {
            MalformedJson(ref err) => Some(err),
            Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};

use gluon::{
    new_vm,
    vm::{
        api::{primitive, FunctionRef, Primitive},
        thread::{Status, Thread},
    },
    ThreadExt,
};

// Benchmarks function calls
fn factorial(b: &mut Bencher) {
    let vm = new_vm();
    let text = r#"
    let factorial n =
        if n < 2
        then 1
        else n * factorial (n - 1)
    factorial
    "#;
    vm.load_script("factorial", text).unwrap();
    let mut factorial: FunctionRef<fn(i32) -> i32> = vm.get_global("factorial").unwrap();
    b.iter(|| {
        let result = factorial.call(20).unwrap();
        black_box(result)
    })
}

fn factorial_tail_call(b: &mut Bencher) {
    let vm = new_vm();
    let text = r#"
    let factorial a n =
        if n < 2
        then a
        else factorial (a * n) (n - 1)
    factorial 1
    "#;
    vm.load_script("factorial", text).unwrap();
    let mut factorial: FunctionRef<fn(i32) -> i32> = vm.get_global("factorial").unwrap();
    b.iter(|| {
        let result = factorial.call(20).unwrap();
        black_box(result)
    })
}

fn gluon_rust_boundary_overhead(b: &mut Bencher) {
    let vm = new_vm();

    extern "C" fn test_fn(_: &Thread) -> Status {
        Status::Ok
    }

    let text = r#"
    let for n f =
        if n #Int== 0 then
            ()
        else
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            for (n #Int- 10) f
    for
    "#;
    vm.load_script("test", text).unwrap();

    let mut test: FunctionRef<fn(i32, Primitive<fn(i32)>) -> ()> = vm.get_global("test").unwrap();
    b.iter(|| {
        let result = test
            .call(1000, primitive::<fn(i32)>("test_fn", test_fn))
            .unwrap();
        black_box(result)
    })
}

fn function_call_benchmark(c: &mut Criterion) {
    c.bench_function("factorial", factorial);
    c.bench_function("factorial tail call", factorial_tail_call);
    c.bench_function("gluon rust boundary overhead", gluon_rust_boundary_overhead);
}

criterion_group!(function_call, function_call_benchmark);
criterion_main!(function_call);
<commit_msg>chore: Add an identity benchmark for function calls<commit_after>use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};

use gluon::{
    new_vm,
    vm::{
        api::{primitive, FunctionRef, Primitive},
        thread::{Status, Thread},
    },
    ThreadExt,
};

// Benchmarks function calls

fn identity(b: &mut Bencher) {
    let vm = new_vm();
    let text = r#"
    let id x = x
    id
    "#;
    vm.load_script("id", text).unwrap();
    let mut id: FunctionRef<fn(i32) -> i32> = vm.get_global("id").unwrap();
    b.iter(|| {
        let result = id.call(20).unwrap();
        black_box(result)
    })
}

fn factorial(b: &mut Bencher) {
    let vm = new_vm();
    let text = r#"
    let factorial n =
        if n < 2
        then 1
        else n * factorial (n - 1)
    factorial
    "#;
    vm.load_script("factorial", text).unwrap();
    let mut factorial: FunctionRef<fn(i32) -> i32> = vm.get_global("factorial").unwrap();
    b.iter(|| {
        let result = factorial.call(20).unwrap();
        black_box(result)
    })
}

fn factorial_tail_call(b: &mut Bencher) {
    let vm = new_vm();
    let text = r#"
    let factorial a n =
        if n < 2
        then a
        else factorial (a * n) (n - 1)
    factorial 1
    "#;
    vm.load_script("factorial", text).unwrap();
    let mut factorial: FunctionRef<fn(i32) -> i32> = vm.get_global("factorial").unwrap();
    b.iter(|| {
        let result = factorial.call(20).unwrap();
        black_box(result)
    })
}

fn gluon_rust_boundary_overhead(b: &mut Bencher) {
    let vm = new_vm();

    extern "C" fn test_fn(_: &Thread) -> Status {
        Status::Ok
    }

    let text = r#"
    let for n f =
        if n #Int== 0 then
            ()
        else
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            f n
            for (n #Int- 10) f
    for
    "#;
    vm.load_script("test", text).unwrap();

    let mut test: FunctionRef<fn(i32, Primitive<fn(i32)>) -> ()> = vm.get_global("test").unwrap();
    b.iter(|| {
        let result = test
            .call(1000, primitive::<fn(i32)>("test_fn", test_fn))
            .unwrap();
        black_box(result)
    })
}

fn function_call_benchmark(c: &mut Criterion) {
    c.bench_function("identity", identity);
    c.bench_function("factorial", factorial);
    c.bench_function("factorial tail call", factorial_tail_call);
    c.bench_function("gluon rust boundary overhead", gluon_rust_boundary_overhead);
}

criterion_group!(function_call, function_call_benchmark);
criterion_main!(function_call);
<|endoftext|>