<commit_before>//! Oration: a Rocket/Elm self hosted commenting system for static sites.
//!
//! Inspired by ![Isso](https://posativ.org/isso/), which is a welcomed change from Disqus.
//! However, the codebase is unmaintained and ![security concerns](https://axiomatic.neophilus.net/posts/2017-04-16-from-disqus-to-isso.html) abound.
//! Oration aims to be a fast, lightweight and secure platform for your comments. Nothing more, but importantly, nothing less.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(missing_docs_in_private_items))]
#![cfg_attr(feature="clippy", warn(single_match_else))]

#![feature(plugin, custom_derive, use_extern_macros)]
#![plugin(rocket_codegen)]

// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

extern crate dotenv;
#[macro_use]
extern crate error_chain;
extern crate rand;
extern crate rocket;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
extern crate r2d2_diesel;
extern crate r2d2;
extern crate yansi;
#[macro_use(log)] extern crate log;

/// Handles the database connection pool.
mod db;
/// SQL <----> Rust inerop using Diesel.
mod models;
/// Verbose schema for the comment database.
mod schema;
/// Serves up static files through Rocket.
mod static_files;
/// Handles the error chain of the program.
mod errors;
/// Tests for the Rocket side of the app.
#[cfg(test)]
mod tests;

use std::io;
use rocket::response::NamedFile;
use models::preferences::Preference;
use std::process;
use yansi::Paint;

/// Serve up the index file, which ultimately launches the Elm app.
#[get("/")]
fn index() -> io::Result<NamedFile> {
    NamedFile::open("public/index.html")
}

/// Test function that returns the session hash from the database.
#[get("/session")]
fn get_session(conn: db::Conn) -> String {
    let session = match Preference::get_session(&conn) {
        Ok(s) => s,
        Err(err) => {
            //TODO: Pretty print these
            log::warn!("{}", err);
            for e in err.iter().skip(1) {
                log::warn!("caused by: {}", e);
            }
            err.to_string()
        }
    };
    session
}

/// Ignite Rocket, connect to the database and start serving data.
/// Exposes a connection to the database so we can set the session on startup.
fn rocket() -> (rocket::Rocket, db::Conn) {
    let pool = db::init_pool();
    let conn = db::Conn(pool.get().expect("database connection for initialisation"));
    let rocket = rocket::ignite().manage(pool).mount(
        "/",
        routes![
            index,
            static_files::files,
            get_session,
        ],
    );

    (rocket, conn)
}

/// Application entry point.
fn main() {
    //Initialise webserver routes and database connection pool
    let (rocket, conn) = rocket();

    //Set the session info in the database
    log::info!("💿  {}", Paint::purple("Saving session hash to database"));
    match Preference::set_session(&conn) {
        Ok(b) => {
            if b == false {
                //TODO: This may need to be a crit as well. Unsure.
                log::warn!("Failed to set session hash");
            }
        }
        Err(err) => {
            log::error!("{}", err);
            for e in err.iter().skip(1) {
                log::error!("caused by: {}", e);
            }
            process::exit(1);
        },
    };

    //Start the web service
    rocket.launch();
}
<commit_msg>Pretty print extra logs<commit_after>//! Oration: a Rocket/Elm self hosted commenting system for static sites.
//!
//! Inspired by ![Isso](https://posativ.org/isso/), which is a welcomed change from Disqus.
//! However, the codebase is unmaintained and ![security concerns](https://axiomatic.neophilus.net/posts/2017-04-16-from-disqus-to-isso.html) abound.
//! Oration aims to be a fast, lightweight and secure platform for your comments. Nothing more, but importantly, nothing less.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(missing_docs_in_private_items))]
#![cfg_attr(feature="clippy", warn(single_match_else))]

#![feature(plugin, custom_derive, use_extern_macros)]
#![plugin(rocket_codegen)]

// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

extern crate dotenv;
#[macro_use]
extern crate error_chain;
extern crate rand;
extern crate rocket;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
extern crate r2d2_diesel;
extern crate r2d2;
extern crate yansi;
#[macro_use(log)] extern crate log;

/// Handles the database connection pool.
mod db;
/// SQL <----> Rust inerop using Diesel.
mod models;
/// Verbose schema for the comment database.
mod schema;
/// Serves up static files through Rocket.
mod static_files;
/// Handles the error chain of the program.
mod errors;
/// Tests for the Rocket side of the app.
#[cfg(test)]
mod tests;

use std::io;
use rocket::response::NamedFile;
use models::preferences::Preference;
use std::process;
use yansi::Paint;

/// Serve up the index file, which ultimately launches the Elm app.
#[get("/")]
fn index() -> io::Result<NamedFile> {
    NamedFile::open("public/index.html")
}

/// Test function that returns the session hash from the database.
#[get("/session")]
fn get_session(conn: db::Conn) -> String {
    let session = match Preference::get_session(&conn) {
        Ok(s) => s,
        Err(err) => {
            log::warn!("{}", err);
            for e in err.iter().skip(1) {
                log::warn!("    {} {}", Paint::white("=> Caused by:"), Paint::red(&e));
            }
            err.to_string()
        }
    };
    session
}

/// Ignite Rocket, connect to the database and start serving data.
/// Exposes a connection to the database so we can set the session on startup.
fn rocket() -> (rocket::Rocket, db::Conn) {
    let pool = db::init_pool();
    let conn = db::Conn(pool.get().expect("database connection for initialisation"));
    let rocket = rocket::ignite().manage(pool).mount(
        "/",
        routes![
            index,
            static_files::files,
            get_session,
        ],
    );

    (rocket, conn)
}

/// Application entry point.
fn main() {
    //Initialise webserver routes and database connection pool
    let (rocket, conn) = rocket();

    //Set the session info in the database
    log::info!("💿  {}", Paint::purple("Saving session hash to database"));
    match Preference::set_session(&conn) {
        Ok(b) => {
            if b == false {
                //TODO: This may need to be a crit as well. Unsure.
                log::warn!("Failed to set session hash");
            }
        }
        Err(err) => {
            log::error!("{}", err);
            for e in err.iter().skip(1) {
                log::error!("    {} {}", Paint::white("=> Caused by:"), Paint::red(&e));
            }
            process::exit(1);
        },
    };

    log::info!("📢  {}", Paint::blue("Oration is now serving your comments"));
    //Start the web service
    rocket.launch();

}
<|endoftext|>


<commit_before>#![feature(io, os, env, core, collections, rustc_private)]

extern crate getopts;
extern crate selecta;

use getopts::{optopt,getopts};
use std::cmp::min;

use selecta::search::Search;
use selecta::tty::IO;
use selecta::screen::Screen;

#[allow(dead_code)]
fn main() {
    let initial_query = extract_initial_query();
    let lines = read_lines();
    let mut screen = Screen::new();

    let height = min(20, screen.height);
    let mut search = Search::blank(&lines, initial_query, Some(height));

    screen.blank(search.visible_limit);

    while !search.is_done() {
        screen.print(&search);
        let input = screen.ansi.io.read();
        match input {
            Some(character) => {
                search = screen.handle_keystroke(search, character.as_slice());
            },
            None => break,
        };
    }
    screen.move_cursor_to_end();
    screen.ansi.io.reset();
    println!("{}", search.selection().unwrap_or("".to_string()));
}

fn extract_initial_query() -> Option<String> {
    let args: Vec<String> = get_args();
    let opts = &[
        optopt("s", "search", "initial search query", ""),
    ];
    let matches = getopts(args.tail(), opts).unwrap();

    matches.opt_str("s")
}

fn get_args() -> Vec<String> {
    std::env::args().collect()
}

fn read_lines() -> Vec<String> {
    std::old_io::stdio::stdin().lock().lines().map( |line| {
        line.unwrap().trim().to_string()
    }).collect()
}
<commit_msg>Split usage of stdin() into multiple lets<commit_after>#![feature(io, os, env, core, collections, rustc_private)]

extern crate getopts;
extern crate selecta;

use getopts::{optopt,getopts};
use std::cmp::min;
use std::old_io::stdio;

use selecta::search::Search;
use selecta::tty::IO;
use selecta::screen::Screen;

#[allow(dead_code)]
fn main() {
    let initial_query = extract_initial_query();
    let lines = read_lines();
    let mut screen = Screen::new();

    let height = min(20, screen.height);
    let mut search = Search::blank(&lines, initial_query, Some(height));

    screen.blank(search.visible_limit);

    while !search.is_done() {
        screen.print(&search);
        let input = screen.ansi.io.read();
        match input {
            Some(character) => {
                search = screen.handle_keystroke(search, character.as_slice());
            },
            None => break,
        };
    }
    screen.move_cursor_to_end();
    screen.ansi.io.reset();
    println!("{}", search.selection().unwrap_or("".to_string()));
}

fn extract_initial_query() -> Option<String> {
    let args: Vec<String> = get_args();
    let opts = &[
        optopt("s", "search", "initial search query", ""),
    ];
    let matches = getopts(args.tail(), opts).unwrap();

    matches.opt_str("s")
}

fn get_args() -> Vec<String> {
    std::env::args().collect()
}

fn read_lines() -> Vec<String> {
    let mut stdin = stdio::stdin();
    let mut reader = stdin.lock();
    reader.lines().map( |line| {
        line.unwrap().trim().to_string()
    }).collect()
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::cmp;
use std::env;
use std::fs::File;
use std::io::{self, Stdin, Stdout, Read, BufRead, BufReader, Error, ErrorKind, Write};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const TAB_STOP: usize = 8;

#[derive(PartialEq, Clone, Copy)]
enum Key {
    Character(u8),
    ArrowLeft,
    ArrowRight,
    ArrowUp,
    ArrowDown,
    Delete,
    Home,
    End,
    PageUp,
    PageDown,
}

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct RawMode {
    orig_term: Termios,
}

struct Row {
    chars: String,
    render: String,
}

struct Editor {
    _mode: RawMode,
    cx: usize,
    cy: usize,
    rx: usize,
    rowoff: usize,
    coloff: usize,
    screenrows: usize,
    screencols: usize,
    rows: Option<Vec<Row>>,
    stdin: Stdin,
    stdout: Stdout,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

impl Row {
    fn new<T>(s: T) -> Row
        where T: Into<String>
    {
        let chars = s.into();
        let mut idx = 0;
        let mut render = String::new();
        for ch in chars.chars() {
            if ch == '\t' {
                render.push(' ');
                idx += 1;
                while idx % TAB_STOP != 0 {
                    render.push(' ');
                    idx += 1;
                }
            } else {
                render.push(ch);
                idx += 1;
            }
        }
        Row { chars, render }
    }

    fn cx_to_rx(&self, cx: usize) -> usize {
        let mut rx = 0;
        for ch in self.chars.chars().take(cx) {
            if ch == '\t' {
                rx += (TAB_STOP - 1) - (rx % TAB_STOP);
            }
            rx += 1;
        }
        rx
    }
}

fn read_non_blocking<R: Read>(r: &mut R, buf: &mut [u8]) -> usize {
    r.read(buf)
        .or_else(|e| if e.kind() == ErrorKind::WouldBlock {
                     Ok(0)
                 } else {
                     Err(e)
                 })
        .expect("read_non_blocking")
}

fn byte_slice(s: &str, offset: usize, max_len: usize) -> &[u8] {
    if s.len() > max_len + offset {
        s[offset..(max_len + offset)].as_bytes()
    } else if s.len() > offset {
        s[offset..].as_bytes()
    } else {
        s[0..0].as_bytes()
    }
}

fn editor_read_key(stdin: &mut Stdin) -> Key {
    let mut buf = [0; 1];
    loop {
        let n = read_non_blocking(stdin, &mut buf);
        if n == 1 {
            if buf[0] == b'\x1b' {
                let mut seq = [0; 2];
                let n = read_non_blocking(stdin, &mut seq);
                if n == 2 && seq[0] == b'[' {
                    if seq[1] >= b'0' && seq[1] <= b'9' {
                        let mut last = [0; 1];
                        let n = read_non_blocking(stdin, &mut last);
                        if n == 1 && last[0] == b'~' {
                            return match seq[1] {
                                       b'1' | b'7' => Key::Home,
                                       b'3' => Key::Delete,
                                       b'4' | b'8' => Key::End,
                                       b'5' => Key::PageUp,
                                       b'6' => Key::PageDown,
                                       _ => Key::Character(b'\x1b'),
                                   };
                        }
                    } else {
                        return match seq[1] {
                                   b'A' => Key::ArrowUp,
                                   b'B' => Key::ArrowDown,
                                   b'C' => Key::ArrowRight,
                                   b'D' => Key::ArrowLeft,
                                   b'H' => Key::Home,
                                   b'F' => Key::End,
                                   _ => Key::Character(b'\x1b'),
                               };
                    }
                } else if n == 2 && seq[0] == b'O' {
                    return match seq[1] {
                               b'H' => Key::Home,
                               b'F' => Key::End,
                               _ => Key::Character(b'\x1b'),
                           };
                }
                return Key::Character(b'\x1b');
            } else {
                return Key::Character(buf[0]);
            }
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize {
        ws_col: 0,
        ws_row: 0,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"));
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor {
               _mode: mode,
               cx: 0,
               cy: 0,
               rx: 0,
               rowoff: 0,
               coloff: 0,
               screenrows: rows as usize,
               screencols: cols as usize,
               rows: None,
               stdin: stdin,
               stdout: stdout,
           })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<()> {
        self.stdout.write_all(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn scroll(&mut self) {
        self.rx = self.cx;
        if self.cy < self.numrows() {
            self.rx = self.rows.as_ref().unwrap()[self.cy].cx_to_rx(self.cx);
        }

        if self.cy < self.rowoff {
            self.rowoff = self.cy;
        }
        if self.cy >= self.rowoff + self.screenrows {
            self.rowoff = self.cy - self.screenrows + 1;
        }
        if self.rx < self.coloff {
            self.coloff = self.rx;
        }
        if self.rx >= self.coloff + self.screencols {
            self.coloff = (1 + self.rx) - self.screencols;
        }
    }

    fn numrows(&self) -> usize {
        self.rows.as_ref().map_or(0, |v| v.len())
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        let numrows = self.numrows();
        for y in 0..(self.screenrows) {
            let filerow = y + self.rowoff;
            if filerow >= numrows {
                if self.rows.is_none() && y == self.screenrows / 3 {
                    let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                    msg.truncate(self.screencols);
                    let padding = (self.screencols - msg.len()) / 2;
                    if padding > 0 {
                        self.write(b"~")?;
                        for _ in 1..padding {
                            self.write(b" ")?;
                        }
                    }
                    self.write(msg.as_bytes())?;
                } else {
                    self.write(b"~")?;
                }
            } else {
                let rows = self.rows.as_ref().unwrap();
                self.stdout
                    .write_all(byte_slice(&rows[filerow].render, self.coloff, self.screencols))?;
            }

            self.write(b"\x1b[K")?;
            if y < self.screenrows - 1 {
                self.write(b"\r\n")?;
            }
        }
        Ok(())
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.scroll();

        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;

        let move_cursor = format!("\x1b[{};{}H",
                                  (self.cy - self.rowoff) + 1,
                                  (self.rx - self.coloff) + 1)
                .into_bytes();
        self.write(&move_cursor)?;

        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn rowlen(&self, index: usize) -> usize {
        let row = self.rows.as_ref().and_then(|r| r.get(index));
        row.map_or(0, |r| r.chars.len())
    }

    fn move_cursor(&mut self, k: Key) {
        match k {
            Key::ArrowUp => {
                if self.cy > 0 {
                    self.cy -= 1;
                }
            }
            Key::ArrowDown => {
                if self.cy < self.numrows() {
                    self.cy += 1;
                }
            }
            Key::ArrowLeft => {
                if self.cx > 0 {
                    self.cx -= 1;
                } else if self.cy > 0 {
                    self.cy -= 1;
                    self.cx = self.rowlen(self.cy);
                }
            }
            Key::ArrowRight => {
                let row = self.rows.as_ref().and_then(|r| r.get(self.cy));
                let rowlen = row.map_or(0, |r| r.chars.len());
                if self.cx < rowlen {
                    self.cx += 1;
                } else if row.is_some() && self.cx == rowlen {
                    self.cx = 0;
                    self.cy += 1;
                }
            }
            _ => (),
        }

        let rowlen = self.rowlen(self.cy);
        if self.cx > rowlen {
            self.cx = rowlen;
        }
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            Key::Character(k) if k == ctrl_key!(b'q') => return false,
            Key::Home => {
                self.cx = 0;
            }
            Key::End => {
                self.cx = self.rowlen(self.cy);
            }
            Key::PageUp | Key::PageDown => {
                let key = if c == Key::PageUp {
                    self.cy = self.rowoff;
                    Key::ArrowUp
                } else {
                    self.cy = cmp::min(self.rowoff + self.screenrows - 1, self.numrows());
                    Key::ArrowDown
                };
                for _ in 0..(self.screenrows) {
                    self.move_cursor(key);
                }
            }
            Key::ArrowUp | Key::ArrowDown | Key::ArrowLeft | Key::ArrowRight => {
                self.move_cursor(c);
            }
            _ => (),
        };
        true
    }

    fn open(&mut self, filename: &str) -> io::Result<()> {
        let f = File::open(filename)?;
        let file = BufReader::new(&f);
        let results: io::Result<Vec<Row>> = file.lines().map(|r| r.map(Row::new)).collect();
        self.rows = Some(results?);
        Ok(())
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write_all(b"\x1b[2J").unwrap();
        self.stdout.write_all(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();
    if let Some(filename) = env::args().nth(1) {
        editor.open(&filename).unwrap();
    }

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<commit_msg>Status bar<commit_after>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::cmp;
use std::env;
use std::fs::File;
use std::io::{self, Stdin, Stdout, Read, BufRead, BufReader, Error, ErrorKind, Write};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const TAB_STOP: usize = 8;

#[derive(PartialEq, Clone, Copy)]
enum Key {
    Character(u8),
    ArrowLeft,
    ArrowRight,
    ArrowUp,
    ArrowDown,
    Delete,
    Home,
    End,
    PageUp,
    PageDown,
}

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct RawMode {
    orig_term: Termios,
}

struct Row {
    chars: String,
    render: String,
}

struct Editor {
    _mode: RawMode,
    cx: usize,
    cy: usize,
    rx: usize,
    rowoff: usize,
    coloff: usize,
    screenrows: usize,
    screencols: usize,
    rows: Option<Vec<Row>>,
    stdin: Stdin,
    stdout: Stdout,
    filename: Option<String>,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

impl Row {
    fn new<T>(s: T) -> Row
        where T: Into<String>
    {
        let chars = s.into();
        let mut idx = 0;
        let mut render = String::new();
        for ch in chars.chars() {
            if ch == '\t' {
                render.push(' ');
                idx += 1;
                while idx % TAB_STOP != 0 {
                    render.push(' ');
                    idx += 1;
                }
            } else {
                render.push(ch);
                idx += 1;
            }
        }
        Row { chars, render }
    }

    fn cx_to_rx(&self, cx: usize) -> usize {
        let mut rx = 0;
        for ch in self.chars.chars().take(cx) {
            if ch == '\t' {
                rx += (TAB_STOP - 1) - (rx % TAB_STOP);
            }
            rx += 1;
        }
        rx
    }
}

fn read_non_blocking<R: Read>(r: &mut R, buf: &mut [u8]) -> usize {
    r.read(buf)
        .or_else(|e| if e.kind() == ErrorKind::WouldBlock {
                     Ok(0)
                 } else {
                     Err(e)
                 })
        .expect("read_non_blocking")
}

fn byte_slice(s: &str, offset: usize, max_len: usize) -> &[u8] {
    if s.len() > max_len + offset {
        s[offset..(max_len + offset)].as_bytes()
    } else if s.len() > offset {
        s[offset..].as_bytes()
    } else {
        s[0..0].as_bytes()
    }
}

fn editor_read_key(stdin: &mut Stdin) -> Key {
    let mut buf = [0; 1];
    loop {
        let n = read_non_blocking(stdin, &mut buf);
        if n == 1 {
            if buf[0] == b'\x1b' {
                let mut seq = [0; 2];
                let n = read_non_blocking(stdin, &mut seq);
                if n == 2 && seq[0] == b'[' {
                    if seq[1] >= b'0' && seq[1] <= b'9' {
                        let mut last = [0; 1];
                        let n = read_non_blocking(stdin, &mut last);
                        if n == 1 && last[0] == b'~' {
                            return match seq[1] {
                                       b'1' | b'7' => Key::Home,
                                       b'3' => Key::Delete,
                                       b'4' | b'8' => Key::End,
                                       b'5' => Key::PageUp,
                                       b'6' => Key::PageDown,
                                       _ => Key::Character(b'\x1b'),
                                   };
                        }
                    } else {
                        return match seq[1] {
                                   b'A' => Key::ArrowUp,
                                   b'B' => Key::ArrowDown,
                                   b'C' => Key::ArrowRight,
                                   b'D' => Key::ArrowLeft,
                                   b'H' => Key::Home,
                                   b'F' => Key::End,
                                   _ => Key::Character(b'\x1b'),
                               };
                    }
                } else if n == 2 && seq[0] == b'O' {
                    return match seq[1] {
                               b'H' => Key::Home,
                               b'F' => Key::End,
                               _ => Key::Character(b'\x1b'),
                           };
                }
                return Key::Character(b'\x1b');
            } else {
                return Key::Character(buf[0]);
            }
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize {
        ws_col: 0,
        ws_row: 0,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"));
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor {
               _mode: mode,
               cx: 0,
               cy: 0,
               rx: 0,
               rowoff: 0,
               coloff: 0,
               screenrows: (rows - 1) as usize,
               screencols: cols as usize,
               rows: None,
               stdin: stdin,
               stdout: stdout,
               filename: None,
           })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<()> {
        self.stdout.write_all(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn scroll(&mut self) {
        self.rx = self.cx;
        if self.cy < self.numrows() {
            self.rx = self.rows.as_ref().unwrap()[self.cy].cx_to_rx(self.cx);
        }

        if self.cy < self.rowoff {
            self.rowoff = self.cy;
        }
        if self.cy >= self.rowoff + self.screenrows {
            self.rowoff = self.cy - self.screenrows + 1;
        }
        if self.rx < self.coloff {
            self.coloff = self.rx;
        }
        if self.rx >= self.coloff + self.screencols {
            self.coloff = (1 + self.rx) - self.screencols;
        }
    }

    fn numrows(&self) -> usize {
        self.rows.as_ref().map_or(0, |v| v.len())
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        let numrows = self.numrows();
        for y in 0..(self.screenrows) {
            let filerow = y + self.rowoff;
            if filerow >= numrows {
                if self.rows.is_none() && y == self.screenrows / 3 {
                    let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                    msg.truncate(self.screencols);
                    let padding = (self.screencols - msg.len()) / 2;
                    if padding > 0 {
                        self.write(b"~")?;
                        for _ in 1..padding {
                            self.write(b" ")?;
                        }
                    }
                    self.write(msg.as_bytes())?;
                } else {
                    self.write(b"~")?;
                }
            } else {
                let rows = self.rows.as_ref().unwrap();
                self.stdout
                    .write_all(byte_slice(&rows[filerow].render, self.coloff, self.screencols))?;
            }

            self.write(b"\x1b[K")?;
            self.write(b"\r\n")?;
        }
        Ok(())
    }

    fn draw_status_bar(&mut self) -> io::Result<()> {
        self.write(b"\x1b[7m")?;
        let status;
        {
            let name = self.filename
                .as_ref()
                .map_or("[No name]", |s| s.as_str());
            let mut content = format!("{:.20} - {} lines", name, self.numrows());
            content.truncate(self.screencols);
            status = content;
        }
        let rstatus = format!("{}/{}", self.cy + 1, self.numrows());

        self.write(status.as_bytes())?;
        let mut len = status.len();
        while len < self.screencols {
            if self.screencols - len == rstatus.len() {
                self.write(rstatus.as_bytes())?;
                break;
            } else {
                self.write(b" ")?;
                len += 1;
            }
        }
        self.write(b"\x1b[m")
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.scroll();

        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;
        self.draw_status_bar()?;

        let move_cursor = format!("\x1b[{};{}H",
                                  (self.cy - self.rowoff) + 1,
                                  (self.rx - self.coloff) + 1)
                .into_bytes();
        self.write(&move_cursor)?;

        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn rowlen(&self, index: usize) -> usize {
        let row = self.rows.as_ref().and_then(|r| r.get(index));
        row.map_or(0, |r| r.chars.len())
    }

    fn move_cursor(&mut self, k: Key) {
        match k {
            Key::ArrowUp => {
                if self.cy > 0 {
                    self.cy -= 1;
                }
            }
            Key::ArrowDown => {
                if self.cy < self.numrows() {
                    self.cy += 1;
                }
            }
            Key::ArrowLeft => {
                if self.cx > 0 {
                    self.cx -= 1;
                } else if self.cy > 0 {
                    self.cy -= 1;
                    self.cx = self.rowlen(self.cy);
                }
            }
            Key::ArrowRight => {
                let row = self.rows.as_ref().and_then(|r| r.get(self.cy));
                let rowlen = row.map_or(0, |r| r.chars.len());
                if self.cx < rowlen {
                    self.cx += 1;
                } else if row.is_some() && self.cx == rowlen {
                    self.cx = 0;
                    self.cy += 1;
                }
            }
            _ => (),
        }

        let rowlen = self.rowlen(self.cy);
        if self.cx > rowlen {
            self.cx = rowlen;
        }
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            Key::Character(k) if k == ctrl_key!(b'q') => return false,
            Key::Home => {
                self.cx = 0;
            }
            Key::End => {
                self.cx = self.rowlen(self.cy);
            }
            Key::PageUp | Key::PageDown => {
                let key = if c == Key::PageUp {
                    self.cy = self.rowoff;
                    Key::ArrowUp
                } else {
                    self.cy = cmp::min(self.rowoff + self.screenrows - 1, self.numrows());
                    Key::ArrowDown
                };
                for _ in 0..(self.screenrows) {
                    self.move_cursor(key);
                }
            }
            Key::ArrowUp | Key::ArrowDown | Key::ArrowLeft | Key::ArrowRight => {
                self.move_cursor(c);
            }
            _ => (),
        };
        true
    }

    fn open(&mut self, filename: &str) -> io::Result<()> {
        self.filename = Some(filename.to_owned());
        let f = File::open(filename)?;
        let file = BufReader::new(&f);
        let results: io::Result<Vec<Row>> = file.lines().map(|r| r.map(Row::new)).collect();
        self.rows = Some(results?);
        Ok(())
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write_all(b"\x1b[2J").unwrap();
        self.stdout.write_all(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();
    if let Some(filename) = env::args().nth(1) {
        editor.open(&filename).unwrap();
    }

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate requests;
extern crate json;

mod crates;

use clap::{App, SubCommand, Arg, AppSettings, ArgMatches};

const CARGO: &'static str = "cargo";

#[derive(Debug, PartialEq)]
enum Flag {
    Repository,
    Documentation,
    Downloads,
    Homepage,
    Default,
}

#[derive(Debug)]
struct Report {
    flags: Vec<Flag>,
    verbose: bool,
    json: bool,
}

impl Report {
    pub fn new(info: &ArgMatches) -> Self {

        let mut flags: Vec<Flag> = vec![];
        if info.is_present("repository") {
            flags.push(Flag::Repository);
        }
        if info.is_present("documentation") {
            flags.push(Flag::Documentation);
        }
        if info.is_present("downloads") {
            flags.push(Flag::Downloads);
        }
        if info.is_present("homepage") {
            flags.push(Flag::Homepage);
        }

        if flags.is_empty() {
            flags.push(Flag::Default);
        }

        Report {
            flags: flags,
            verbose: info.is_present("verbose"),
            json: info.is_present("json"),
        }
    }

    pub fn report(&self, name: &str) {
        let response = query(name);
        if self.json {
            self.report_json(&response);
        } else {
            if let Some(krate) = get_crate(&response) {
                self.report_crate(&krate);
            }
        }
    }

    pub fn report_json(&self, response: &requests::Response) {
        if self.verbose {
            if let Ok(json) = response.json() {
                println!("{:?}", json);
            }
        } else {
            if let Some(json) = response.text() {
                println!("{}", json);
            }
        }
    }

    pub fn report_crate(&self, krate: &crates::Crate) {
        for flag in &self.flags {
            match *flag {
                Flag::Repository => krate.print_repository(self.verbose),
                Flag::Documentation => krate.print_documentation(self.verbose),
                Flag::Downloads => krate.print_downloads(self.verbose),
                Flag::Homepage => krate.print_homepage(self.verbose),
                Flag::Default => reportv(krate, self.verbose),
            }
        }
    }
}

fn reportv(krate: &crates::Crate, verbose: bool) {
    if verbose {
        println!("{:#}", krate);
    } else {
        println!("{}", krate);
    }
}

fn query(krate: &str) -> requests::Response {
    requests::get(&format!("http://crates.io/api/v1/crates/{}", krate)).unwrap()
    // println!("{:?}", response.json().unwrap());
    // response.from_json::<crates::Reply>()
}

fn get_crate(response: &requests::Response) -> Option<crates::Crate> {
    response.json().ok().map(|k| crates::Crate::new(k["crate"].clone()))
}

// use std::fmt;
// fn debug<T>(item: &T)
//     where T: fmt::Debug
// {
//     println!("{:#?}", item);
// }

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .author(crate_authors!())
        .version(crate_version!())
        .about("Query crates.io registry for crates details")
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("info")
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("documentation")
                .short("d")
                .long("documentation")
                .help("Report documentation URL"))
            .arg(Arg::with_name("downloads")
                .short("D")
                .long("downloads")
                .help("Report number of crate downloads"))
            .arg(Arg::with_name("homepage")
                .short("H")
                .long("homepage")
                .help("Report home page URL"))
            .arg(Arg::with_name("repository")
                .short("r")
                .long("repository")
                .help("Report crate repository URL"))
            .arg(Arg::with_name("json")
                .short("j")
                .long("json")
                .help("Report raw JSON data from crates.io")
                .conflicts_with_all(&["documentation", "downloads", "homepage", "repository"]))
            .arg(Arg::with_name("verbose")
                .short("v")
                .long("verbose")
                .help("Report more details"))
            .arg_from_usage("<crate>... 'crate to query'"))
        .get_matches();

    if let Some(info) = matches.subcommand_matches("info") {
        if let Some(crates) = info.values_of("crate") {
            let rep = Report::new(info);
            for krate in crates {
                // debug(&krate);
                rep.report(krate);
            }
        }
    }
}
<commit_msg>Translate json back to readable text<commit_after>#[macro_use]
extern crate clap;
extern crate requests;
extern crate json;

mod crates;

use clap::{App, SubCommand, Arg, AppSettings, ArgMatches};

const CARGO: &'static str = "cargo";

#[derive(Debug, PartialEq)]
enum Flag {
    Repository,
    Documentation,
    Downloads,
    Homepage,
    Default,
}

#[derive(Debug)]
struct Report {
    flags: Vec<Flag>,
    verbose: bool,
    json: bool,
}

impl Report {
    pub fn new(info: &ArgMatches) -> Self {

        let mut flags: Vec<Flag> = vec![];
        if info.is_present("repository") {
            flags.push(Flag::Repository);
        }
        if info.is_present("documentation") {
            flags.push(Flag::Documentation);
        }
        if info.is_present("downloads") {
            flags.push(Flag::Downloads);
        }
        if info.is_present("homepage") {
            flags.push(Flag::Homepage);
        }

        if flags.is_empty() {
            flags.push(Flag::Default);
        }

        Report {
            flags: flags,
            verbose: info.is_present("verbose"),
            json: info.is_present("json"),
        }
    }

    pub fn report(&self, name: &str) {
        let response = query(name);
        if self.json {
            self.report_json(&response);
        } else {
            if let Some(krate) = get_crate(&response) {
                self.report_crate(&krate);
            }
        }
    }

    pub fn report_json(&self, response: &requests::Response) {
        if self.verbose {
            if let Ok(json) = response.json() {
                println!("{}", json::stringify(json));
            }
        } else {
            if let Some(json) = response.text() {
                println!("{}", json);
            }
        }
    }

    pub fn report_crate(&self, krate: &crates::Crate) {
        for flag in &self.flags {
            match *flag {
                Flag::Repository => krate.print_repository(self.verbose),
                Flag::Documentation => krate.print_documentation(self.verbose),
                Flag::Downloads => krate.print_downloads(self.verbose),
                Flag::Homepage => krate.print_homepage(self.verbose),
                Flag::Default => reportv(krate, self.verbose),
            }
        }
    }
}

fn reportv(krate: &crates::Crate, verbose: bool) {
    if verbose {
        println!("{:#}", krate);
    } else {
        println!("{}", krate);
    }
}

fn query(krate: &str) -> requests::Response {
    requests::get(&format!("http://crates.io/api/v1/crates/{}", krate)).unwrap()
    // println!("{:?}", response.json().unwrap());
    // response.from_json::<crates::Reply>()
}

fn get_crate(response: &requests::Response) -> Option<crates::Crate> {
    response.json().ok().map(|k| crates::Crate::new(k["crate"].clone()))
}

// use std::fmt;
// fn debug<T>(item: &T)
//     where T: fmt::Debug
// {
//     println!("{:#?}", item);
// }

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .author(crate_authors!())
        .version(crate_version!())
        .about("Query crates.io registry for crates details")
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("info")
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("documentation")
                .short("d")
                .long("documentation")
                .help("Report documentation URL"))
            .arg(Arg::with_name("downloads")
                .short("D")
                .long("downloads")
                .help("Report number of crate downloads"))
            .arg(Arg::with_name("homepage")
                .short("H")
                .long("homepage")
                .help("Report home page URL"))
            .arg(Arg::with_name("repository")
                .short("r")
                .long("repository")
                .help("Report crate repository URL"))
            .arg(Arg::with_name("json")
                .short("j")
                .long("json")
                .help("Report raw JSON data from crates.io")
                .conflicts_with_all(&["documentation", "downloads", "homepage", "repository"]))
            .arg(Arg::with_name("verbose")
                .short("v")
                .long("verbose")
                .help("Report more details"))
            .arg_from_usage("<crate>... 'crate to query'"))
        .get_matches();

    if let Some(info) = matches.subcommand_matches("info") {
        if let Some(crates) = info.values_of("crate") {
            let rep = Report::new(info);
            for krate in crates {
                // debug(&krate);
                rep.report(krate);
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate gitjournal;

use std::process::exit;
use clap::App;
use gitjournal::GitJournal;

fn print(string: &str) {
    println!("[Git-Journal] {}", string)
}

fn main() {
    // Load the CLI parameters from the yaml file
    let yaml = load_yaml!("cli.yaml");
    let matches = App::from_yaml(yaml).get_matches();
    let path = matches.value_of("path").expect("Could not parse 'path' parameter");

    if matches.is_present("verify") {
        // Verify a commit message and panic! if verification failed.
        match GitJournal::verify(matches.value_of("verify").expect("Could not parse 'verify' value.")) {
            Ok(()) => print("Commit message valid."),
            Err(error) => {
                print(&format!("Commit message invalid: {}", error));
                exit(1);
            }
        }
    } else if matches.is_present("prepare") {
        // Prepare a commit message before editing by the user
        match GitJournal::prepare(matches.value_of("prepare").expect("Could not parse 'prepare' value.")) {
            Ok(()) => print("Commit message prepared."),
            Err(error) => {
                print(&format!("Commit message preparation failed: {}", error));
                exit(1);
            }
        }
    } else {
        // Create the journal
        let mut journal = GitJournal::new(path).expect("Could not initialize journal");

        if matches.is_present("setup") {
            journal.setup().expect("Setup error");
        } else {
            // Get all values of the given CLI parameters
            let revision_range = matches.value_of("revision_range")
                .expect("Could not parse 'revision range' parameter");
            let tag_skip_pattern = matches.value_of("tag_skip_pattern")
                .expect("Could not parse 'skip pattern' parameter");
            let tags_count = matches.value_of("tags_count").expect("Could not parse 'tags count' parameter");

            let max_tags = tags_count.parse::<u32>().expect("Could not parse tags count to integer");

            // Parse the log
            journal.parse_log(revision_range,
                           tag_skip_pattern,
                           &max_tags,
                           &matches.is_present("all"),
                           &matches.is_present("skip_unreleased"))
                .expect("Log parsing error");
            journal.print_log(matches.is_present("short")).expect("Could not print log.");
        }
    }
}
<commit_msg>Changed main error handling and printing<commit_after>#[macro_use]
extern crate clap;
extern crate gitjournal;
extern crate term;

use std::process::exit;
use clap::App;
use gitjournal::GitJournal;

fn print(string: &str, color: term::color::Color) -> Result<(), term::Error> {
    let mut t = try!(term::stderr().ok_or(term::Error::NotSupported));
    try!(t.fg(color));
    try!(writeln!(t, "[git-journal] {}", string));
    try!(t.reset());
    Ok(())
}

fn print_ok(string: &str) -> Result<(), term::Error> {
    try!(print(string, term::color::GREEN));
    Ok(())
}

fn print_err(string: &str) -> Result<(), term::Error> {
    try!(print(string, term::color::RED));
    exit(1);
}

static TERM_ERR: &'static str = "Could not write to terminal";
static PATH_ERR: &'static str = "Could not parse 'path' parameter";
static VERI_ERR: &'static str = "Could not parse 'verify' value";
static TAGS_ERR: &'static str = "Could not parse 'tags count' parameter";
static CONV_ERR: &'static str = "Could not parse 'tags count' to integer";
static REVR_ERR: &'static str = "Could not parse 'revision range' parameter";
static SKIP_ERR: &'static str = "Could not parse 'skip pattern' parameter";
static PREP_ERR: &'static str = "Could not parse 'prepare' parameter";
static JOUR_ERR: &'static str = "Could not initialize git-journal";

fn main() {
    // Load the CLI parameters from the yaml file
    let yaml = load_yaml!("cli.yaml");
    let matches = App::from_yaml(yaml).get_matches();
    let path = matches.value_of("path").expect(PATH_ERR);

    if matches.is_present("verify") {
        // Verify a commit message and panic! if verification failed.
        match GitJournal::verify(matches.value_of("verify").expect(VERI_ERR)) {
            Ok(()) => print_ok("Commit message valid.").expect(TERM_ERR),
            Err(error) => print_err(&format!("Commit message invalid: {}", error)).expect(TERM_ERR),
        }
    } else if matches.is_present("prepare") {
        // Prepare a commit message before editing by the user
        match GitJournal::prepare(matches.value_of("prepare").expect(PREP_ERR)) {
            Ok(()) => print_ok("Commit message prepared.").expect(TERM_ERR),
            Err(error) => print_err(&format!("Commit message preparation failed: {}", error)).expect(TERM_ERR),
        }
    } else {
        // Create the journal
        let mut journal = GitJournal::new(path).expect(JOUR_ERR);

        if matches.is_present("setup") {
            journal.setup().expect("Setup error");
        } else {
            // Get all values of the given CLI parameters
            let revision_range = matches.value_of("revision_range").expect(REVR_ERR);
            let tag_skip_pattern = matches.value_of("tag_skip_pattern").expect(SKIP_ERR);
            let tags_count = matches.value_of("tags_count").expect(TAGS_ERR);
            let max_tags = tags_count.parse::<u32>().expect(CONV_ERR);

            // Parse the log
            journal.parse_log(revision_range,
                           tag_skip_pattern,
                           &max_tags,
                           &matches.is_present("all"),
                           &matches.is_present("skip_unreleased"))
                .expect("Log parsing error");
            journal.print_log(matches.is_present("short")).expect("Could not print log.");
        }
    }
}
<|endoftext|>


<commit_before>#![deny(
    missing_debug_implementations,
    trivial_numeric_casts,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

extern crate clap;
extern crate notify;
extern crate hyper;
extern crate futures;
extern crate tempdir;
extern crate reqwest;
extern crate pbr;
extern crate libflate;
extern crate tar;
extern crate sha1;
extern crate sha2;
extern crate digest;
extern crate toml;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate handlebars;
extern crate unicode_categories;
extern crate ordermap;
extern crate websocket;
extern crate regex;
extern crate walkdir;
extern crate base_x;

extern crate parity_wasm;
#[macro_use]
extern crate log;
extern crate rustc_demangle;
extern crate env_logger;

extern crate cargo_metadata;
extern crate ansi_term;

extern crate semver;
extern crate memmap;

use std::process::exit;
use std::env;

use clap::{
    Arg,
    App,
    AppSettings,
    SubCommand
};

#[allow(dead_code)]
mod app_dirs;

mod cargo_shim;

#[macro_use]
mod utils;
mod http_utils;
mod config;
mod package;
mod build;
mod deployment;
mod error;
mod wasm;
mod wasm_gc;
mod wasm_inline_js;
mod wasm_export_main;
mod wasm_export_table;
mod wasm_hook_grow;
mod wasm_runtime;
mod wasm_context;
mod wasm_intrinsics;
mod wasm_js_export;
mod emscripten;
mod test_chromium;
mod chrome_devtools;
mod cmd_build;
mod cmd_start;
mod cmd_test;
mod cmd_deploy;
mod cmd_prepare_emscripten;

fn add_shared_build_params< 'a, 'b >( app: App< 'a, 'b > ) -> App< 'a, 'b > {
    return app
        .arg(
            Arg::with_name( "package" )
                .short( "p" )
                .long( "package" )
                .help( "Package to build" )
                .value_name( "NAME" )
                .takes_value( true )
        )
        .arg(
            Arg::with_name( "features" )
                .long( "features" )
                .help( "Space-separated list of features to also build" )
                .value_name( "FEATURES" )
                .takes_value( true )
        )
        .arg(
            Arg::with_name( "all-features" )
                .long( "all-features" )
                .help( "Build all available features" )
                // Technically Cargo doesn't treat it as conflicting,
                // but it seems less confusing to *not* allow these together.
                .conflicts_with_all( &[ "features", "no-default-features" ] )
        )
        .arg(
            Arg::with_name( "no-default-features" )
                .long( "no-default-features" )
                .help( "Do not build the `default` feature" )
        )
        .arg(
            Arg::with_name( "use-system-emscripten" )
                .long( "use-system-emscripten" )
                .help( "Won't try to download Emscripten; will always use the system one" )
        )
        .arg(
            Arg::with_name( "release" )
                .long( "release" )
                .help( "Build artifacts in release mode, with optimizations" )
        )
        .arg(
            Arg::with_name( "target" )
                .long( "target" )
                .takes_value( true )
                .value_name( "TRIPLE" )
                .help( "Build for the target [default: asmjs-unknown-emscripten]" )
                .possible_values( &[ "asmjs-unknown-emscripten", "wasm32-unknown-emscripten", "wasm32-unknown-unknown" ] )
                .conflicts_with_all( &["target-asmjs-emscripten", "target-webasm-emscripten", "target-webasm"] )
        )
        .arg(
            Arg::with_name( "verbose" )
                .short( "v" )
                .long( "verbose" )
                .help( "Use verbose output" )
        )
        // These three are legacy options kept for compatibility.
        .arg(
            Arg::with_name( "target-asmjs-emscripten" )
                .long( "target-asmjs-emscripten" )
                .help( "Generate asmjs through Emscripten (default)" )
                .overrides_with_all( &["target-webasm-emscripten", "target-webasm"] )
                .hidden( true )
        )
        .arg(
            Arg::with_name( "target-webasm-emscripten" )
                .long( "target-webasm-emscripten" )
                .help( "Generate webasm through Emscripten" )
                .overrides_with_all( &["target-asmjs-emscripten", "target-webasm"] )
                .hidden( true )
        )
        .arg(
            Arg::with_name( "target-webasm" )
                .long( "target-webasm" )
                .help( "Generates webasm through Rust's native backend (HIGHLY EXPERIMENTAL!)" )
                .overrides_with_all( &["target-asmjs-emscripten", "target-webasm-emscripten"] )
                .hidden( true )
        );
}

fn main() {
    if let Ok( value ) = env::var( "CARGO_WEB_LOG" ) {
        let mut builder = env_logger::Builder::new();
        builder.parse( &value );
        builder.init();
    }

    let args = {
        // To allow running both as 'cargo-web' and as 'cargo web'.
        let mut args = env::args();
        let mut filtered_args = Vec::new();
        filtered_args.push( args.next().unwrap() );

        match args.next() {
            None => {},
            #[cfg(any(unix))]
            Some( ref arg ) if filtered_args[ 0 ].ends_with( "cargo-web" ) && arg == "web" => {},
            #[cfg(any(windows))]
            Some( ref arg ) if filtered_args[ 0 ].ends_with( "cargo-web.exe" ) && arg == "web" => {},
            Some( arg ) => filtered_args.push( arg )
        }

        filtered_args.extend( args );
        filtered_args
    };

    let mut build_subcommand =
        SubCommand::with_name( "build" )
            .about( "Compile a local package and all of its dependencies" )
            .arg(
                Arg::with_name( "lib" )
                    .long( "lib" )
                    .help( "Build only this package's library" )
            )
            .arg(
                Arg::with_name( "bin" )
                    .long( "bin" )
                    .help( "Build only the specified binary" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "example" )
                    .long( "example" )
                    .help( "Build only the specified example" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "test" )
                    .long( "test" )
                    .help( "Build only the specified test target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "bench" )
                    .long( "bench" )
                    .help( "Build only the specified benchmark target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "message-format" )
                    .long( "message-format" )
                    .help( "Selects the stdout output format" )
                    .value_name( "FMT" )
                    .takes_value( true )
                    .default_value( "human" )
                    .possible_values( &[
                        "human",
                        "json"
                    ])
            )
            .arg(
                Arg::with_name( "runtime" )
                    .long( "runtime" )
                    .takes_value( true )
                    .value_name( "RUNTIME" )
                    .help( "Selects the type of JavaScript runtime which will be generated; valid only for the `wasm32-unknown-unknown` target" )
                    .possible_values( &["standalone", "experimental-only-loader"] )
                    .default_value_if(
                        "target", Some( "wasm32-unknown-unknown" ),
                        "standalone"
                    )
                    .hidden( true ) // This is experimental for now.
            );

    let mut test_subcommand =
        SubCommand::with_name( "test" )
            .about( "Compiles and runs tests" )
            .arg(
                Arg::with_name( "no-run" )
                    .long( "no-run" )
                    .help( "Compile, but don't run tests" )
            )
            .arg(
                Arg::with_name( "nodejs" )
                    .long( "nodejs" )
                    .help( "Uses Node.js to run the tests" )
            )
            .arg(
                Arg::with_name( "passthrough" )
                    .help( "-- followed by anything will pass the arguments to the test runner")
                    .multiple( true )
                    .takes_value( true )
                    .last( true )
            );

    let mut start_subcommand =
        SubCommand::with_name( "start" )
            .about( "Runs an embedded web server serving the built project" )
            .arg(
                Arg::with_name( "bin" )
                    .long( "bin" )
                    .help( "Build only the specified binary" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "example" )
                    .long( "example" )
                    .help( "Serves the specified example" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "test" )
                    .long( "test" )
                    .help( "Build only the specified test target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "bench" )
                    .long( "bench" )
                    .help( "Build only the specified benchmark target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "host" )
                    .long( "host" )
                    .help( "Bind the server to this address, default `localhost`")
                    .value_name( "HOST" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "port" )
                    .long( "port" )
                    .help( "Bind the server to this port, default 8000" )
                    .value_name( "PORT" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "auto-reload" )
                    .long( "auto-reload" )
                    .help( "Will try to automatically reload the page on rebuild" )
            );

    let mut deploy_subcommand =
        SubCommand::with_name( "deploy" )
            .about( "Deploys your project so that its ready to be served statically" );

    let mut prepare_emscripten_subcommand =
        SubCommand::with_name( "prepare-emscripten" )
            .about( "Fetches and installs prebuilt Emscripten packages" );

    build_subcommand = add_shared_build_params( build_subcommand );
    test_subcommand = add_shared_build_params( test_subcommand );
    start_subcommand = add_shared_build_params( start_subcommand );
    deploy_subcommand = add_shared_build_params( deploy_subcommand );

    let matches = App::new( "cargo-web" )
        .version( env!( "CARGO_PKG_VERSION" ) )
        .setting( AppSettings::SubcommandRequiredElseHelp )
        .setting( AppSettings::VersionlessSubcommands )
        .subcommand( build_subcommand )
        .subcommand( test_subcommand )
        .subcommand( start_subcommand )
        .subcommand( deploy_subcommand )
        .subcommand( prepare_emscripten_subcommand )
        .get_matches_from( args );

    let result = if let Some( matches ) = matches.subcommand_matches( "build" ) {
        cmd_build::command_build( matches )
    } else if let Some( matches ) = matches.subcommand_matches( "test" ) {
        cmd_test::command_test( matches )
    } else if let Some( matches ) = matches.subcommand_matches( "start" ) {
        cmd_start::command_start( matches )
    } else if let Some( matches ) = matches.subcommand_matches( "deploy" ) {
        cmd_deploy::command_deploy( matches )
    } else if let Some( matches ) = matches.subcommand_matches( "prepare-emscripten" ) {
        cmd_prepare_emscripten::command_prepare_emscripten()
    } else {
        return;
    };

    match result {
        Ok( _ ) => {},
        Err( error ) => {
            eprintln!( "error: {}", error );
            exit( 101 );
        }
    }
}
<commit_msg>Clean some warnings<commit_after>#![deny(
    missing_debug_implementations,
    trivial_numeric_casts,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

extern crate clap;
extern crate notify;
extern crate hyper;
extern crate futures;
extern crate tempdir;
extern crate reqwest;
extern crate pbr;
extern crate libflate;
extern crate tar;
extern crate sha1;
extern crate sha2;
extern crate digest;
extern crate toml;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate handlebars;
extern crate unicode_categories;
extern crate ordermap;
extern crate websocket;
extern crate regex;
extern crate walkdir;
extern crate base_x;

extern crate parity_wasm;
#[macro_use]
extern crate log;
extern crate rustc_demangle;
extern crate env_logger;

extern crate cargo_metadata;
extern crate ansi_term;

extern crate semver;
extern crate memmap;

use std::process::exit;
use std::env;

use clap::{
    Arg,
    App,
    AppSettings,
    SubCommand
};

#[allow(dead_code)]
mod app_dirs;

mod cargo_shim;

#[macro_use]
mod utils;
mod http_utils;
mod config;
mod package;
mod build;
mod deployment;
mod error;
mod wasm;
mod wasm_gc;
mod wasm_inline_js;
mod wasm_export_main;
mod wasm_export_table;
mod wasm_hook_grow;
mod wasm_runtime;
mod wasm_context;
mod wasm_intrinsics;
mod wasm_js_export;
mod emscripten;
mod test_chromium;
mod chrome_devtools;
mod cmd_build;
mod cmd_start;
mod cmd_test;
mod cmd_deploy;
mod cmd_prepare_emscripten;

fn add_shared_build_params< 'a, 'b >( app: App< 'a, 'b > ) -> App< 'a, 'b > {
    return app
        .arg(
            Arg::with_name( "package" )
                .short( "p" )
                .long( "package" )
                .help( "Package to build" )
                .value_name( "NAME" )
                .takes_value( true )
        )
        .arg(
            Arg::with_name( "features" )
                .long( "features" )
                .help( "Space-separated list of features to also build" )
                .value_name( "FEATURES" )
                .takes_value( true )
        )
        .arg(
            Arg::with_name( "all-features" )
                .long( "all-features" )
                .help( "Build all available features" )
                // Technically Cargo doesn't treat it as conflicting,
                // but it seems less confusing to *not* allow these together.
                .conflicts_with_all( &[ "features", "no-default-features" ] )
        )
        .arg(
            Arg::with_name( "no-default-features" )
                .long( "no-default-features" )
                .help( "Do not build the `default` feature" )
        )
        .arg(
            Arg::with_name( "use-system-emscripten" )
                .long( "use-system-emscripten" )
                .help( "Won't try to download Emscripten; will always use the system one" )
        )
        .arg(
            Arg::with_name( "release" )
                .long( "release" )
                .help( "Build artifacts in release mode, with optimizations" )
        )
        .arg(
            Arg::with_name( "target" )
                .long( "target" )
                .takes_value( true )
                .value_name( "TRIPLE" )
                .help( "Build for the target [default: asmjs-unknown-emscripten]" )
                .possible_values( &[ "asmjs-unknown-emscripten", "wasm32-unknown-emscripten", "wasm32-unknown-unknown" ] )
                .conflicts_with_all( &["target-asmjs-emscripten", "target-webasm-emscripten", "target-webasm"] )
        )
        .arg(
            Arg::with_name( "verbose" )
                .short( "v" )
                .long( "verbose" )
                .help( "Use verbose output" )
        )
        // These three are legacy options kept for compatibility.
        .arg(
            Arg::with_name( "target-asmjs-emscripten" )
                .long( "target-asmjs-emscripten" )
                .help( "Generate asmjs through Emscripten (default)" )
                .overrides_with_all( &["target-webasm-emscripten", "target-webasm"] )
                .hidden( true )
        )
        .arg(
            Arg::with_name( "target-webasm-emscripten" )
                .long( "target-webasm-emscripten" )
                .help( "Generate webasm through Emscripten" )
                .overrides_with_all( &["target-asmjs-emscripten", "target-webasm"] )
                .hidden( true )
        )
        .arg(
            Arg::with_name( "target-webasm" )
                .long( "target-webasm" )
                .help( "Generates webasm through Rust's native backend (HIGHLY EXPERIMENTAL!)" )
                .overrides_with_all( &["target-asmjs-emscripten", "target-webasm-emscripten"] )
                .hidden( true )
        );
}

fn main() {
    if let Ok( value ) = env::var( "CARGO_WEB_LOG" ) {
        let mut builder = env_logger::Builder::new();
        builder.parse( &value );
        builder.init();
    }

    let args = {
        // To allow running both as 'cargo-web' and as 'cargo web'.
        let mut args = env::args();
        let mut filtered_args = Vec::new();
        filtered_args.push( args.next().unwrap() );

        match args.next() {
            None => {},
            #[cfg(any(unix))]
            Some( ref arg ) if filtered_args[ 0 ].ends_with( "cargo-web" ) && arg == "web" => {},
            #[cfg(any(windows))]
            Some( ref arg ) if filtered_args[ 0 ].ends_with( "cargo-web.exe" ) && arg == "web" => {},
            Some( arg ) => filtered_args.push( arg )
        }

        filtered_args.extend( args );
        filtered_args
    };

    let mut build_subcommand =
        SubCommand::with_name( "build" )
            .about( "Compile a local package and all of its dependencies" )
            .arg(
                Arg::with_name( "lib" )
                    .long( "lib" )
                    .help( "Build only this package's library" )
            )
            .arg(
                Arg::with_name( "bin" )
                    .long( "bin" )
                    .help( "Build only the specified binary" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "example" )
                    .long( "example" )
                    .help( "Build only the specified example" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "test" )
                    .long( "test" )
                    .help( "Build only the specified test target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "bench" )
                    .long( "bench" )
                    .help( "Build only the specified benchmark target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "message-format" )
                    .long( "message-format" )
                    .help( "Selects the stdout output format" )
                    .value_name( "FMT" )
                    .takes_value( true )
                    .default_value( "human" )
                    .possible_values( &[
                        "human",
                        "json"
                    ])
            )
            .arg(
                Arg::with_name( "runtime" )
                    .long( "runtime" )
                    .takes_value( true )
                    .value_name( "RUNTIME" )
                    .help( "Selects the type of JavaScript runtime which will be generated; valid only for the `wasm32-unknown-unknown` target" )
                    .possible_values( &["standalone", "experimental-only-loader"] )
                    .default_value_if(
                        "target", Some( "wasm32-unknown-unknown" ),
                        "standalone"
                    )
                    .hidden( true ) // This is experimental for now.
            );

    let mut test_subcommand =
        SubCommand::with_name( "test" )
            .about( "Compiles and runs tests" )
            .arg(
                Arg::with_name( "no-run" )
                    .long( "no-run" )
                    .help( "Compile, but don't run tests" )
            )
            .arg(
                Arg::with_name( "nodejs" )
                    .long( "nodejs" )
                    .help( "Uses Node.js to run the tests" )
            )
            .arg(
                Arg::with_name( "passthrough" )
                    .help( "-- followed by anything will pass the arguments to the test runner")
                    .multiple( true )
                    .takes_value( true )
                    .last( true )
            );

    let mut start_subcommand =
        SubCommand::with_name( "start" )
            .about( "Runs an embedded web server serving the built project" )
            .arg(
                Arg::with_name( "bin" )
                    .long( "bin" )
                    .help( "Build only the specified binary" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "example" )
                    .long( "example" )
                    .help( "Serves the specified example" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "test" )
                    .long( "test" )
                    .help( "Build only the specified test target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "bench" )
                    .long( "bench" )
                    .help( "Build only the specified benchmark target" )
                    .value_name( "NAME" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "host" )
                    .long( "host" )
                    .help( "Bind the server to this address, default `localhost`")
                    .value_name( "HOST" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "port" )
                    .long( "port" )
                    .help( "Bind the server to this port, default 8000" )
                    .value_name( "PORT" )
                    .takes_value( true )
            )
            .arg(
                Arg::with_name( "auto-reload" )
                    .long( "auto-reload" )
                    .help( "Will try to automatically reload the page on rebuild" )
            );

    let mut deploy_subcommand =
        SubCommand::with_name( "deploy" )
            .about( "Deploys your project so that its ready to be served statically" );

    let prepare_emscripten_subcommand =
        SubCommand::with_name( "prepare-emscripten" )
            .about( "Fetches and installs prebuilt Emscripten packages" );

    build_subcommand = add_shared_build_params( build_subcommand );
    test_subcommand = add_shared_build_params( test_subcommand );
    start_subcommand = add_shared_build_params( start_subcommand );
    deploy_subcommand = add_shared_build_params( deploy_subcommand );

    let matches = App::new( "cargo-web" )
        .version( env!( "CARGO_PKG_VERSION" ) )
        .setting( AppSettings::SubcommandRequiredElseHelp )
        .setting( AppSettings::VersionlessSubcommands )
        .subcommand( build_subcommand )
        .subcommand( test_subcommand )
        .subcommand( start_subcommand )
        .subcommand( deploy_subcommand )
        .subcommand( prepare_emscripten_subcommand )
        .get_matches_from( args );

    let result = if let Some( matches ) = matches.subcommand_matches( "build" ) {
        cmd_build::command_build( matches )
    } else if let Some( matches ) = matches.subcommand_matches( "test" ) {
        cmd_test::command_test( matches )
    } else if let Some( matches ) = matches.subcommand_matches( "start" ) {
        cmd_start::command_start( matches )
    } else if let Some( matches ) = matches.subcommand_matches( "deploy" ) {
        cmd_deploy::command_deploy( matches )
    } else if let Some( _ ) = matches.subcommand_matches( "prepare-emscripten" ) {
        cmd_prepare_emscripten::command_prepare_emscripten()
    } else {
        return;
    };

    match result {
        Ok( _ ) => {},
        Err( error ) => {
            eprintln!( "error: {}", error );
            exit( 101 );
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt::Display;
use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Clone, Debug)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(PartialEq, Clone, Debug)]
enum Type<'a> {
    Number,
    Boolean,
    Unbound(FreeVariable),
    Lambda(&'a [Type<'a>], &'a Type<'a>),

}

type TaggedType<'a> = (Expression<'a>, Type<'a>);

type TypeEnv<'a> = HashMap<VariableName<'a>, Type<'a>>;

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type<'a> {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable")
        },

        Expression::Lambda(args, body) => Type::Unbound(inst.next()),

        Expression::Let(name, expr, body) => Type::Unbound(inst.next()),

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let mut type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            match w(callable, env, inst) {
                Type::Lambda(expected, _) if args.len() != expected.len() => {
                    panic!("wrong number of args given")
                },

                Type::Lambda(expected, body_type) => {
                    if expected != type_args.as_slice() {
                        panic!("bad types given")
                    }

                    body_type.clone()
                }

                _ => panic!("trying to call non-function")
            }
        }
    }
}

fn main() {
    // FIXME: this is so gnarly.
    // let incr = \x -> 123.0 in incr (123.0) :: Number
    let num = Expression::Number(123.0);
    let incr = Expression::Variable("incr");
    let args = ["x"];
    let args_ = [&num];
    let fn_call = Expression::FnCall(&incr, &args_);
    let lambda = Expression::Lambda(&args, &num);

    let expr = Expression::Let(
        "incr", &lambda,
        &fn_call);

    let mut inst = VariableGen {current: 0};
    let mut env = HashMap::new();

    println!("this is the thing: {:?}\n", w(&expr, &mut env, &mut inst))
}
<commit_msg>fill out more branches, sort of works for simple exprs<commit_after>use std::fmt::Display;
use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Clone, Debug)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(PartialEq, Clone, Debug)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TaggedType<'a> = (Expression<'a>, Type);

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            // TODO: args -> types. modify the environment
            let body_ty = w(body, env, inst);
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            match w(callable, env, inst) {
                Type::Lambda(expected, body_type) => {
                    if args.len() != expected.len() {
                        panic!("wrong number of args given, {} for {}", args.len(), expected.len())
                    } else if expected != type_args.as_slice() {
                        panic!("bad types given: {:?} for {:?}", type_args.as_slice(), expected)
                    }

                    *body_type.clone()
                }

                _ => panic!("trying to call non-function")
            }
        }
    }
}

fn main() {
    // FIXME: this is so gnarly.
    // let incr = \x -> 123.0 in incr (123.0) :: Number
    let num = Expression::Number(123.0);
    let incr = Expression::Variable("incr");
    let x = Expression::Variable("x");
    let args = ["x"];
    let args_ = [&num];
    let fn_call = Expression::FnCall(&incr, &args_);
    let lambda = Expression::Lambda(&args, &num);

    // let expr = Expression::Let(
    //     "incr", &lambda,
    //     &fn_call);

    let expr = Expression::Let("y", &num, &x);

    let mut inst = VariableGen {current: 0};
    let mut env = HashMap::new();

    println!("this is the thing: {:?}\n", w(&expr, &mut env, &mut inst))
}
<|endoftext|>