<commit_before>extern crate linked_list_allocator;

use self::linked_list_allocator::Heap;
use crate::syscalls;
use core::alloc::Alloc;
use core::alloc::GlobalAlloc;
use core::alloc::Layout;
use core::intrinsics;
use core::mem;
use core::ptr;
use core::ptr::NonNull;

// None-threaded heap wrapper based on `r9` register instead of global variable
pub(crate) struct TockAllocator;

impl TockAllocator {
    unsafe fn heap(&self) -> &mut Heap {
        let heap: *mut Heap;
        asm!("mov $0, r9" : "=r"(heap) : : : "volatile");
        &mut *heap
    }

    /// Initializes an empty heap
    ///
    /// # Unsafety
    ///
    /// This function must be called at most once. The memory between [`heap_location`] and [`heap_top`] must not overlap with any other memory section.
    #[inline(never)]
    unsafe fn init(&mut self, heap_bottom: usize, heap_top: usize) {
        asm!("mov r9, $0" : : "r"(heap_bottom) : : "volatile");

        let effective_heap_bottom = heap_bottom + mem::size_of::<Heap>();

        let heap = heap_bottom as *mut Heap;
        *heap = Heap::new(effective_heap_bottom, heap_top - effective_heap_bottom);
    }
}

unsafe impl GlobalAlloc for TockAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        self.heap().alloc(layout).unwrap().as_ptr()
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        self.heap().dealloc(NonNull::new_unchecked(ptr), layout)
    }
}

// Note: At the moment, rust_start is incomplete. The rest of this comment
// describes how rust_start *should* work. It does not currently perform data
// relocation (note the TODO in rust_start's source).
//
// _start and rust_start are the first two procedures executed when a Tock
// application starts. _start is invoked directly by the Tock kernel; it
// performs stack setup then calls rust_start. rust_start performs data
// relocation and sets up the heap before calling the rustc-generated main.
// rust_start and _start are tightly coupled: the order of rust_start's
// parameters is designed to simplify _start's implementation.
//
// The memory layout is controlled by the linker script and these methods. These
// are written for the following memory layout:
//
//     +----------------+ <- app_heap_break
//     | Heap           |
//     +----------------| <- heap_bottom
//     | .data and .bss |
//     +----------------+ <- stack_top
//     | Stack          |
//     | (grows down)   |
//     +----------------+ <- mem_start
//
// app_heap_break and mem_start are given to us by the kernel. The stack size is
// determined using pointer text_start, and is used with mem_start to compute
// stack_top. The placement of .data and .bss are given to us by the linker
// script; the heap is located between the end of .bss and app_heap_break. This
// requires that .bss is the last (highest-address) section placed by the linker
// script.

/// Tock programs' entry point. Called by the kernel at program start. Sets up
/// the stack then calls rust_start() for the remainder of setup.
#[doc(hidden)]
#[no_mangle]
#[naked]
#[link_section = ".start"]
pub unsafe extern "C" fn _start(
    text_start: usize,
    mem_start: usize,
    _memory_len: usize,
    app_heap_break: usize,
) -> ! {
    asm!("
        // Initialize the stack pointer. The stack pointer is computed as
        // stack_size + mem_start plus padding to align the stack to a multiple
        // of 8 bytes. The 8 byte alignment is to follow ARM AAPCS:
        // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html
        ldr ip, [r0, #36]  // ip = text_start->stack_size
        add ip, ip, r1     // ip = text_start->stack_size + mem_start
        add ip, #7         // ip = text_start->stack_size + mem_start + 7
        bic r1, ip, #7     // r1 = (text_start->stack_size + mem_start + 7) & ~0x7
        mov sp, r1         // sp = r1

        // Call rust_start. text_start, stack_top, and app_heap_break are
        // already in the correct registers.
        bl rust_start"
        :                                                              // No output operands
        : "{r0}"(text_start) "{r1}"(mem_start) "{r3}"(app_heap_break)  // Input operands
        : "cc" "ip" "lr" "memory" "r0" "r1" "r2" "r3"                  // Clobbers
        :                                                              // Options
    );
    intrinsics::unreachable();
}

/// The header encoded at the beginning of .text by the linker script. It is
/// accessed by rust_start() using its text_start parameter.
#[repr(C)]
struct LayoutHeader {
    got_sym_start: usize,
    got_start: usize,
    got_size: usize,
    data_sym_start: usize,
    data_start: usize,
    data_size: usize,
    bss_start: usize,
    bss_size: usize,
    reldata_start: usize,
    stack_size: usize,
}

/// Rust setup, called by _start. Uses the extern "C" calling convention so that
/// the assembly in _start knows how to call it (the Rust ABI is not defined).
/// Sets up the data segment (including relocations) and the heap, then calls
/// into the rustc-generated main(). This cannot use mutable global variables or
/// global references to globals until it is done setting up the data segment.
#[no_mangle]
pub unsafe extern "C" fn rust_start(
    text_start: usize,
    stack_top: usize,
    _skipped: usize,
    app_heap_break: usize,
) -> ! {
    extern "C" {
        // This function is created internally by`rustc`. See `src/lang_items.rs` for more details.
        fn main(argc: isize, argv: *const *const u8) -> isize;
    }

    // Copy .data into its final location in RAM (determined by the linker
    // script -- should be immediately above the stack).
    let layout_header: &LayoutHeader = core::mem::transmute(text_start);
    intrinsics::copy_nonoverlapping(
        (text_start + layout_header.data_sym_start) as *const u8,
        stack_top as *mut u8,
        layout_header.data_size,
    );

    // Zero .bss (specified by the linker script).
    let bss_end = layout_header.bss_start + layout_header.bss_size; // 1 past the end of .bss
    for i in layout_header.bss_start..bss_end {
        core::ptr::write(i as *mut u8, 0);
    }

    // TODO: Wait for rustc to have working ROPI-RWPI relocation support, then
    // implement dynamic relocations here. At the moment, rustc does not have
    // working ROPI-RWPI support, and it is not clear what that support would
    // look like at the LLVM level. Once we know what the relocation strategy
    // looks like we can write the dynamic linker.

    // Initialize the heap and tell the kernel where everything is. The heap is
    // placed between .bss and the end of application memory.
    TockAllocator.init(bss_end, app_heap_break);
    syscalls::memop(10, stack_top);
    syscalls::memop(11, bss_end);

    main(0, ptr::null());

    loop {
        syscalls::yieldk();
    }
}
<commit_msg>Revert "Initialize .data and .bss in libtock-rs."<commit_after>extern crate linked_list_allocator;

use self::linked_list_allocator::Heap;
use crate::syscalls;
use core::alloc::Alloc;
use core::alloc::GlobalAlloc;
use core::alloc::Layout;
use core::intrinsics;
use core::mem;
use core::ptr;
use core::ptr::NonNull;

const HEAP_SIZE: usize = 0x400;

// None-threaded heap wrapper based on `r9` register instead of global variable
pub(crate) struct TockAllocator;

impl TockAllocator {
    unsafe fn heap(&self) -> &mut Heap {
        let heap: *mut Heap;
        asm!("mov $0, r9" : "=r"(heap) : : : "volatile");
        &mut *heap
    }

    /// Initializes an empty heap
    ///
    /// # Unsafety
    ///
    /// This function must be called at most once. The memory between [`heap_location`] and [`heap_top`] must not overlap with any other memory section.
    #[inline(never)]
    unsafe fn init(&mut self, heap_bottom: usize, heap_top: usize) {
        asm!("mov r9, $0" : : "r"(heap_bottom) : : "volatile");

        let effective_heap_bottom = heap_bottom + mem::size_of::<Heap>();

        let heap = heap_bottom as *mut Heap;
        *heap = Heap::new(effective_heap_bottom, heap_top - effective_heap_bottom);
    }
}

unsafe impl GlobalAlloc for TockAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        self.heap().alloc(layout).unwrap().as_ptr()
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        self.heap().dealloc(NonNull::new_unchecked(ptr), layout)
    }
}

// Note: At the moment, rust_start is incomplete. The rest of this comment
// describes how rust_start *should* work. It does not currently perform data
// relocation (note the TODO in rust_start's source).
//
// _start and rust_start are the first two procedures executed when a Tock
// application starts. _start is invoked directly by the Tock kernel; it
// performs stack setup then calls rust_start. rust_start performs data
// relocation and sets up the heap before calling the rustc-generated main.
// rust_start and _start are tightly coupled: the order of rust_start's
// parameters is designed to simplify _start's implementation.
//
// The memory layout set up by these methods is as follows:
//
//     +----------------+ <- app_heap_break
//     | Heap           |
//     +----------------| <- heap_bottom
//     | Data (globals) |
//     +----------------+ <- stack_top
//     | Stack          |
//     | (grows down)   |
//     +----------------+ <- mem_start
//
// app_heap_break and mem_start are given to us by the kernel. The stack size is
// determined using pointer text_start, and is used with mem_start to compute
// stack_top.

/// Tock programs' entry point. Called by the kernel at program start. Sets up
/// the stack then calls rust_start() for the remainder of setup.
#[doc(hidden)]
#[no_mangle]
#[naked]
#[link_section = ".start"]
pub unsafe extern "C" fn _start(
    text_start: usize,
    mem_start: usize,
    _memory_len: usize,
    app_heap_break: usize,
) -> ! {
    asm!("
        // Initialize the stack pointer. The stack pointer is computed as
        // stack_size + mem_start plus padding to align the stack to a multiple
        // of 8 bytes. The 8 byte alignment is to follow ARM AAPCS:
        // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html
        ldr ip, [r0, #36]  // ip = text_start->stack_size
        add ip, ip, r1     // ip = text_start->stack_size + mem_start
        add ip, #7         // ip = text_start->stack_size + mem_start + 7
        bic r1, ip, #7     // r1 = (text_start->stack_size + mem_start + 7) & ~0x7
        mov sp, r1         // sp = r1

        // Call rust_start. text_start, stack_top, and app_heap_break are
        // already in the correct registers.
        bl rust_start"
        :                                                              // No output operands
        : "{r0}"(text_start) "{r1}"(mem_start) "{r3}"(app_heap_break)  // Input operands
        : "cc" "ip" "lr" "memory" "r0" "r1" "r2" "r3"                  // Clobbers
        :                                                              // Options
    );
    intrinsics::unreachable();
}

/// Rust setup, called by _start. Uses the extern "C" calling convention so that
/// the assembly in _start knows how to call it (the Rust ABI is not defined).
/// Sets up the data segment (including relocations) and the heap, then calls
/// into the rustc-generated main(). This cannot use mutable global variables or
/// global references to globals until it is done setting up the data segment.
#[no_mangle]
pub unsafe extern "C" fn rust_start(
    _text_start: usize,
    stack_top: usize,
    _skipped: usize,
    _app_heap_break: usize,
) -> ! {
    extern "C" {
        // This function is created internally by`rustc`. See `src/lang_items.rs` for more details.
        fn main(argc: isize, argv: *const *const u8) -> isize;
    }

    // TODO: Copy over .data and perform relocations, *then* initialize the heap.
    TockAllocator.init(stack_top, stack_top + HEAP_SIZE);

    syscalls::memop(10, stack_top);
    syscalls::memop(11, stack_top + HEAP_SIZE);

    main(0, ptr::null());

    loop {
        syscalls::yieldk();
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * 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.
 */

extern crate gtk;
#[macro_use]
extern crate relm;
#[macro_use]
extern crate relm_derive;
extern crate tokio_core;

use gtk::{Dialog, DialogExt, Inhibit, WidgetExt, Window, WindowType, DIALOG_MODAL};
use relm::{Relm, Update, Widget};

use self::Msg::*;

#[derive(Msg)]
enum Msg {
    Quit,
}

struct Win {
    window: Window,
}

impl Update for Win {
    type Model = ();
    type ModelParam = ();
    type Msg = Msg;

    fn model(_: &Relm<Self>, _: ()) -> () {
        ()
    }

    fn update(&mut self, event: Msg) {
        match event {
            Quit => gtk::main_quit(),
        }
    }
}

impl Widget for Win {
    type Root = Window;

    fn root(&self) -> Self::Root {
        self.window.clone()
    }

    fn view(relm: &Relm<Self>, _model: Self::Model) -> Self {
        let window = Window::new(WindowType::Toplevel);

        window.show_all();

        let parent = window.clone();
        connect!(relm, window, connect_delete_event(_, _), return {
            let num = dialog(&parent);
            match num {
                1 => (Some(Quit), Inhibit(false)),
                _ => (None, Inhibit(true)),
            }
        });

        Win {
            window: window,
        }
    }
}

fn dialog(window: &Window) -> i32 {
    let buttons = &[("Yes", 1), ("No", 2)];
    let dialog = Dialog::new_with_buttons(Some("Quit?"), Some(window), DIALOG_MODAL, buttons);
    let result = dialog.run();
    dialog.destroy();
    result
}

fn main() {
    Win::run(()).unwrap();
}
<commit_msg>Remove sync callback example<commit_after><|endoftext|>


<commit_before>pub use self::decode::Decoder;
pub use self::encode::Encoder;

mod decode;
mod encode;
pub mod parse;

/*
fn should_have_response_body(method: &Method, status: u16) -> bool {
    trace!("should_have_response_body({:?}, {})", method, status);
    match (method, status) {
        (&Method::Head, _) |
        (_, 100...199) |
        (_, 204) |
        (_, 304) |
        (&Method::Connect, 200...299) => false,
        _ => true
    }
}
*/
/*
const MAX_INVALID_RESPONSE_BYTES: usize = 1024 * 128;
impl HttpMessage for Http11Message {

    fn get_incoming(&mut self) -> ::Result<ResponseHead> {
        unimplemented!();
        /*
        try!(self.flush_outgoing());
        let stream = match self.stream.take() {
            Some(stream) => stream,
            None => {
                // The message was already in the reading state...
                // TODO Decide what happens in case we try to get a new incoming at that point
                return Err(From::from(
                        io::Error::new(io::ErrorKind::Other,
                        "Read already in progress")));
            }
        };

        let expected_no_content = stream.previous_response_expected_no_content();
        trace!("previous_response_expected_no_content = {}", expected_no_content);

        let mut stream = BufReader::new(stream);

        let mut invalid_bytes_read = 0;
        let head;
        loop {
            head = match parse_response(&mut stream) {
                Ok(head) => head,
                Err(::Error::Version)
                if expected_no_content && invalid_bytes_read < MAX_INVALID_RESPONSE_BYTES => {
                    trace!("expected_no_content, found content");
                    invalid_bytes_read += 1;
                    stream.consume(1);
                    continue;
                }
                Err(e) => {
                    self.stream = Some(stream.into_inner());
                    return Err(e);
                }
            };
            break;
        }

        let raw_status = head.subject;
        let headers = head.headers;

        let method = self.method.take().unwrap_or(Method::Get);

        let is_empty = !should_have_response_body(&method, raw_status.0);
        stream.get_mut().set_previous_response_expected_no_content(is_empty);
        // According to https://tools.ietf.org/html/rfc7230#section-3.3.3
        // 1. HEAD responses, and Status 1xx, 204, and 304 cannot have a body.
        // 2. Status 2xx to a CONNECT cannot have a body.
        // 3. Transfer-Encoding: chunked has a chunked body.
        // 4. If multiple differing Content-Length headers or invalid, close connection.
        // 5. Content-Length header has a sized body.
        // 6. Not Client.
        // 7. Read till EOF.
        self.reader = Some(if is_empty {
            SizedReader(stream, 0)
        } else {
             if let Some(&TransferEncoding(ref codings)) = headers.get() {
                if codings.last() == Some(&Chunked) {
                    ChunkedReader(stream, None)
                } else {
                    trace!("not chunked. read till eof");
                    EofReader(stream)
                }
            } else if let Some(&ContentLength(len)) =  headers.get() {
                SizedReader(stream, len)
            } else if headers.has::<ContentLength>() {
                trace!("illegal Content-Length: {:?}", headers.get_raw("Content-Length"));
                return Err(Error::Header);
            } else {
                trace!("neither Transfer-Encoding nor Content-Length");
                EofReader(stream)
            }
        });

        trace!("Http11Message.reader = {:?}", self.reader);

        Ok(ResponseHead {
            headers: headers,
            raw_status: raw_status,
            version: head.version,
        })
        */
    }
}

*/

<commit_msg>refactor(http): remove the commented out code in http::h1<commit_after>pub use self::decode::Decoder;
pub use self::encode::Encoder;

mod decode;
mod encode;
pub mod parse;

<|endoftext|>


<commit_before>use super::elaborate::{self, ElabCx, LocalElabCx};
use super::parser;
use super::core;
use super::typeck::{TyCtxt, LocalCx};

use std::io;
use std::path::{PathBuf};
use readline;

pub struct Repl {
    elab_cx: ElabCx,
    root_file: Option<PathBuf>,
}

// impl From<parser::Error> for Error {
//     fn from(err: parser::Error) -> Error {
//         Error::Parser(err)
//     }
// }
//

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

impl From<elaborate::Error> for Error {
    fn from(err: elaborate::Error) -> Error {
        Error::Elaborator(err)
    }
}

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Elaborator(elaborate::Error),
    // Parser(parser::Error),
}

#[derive(Debug)]
enum Command {
    Quit,
    Reload,
    Unknown(String),
    TypeOf(String),
    Help,
}

impl Repl {
    pub fn from_path(file: &Option<PathBuf>) -> Result<Repl, Error> {
        match file {
            &None => panic!("need to load the REPL with a file"),
            &Some(ref file_path) => {
                let parser = try!(parser::from_file(file_path));
                let module = parser.parse();

                let mut ecx = ElabCx::from_module(
                    module,
                    parser.source_map.clone());

                let emodule = try!(ecx.elaborate_module(
                    file_path));

                let ty_cx = TyCtxt::from_module(&emodule, parser.source_map);

                // let term = term::stdout().unwrap();

                for def in &emodule.defs {
                    match ty_cx.type_check_def(def) {
                        Err(err) => {
                            panic!("error {:?}", err);
                            //report_type_error(&ty_cx, term, err).unwrap(); // handle this properly
                            // return Ok(());
                        }
                        Ok(_) => {
                            let t = ty_cx.get_main_body();
                            println!("main={}",
                                ty_cx.eval(t).unwrap());
                        }
                    }
                }

                Ok(Repl {
                    elab_cx: ecx,
                    root_file: file.clone(),
                })
            }
        }
    }

    /// Starts the read-eval-print-loop for querying the language.
    pub fn start(mut self) -> Result<(), Error> {
        loop {
            let input = match readline::readline("hubris> ") {
                None => {
                    println!("");
                    break;
                },
                Some(input) => input,
            };

            if &input[0..1] == ":" {
                let cmd = self.parse_command(&input[1..]);
                match cmd {
                    Command::Quit => return Ok(()),
                    Command::Reload => panic!("unsupported command"),
                    Command::Unknown(u) => panic!("unknown name"),
                    Command::TypeOf(t) => {
                        let term = self.preprocess_term(t);
                        println!("{}", self.type_check_term(&term));
                    },
                    Command::Help => {
                        println!("HELP!")
                    }
                }
            } else {
                self.handle_input(input.to_string());
                readline::add_history(input.as_ref());
            }
        }

        Ok(())
    }

    fn preprocess_term(&mut self, source: String) -> core::Term {
        let parser = parser::from_string(source).unwrap();
        let term = parser.parse_term();

        let mut lcx = LocalElabCx::from_elab_cx(&mut self.elab_cx);
        lcx.elaborate_term(term).unwrap()
    }

    fn type_check_term(&mut self, term: &core::Term) -> core::Term {
        let mut ltycx = LocalCx::from_cx(&self.elab_cx.ty_cx);
        ltycx.type_infer_term(&term).unwrap()
    }

    fn handle_input(&mut self, source: String) {
        let term = self.preprocess_term(source);
        let ty = self.type_check_term(&term);
        println!("{} : {}", term, ty);
        println!("{}", self.elab_cx.ty_cx.eval(&term).unwrap());
    }

    fn parse_command(&self, command_text: &str) -> Command {
        if command_text == "quit" {
            Command::Quit
        } else if command_text == "reload" {
            Command::Reload
        } else if &command_text[0..4] == "type" {
            Command::TypeOf(command_text[4..].to_string())
        } else if command_text == "help" {
            Command::Help
        } else {
            Command::Unknown(command_text.to_string())
        }
    }
}
<commit_msg>Add a help message<commit_after>use super::elaborate::{self, ElabCx, LocalElabCx};
use super::parser;
use super::core;
use super::typeck::{TyCtxt, LocalCx};

use std::io;
use std::path::{PathBuf};
use readline;

const HELP_MESSAGE: &'static str = r#"
Commands:
    :help        Show this message
    :type <term> Infer the type of <term>
    :quit        Exit
"#;

pub struct Repl {
    elab_cx: ElabCx,
    root_file: Option<PathBuf>,
}

// impl From<parser::Error> for Error {
//     fn from(err: parser::Error) -> Error {
//         Error::Parser(err)
//     }
// }
//

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

impl From<elaborate::Error> for Error {
    fn from(err: elaborate::Error) -> Error {
        Error::Elaborator(err)
    }
}

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Elaborator(elaborate::Error),
    UnknownCommand(String),
    // Parser(parser::Error),
}

#[derive(Debug)]
enum Command {
    Quit,
    Reload,
    Unknown(String),
    TypeOf(String),
    Help,
}

pub enum Cont {
    Quit,
    Done,
}

impl Repl {
    pub fn from_path(file: &Option<PathBuf>) -> Result<Repl, Error> {
        match file {
            &None => panic!("need to load the REPL with a file"),
            &Some(ref file_path) => {
                let parser = try!(parser::from_file(file_path));
                let module = parser.parse();

                let mut ecx = ElabCx::from_module(
                    module,
                    parser.source_map.clone());

                let emodule = try!(ecx.elaborate_module(
                    file_path));

                let ty_cx = TyCtxt::from_module(&emodule, parser.source_map);

                // let term = term::stdout().unwrap();

                for def in &emodule.defs {
                    match ty_cx.type_check_def(def) {
                        Err(err) => {
                            panic!("error {:?}", err);
                            //report_type_error(&ty_cx, term, err).unwrap(); // handle this properly
                            // return Ok(());
                        }
                        Ok(_) => {
                            let t = ty_cx.get_main_body();
                            println!("main={}",
                                ty_cx.eval(t).unwrap());
                        }
                    }
                }

                Ok(Repl {
                    elab_cx: ecx,
                    root_file: file.clone(),
                })
            }
        }
    }

    /// Starts the read-eval-print-loop for querying the language.
    pub fn start(mut self) -> Result<(), Error> {
        loop {
            // First we grab a line ...
            let input = match readline::readline("hubris> ") {
                None => {
                    println!("");
                    break;
                },
                Some(input) => input,
            };

            // Add it to the history
            readline::add_history(input.as_ref());

            match self.repl_iteration(input) {
                // please make me look better
                Err(e) => println!("repl error: {:?}", e),
                Ok(cont) => match cont {
                    Cont::Quit => break,
                    Cont::Done => {},
                }
            }
        }

        Ok(())
    }

    pub fn repl_iteration(&mut self, input: String) -> Result<Cont, Error> {
        if &input[0..1] == ":" {
            let cmd = self.parse_command(&input[1..]);
            match cmd {
                Command::Quit => return Ok(Cont::Quit),
                Command::Reload => panic!("unsupported command"),
                Command::Unknown(u) => return Err(Error::UnknownCommand(u)),
                Command::TypeOf(t) => {
                    let term = self.preprocess_term(t);
                    println!("{}", self.type_check_term(&term));
                },
                Command::Help => {
                    println!("{}", HELP_MESSAGE)
                }
            }
        } else {
            self.handle_input(input.to_string());
        }

        Ok(Cont::Done)
    }

    fn preprocess_term(&mut self, source: String) -> core::Term {
        let parser = parser::from_string(source).unwrap();
        let term = parser.parse_term();

        let mut lcx = LocalElabCx::from_elab_cx(&mut self.elab_cx);
        lcx.elaborate_term(term).unwrap()
    }

    fn type_check_term(&mut self, term: &core::Term) -> core::Term {
        let mut ltycx = LocalCx::from_cx(&self.elab_cx.ty_cx);
        ltycx.type_infer_term(&term).unwrap()
    }

    fn handle_input(&mut self, source: String) {
        let term = self.preprocess_term(source);
        let ty = self.type_check_term(&term);
        println!("{} : {}", term, ty);
        println!("{}", self.elab_cx.ty_cx.eval(&term).unwrap());
    }

    fn parse_command(&self, command_text: &str) -> Command {
        if command_text == "quit" {
            Command::Quit
        } else if command_text == "reload" {
            Command::Reload
        } else if &command_text[0..4] == "type" {
            Command::TypeOf(command_text[4..].to_string())
        } else if &command_text[0..4] == "help" {
            Command::Help
        } else {
            Command::Unknown(command_text.to_string())
        }
    }
}
<|endoftext|>


<commit_before>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char, c_double};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldDefnRef(hFeat: *(), i: c_int) -> *();
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_GetFieldAsDouble(hFeat: *(), iField: c_int) -> c_double;
    fn OGR_F_GetGeometryRef(hFeat: *()) -> *();
    fn OGR_F_Destroy(hFeat: *());
    fn OGR_G_ExportToWkt(hGeom: *(), ppszSrcText: **c_char) -> c_int;
    fn OGR_Fld_GetType(hDefn: *()) -> c_int;
    fn OGRFree(ptr: *());
}

static OFTInteger:        c_int = 0;
static OFTIntegerList:    c_int = 1;
static OFTReal:           c_int = 2;
static OFTRealList:       c_int = 3;
static OFTString:         c_int = 4;
static OFTStringList:     c_int = 5;
static OFTWideString:     c_int = 6;
static OFTWideStringList: c_int = 7;
static OFTBinary:         c_int = 8;
static OFTDate:           c_int = 9;
static OFTTime:           c_int = 10;
static OFTDateTime:       c_int = 11;

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *(),
}

impl<'a> Feature<'a> {
    pub fn field(&self, name: String) -> Option<FieldValue> {
        return name.with_c_str(|c_name| unsafe {
            let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
            if field_id == -1 {
                return None;
            }
            let field_defn = OGR_F_GetFieldDefnRef(self.c_feature, field_id);
            let field_type = OGR_Fld_GetType(field_defn);
            return match field_type {
                OFTString => {
                    let rv = OGR_F_GetFieldAsString(self.c_feature, field_id);
                    return Some(StringValue(raw::from_c_str(rv)));
                },
                OFTReal => {
                    let rv = OGR_F_GetFieldAsDouble(self.c_feature, field_id);
                    return Some(F64Value(rv as f64));
                },
                _ => fail!("Unknown field type {}", field_type)
            }
        });
    }

    pub fn wkt(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_wkt: *c_char = null();
            OGR_G_ExportToWkt(c_geom, &c_wkt);
            let wkt = raw::from_c_str(c_wkt);
            OGRFree(c_wkt as *());
            return wkt;
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

pub enum FieldValue {
    StringValue(String),
    F64Value(f64),
}

impl FieldValue {
    pub fn as_string(self) -> String {
        match self { StringValue(rv) => rv, _ => fail!("not a string") }
    }

    pub fn as_f64(self) -> f64 {
        match self { F64Value(rv) => rv, _ => fail!("not an f64") }
    }
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, FeatureIterator, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    fn assert_almost_eq(a: f64, b: f64) {
        let f: f64 = a / b;
        assert!(f < 1.00001);
        assert!(f > 0.99999);
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.layer_count(), 1);
    }

    fn with_features(fixture: &str, f: |FeatureIterator|) {
        let ds = open(&fixture_path(fixture)).unwrap();
        let layer = ds.layer(0).unwrap();
        f(layer.features());
    }

    fn with_first_feature(fixture: &str, f: |Feature|) {
        with_features(fixture, |mut features| f(features.next().unwrap()));
    }

    #[test]
    fn test_iterate_features() {
        with_features("roads.geojson", |mut features| {
            let feature_vec: Vec<Feature> = features.collect();
            assert_eq!(feature_vec.len(), 21);
        });
    }

    #[test]
    fn test_string_field() {
        with_features("roads.geojson", |mut features| {
            let feature = features.next().unwrap();
            assert_eq!(feature.field("highway".to_string())
                              .unwrap()
                              .as_string(),
                       "footway".to_string());
            assert_eq!(
                features.count(|field| {
                    let highway = field.field("highway".to_string())
                                       .unwrap()
                                       .as_string();
                    highway == "residential".to_string() }),
                2);
        });
    }

    #[test]
    fn test_float_field() {
        with_first_feature("roads.geojson", |feature| {
            assert_almost_eq(
                feature.field("sort_key".to_string())
                       .unwrap()
                       .as_f64(),
                -9.0
            );
        });
    }

    #[test]
    fn test_missing_field() {
        with_first_feature("roads.geojson", |feature| {
            assert!(feature.field("no such field".to_string()).is_none());
        });
    }

    #[test]
    fn test_wkt() {
        with_first_feature("roads.geojson", |feature| {
            let wkt = feature.wkt();
            let wkt_ok = format!("{}{}",
                "LINESTRING (26.1019276 44.4302748,",
                "26.1019382 44.4303191,26.1020002 44.4304202)"
                ).to_string();
            assert_eq!(wkt, wkt_ok);
        });
    }
}
<commit_msg>export geojson geometry<commit_after>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char, c_double};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldDefnRef(hFeat: *(), i: c_int) -> *();
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_GetFieldAsDouble(hFeat: *(), iField: c_int) -> c_double;
    fn OGR_F_GetGeometryRef(hFeat: *()) -> *();
    fn OGR_F_Destroy(hFeat: *());
    fn OGR_G_ExportToWkt(hGeom: *(), ppszSrcText: **c_char) -> c_int;
    fn OGR_G_ExportToJson(hGeometry: *()) -> *c_char;
    fn OGR_Fld_GetType(hDefn: *()) -> c_int;
    fn OGRFree(ptr: *());
    fn VSIFree(ptr: *());
}

static OFTInteger:        c_int = 0;
static OFTIntegerList:    c_int = 1;
static OFTReal:           c_int = 2;
static OFTRealList:       c_int = 3;
static OFTString:         c_int = 4;
static OFTStringList:     c_int = 5;
static OFTWideString:     c_int = 6;
static OFTWideStringList: c_int = 7;
static OFTBinary:         c_int = 8;
static OFTDate:           c_int = 9;
static OFTTime:           c_int = 10;
static OFTDateTime:       c_int = 11;

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *(),
}

impl<'a> Feature<'a> {
    pub fn field(&self, name: String) -> Option<FieldValue> {
        return name.with_c_str(|c_name| unsafe {
            let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
            if field_id == -1 {
                return None;
            }
            let field_defn = OGR_F_GetFieldDefnRef(self.c_feature, field_id);
            let field_type = OGR_Fld_GetType(field_defn);
            return match field_type {
                OFTString => {
                    let rv = OGR_F_GetFieldAsString(self.c_feature, field_id);
                    return Some(StringValue(raw::from_c_str(rv)));
                },
                OFTReal => {
                    let rv = OGR_F_GetFieldAsDouble(self.c_feature, field_id);
                    return Some(F64Value(rv as f64));
                },
                _ => fail!("Unknown field type {}", field_type)
            }
        });
    }

    pub fn wkt(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_wkt: *c_char = null();
            OGR_G_ExportToWkt(c_geom, &c_wkt);
            let wkt = raw::from_c_str(c_wkt);
            OGRFree(c_wkt as *());
            return wkt;
        }
    }

    pub fn json(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_json = OGR_G_ExportToJson(c_geom);
            let json = raw::from_c_str(c_json);
            VSIFree(c_json as *());
            return json;
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

pub enum FieldValue {
    StringValue(String),
    F64Value(f64),
}

impl FieldValue {
    pub fn as_string(self) -> String {
        match self { StringValue(rv) => rv, _ => fail!("not a string") }
    }

    pub fn as_f64(self) -> f64 {
        match self { F64Value(rv) => rv, _ => fail!("not an f64") }
    }
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, FeatureIterator, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    fn assert_almost_eq(a: f64, b: f64) {
        let f: f64 = a / b;
        assert!(f < 1.00001);
        assert!(f > 0.99999);
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.layer_count(), 1);
    }

    fn with_features(fixture: &str, f: |FeatureIterator|) {
        let ds = open(&fixture_path(fixture)).unwrap();
        let layer = ds.layer(0).unwrap();
        f(layer.features());
    }

    fn with_first_feature(fixture: &str, f: |Feature|) {
        with_features(fixture, |mut features| f(features.next().unwrap()));
    }

    #[test]
    fn test_iterate_features() {
        with_features("roads.geojson", |mut features| {
            let feature_vec: Vec<Feature> = features.collect();
            assert_eq!(feature_vec.len(), 21);
        });
    }

    #[test]
    fn test_string_field() {
        with_features("roads.geojson", |mut features| {
            let feature = features.next().unwrap();
            assert_eq!(feature.field("highway".to_string())
                              .unwrap()
                              .as_string(),
                       "footway".to_string());
            assert_eq!(
                features.count(|field| {
                    let highway = field.field("highway".to_string())
                                       .unwrap()
                                       .as_string();
                    highway == "residential".to_string() }),
                2);
        });
    }

    #[test]
    fn test_float_field() {
        with_first_feature("roads.geojson", |feature| {
            assert_almost_eq(
                feature.field("sort_key".to_string())
                       .unwrap()
                       .as_f64(),
                -9.0
            );
        });
    }

    #[test]
    fn test_missing_field() {
        with_first_feature("roads.geojson", |feature| {
            assert!(feature.field("no such field".to_string()).is_none());
        });
    }

    #[test]
    fn test_wkt() {
        with_first_feature("roads.geojson", |feature| {
            let wkt = feature.wkt();
            let wkt_ok = format!("{}{}",
                "LINESTRING (26.1019276 44.4302748,",
                "26.1019382 44.4303191,26.1020002 44.4304202)"
                ).to_string();
            assert_eq!(wkt, wkt_ok);
        });
    }

    #[test]
    fn test_json() {
        with_first_feature("roads.geojson", |feature| {
            let json = feature.json();
            let json_ok = format!("{}{}{}{}",
                "{ \"type\": \"LineString\", \"coordinates\": [ ",
                "[ 26.1019276, 44.4302748 ], ",
                "[ 26.1019382, 44.4303191 ], ",
                "[ 26.1020002, 44.4304202 ] ] }"
                ).to_string();
            assert_eq!(json, json_ok);
        });
    }
}
<|endoftext|>


<commit_before>use nom::types::CompleteStr;

/// Represents an opcode, which tells our interpreter what to do with the following operands
/// Opcodes are a nice way to represent each of our Opcodes
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Opcode {
    LOAD,
    ADD,
    SUB,
    MUL,
    DIV,
    HLT,
    JMP,
    JMPF,
    JMPB,
    EQ,
    NEQ,
    GTE,
    LTE,
    LT,
    GT,
    JMPE,
    NOP,
    ALOC,
    INC,
    DEC,
    DJMPE,
    IGL,
    PRTS,
}

/// We implement this trait to make it easy to convert from a u8 to an Opcode
impl From<u8> for Opcode {
    fn from(v: u8) -> Self {
        match v {
            0 => Opcode::LOAD,
            1 => Opcode::ADD,
            2 => Opcode::SUB,
            3 => Opcode::MUL,
            4 => Opcode::DIV,
            5 => Opcode::HLT,
            6 => Opcode::JMP,
            7 => Opcode::JMPF,
            8 => Opcode::JMPB,
            9 => Opcode::EQ,
            10 => Opcode::NEQ,
            11 => Opcode::GTE,
            12 => Opcode::LTE,
            13 => Opcode::LT,
            14 => Opcode::GT,
            15 => Opcode::JMPE,
            16 => Opcode::NOP,
            17 => Opcode::ALOC,
            18 => Opcode::INC,
            19 => Opcode::DEC,
            20 => Opcode::DJMPE,
            21 => Opcode::PRTS,
            _ => Opcode::IGL,
        }
    }
}

/// Convenience function to convert nom CompleteStr into an opcode
impl<'a> From<CompleteStr<'a>> for Opcode {
    fn from(v: CompleteStr<'a>) -> Self {
        match v {
            CompleteStr("load") => Opcode::LOAD,
            CompleteStr("add") => Opcode::ADD,
            CompleteStr("sub") => Opcode::SUB,
            CompleteStr("mul") => Opcode::MUL,
            CompleteStr("div") => Opcode::DIV,
            CompleteStr("hlt") => Opcode::HLT,
            CompleteStr("jmp") => Opcode::JMP,
            CompleteStr("jmpf") => Opcode::JMPF,
            CompleteStr("jmpb") => Opcode::JMPB,
            CompleteStr("eq") => Opcode::EQ,
            CompleteStr("neq") => Opcode::NEQ,
            CompleteStr("gte") => Opcode::GTE,
            CompleteStr("gt") => Opcode::GT,
            CompleteStr("lte") => Opcode::LTE,
            CompleteStr("lt") => Opcode::LT,
            CompleteStr("jmpe") => Opcode::JMPE,
            CompleteStr("nop") => Opcode::NOP,
            CompleteStr("aloc") => Opcode::ALOC,
            CompleteStr("inc") => Opcode::INC,
            CompleteStr("dec") => Opcode::DEC,
            CompleteStr("djmpe") => Opcode::DJMPE,
            CompleteStr("prts") => Opcode::PRTS,
            _ => Opcode::IGL,
        }
    }
}

/// Represents a combination of an opcode and operands for the VM to execute
#[derive(Debug, PartialEq)]
pub struct Instruction {
    opcode: Opcode,
}

impl Instruction {
    /// Creates and returns a new Instruction
    pub fn new(opcode: Opcode) -> Instruction {
        Instruction { opcode }
    }
}

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

    #[test]
    fn test_create_hlt() {
        let opcode = Opcode::HLT;
        assert_eq!(opcode, Opcode::HLT);
    }

    #[test]
    fn test_create_instruction() {
        let instruction = Instruction::new(Opcode::HLT);
        assert_eq!(instruction.opcode, Opcode::HLT);
    }

    #[test]
    fn test_str_to_opcode() {
        let opcode = Opcode::from(CompleteStr("load"));
        assert_eq!(opcode, Opcode::LOAD);
        let opcode = Opcode::from(CompleteStr("illegal"));
        assert_eq!(opcode, Opcode::IGL);
    }
}
<commit_msg>Made opcodes case insensitive<commit_after>use nom::types::CompleteStr;

/// Represents an opcode, which tells our interpreter what to do with the following operands
/// Opcodes are a nice way to represent each of our Opcodes
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Opcode {
    LOAD,
    ADD,
    SUB,
    MUL,
    DIV,
    HLT,
    JMP,
    JMPF,
    JMPB,
    EQ,
    NEQ,
    GTE,
    LTE,
    LT,
    GT,
    JMPE,
    NOP,
    ALOC,
    INC,
    DEC,
    DJMPE,
    IGL,
    PRTS,
}

/// We implement this trait to make it easy to convert from a u8 to an Opcode
impl From<u8> for Opcode {
    fn from(v: u8) -> Self {
        match v {
            0 => Opcode::LOAD,
            1 => Opcode::ADD,
            2 => Opcode::SUB,
            3 => Opcode::MUL,
            4 => Opcode::DIV,
            5 => Opcode::HLT,
            6 => Opcode::JMP,
            7 => Opcode::JMPF,
            8 => Opcode::JMPB,
            9 => Opcode::EQ,
            10 => Opcode::NEQ,
            11 => Opcode::GTE,
            12 => Opcode::LTE,
            13 => Opcode::LT,
            14 => Opcode::GT,
            15 => Opcode::JMPE,
            16 => Opcode::NOP,
            17 => Opcode::ALOC,
            18 => Opcode::INC,
            19 => Opcode::DEC,
            20 => Opcode::DJMPE,
            21 => Opcode::PRTS,
            _ => Opcode::IGL,
        }
    }
}

/// Convenience function to convert nom CompleteStr into an opcode
impl<'a> From<CompleteStr<'a>> for Opcode {
    fn from(v: CompleteStr<'a>) -> Self {
        let lowercased_opcode = v.to_lowercase();
        match CompleteStr(&lowercased_opcode) {
            CompleteStr("load") => Opcode::LOAD,
            CompleteStr("add") => Opcode::ADD,
            CompleteStr("sub") => Opcode::SUB,
            CompleteStr("mul") => Opcode::MUL,
            CompleteStr("div") => Opcode::DIV,
            CompleteStr("hlt") => Opcode::HLT,
            CompleteStr("jmp") => Opcode::JMP,
            CompleteStr("jmpf") => Opcode::JMPF,
            CompleteStr("jmpb") => Opcode::JMPB,
            CompleteStr("eq") => Opcode::EQ,
            CompleteStr("neq") => Opcode::NEQ,
            CompleteStr("gte") => Opcode::GTE,
            CompleteStr("gt") => Opcode::GT,
            CompleteStr("lte") => Opcode::LTE,
            CompleteStr("lt") => Opcode::LT,
            CompleteStr("jmpe") => Opcode::JMPE,
            CompleteStr("nop") => Opcode::NOP,
            CompleteStr("aloc") => Opcode::ALOC,
            CompleteStr("inc") => Opcode::INC,
            CompleteStr("dec") => Opcode::DEC,
            CompleteStr("djmpe") => Opcode::DJMPE,
            CompleteStr("prts") => Opcode::PRTS,
            _ => Opcode::IGL,
        }
    }
}

/// Represents a combination of an opcode and operands for the VM to execute
#[derive(Debug, PartialEq)]
pub struct Instruction {
    opcode: Opcode,
}

impl Instruction {
    /// Creates and returns a new Instruction
    pub fn new(opcode: Opcode) -> Instruction {
        Instruction { opcode }
    }
}

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

    #[test]
    fn test_create_hlt() {
        let opcode = Opcode::HLT;
        assert_eq!(opcode, Opcode::HLT);
    }

    #[test]
    fn test_create_instruction() {
        let instruction = Instruction::new(Opcode::HLT);
        assert_eq!(instruction.opcode, Opcode::HLT);
    }

    #[test]
    fn test_str_to_opcode() {
        let opcode = Opcode::from(CompleteStr("load"));
        assert_eq!(opcode, Opcode::LOAD);
        let opcode = Opcode::from(CompleteStr("Load"));
        assert_eq!(opcode, Opcode::LOAD);
        let opcode = Opcode::from(CompleteStr("illegal"));
        assert_eq!(opcode, Opcode::IGL);
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, Write};

use super::state::State;
use super::ast::Instruction;

pub fn interpret<R, W>(instructions: &[Instruction], state: &mut State,
                   input: &mut R, output: &mut W)
    where R: Read, W: Write
{
    for instruction in instructions {
        interpret_instruction(instruction, state, input, output);
    }
}

#[inline]
fn interpret_instruction<R, W>(instruction: &Instruction, state: &mut State,
                       input: &mut R, output: &mut W)
    where R: Read, W: Write
{
    match *instruction {
        Instruction::Left => state.left(),
        Instruction::Right => state.right(),
        Instruction::Up => state.up(),
        Instruction::Down => state.down(),
        Instruction::In => {
            let mut byte = [0];
            let _ = input.read_exact(&mut byte);
            state.store(byte[0]);
        }
        Instruction::Out => {
            let _ = output.write_all(&[state.load()]);
        }
        Instruction::Loop(ref program) => {
            while state.load() != 0  {
                interpret(&program, state, input, output);
            }
        }
    }
}

<commit_msg>Some interpreter tests.<commit_after>use std::io::{Read, Write};

use super::state::State;
use super::ast::Instruction;

pub fn interpret<R, W>(instructions: &[Instruction], state: &mut State,
                   input: &mut R, output: &mut W)
    where R: Read, W: Write
{
    for instruction in instructions {
        interpret_instruction(instruction, state, input, output);
    }
}

#[inline]
fn interpret_instruction<R, W>(instruction: &Instruction, state: &mut State,
                       input: &mut R, output: &mut W)
    where R: Read, W: Write
{
    match *instruction {
        Instruction::Left => state.left(),
        Instruction::Right => state.right(),
        Instruction::Up => state.up(),
        Instruction::Down => state.down(),
        Instruction::In => {
            let mut byte = [0];
            let _ = input.read_exact(&mut byte);
            state.store(byte[0]);
        }
        Instruction::Out => {
            let _ = output.write_all(&[state.load()]);
        }
        Instruction::Loop(ref program) => {
            while state.load() != 0  {
                interpret(&program, state, input, output);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::interpret;
    use super::super::ast::{Instruction, make_loop};
    use super::super::ast::Instruction::*;

    #[test]
    fn assert_no_output() {
        assert_interpret(&[Right], &[], &[]);
    }

    #[test]
    fn assert_output_0() {
        assert_interpret(&[Right, Out], &[], &[0]);
    }

    #[test]
    fn assert_output_1() {
        assert_interpret(&[Up, Out], &[], &[1]);
    }

    #[test]
    fn assert_increment_input() {
        let prog = &[In, Up, Out];
        assert_interpret(prog, &[0], &[1]);
        assert_interpret(prog, &[5], &[6]);
        assert_interpret(prog, &[255], &[0]);
    }

    #[test]
    fn assert_increment_loop() {
        let prog = &[In, make_loop(vec![Up, Out, In])];
        assert_interpret(prog, &[0], &[]);
        assert_interpret(prog, &[1, 0], &[2]);
        assert_interpret(prog, &[1, 4, 0], &[2, 5]);
        assert_interpret(prog, &[8, 255, 18, 0], &[9, 0, 19]);
    }

    #[test]
    fn hello_world() {
        assert_parse_interpret("++++++[>++++++++++++<-]>.\
                                >++++++++++[>++++++++++<-]>+.\
                                +++++++..+++.>++++[>+++++++++++<-]>.\
                                <+++[>----<-]>.<<<<<+++[>+++++<-]>.\
                                >>.+++.------.--------.>>+.",
            "",
            "Hello, World!");
    }

    fn assert_interpret(program: &[Instruction], input: &[u8], output: &[u8]) {
        use super::super::state::State;
        use std::io::Cursor;
        use std::str;

        let mut reader = Cursor::new(input);
        let mut writer = Cursor::new(Vec::<u8>::new());
        let mut state  = State::new();

        interpret(program, &mut state, &mut reader, &mut writer);
        assert_eq!(str::from_utf8(writer.into_inner().as_slice()),
                   str::from_utf8(output));
    }

    fn assert_parse_interpret(program: &str, input: &str, output: &str) {
        use super::super::parser::parse_program;

        let program = parse_program(program.as_bytes()).unwrap();
        assert_interpret(&program, input.as_bytes(), output.as_bytes());
    }
}
<|endoftext|>


<commit_before>use ::glob;
use ::serde_json;
use ::zip;
use std::collections::HashMap;
use std::fs;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MultiArchiveJsonWriter {
    archives: HashMap<String, ZipArchiveJsonWriter>,
}

impl MultiArchiveJsonWriter {
    pub fn new() -> MultiArchiveJsonWriter {
        MultiArchiveJsonWriter { archives: HashMap::new() }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipArchiveJsonWriter {
    objects: HashMap<String, ZipObjectJsonWriter>,
}

impl ZipArchiveJsonWriter {
    /// Construct ZipArchiveJsonWriter with empty map of objects:
    pub fn new() -> ZipArchiveJsonWriter {
        ZipArchiveJsonWriter { objects: HashMap::new() }
    }

    /// Create and fill ZipArchiveJsonWriter representing a
    /// .zip file:
    pub fn from(file_path: &str, exclude: &str) -> ZipArchiveJsonWriter {
        let mut archive_writer = ZipArchiveJsonWriter::new();
        let exclude_pattern = glob::Pattern::new(exclude).unwrap();

        let file = fs::File::open(file_path).unwrap();
        let mut whole_archive = zip::ZipArchive::new(file).unwrap();

        // Loop through the objects within the archive (.zip file)...
        for i in 0..whole_archive.len() {
            let zip_object = whole_archive.by_index(i).unwrap();

            // ...and if it isn't excluded, add the object to
            // the HashMap in the archive writer:
            if !exclude_pattern.matches(zip_object.name()) {
                archive_writer.objects.insert(
                    String::from(zip_object.name()),
                    ZipObjectJsonWriter {
                        compression_type: format!("{}", zip_object.compression()),
                        original_size: zip_object.size(),
                        compressed_size: zip_object.compressed_size(),
                        compression_rate: String::from("0%"),
                    }
                );
            }
        }

        archive_writer
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipObjectJsonWriter {
    compression_type: String,
    original_size: u64,
    compressed_size: u64,
    compression_rate: String,
}

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

    fn get_zip_object() -> ZipObjectJsonWriter {
        ZipObjectJsonWriter {
            compression_type: format!("{}", zip::CompressionMethod::Deflated),
            original_size: 100,
            compressed_size: 50,
            compression_rate: String::from("50%"),
        }
    }

    fn get_zip_archive() -> ZipArchiveJsonWriter {
        let mut zip_archive = ZipArchiveJsonWriter::new();
        zip_archive.objects.insert(String::from("foo.txt"), get_zip_object());
        zip_archive
    }

    #[test]
    fn test_serialize_object_json_writer() {
        let zip_object = get_zip_object();

        let zip_object_serialized = serde_json::to_string(&zip_object)
            .unwrap();

        let zip_object_pretty = serde_json::
            to_string_pretty(&zip_object).unwrap();

        let zip_object_deserialized: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_serialized.as_str()).unwrap();

        let zip_object_depretty: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_pretty.as_str()).unwrap();

        assert_eq!(zip_object, zip_object_deserialized);
        assert_eq!(zip_object, zip_object_depretty);
    }

    #[test]
    fn test_new_archive_has_empty_map_of_zip_objects() {
        let zip_archive = ZipArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipObjectJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, zip_archive.objects);
    }

    #[test]
    fn test_serialize_archive_json_writer() {
        let zip_archive = get_zip_archive();

        let zip_archive_serialized = serde_json::to_string(&zip_archive)
            .unwrap();

        let zip_archive_pretty = serde_json::to_string_pretty(&zip_archive)
            .unwrap();

        let zip_archive_deserialized: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_serialized.as_str()).unwrap();

        let zip_archive_depretty: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_pretty.as_str()).unwrap();

        assert_eq!(zip_archive, zip_archive_deserialized);
        assert_eq!(zip_archive, zip_archive_depretty);
    }

    #[test]
    fn test_new_multi_archive_has_empty_map_of_zip_archives() {
        let multi_archive = MultiArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipArchiveJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, multi_archive.archives);
    }

    #[test]
    fn test_serialize_multi_archive_json_writer() {
        let zip_archive = get_zip_archive();
        let mut multi_archive = MultiArchiveJsonWriter::new();

        multi_archive.archives.insert(String::from("bar.zip"), zip_archive);

        let multi_archive_serialized = serde_json::to_string(&multi_archive)
            .unwrap();

        let multi_archive_pretty = serde_json::to_string_pretty(&multi_archive)
            .unwrap();

        let multi_archive_deserialized: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_serialized.as_str()).unwrap();

        let multi_archive_depretty: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_pretty.as_str()).unwrap();

        assert_eq!(multi_archive, multi_archive_deserialized);
        assert_eq!(multi_archive, multi_archive_depretty);
    }
}
<commit_msg>Add constructor for ZipObjectJsonWriter<commit_after>use ::glob;
use ::serde_json;
use ::zip;
use std::collections::HashMap;
use std::fs;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MultiArchiveJsonWriter {
    archives: HashMap<String, ZipArchiveJsonWriter>,
}

impl MultiArchiveJsonWriter {
    pub fn new() -> MultiArchiveJsonWriter {
        MultiArchiveJsonWriter { archives: HashMap::new() }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipArchiveJsonWriter {
    objects: HashMap<String, ZipObjectJsonWriter>,
}

impl ZipArchiveJsonWriter {
    /// Construct ZipArchiveJsonWriter with empty map of objects:
    pub fn new() -> ZipArchiveJsonWriter {
        ZipArchiveJsonWriter { objects: HashMap::new() }
    }

    /// Create and fill ZipArchiveJsonWriter representing a
    /// .zip file:
    pub fn from(file_path: &str, exclude: &str) -> ZipArchiveJsonWriter {
        let mut archive_writer = ZipArchiveJsonWriter::new();
        let exclude_pattern = glob::Pattern::new(exclude).unwrap();

        let file = fs::File::open(file_path).unwrap();
        let mut whole_archive = zip::ZipArchive::new(file).unwrap();

        // Loop through the objects within the archive (.zip file)...
        for i in 0..whole_archive.len() {
            let zip_object = whole_archive.by_index(i).unwrap();

            // ...and if it isn't excluded, add the object to
            // the HashMap in the archive writer:
            if !exclude_pattern.matches(zip_object.name()) {
                archive_writer.objects.insert(
                    String::from(zip_object.name()),
                    ZipObjectJsonWriter {
                        compression_type: format!("{}", zip_object.compression()),
                        original_size: zip_object.size(),
                        compressed_size: zip_object.compressed_size(),
                        compression_rate: String::from("0%"),
                    }
                );
            }
        }

        archive_writer
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipObjectJsonWriter {
    compression_type: String,
    original_size: u64,
    compressed_size: u64,
    compression_rate: String,
}

impl ZipObjectJsonWriter {
    pub fn new(
        compression_type: zip::CompressionMethod,
        original_size: u64,
        compressed_size: u64,
    ) -> ZipObjectJsonWriter {
        let compression_rate = match original_size {
            0 => 0.0 as f64,
            _ => {
                (original_size as f64 - compressed_size as f64)
                / original_size as f64
            },
        };

        ZipObjectJsonWriter {
            compression_type: format!("{}", compression_type),
            original_size: original_size,
            compressed_size: compressed_size,
            compression_rate: format!("{:.*}%", 2, compression_rate * 100.0),
        }
    }
}

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

    fn get_zip_object() -> ZipObjectJsonWriter {
        ZipObjectJsonWriter {
            compression_type: format!("{}", zip::CompressionMethod::Deflated),
            original_size: 100,
            compressed_size: 50,
            compression_rate: String::from("50%"),
        }
    }

    fn get_zip_archive() -> ZipArchiveJsonWriter {
        let mut zip_archive = ZipArchiveJsonWriter::new();
        zip_archive.objects.insert(String::from("foo.txt"), get_zip_object());
        zip_archive
    }

    #[test]
    fn test_new_zip_object_calculates_percentages() {
        let zip_object = ZipObjectJsonWriter::new(
            zip::CompressionMethod::Deflated,
            100,
            50,
        );

        assert_eq!("50.00%", zip_object.compression_rate);

        let zip_object_empty = ZipObjectJsonWriter::new(
            zip::CompressionMethod::Stored,
            0,
            0,
        );

        assert_eq!("0.00%", zip_object_empty.compression_rate);

        let zip_object_grew = ZipObjectJsonWriter::new(
            zip::CompressionMethod::Deflated,
            100,
            150,
        );

        assert_eq!("-50.00%", zip_object_grew.compression_rate);
    }

    #[test]
    fn test_serialize_object_json_writer() {
        let zip_object = get_zip_object();

        let zip_object_serialized = serde_json::to_string(&zip_object)
            .unwrap();

        let zip_object_pretty = serde_json::
            to_string_pretty(&zip_object).unwrap();

        let zip_object_deserialized: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_serialized.as_str()).unwrap();

        let zip_object_depretty: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_pretty.as_str()).unwrap();

        assert_eq!(zip_object, zip_object_deserialized);
        assert_eq!(zip_object, zip_object_depretty);
    }

    #[test]
    fn test_new_archive_has_empty_map_of_zip_objects() {
        let zip_archive = ZipArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipObjectJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, zip_archive.objects);
    }

    #[test]
    fn test_serialize_archive_json_writer() {
        let zip_archive = get_zip_archive();

        let zip_archive_serialized = serde_json::to_string(&zip_archive)
            .unwrap();

        let zip_archive_pretty = serde_json::to_string_pretty(&zip_archive)
            .unwrap();

        let zip_archive_deserialized: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_serialized.as_str()).unwrap();

        let zip_archive_depretty: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_pretty.as_str()).unwrap();

        assert_eq!(zip_archive, zip_archive_deserialized);
        assert_eq!(zip_archive, zip_archive_depretty);
    }

    #[test]
    fn test_new_multi_archive_has_empty_map_of_zip_archives() {
        let multi_archive = MultiArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipArchiveJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, multi_archive.archives);
    }

    #[test]
    fn test_serialize_multi_archive_json_writer() {
        let zip_archive = get_zip_archive();
        let mut multi_archive = MultiArchiveJsonWriter::new();

        multi_archive.archives.insert(String::from("bar.zip"), zip_archive);

        let multi_archive_serialized = serde_json::to_string(&multi_archive)
            .unwrap();

        let multi_archive_pretty = serde_json::to_string_pretty(&multi_archive)
            .unwrap();

        let multi_archive_deserialized: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_serialized.as_str()).unwrap();

        let multi_archive_depretty: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_pretty.as_str()).unwrap();

        assert_eq!(multi_archive, multi_archive_deserialized);
        assert_eq!(multi_archive, multi_archive_depretty);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use foxbox_core::traits::Controller;
use foxbox_taxonomy::manager::*;
use hyper::net::{ NetworkListener };
use iron::{ AfterMiddleware, Chain, Handler,
            HttpServerFactory, Iron, IronResult, Request,
            Response, ServerFactory };
use iron_cors::CORS;
use iron::error::{ IronError };
use iron::method::Method;
use iron::status::Status;
use mount::Mount;
use router::NoRoute;
use static_router;
use std::net::SocketAddr;
use std::sync::Arc;
use std::thread;
use taxonomy_router;
use tls::SniServerFactory;

const THREAD_COUNT: usize = 8;

struct Custom404;

impl AfterMiddleware for Custom404 {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        use std::io::Error as StdError;
        use std::io::ErrorKind;

        if let Some(_) = err.error.downcast::<NoRoute>() {
            // Router error
            return Ok(Response::with((Status::NotFound,
                                      format!("Unknown resource: {}", err))));
        } else if let Some(err) = err.error.downcast::<StdError>() {
            // StaticFile error
            if err.kind() == ErrorKind::NotFound {
                return Ok(Response::with((Status::NotFound,
                                          format!("Unknown resource: {}", err))));
            }
        }

        // Just let other errors go through, like 401.
        Err(err)
    }
}

struct Ping;

impl Handler for Ping {
    fn handle (&self, _: &mut Request) -> IronResult<Response> {
        Ok(Response::with(Status::NoContent))
    }
}

pub struct HttpServer<T: Controller> {
    controller: T
}

impl<T: Controller> HttpServer<T> {
    pub fn new(controller: T) -> Self {
        HttpServer { controller: controller }
    }

    pub fn start(&mut self, adapter_api: &Arc<AdapterManager>) {
        let taxonomy_chain = taxonomy_router::create(self.controller.clone(),
                                                      adapter_api);

        let users_manager = self.controller.get_users_manager();
        let mut mount = Mount::new();
        mount.mount("/", static_router::create(users_manager.clone()))
             .mount("/ping", Ping)
             .mount("/api/v1", taxonomy_chain)
             .mount("/users", users_manager.get_router_chain());

        let mut chain = Chain::new(mount);
        chain.link_after(Custom404);

        let cors = CORS::new(vec![
            (vec![Method::Get], "ping".to_owned()),
            (vec![Method::Get, Method::Post, Method::Put, Method::Delete],
             "services/:service/:command".to_owned()),
            (vec![Method::Get], "services/list".to_owned()),

            // Taxonomy router paths. Keep in sync with taxonomy_router.rs
            (vec![Method::Get, Method::Post], "api/v1/services".to_owned()),
            (vec![Method::Post, Method::Delete], "api/v1/services/tags".to_owned()),
            (vec![Method::Get, Method::Post], "api/v1/channels".to_owned()),
            (vec![Method::Put], "api/v1/channels/get".to_owned()),
            (vec![Method::Put], "api/v1/channels/set".to_owned()),
            (vec![Method::Post, Method::Delete], "api/v1/channels/tags".to_owned())
        ]);
        chain.link_after(cors);

        let addrs: Vec<_> = self.controller.http_as_addrs().unwrap().collect();

        if self.controller.get_tls_enabled() {
            let mut certificate_manager = self.controller.get_certificate_manager();
            let server_factory = SniServerFactory::new(&mut certificate_manager);
            start_server(addrs, chain, server_factory);
        } else {
            start_server(addrs, chain, HttpServerFactory {});
        }
    }
}

fn start_server<TListener, T>(addrs: Vec<SocketAddr>, chain: Chain, factory: T)
    where TListener: NetworkListener + Send + 'static,
          T: ServerFactory<TListener> + Send + 'static {

    thread::Builder::new().name("HttpServer".to_owned())
                          .spawn(move || {
        Iron::new(chain)
             .listen_with(addrs[0], THREAD_COUNT, &factory, None)
             .unwrap();
    }).unwrap();
}

#[cfg(test)]
describe! ping {
    before_each {
        use mount::Mount;
        use iron::Headers;
        use iron::status::Status;
        use iron_test::request;
        use super::Ping;

        let mut mount = Mount::new();
        mount.mount("/ping", Ping);
    }

    it "should response 204 NoContent" {
        let response = request::get("http://localhost:3000/ping",
                                    Headers::new(),
                                    &mount).unwrap();
        assert_eq!(response.status.unwrap(), Status::NoContent);
    }
}

#[cfg(test)]
describe! http_server {
    before_each {
        extern crate hyper;

        use foxbox_taxonomy::manager::AdapterManager;
        use std::thread;
        use std::sync::Arc;
        use std::time::Duration;
        use stubs::controller::ControllerStub;

        let taxo_manager = Arc::new(AdapterManager::new(None));

        let mut http_server = HttpServer::new(ControllerStub::new());
        http_server.start(&taxo_manager);
        // HACK: Let some time for the http server to start.
        thread::sleep(Duration::new(3, 0));
    }

    it "should get the appropriate CORS headers" {
        use iron::headers;
        use iron::method::Method;

        let endpoints = vec![
            (vec![Method::Get, Method::Post, Method::Put],
             "services/:service/:command".to_owned()),
            (vec![Method::Get], "services/list".to_owned())
        ];
        let client = hyper::Client::new();
        for endpoint in endpoints {
            let (_, path) = endpoint;
            let path = "http://localhost:3000/".to_owned() +
                       &(path.replace(":", "foo"));
            let res = client.get(&path).send();
            let headers = &res.unwrap().headers;
            assert!(headers.has::<headers::AccessControlAllowOrigin>());
            assert!(headers.has::<headers::AccessControlAllowHeaders>());
            assert!(headers.has::<headers::AccessControlAllowMethods>());
        };
    }

    it "should respond with 404" {
        use iron::status::Status;
        use std::io::Read;

        let client = hyper::Client::new();
        let path = "http://localhost:3000/foo/bar".to_owned();
        let mut res = client.get(&path).send().unwrap();
        assert_eq!(res.status, Status::NotFound);
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        assert_eq!(body, "Unknown resource: No such file or \
                   directory (os error 2)".to_owned());
    }
}
<commit_msg>Remove references to outdated endpoints (#530)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use foxbox_core::traits::Controller;
use foxbox_taxonomy::manager::*;
use hyper::net::{ NetworkListener };
use iron::{ AfterMiddleware, Chain, Handler,
            HttpServerFactory, Iron, IronResult, Request,
            Response, ServerFactory };
use iron_cors::CORS;
use iron::error::{ IronError };
use iron::method::Method;
use iron::status::Status;
use mount::Mount;
use router::NoRoute;
use static_router;
use std::net::SocketAddr;
use std::sync::Arc;
use std::thread;
use taxonomy_router;
use tls::SniServerFactory;

const THREAD_COUNT: usize = 8;

struct Custom404;

impl AfterMiddleware for Custom404 {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        use std::io::Error as StdError;
        use std::io::ErrorKind;

        if let Some(_) = err.error.downcast::<NoRoute>() {
            // Router error
            return Ok(Response::with((Status::NotFound,
                                      format!("Unknown resource: {}", err))));
        } else if let Some(err) = err.error.downcast::<StdError>() {
            // StaticFile error
            if err.kind() == ErrorKind::NotFound {
                return Ok(Response::with((Status::NotFound,
                                          format!("Unknown resource: {}", err))));
            }
        }

        // Just let other errors go through, like 401.
        Err(err)
    }
}

struct Ping;

impl Handler for Ping {
    fn handle (&self, _: &mut Request) -> IronResult<Response> {
        Ok(Response::with(Status::NoContent))
    }
}

pub struct HttpServer<T: Controller> {
    controller: T
}

impl<T: Controller> HttpServer<T> {
    pub fn new(controller: T) -> Self {
        HttpServer { controller: controller }
    }

    pub fn start(&mut self, adapter_api: &Arc<AdapterManager>) {
        let taxonomy_chain = taxonomy_router::create(self.controller.clone(),
                                                      adapter_api);

        let users_manager = self.controller.get_users_manager();
        let mut mount = Mount::new();
        mount.mount("/", static_router::create(users_manager.clone()))
             .mount("/ping", Ping)
             .mount("/api/v1", taxonomy_chain)
             .mount("/users", users_manager.get_router_chain());

        let mut chain = Chain::new(mount);
        chain.link_after(Custom404);

        let cors = CORS::new(vec![
            (vec![Method::Get], "ping".to_owned()),

            // Taxonomy router paths. Keep in sync with taxonomy_router.rs
            (vec![Method::Get, Method::Post], "api/v1/services".to_owned()),
            (vec![Method::Post, Method::Delete], "api/v1/services/tags".to_owned()),
            (vec![Method::Get, Method::Post], "api/v1/channels".to_owned()),
            (vec![Method::Put], "api/v1/channels/get".to_owned()),
            (vec![Method::Put], "api/v1/channels/set".to_owned()),
            (vec![Method::Post, Method::Delete], "api/v1/channels/tags".to_owned())
        ]);
        chain.link_after(cors);

        let addrs: Vec<_> = self.controller.http_as_addrs().unwrap().collect();

        if self.controller.get_tls_enabled() {
            let mut certificate_manager = self.controller.get_certificate_manager();
            let server_factory = SniServerFactory::new(&mut certificate_manager);
            start_server(addrs, chain, server_factory);
        } else {
            start_server(addrs, chain, HttpServerFactory {});
        }
    }
}

fn start_server<TListener, T>(addrs: Vec<SocketAddr>, chain: Chain, factory: T)
    where TListener: NetworkListener + Send + 'static,
          T: ServerFactory<TListener> + Send + 'static {

    thread::Builder::new().name("HttpServer".to_owned())
                          .spawn(move || {
        Iron::new(chain)
             .listen_with(addrs[0], THREAD_COUNT, &factory, None)
             .unwrap();
    }).unwrap();
}

#[cfg(test)]
describe! ping {
    before_each {
        use mount::Mount;
        use iron::Headers;
        use iron::status::Status;
        use iron_test::request;
        use super::Ping;

        let mut mount = Mount::new();
        mount.mount("/ping", Ping);
    }

    it "should response 204 NoContent" {
        let response = request::get("http://localhost:3000/ping",
                                    Headers::new(),
                                    &mount).unwrap();
        assert_eq!(response.status.unwrap(), Status::NoContent);
    }
}

#[cfg(test)]
describe! http_server {
    before_each {
        extern crate hyper;

        use foxbox_taxonomy::manager::AdapterManager;
        use std::thread;
        use std::sync::Arc;
        use std::time::Duration;
        use stubs::controller::ControllerStub;

        let taxo_manager = Arc::new(AdapterManager::new(None));

        let mut http_server = HttpServer::new(ControllerStub::new());
        http_server.start(&taxo_manager);
        // HACK: Let some time for the http server to start.
        thread::sleep(Duration::new(3, 0));
    }

    it "should get the appropriate CORS headers" {
        use iron::headers;
        use iron::method::Method;

        let endpoints = vec![
            (vec![Method::Get], "ping".to_owned())
        ];
        let client = hyper::Client::new();
        for endpoint in endpoints {
            let (_, path) = endpoint;
            let path = "http://localhost:3000/".to_owned() +
                       &(path.replace(":", "foo"));
            let res = client.get(&path).send();
            let headers = &res.unwrap().headers;
            assert!(headers.has::<headers::AccessControlAllowOrigin>());
            assert!(headers.has::<headers::AccessControlAllowHeaders>());
            assert!(headers.has::<headers::AccessControlAllowMethods>());
        };
    }

    it "should respond with 404" {
        use iron::status::Status;
        use std::io::Read;

        let client = hyper::Client::new();
        let path = "http://localhost:3000/foo/bar".to_owned();
        let mut res = client.get(&path).send().unwrap();
        assert_eq!(res.status, Status::NotFound);
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        assert_eq!(body, "Unknown resource: No such file or \
                   directory (os error 2)".to_owned());
    }
}
<|endoftext|>