<commit_before>extern crate mio;
extern crate bytes;
extern crate log4rs;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;

use mio::tcp::*;
use mio::{Sender,EventLoop};
use mio::{TryRead,TryWrite};
use mio::util::Slab;
use std::thread;
use std::sync::mpsc::{self,channel};
use std::io::Cursor;
use clap::{Arg, App, SubCommand};

use bytes::{Buf, Take};

const SERVER: mio::Token = mio::Token(0);

#[derive(Debug)]
enum MiChatCommand {
    Set(String),
    Get(mio::Token),
}

#[derive(Debug)]
struct MiChatIoEvent (mio::Token, String);

struct MiChat {
    listener: TcpListener,
    connections: Slab<Connection>,
    commands: mpsc::Sender<MiChatCommand>
}

impl MiChat {
    fn new(listener: TcpListener, commands: mpsc::Sender<MiChatCommand>) -> MiChat {
        MiChat {
            listener: listener,
            connections: Slab::new_starting_at(mio::Token(1), 1024),
            commands: commands
        }
    }
}

struct Connection {
    socket: TcpStream,
    sock_status: mio::EventSet,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    failed: bool,
    write_buf: Vec<u8>,
    commands: mpsc::Sender<MiChatCommand>,
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token, commands: mpsc::Sender<MiChatCommand>) -> Connection {
        Connection {
            socket: socket,
            sock_status: mio::EventSet::none(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
            failed: false,
            commands: commands,
        }
    }
    fn ready(&mut self, event_loop: &mut mio::EventLoop<MiChat>, events: mio::EventSet) {
        self.sock_status.insert(events);
        info!("Connection::ready: {:?}; this time: {:?}; now: {:?}",
                self.socket.peer_addr(), events, self.sock_status);
        if self.sock_status.is_readable() {
            self.read(event_loop);
            self.sock_status.remove(mio::EventSet::readable());
        }

        self.process();

        if self.sock_status.is_writable() {
            self.write(event_loop);
            self.sock_status.remove(mio::EventSet::writable());
        }

        if !self.is_closed() {
            self.reregister(event_loop)
        }
    }

    fn read(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut abuf = Vec::new();
        match self.socket.try_read_buf(&mut abuf) {
            Ok(Some(0)) => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true
            },
            Ok(Some(n)) => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(abuf);
            },
            Ok(None) => {
                info!("{:?}: Noop!", self.socket.peer_addr());
            },
            Err(e) => {
                error!("got an error trying to read; err={:?}", e);
                self.failed =true;
            }
        }
    }

    fn notify(&mut self, event_loop: &mut mio::EventLoop<MiChat>, s: String) {
        self.write_buf.extend(s.bytes());
        self.write_buf.push('\n' as u8);
        self.write(event_loop);
        self.reregister(event_loop);
    }

    fn write(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        match self.socket.try_write(&mut self.write_buf) {
            Ok(Some(n)) => {
                info!("Wrote {} of {} in buffer", n, self.write_buf.len());
                self.write_buf = self.write_buf[n..].to_vec();
            },
            Ok(None) => {
                info!("Write unready");
            },
            Err(e) => {
                error!("got an error trying to write; err={:?}", e);
                self.failed = true;
            }
        }
    }

    fn reregister(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut flags = mio::EventSet::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::EventSet::writable());
        }

        event_loop.reregister(
                &self.socket,
                self.token,
                flags,
                mio::PollOpt::oneshot()).expect("EventLoop#reregister")

    }
    fn process(&mut self) {
        let mut prev = 0;
        for n in self.read_buf.iter().enumerate()
                .filter_map(|(i, e)| if *e == '\n' as u8 { Some(i) } else { None } ) {
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).into_owned();
            let cmd = if s.is_empty() { MiChatCommand::Get(self.token) } else { MiChatCommand::Set(s) };
            info!("Send! {:?}", cmd);
            self.commands.send(cmd).expect("Sender#send");
            prev = n;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!("Remainder: {}", String::from_utf8_lossy(&remainder));
        self.read_buf = remainder;
    }

    fn buffer_slice(buf: &mut Vec<u8>, slice: &[u8]) {
        buf.extend(slice.iter().cloned());
    }

    fn is_closed(&self) -> bool {
        self.failed || (self.read_eof && self.write_buf.is_empty())
    }
}

impl mio::Handler for MiChat {
    type Timeout = ();
    type Message = MiChatIoEvent;
    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        info!("{:?}: {:?}", token, events);
        match token {
            SERVER => {
                // Only receive readable events
                assert!(events.is_readable());

                info!("the listener socket is ready to accept a connection");
                match self.listener.accept() {
                    Ok(Some(socket)) => {
                        let commands = self.commands.clone();
                        let token = self.connections
                            .insert_with(|token| Connection::new(socket, token, commands))
                            .expect("token insert");

                        event_loop.register_opt(
                            &self.connections[token].socket,
                            token,
                            mio::EventSet::readable(),
                            mio::PollOpt::edge() | mio::PollOpt::oneshot())
                        .expect("event loop register");
                    }
                    Ok(None) => {
                        info!("the listener socket wasn't actually ready");
                    }
                    Err(e) => {
                        info!("listener.accept() errored: {}", e);
                        event_loop.shutdown();
                    }
                }
            }
            _ => {
                self.connections[token].ready(event_loop, events);
                if self.connections[token].is_closed() {
                    info!("Removing; {:?}", token);
                    self.connections.remove(token);
                }
            }
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: MiChatIoEvent) {
        info!("Notify: {:?}", msg);
        let MiChatIoEvent(token, s) = msg;
        if let Some(conn) = self.connections.get_mut(token) {
            conn.notify(event_loop, s);
        } else {
            warn!("No connection for token: {:?}", token);
        }
    }
}

#[derive(Debug)]
struct Model {
    value: String
}

impl Model {
    fn new() -> Model {
        Model { value: "".to_string() }
    }

    fn process_from(&mut self, rx: mpsc::Receiver<MiChatCommand>, replies: Sender<MiChatIoEvent>) {
        loop {
            let msg = rx.recv().expect("Model receive");
            info!("{:?}; got {:?}", self, msg);
            match msg {
                MiChatCommand::Set(s) => {
                    self.value = s;
                },
                MiChatCommand::Get(tok) => {
                    while let Err(e) = replies.send(MiChatIoEvent(tok, self.value.clone())) {
                        info!("Backoff on notify: {:?}", e);
                        thread::sleep_ms(10);
                    }
                }
            }
        }
    }
}

const LOG_FILE: &'static str = "log.toml";

fn main() {
    if let Err(e) = log4rs::init_file(LOG_FILE, Default::default()) {
        panic!("Could not init logger from file {}: {}", LOG_FILE, e);
    }
    let matches = App::new("michat")
        .arg(Arg::with_name("bind").short("l").takes_value(true).required(true))
        .get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::EventLoop::new().expect("Create event loop");
    event_loop.register(&listener, SERVER).expect("Register listener");

    info!("running michat listener at: {:?}", address);

    let (command_tx, command_rx) = channel();
    let replies = event_loop.channel();
    let t = thread::Builder::new().name("Model".to_owned()).spawn(move|| {
        let mut model = Model::new();
        model.process_from(command_rx, replies);
    }).expect("spawn model thread");

    let mut service = MiChat::new(listener, command_tx);
    event_loop.run(&mut service).expect("Run loop");
    t.join().expect("Model join");
}
<commit_msg>At least trivial chat works. ish.<commit_after>extern crate mio;
extern crate bytes;
extern crate log4rs;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;

use mio::tcp::*;
use mio::{Sender,EventLoop};
use mio::{TryRead,TryWrite};
use mio::util::Slab;
use std::thread;
use std::sync::mpsc::{self,channel};
use std::io::Cursor;
use std::collections::VecDeque;
use clap::{Arg, App, SubCommand};

use bytes::{Buf, Take};

const SERVER: mio::Token = mio::Token(0);

#[derive(Debug)]
enum MiChatCommand {
    Broadcast(String),
}

#[derive(Debug)]
struct MiChatIoEvent (mio::Token, String);

struct MiChat {
    listener: TcpListener,
    connections: Slab<Connection>,
    register: String,
}

impl MiChat {
    fn new(listener: TcpListener) -> MiChat {
        MiChat {
            listener: listener,
            connections: Slab::new_starting_at(mio::Token(1), 1024),
            register: String::new(),
        }
    }
    fn process_action(&mut self, msg: &MiChatCommand) {
        trace!("{:p}; got {:?}", self, msg);
        match msg {
            &MiChatCommand::Broadcast(ref s) => {
                for c in self.connections.iter_mut() {
                    c.enqueue(&s);
                }
            }
        }
    }
}

#[derive(Debug)]
struct Connection {
    socket: TcpStream,
    sock_status: mio::EventSet,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    failed: bool,
    write_buf: Vec<u8>,
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token) -> Connection {
        Connection {
            socket: socket,
            sock_status: mio::EventSet::none(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
            failed: false,
        }
    }
    // Event updates arrive here
    fn ready(&mut self, event_loop: &mut mio::EventLoop<MiChat>, events: mio::EventSet) {
        self.sock_status.insert(events);
        info!("Connection::ready: {:?}; this time: {:?}; now: {:?}",
                self.socket.peer_addr(), events, self.sock_status);
    }

    // actions are processed here on down.

    fn process_rules(&mut self, event_loop: &mut mio::EventLoop<MiChat>,
        to_parent: &mut VecDeque<MiChatCommand>) {
        if self.sock_status.is_readable() {
            self.read(event_loop);
            self.sock_status.remove(mio::EventSet::readable());
        }

        self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.write(event_loop);
            self.sock_status.remove(mio::EventSet::writable());
        }

        if !self.is_closed() {
            self.reregister(event_loop)
        }
    }

    fn process_buffer(&mut self, to_parent: &mut VecDeque<MiChatCommand>) {
        let mut prev = 0;
        info!("{:?}: Read buffer: {:?}", self.socket.peer_addr(), self.read_buf);
        for n in self.read_buf.iter().enumerate()
                .filter_map(|(i, e)| if *e == '\n' as u8 { Some(i) } else { None } ) {
            info!("{:?}: Pos: {:?}; chunk: {:?}", self.socket.peer_addr(), n, &self.read_buf[prev..n]);
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).to_string();
            let cmd = MiChatCommand::Broadcast(s);
            info!("Send! {:?}", cmd);
            to_parent.push_back(cmd);
            prev = n+1;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!("{:?}: read Remainder: {}", self.socket.peer_addr(), remainder.len());
        self.read_buf = remainder;
    }

    fn read(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut abuf = Vec::new();
        match self.socket.try_read_buf(&mut abuf) {
            Ok(Some(0)) => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true
            },
            Ok(Some(n)) => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(abuf);
            },
            Ok(None) => {
                info!("{:?}: Noop!", self.socket.peer_addr());
            },
            Err(e) => {
                error!("got an error trying to read; err={:?}", e);
                self.failed =true;
            }
        }
    }

    fn write(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        match self.socket.try_write(&mut self.write_buf) {
            Ok(Some(n)) => {
                info!("{:?}: Wrote {} of {} in buffer ({:?}...)", self.socket.peer_addr(), n,
                    self.write_buf.len(), &self.write_buf[0..2]);
                self.write_buf = self.write_buf[n..].to_vec();
                info!("{:?}: Now {:?}b", self.socket.peer_addr(), self.write_buf.len());
            },
            Ok(None) => {
                info!("Write unready");
            },
            Err(e) => {
                error!("got an error trying to write; err={:?}", e);
                self.failed = true;
            }
        }
    }

    fn reregister(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut flags = mio::EventSet::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::EventSet::writable());
        }
        info!("Registering {:?} with {:?}", self, flags);

        event_loop.reregister(
                &self.socket,
                self.token,
                flags,
                mio::PollOpt::oneshot()).expect("EventLoop#reregister")
    }

    fn buffer_slice(buf: &mut Vec<u8>, slice: &[u8]) {
        buf.extend(slice.iter().cloned());
    }

    fn is_closed(&self) -> bool {
        self.failed || (self.read_eof && self.write_buf.is_empty())
    }

    fn enqueue(&mut self, s: &str) {
        self.write_buf.extend(s.as_bytes());
        self.write_buf.push('\n' as u8);
    }
}

impl mio::Handler for MiChat {
    type Timeout = ();
    type Message = ();
    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        info!("{:?}: {:?}", token, events);
        let mut next = VecDeque::new();

        match token {
            SERVER => {
                // Only receive readable events
                assert!(events.is_readable());

                info!("the listener socket is ready to accept a connection");
                match self.listener.accept() {
                    Ok(Some(socket)) => {
                        let token = self.connections
                            .insert_with(|token| Connection::new(socket, token))
                            .expect("token insert");

                        event_loop.register_opt(
                            &self.connections[token].socket,
                            token,
                            mio::EventSet::readable(),
                            mio::PollOpt::edge() | mio::PollOpt::oneshot())
                        .expect("event loop register");
                    }
                    Ok(None) => {
                        info!("the listener socket wasn't actually ready");
                    }
                    Err(e) => {
                        info!("listener.accept() errored: {}", e);
                        event_loop.shutdown();
                    }
                }
            }
            _ => {
                self.connections[token].ready(event_loop, events);
                info!("TODO next on {:?}: {:?}", token, next);
                if self.connections[token].is_closed() {
                    info!("Removing; {:?}", token);
                    self.connections.remove(token);
                }
            }
        }

        loop {
            for conn in self.connections.iter_mut() {
                conn.process_rules(event_loop, &mut next);
            }
            if next.is_empty() { break; }

            for action in next.iter() {
                self.process_action(action);
            }
            next.clear();
        }
    }
}

#[derive(Debug)]
struct Model {
    value: String
}

impl Model {
    fn new() -> Model {
        Model { value: "".to_string() }
    }

    fn process_from(&mut self, rx: mpsc::Receiver<MiChatCommand>, replies: Sender<MiChatIoEvent>) {
        loop {
            let msg = rx.recv().expect("Model receive");

        }
    }
}

const LOG_FILE: &'static str = "log.toml";

fn main() {
    if let Err(e) = log4rs::init_file(LOG_FILE, Default::default()) {
        panic!("Could not init logger from file {}: {}", LOG_FILE, e);
    }
    let matches = App::new("michat")
        .arg(Arg::with_name("bind").short("l").takes_value(true).required(true))
        .get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::EventLoop::new().expect("Create event loop");
    event_loop.register(&listener, SERVER).expect("Register listener");

    info!("running michat listener at: {:?}", address);

    let mut service = MiChat::new(listener);
    event_loop.run(&mut service).expect("Run loop");
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

//! # SymbolAPI
//!
//! SymbolAPI is a microservice which accepts lists of debug symbol names + memory addresses, and
//! returns a list of symbolicated function names.
//!

/// The goal is to take an HTTP JSON request such as:
/// ```
/// {"stacks":[
///   [
///     [0,11723767],
///     [1, 65802]
///    ]
///  ],
///  "memoryMap":[
///    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
///    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
///  ],
///  "version":4
/// }
/// ```
/// The symbolapi service then downloads the corresponding symbol files (e.g. from S3), parses them,
/// and returns the function names for the corresponding addresses (in the "stacks" array)
/// and returns JSON such as:
/// ```
/// {"symbolicatedStacks": [
///   [
///     "XREMain::XRE_mainRun() (in xul.pdb)",
///     "KiUserCallbackDispatcher (in wntdll.pdb)"]
///   ],
///   "knownModules": [true, true]
/// }
/// ```

extern crate breakpad_symbols;
extern crate flate2;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::env;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use flate2::read::GzDecoder;
use hyper::Client;
use hyper::header::{ContentEncoding, Encoding};
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/// Incoming JSON request format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
#[derive(Debug)]
pub struct SymbolRequest {
    pub memoryMap: Vec<(String,String)>,
    // index, offset
    pub stacks: Vec<Vec<(i8,u64)>>,
    pub version: u8,
}

/// Outgoing JSON response format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
#[derive(Debug)]
pub struct SymbolResponse {
    pub symbolicatedStacks: Vec<Vec<String>>,
    pub knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let default_port = 5000;
    let port;
    match env::var("PORT") {
        Ok(val) => port = val,
        Err(_) => {
            println!("$PORT unset, using default {}", default_port);
            port = format!("{}", default_port);
        }
    }

    let address = &*format!("0.0.0.0:{}", port);

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/// Stacks come in as a JSON array, but really is a hash map of `<index, address>`
/// where `index` is the position of the `memoryMap` JSON result.
pub fn stacks_to_stack_map(decoded_stacks: Vec<Vec<(i8,u64)>>) -> HashMap<i8, Vec<u64>> {
    debug!("decoded_stacks: {:?}", decoded_stacks);

    let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();
    for stack in &decoded_stacks[0] {
        let (index, offset) = *stack;

        let mut offsets = vec!();
        if stack_map.contains_key(&index) {
            offsets = stack_map.get(&index).unwrap().clone();
        }
        offsets.push(offset);
        &stack_map.insert(index, offsets);
    }

    debug!("stack_map: {:?}", stack_map);
    stack_map
}

/// Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket via the
/// client function.
pub fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");
            debug!("symbol url: {:?}", symbol_url);
            let stack_map = stacks_to_stack_map(decoded.stacks);

            // FIXME limit the number of possible threads spawned by client()
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response: SymbolResponse = client(symbol_url, decoded.memoryMap, stack_map);
            let json_response = match json::encode(&symbol_response) {
                Ok(x) => x,
                Err(x) => panic!("cannot JSON encode {:?}, {:?}", symbol_response, x)
            };
            res.write_all(json_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/// Creates multiple client connections to fetch debug symbols from S3 bucket and symbolicate using
/// memory and stack maps, aggregates results and returns SymbolResult.
pub fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> SymbolResponse {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        symbol_path.push(get_config("symbols.path"));

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                let is_gzipped = match res.headers.get::<ContentEncoding>() {
                    Some(x) => x.contains(&Encoding::Gzip),
                    None => false,
                };

                let mut body = String::new();
                if is_gzipped {
                    let mut raw_body = vec!();
                    res.read_to_end(&mut raw_body).unwrap();
                    let mut d = match GzDecoder::new(&raw_body[..]) {
                        Ok(x) => x,
                        Err(x) => panic!("cannot gunzip {:?}, {:?}", raw_body, x)
                    };
                    d.read_to_string(&mut body).unwrap();
                } else {
                    res.read_to_string(&mut body).unwrap();
                }

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);

            let mut symbols = vec!();
            let mut known_module = true;
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &full_symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => {
                            symbols.push(format!("0x{:x}", address));
                            known_module = false;
                        },
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    // pass anything with index -1 through
    for stacks in stack_map.get(&-1) {
        for address in stacks {
            symbolicated_stacks.push(
                format!("0x{:x}", address)
            );
        }
    }

    for handle in handles {
        let (debug_file_name, symbols, known_module) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(known_module);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    result
}

/// Returns individual values from the configuration file.
fn get_config(value_name: &str) -> String {
    let mut f = match File::open("config/symbolapi.toml") {
        Ok(x) => x,
        Err(x) => panic!("cannot open config file config/symbolapi.toml: {}", x)
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    let value: toml::Value = match s.parse() {
        Ok(x) => x,
        Err(x) => panic!("cannot parse config file: {:?}", x)
    };

    let result = match value.lookup(value_name) {
        Some(x) => x,
        None => panic!("config value not found for {}", value_name)
    };

    result.as_str().unwrap().to_string()
}
<commit_msg>too many ands in the docs<commit_after>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

//! # SymbolAPI
//!
//! SymbolAPI is a microservice which accepts lists of debug symbol names + memory addresses, and
//! returns a list of symbolicated function names.
//!

/// The goal is to take an HTTP JSON request such as:
/// ```
/// {"stacks":[
///   [
///     [0,11723767],
///     [1, 65802]
///    ]
///  ],
///  "memoryMap":[
///    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
///    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
///  ],
///  "version":4
/// }
/// ```
/// The symbolapi service then downloads the corresponding symbol files (e.g. from S3), parses them,
/// extracts the function names for the corresponding addresses (in the "stacks" array)
/// and returns JSON such as:
/// ```
/// {"symbolicatedStacks": [
///   [
///     "XREMain::XRE_mainRun() (in xul.pdb)",
///     "KiUserCallbackDispatcher (in wntdll.pdb)"]
///   ],
///   "knownModules": [true, true]
/// }
/// ```

extern crate breakpad_symbols;
extern crate flate2;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::env;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use flate2::read::GzDecoder;
use hyper::Client;
use hyper::header::{ContentEncoding, Encoding};
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/// Incoming JSON request format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
#[derive(Debug)]
pub struct SymbolRequest {
    pub memoryMap: Vec<(String,String)>,
    // index, offset
    pub stacks: Vec<Vec<(i8,u64)>>,
    pub version: u8,
}

/// Outgoing JSON response format.
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
#[derive(Debug)]
pub struct SymbolResponse {
    pub symbolicatedStacks: Vec<Vec<String>>,
    pub knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let default_port = 5000;
    let port;
    match env::var("PORT") {
        Ok(val) => port = val,
        Err(_) => {
            println!("$PORT unset, using default {}", default_port);
            port = format!("{}", default_port);
        }
    }

    let address = &*format!("0.0.0.0:{}", port);

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/// Stacks come in as a JSON array, but really is a hash map of `<index, address>`
/// where `index` is the position of the `memoryMap` JSON result.
pub fn stacks_to_stack_map(decoded_stacks: Vec<Vec<(i8,u64)>>) -> HashMap<i8, Vec<u64>> {
    debug!("decoded_stacks: {:?}", decoded_stacks);

    let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();
    for stack in &decoded_stacks[0] {
        let (index, offset) = *stack;

        let mut offsets = vec!();
        if stack_map.contains_key(&index) {
            offsets = stack_map.get(&index).unwrap().clone();
        }
        offsets.push(offset);
        &stack_map.insert(index, offsets);
    }

    debug!("stack_map: {:?}", stack_map);
    stack_map
}

/// Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket via the
/// client function.
pub fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");
            debug!("symbol url: {:?}", symbol_url);
            let stack_map = stacks_to_stack_map(decoded.stacks);

            // FIXME limit the number of possible threads spawned by client()
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response: SymbolResponse = client(symbol_url, decoded.memoryMap, stack_map);
            let json_response = match json::encode(&symbol_response) {
                Ok(x) => x,
                Err(x) => panic!("cannot JSON encode {:?}, {:?}", symbol_response, x)
            };
            res.write_all(json_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/// Creates multiple client connections to fetch debug symbols from S3 bucket and symbolicate using
/// memory and stack maps, aggregates results and returns SymbolResult.
pub fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> SymbolResponse {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        symbol_path.push(get_config("symbols.path"));

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                let is_gzipped = match res.headers.get::<ContentEncoding>() {
                    Some(x) => x.contains(&Encoding::Gzip),
                    None => false,
                };

                let mut body = String::new();
                if is_gzipped {
                    let mut raw_body = vec!();
                    res.read_to_end(&mut raw_body).unwrap();
                    let mut d = match GzDecoder::new(&raw_body[..]) {
                        Ok(x) => x,
                        Err(x) => panic!("cannot gunzip {:?}, {:?}", raw_body, x)
                    };
                    d.read_to_string(&mut body).unwrap();
                } else {
                    res.read_to_string(&mut body).unwrap();
                }

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);

            let mut symbols = vec!();
            let mut known_module = true;
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &full_symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => {
                            symbols.push(format!("0x{:x}", address));
                            known_module = false;
                        },
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    // pass anything with index -1 through
    for stacks in stack_map.get(&-1) {
        for address in stacks {
            symbolicated_stacks.push(
                format!("0x{:x}", address)
            );
        }
    }

    for handle in handles {
        let (debug_file_name, symbols, known_module) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(known_module);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    result
}

/// Returns individual values from the configuration file.
fn get_config(value_name: &str) -> String {
    let mut f = match File::open("config/symbolapi.toml") {
        Ok(x) => x,
        Err(x) => panic!("cannot open config file config/symbolapi.toml: {}", x)
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    let value: toml::Value = match s.parse() {
        Ok(x) => x,
        Err(x) => panic!("cannot parse config file: {:?}", x)
    };

    let result = match value.lookup(value_name) {
        Some(x) => x,
        None => panic!("config value not found for {}", value_name)
    };

    result.as_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>#![feature(proc_macro)]

extern crate postgres;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

extern crate getopts;

use postgres::{Connection, TlsMode};

use std::env;
use getopts::Options;
use std::fs;
use std::fs::File;
use std::io::{Write, BufWriter};

mod pombase {
    use std::rc::Rc;
    use std::collections::HashMap;
    use postgres::Connection;

    use pombase::db::*;

    pub struct Raw {
        pub organisms: Vec<Rc<Organism>>,
        pub cvs: Vec<Rc<Cv>>,
        pub dbs: Vec<Rc<Db>>,
        pub dbxrefs: Vec<Rc<Dbxref>>,
        pub cvterms: Vec<Rc<Cvterm>>,
        pub cvtermpaths: Vec<Rc<Cvtermpath>>,
        pub cvterm_relationships: Vec<Rc<CvtermRelationship>>,
        pub publications: Vec<Rc<Publication>>,
        pub features: Vec<Rc<Feature>>,
    }

    impl Raw {
        pub fn new(conn: &Connection) -> Raw {
            let mut ret = Raw {
                organisms: vec![],
                cvs: vec![], dbs: vec![], dbxrefs: vec![], cvterms: vec![],
                cvtermpaths: vec![], cvterm_relationships: vec![],
                publications: vec![], features: vec![],
            };

            let mut organism_map: HashMap<i32, Rc<Organism>> = HashMap::new();
            let mut cv_map: HashMap<i32, Rc<Cv>> = HashMap::new();
            let mut db_map: HashMap<i32, Rc<Db>> = HashMap::new();
            let mut dbxref_map: HashMap<i32, Rc<Dbxref>> = HashMap::new();
            let mut cvterm_map: HashMap<i32, Rc<Cvterm>> = HashMap::new();
            let mut feature_map: HashMap<i32, Rc<Feature>> = HashMap::new();
            let mut publication_map: HashMap<i32, Rc<Publication>> = HashMap::new();

            for row in &conn.query("SELECT organism_id, genus, species, abbreviation, common_name FROM organism", &[]).unwrap() {
                let organism = Organism {
                    genus: row.get(1),
                    species: row.get(2),
                    abbreviation: row.get(3),
                    common_name: row.get(4),
                };
                let rc_organism = Rc::new(organism);
                ret.organisms.push(rc_organism.clone());
                organism_map.insert(row.get(0), rc_organism);
            }

            for row in &conn.query("SELECT cv_id, name FROM cv", &[]).unwrap() {
                let cv = Cv {
                    name: row.get(1),
                };
                let rc_cv = Rc::new(cv);
                ret.cvs.push(rc_cv.clone());
                cv_map.insert(row.get(0), rc_cv);
            }

            for row in &conn.query("SELECT db_id, name FROM db", &[]).unwrap() {
                let db_id = row.get(0);
                let db = Db {
                    name: row.get(1),
                };
                let rc_db = Rc::new(db);
                ret.dbs.push(rc_db.clone());
                db_map.insert(db_id, rc_db);
            }

            for row in &conn.query("SELECT dbxref_id, db_id, accession FROM dbxref", &[]).unwrap() {
                let dbxref_id: i32 = row.get(0);
                let db_id: i32 = row.get(1);
                let dbxref = Dbxref {
                    db: db_map.get(&db_id).unwrap().clone(),
                    accession: row.get(2),
                };
                let rc_dbxref = Rc::new(dbxref);
                ret.dbxrefs.push(rc_dbxref.clone());
                dbxref_map.insert(dbxref_id, rc_dbxref);
            }

            for row in &conn.query("SELECT cvterm_id, cv_id, dbxref_id, name FROM cvterm", &[]).unwrap() {
                let cvterm_id: i32 = row.get(0);
                let cv_id: i32 = row.get(1);
                let dbxref_id: i32 = row.get(2);
                let cvterm = Cvterm {
                    cv: cv_map.get(&cv_id).unwrap().clone(),
                    dbxref: dbxref_map.get(&dbxref_id).unwrap().clone(),
                    name: row.get(3),
                };
                let rc_cvterm = Rc::new(cvterm);
                ret.cvterms.push(rc_cvterm.clone());
                cvterm_map.insert(cvterm_id, rc_cvterm);
            }

            for row in &conn.query("SELECT pub_id, uniquename, type_id, title FROM pub", &[]).unwrap() {
                let pub_id: i32 = row.get(0);
                let uniquename: String = row.get(1);
                let type_id: i32 = row.get(2);
                let title: Option<String> = row.get(3);
                let publication = Publication {
                    uniquename: uniquename,
                    pub_type: cvterm_map.get(&type_id).unwrap().clone(),
                    title: title
                };
                let rc_publication = Rc::new(publication);
                ret.publications.push(rc_publication.clone());
                publication_map.insert(pub_id, rc_publication);
            }

            for row in &conn.query("SELECT feature_id, uniquename, name, type_id FROM feature", &[]).unwrap() {
                let feature_id = row.get(0);
                let type_id: i32 = row.get(3);
                let feature = Feature {
                    uniquename: row.get(1),
                    name: row.get(2),
                    feat_type: cvterm_map.get(&type_id).unwrap().clone(),
                };
                let rc_feature = Rc::new(feature);
                ret.features.push(rc_feature.clone());
                feature_map.insert(feature_id, rc_feature);
            }

            ret
        }
    }

    pub mod db {
        use std::rc::Rc;

        pub struct Organism {
            pub genus: String,
            pub species: String,
            pub abbreviation: String,
            pub common_name: String,
        }
        pub struct Cv {
            pub name: String,
        }
        pub struct Db {
            pub name: String,
        }
        pub struct Dbxref {
            pub accession: String,
            pub db: Rc<Db>,
        }
        pub struct Cvterm {
            pub name: String,
            pub cv: Rc<Cv>,
            pub dbxref: Rc<Dbxref>,
        }
        pub struct Publication {
            pub uniquename: String,
            pub pub_type: Rc<Cvterm>,
            pub title: Option<String>,
        }
        pub struct CvtermRelationship {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub reltype: Rc<Cvterm>,
        }
        pub struct Cvtermpath {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub rel_type: Rc<Cvterm>,
            pub pathdistance: Option<i32>,
        }
        pub struct Feature {
            pub uniquename: String,
            pub name: Option<String>,
            pub feat_type: Rc<Cvterm>,
        }
    }

    pub mod web {
        pub mod data {
            use std::collections::HashMap;

            type GeneUniquename = String;
            type GeneName = String;

            #[derive(Serialize)]
            pub struct GeneShort {
                pub uniquename: GeneUniquename,
                pub name: Option<GeneName>,
            }

            type TermName = String;
            type TermId = String;
            type TermDef = String;

            #[derive(Serialize)]
            pub struct TermShort {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
            }

            type Evidence = String;

            #[derive(Serialize)]
            pub struct FeatureAnnotation {
                pub term: TermShort,
                pub evidence: Evidence,
            }

            type TypeName = String;
            pub type TypeFeatureAnnotationMap =
                HashMap<TypeName, Vec<FeatureAnnotation>>;

            #[derive(Serialize)]
            pub struct GeneDetails {
                pub uniquename: GeneUniquename,
                pub name: Option<String>,
                pub annotations: TypeFeatureAnnotationMap,
            }

            type UniquenameGeneMap =
                HashMap<GeneUniquename, Vec<GeneDetails>>;

            #[derive(Serialize)]
            pub struct TermAnnotation {
                pub gene: GeneShort,
                pub evidence: Evidence,
            }

            #[derive(Serialize)]
            pub struct TermDetails {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
                pub annotations: Vec<TermAnnotation>,
            }
        }
    }
}

use pombase::Raw;
use pombase::web::data::*;

fn get_web_data(raw: &Raw) -> (Vec<GeneDetails>, Vec<TermDetails>) {
    let mut genes: Vec<GeneDetails> = vec![];
    let terms: Vec<TermDetails> = vec![];

    for feat in raw.features.iter().filter(|&f| f.feat_type.name == "gene") {
        genes.push(GeneDetails {
            uniquename: feat.uniquename.clone(),
            name: feat.name.clone(),
            annotations: TypeFeatureAnnotationMap::new(),
        });
    }

    (genes, terms)
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.reqopt("c", "connection-string",
                "PostgresSQL connection string like: postgres://user:pass@host/db_name",
                "CONN_STR");
    opts.reqopt("d", "output-directory",
                "Destination directory for JSON output", "DIR");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let connection_string = matches.opt_str("c").unwrap();
    let output_dir = matches.opt_str("d").unwrap();

    let subdirs = vec!["gene", "term"];

    for subdir in &subdirs {
        let dir = String::new() + &output_dir + "/" + subdir;
        fs::create_dir_all(&dir).unwrap_or_else(|why| {
            println!("Creating output directory failed: {:?}", why.kind());
        });
    }

    let conn = Connection::connect(connection_string.as_str(), TlsMode::None).unwrap();

    let raw = Raw::new(&conn);

    let (genes, terms) = get_web_data(&raw);

    for gene in &genes {
        let s = serde_json::to_string(&gene).unwrap();
        let file_name = String::new() + &output_dir + "/gene/" + &gene.uniquename + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    for term in &terms {
        let s = serde_json::to_string(&term).unwrap();
        let file_name = String::new() + &output_dir + "/term/" + &term.termid + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    println!("write {} genes", genes.len());
    println!("write {} terms", terms.len());
}
<commit_msg>Add filtering by organism<commit_after>#![feature(proc_macro)]

extern crate postgres;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

extern crate getopts;

use postgres::{Connection, TlsMode};

use std::env;
use getopts::Options;
use std::fs;
use std::fs::File;
use std::io::{Write, BufWriter};

mod pombase {
    use std::rc::Rc;
    use std::collections::HashMap;
    use postgres::Connection;

    use pombase::db::*;

    pub struct Raw {
        pub organisms: Vec<Rc<Organism>>,
        pub cvs: Vec<Rc<Cv>>,
        pub dbs: Vec<Rc<Db>>,
        pub dbxrefs: Vec<Rc<Dbxref>>,
        pub cvterms: Vec<Rc<Cvterm>>,
        pub cvtermpaths: Vec<Rc<Cvtermpath>>,
        pub cvterm_relationships: Vec<Rc<CvtermRelationship>>,
        pub publications: Vec<Rc<Publication>>,
        pub features: Vec<Rc<Feature>>,
    }

    impl Raw {
        pub fn new(conn: &Connection) -> Raw {
            let mut ret = Raw {
                organisms: vec![],
                cvs: vec![], dbs: vec![], dbxrefs: vec![], cvterms: vec![],
                cvtermpaths: vec![], cvterm_relationships: vec![],
                publications: vec![], features: vec![],
            };

            let mut organism_map: HashMap<i32, Rc<Organism>> = HashMap::new();
            let mut cv_map: HashMap<i32, Rc<Cv>> = HashMap::new();
            let mut db_map: HashMap<i32, Rc<Db>> = HashMap::new();
            let mut dbxref_map: HashMap<i32, Rc<Dbxref>> = HashMap::new();
            let mut cvterm_map: HashMap<i32, Rc<Cvterm>> = HashMap::new();
            let mut feature_map: HashMap<i32, Rc<Feature>> = HashMap::new();
            let mut publication_map: HashMap<i32, Rc<Publication>> = HashMap::new();

            for row in &conn.query("SELECT organism_id, genus, species, abbreviation, common_name FROM organism", &[]).unwrap() {
                let organism = Organism {
                    genus: row.get(1),
                    species: row.get(2),
                    abbreviation: row.get(3),
                    common_name: row.get(4),
                };
                let rc_organism = Rc::new(organism);
                ret.organisms.push(rc_organism.clone());
                organism_map.insert(row.get(0), rc_organism);
            }

            for row in &conn.query("SELECT cv_id, name FROM cv", &[]).unwrap() {
                let cv = Cv {
                    name: row.get(1),
                };
                let rc_cv = Rc::new(cv);
                ret.cvs.push(rc_cv.clone());
                cv_map.insert(row.get(0), rc_cv);
            }

            for row in &conn.query("SELECT db_id, name FROM db", &[]).unwrap() {
                let db_id = row.get(0);
                let db = Db {
                    name: row.get(1),
                };
                let rc_db = Rc::new(db);
                ret.dbs.push(rc_db.clone());
                db_map.insert(db_id, rc_db);
            }

            for row in &conn.query("SELECT dbxref_id, db_id, accession FROM dbxref", &[]).unwrap() {
                let dbxref_id: i32 = row.get(0);
                let db_id: i32 = row.get(1);
                let dbxref = Dbxref {
                    db: db_map.get(&db_id).unwrap().clone(),
                    accession: row.get(2),
                };
                let rc_dbxref = Rc::new(dbxref);
                ret.dbxrefs.push(rc_dbxref.clone());
                dbxref_map.insert(dbxref_id, rc_dbxref);
            }

            for row in &conn.query("SELECT cvterm_id, cv_id, dbxref_id, name FROM cvterm", &[]).unwrap() {
                let cvterm_id: i32 = row.get(0);
                let cv_id: i32 = row.get(1);
                let dbxref_id: i32 = row.get(2);
                let cvterm = Cvterm {
                    cv: cv_map.get(&cv_id).unwrap().clone(),
                    dbxref: dbxref_map.get(&dbxref_id).unwrap().clone(),
                    name: row.get(3),
                };
                let rc_cvterm = Rc::new(cvterm);
                ret.cvterms.push(rc_cvterm.clone());
                cvterm_map.insert(cvterm_id, rc_cvterm);
            }

            for row in &conn.query("SELECT pub_id, uniquename, type_id, title FROM pub", &[]).unwrap() {
                let pub_id: i32 = row.get(0);
                let uniquename: String = row.get(1);
                let type_id: i32 = row.get(2);
                let title: Option<String> = row.get(3);
                let publication = Publication {
                    uniquename: uniquename,
                    pub_type: cvterm_map.get(&type_id).unwrap().clone(),
                    title: title
                };
                let rc_publication = Rc::new(publication);
                ret.publications.push(rc_publication.clone());
                publication_map.insert(pub_id, rc_publication);
            }

            for row in &conn.query("SELECT feature_id, uniquename, name, type_id, organism_id FROM feature", &[]).unwrap() {
                let feature_id = row.get(0);
                let type_id: i32 = row.get(3);
                let organism_id: i32 = row.get(4);
                let feature = Feature {
                    uniquename: row.get(1),
                    name: row.get(2),
                    feat_type: cvterm_map.get(&type_id).unwrap().clone(),
                    organism: organism_map.get(&organism_id).unwrap().clone(),
                };
                let rc_feature = Rc::new(feature);
                ret.features.push(rc_feature.clone());
                feature_map.insert(feature_id, rc_feature);
            }

            ret
        }
    }

    pub mod db {
        use std::rc::Rc;

        pub struct Organism {
            pub genus: String,
            pub species: String,
            pub abbreviation: String,
            pub common_name: String,
        }
        pub struct Cv {
            pub name: String,
        }
        pub struct Db {
            pub name: String,
        }
        pub struct Dbxref {
            pub accession: String,
            pub db: Rc<Db>,
        }
        pub struct Cvterm {
            pub name: String,
            pub cv: Rc<Cv>,
            pub dbxref: Rc<Dbxref>,
        }
        pub struct Publication {
            pub uniquename: String,
            pub pub_type: Rc<Cvterm>,
            pub title: Option<String>,
        }
        pub struct CvtermRelationship {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub reltype: Rc<Cvterm>,
        }
        pub struct Cvtermpath {
            pub subject: Rc<Cvterm>,
            pub object: Rc<Cvterm>,
            pub rel_type: Rc<Cvterm>,
            pub pathdistance: Option<i32>,
        }
        pub struct Feature {
            pub uniquename: String,
            pub name: Option<String>,
            pub feat_type: Rc<Cvterm>,
            pub organism: Rc<Organism>,
        }
    }

    pub mod web {
        pub mod data {
            use std::collections::HashMap;

            type GeneUniquename = String;
            type GeneName = String;

            #[derive(Serialize)]
            pub struct GeneShort {
                pub uniquename: GeneUniquename,
                pub name: Option<GeneName>,
            }

            type TermName = String;
            type TermId = String;
            type TermDef = String;

            #[derive(Serialize)]
            pub struct TermShort {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
            }

            type Evidence = String;

            #[derive(Serialize)]
            pub struct FeatureAnnotation {
                pub term: TermShort,
                pub evidence: Evidence,
            }

            type TypeName = String;
            pub type TypeFeatureAnnotationMap =
                HashMap<TypeName, Vec<FeatureAnnotation>>;

            #[derive(Serialize)]
            pub struct GeneDetails {
                pub uniquename: GeneUniquename,
                pub name: Option<String>,
                pub annotations: TypeFeatureAnnotationMap,
            }

            type UniquenameGeneMap =
                HashMap<GeneUniquename, Vec<GeneDetails>>;

            #[derive(Serialize)]
            pub struct TermAnnotation {
                pub gene: GeneShort,
                pub evidence: Evidence,
            }

            #[derive(Serialize)]
            pub struct TermDetails {
                pub name: TermName,
                pub termid: TermId,
                pub definition: TermDef,
                pub is_obsolete: bool,
                pub annotations: Vec<TermAnnotation>,
            }
        }
    }
}

use pombase::Raw;
use pombase::web::data::*;

fn get_web_data(raw: &Raw, organism_genus_species: &String) -> (Vec<GeneDetails>, Vec<TermDetails>) {
    let mut genes: Vec<GeneDetails> = vec![];
    let terms: Vec<TermDetails> = vec![];

    for feat in raw.features.iter().filter(|&f| {
        let feature_org_genus_species = String::new() +
            &f.organism.genus + "_" + &f.organism.species;
        f.feat_type.name == "gene" &&
            feature_org_genus_species == *organism_genus_species
    } ) {
        genes.push(GeneDetails {
            uniquename: feat.uniquename.clone(),
            name: feat.name.clone(),
            annotations: TypeFeatureAnnotationMap::new(),
        });
    }

    (genes, terms)
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.reqopt("c", "connection-string",
                "PostgresSQL connection string like: postgres://user:pass@host/db_name",
                "CONN_STR");
    opts.reqopt("d", "output-directory",
                "Destination directory for JSON output", "DIR");
    opts.reqopt("O", "organism",
                "Only output genes from this organism (eg. 'Schizosaccharomyces_pombe')", "GENUS_SPECIES");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let connection_string = matches.opt_str("c").unwrap();
    let output_dir = matches.opt_str("d").unwrap();
    let organism_genus_species = matches.opt_str("O").unwrap();

    let subdirs = vec!["gene", "term"];

    for subdir in &subdirs {
        let dir = String::new() + &output_dir + "/" + subdir;
        fs::create_dir_all(&dir).unwrap_or_else(|why| {
            println!("Creating output directory failed: {:?}", why.kind());
        });
    }

    let conn = Connection::connect(connection_string.as_str(), TlsMode::None).unwrap();

    let raw = Raw::new(&conn);

    let (genes, terms) = get_web_data(&raw, &organism_genus_species);

    for gene in &genes {
        let s = serde_json::to_string(&gene).unwrap();
        let file_name = String::new() + &output_dir + "/gene/" + &gene.uniquename + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    for term in &terms {
        let s = serde_json::to_string(&term).unwrap();
        let file_name = String::new() + &output_dir + "/term/" + &term.termid + ".json";
        let f = File::create(file_name).expect("Unable to open file");
        let mut writer = BufWriter::new(&f);
        writer.write_all(s.as_bytes()).expect("Unable to write!");
    }

    println!("write {} genes", genes.len());
    println!("write {} terms", terms.len());
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate chrono;

use std::env;
use std::io::Read;
use chrono::*;
use hyper::Client;
use hyper::header::Connection;

static WEEKDAY_WEBSITE: &'static str = "http://www.hvilkenukeerdet.no";

fn print_help() {
    println!("`uke` gives the current week number.
             `uke <number>` gives you the range for that week number");
}

fn is_leap_year(year: i32) -> bool {
    return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)
}

fn find_num_of_weeks_this_year(year: i32) -> usize {
    let last_day_of_year = UTC.ymd(year, 12, 31);
    if last_day_of_year.weekday() == Weekday::Thu ||
        (last_day_of_year.weekday() == Weekday::Fri &&
         is_leap_year(year)) {
        return 53;
    } else {
        return 52;
    }
}

fn show_week_period_for_week_number(w: usize, year: i32) {
    let mut start_date = UTC.ymd(year, 1, 1).and_hms(1,0,0);
    let mut current_week = 1;

    // week 1 is defined by the first week with a thursday
    // in a year

    let jan_1_week_day = start_date.weekday().number_from_monday();
    if jan_1_week_day > 4 {
        /* this week has no thursday */
        /* the calculation will give the date of monday in the first week*/
        start_date = UTC.ymd(year, 1, (1 + 8-jan_1_week_day)).and_hms(1,0,0);
    } else {
        /* start at this week's first day */
        start_date = UTC.ymd(year-1, 12, (31 - (jan_1_week_day - 2))).and_hms(1,0,0);
    }

    while w != current_week {
        start_date = start_date + Duration::weeks(1);
        current_week += 1;
    }

    let end_date = start_date + Duration::days(6);

    println!("Week {}, {} is from \"{}\" to \"{}\"",
             w,
             year,
             start_date.format("%d %b"),
             end_date.format("%d %b")
            );
}

fn main() {
    let year = Local::now().year();

    if let Some(arg1) = env::args().nth(1) {
        let num_of_weeks_this_year = find_num_of_weeks_this_year(year);
        match arg1.parse::<usize>() {
            Ok(week_number) =>
                if week_number >= 1 && week_number <= num_of_weeks_this_year {
                    show_week_period_for_week_number(week_number, year);
                    return;
                },
            _ => {} ,
        }

        print_help();
    } else {
        let client = Client::new();

        let mut res = client
                        .get(WEEKDAY_WEBSITE)
                        .header(Connection::close())
                        .send()
                        .unwrap_or_else(|why| {
                            panic!("Error fetching week number, make sure you have an internet connection");
                        });

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        let body_index = body.find("\n").unwrap() + "\n".len();
        let break_index = body.find("
").unwrap();

        let week_number = &body[body_index..break_index];
        println!("{}", week_number);
    }
}
<commit_msg>Simplify week period calculation<commit_after>extern crate hyper;
extern crate chrono;

use std::env;
use std::io::Read;
use chrono::*;
use hyper::Client;
use hyper::header::Connection;

static WEEKDAY_WEBSITE: &'static str = "http://www.hvilkenukeerdet.no";

fn print_help() {
    println!("`uke` gives the current week number.
             `uke <number>` gives you the range for that week number");
}

fn is_leap_year(year: i32) -> bool {
    return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)
}

fn find_num_of_weeks_this_year(year: i32) -> usize {
    let last_day_of_year = UTC.ymd(year, 12, 31);
    if last_day_of_year.weekday() == Weekday::Thu ||
        (last_day_of_year.weekday() == Weekday::Fri &&
         is_leap_year(year)) {
        return 53;
    } else {
        return 52;
    }
}

fn show_week_period_for_week_number(w: usize, year: i32) {
    let mut start_date = UTC.ymd(year, 1, 1);

    // week 1 is defined by the first week with a thursday
    // in a year

    let jan_1_week_day = start_date.weekday().number_from_monday();
    if jan_1_week_day > 4 {
        /* this week has no thursday */
        /* the calculation will give the date of monday in the first week*/
        start_date = UTC.ymd(year, 1, (1 + 8-jan_1_week_day));
    } else {
        /* start at this week's first day */
        start_date = UTC.ymd(year-1, 12, (31 - (jan_1_week_day - 2)));
    }

    start_date = start_date + Duration::weeks((w as i64) - 1);

    let end_date = start_date + Duration::days(6);

    println!("Week {}, {} is from \"{}\" to \"{}\"",
             w,
             year,
             start_date.format("%d %b"),
             end_date.format("%d %b")
            );
}

fn main() {
    let year = Local::now().year();

    if let Some(arg1) = env::args().nth(1) {
        let num_of_weeks_this_year = find_num_of_weeks_this_year(year);
        match arg1.parse::<usize>() {
            Ok(week_number) =>
                if week_number >= 1 && week_number <= num_of_weeks_this_year {
                    show_week_period_for_week_number(week_number, year);
                    return;
                },
            _ => {} ,
        }

        print_help();
    } else {
        let client = Client::new();

        let mut res = client
                        .get(WEEKDAY_WEBSITE)
                        .header(Connection::close())
                        .send()
                        .unwrap_or_else(|why| {
                            panic!("Error fetching week number, make sure you have an internet connection");
                        });

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        let body_index = body.find("\n").unwrap() + "\n".len();
        let break_index = body.find("
").unwrap();

        let week_number = &body[body_index..break_index];
        println!("{}", week_number);
    }
}
<|endoftext|>


<commit_before>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate httparse;
extern crate hyper;
extern crate tokio_core;
extern crate farmhash;
extern crate futures;
extern crate futures_cpupool;
extern crate rand;
extern crate r2d2;
extern crate r2d2_redis;
extern crate redis;
extern crate unicase;

mod config;
mod header;
mod proxy;
mod cache;
mod control;
mod server;

use std::thread;
use std::ops::Deref;
use std::time::Duration;
use std::str::FromStr;

use clap::{App, Arg};
use log::LevelFilter;

use config::config::Config;
use config::logger::ConfigLogger;
use config::reader::ConfigReader;
use cache::store::{CacheStore, CacheStoreBuilder};
use control::listen::ControlListenBuilder;
use server::listen::ServerListenBuilder;

struct AppArgs {
    config: String,
}

pub static LINE_FEED: &'static str = "\r\n";

pub static THREAD_NAME_WORKER: &'static str = "bloom-worker";
pub static THREAD_NAME_CONTROL_MASTER: &'static str = "bloom-control-master";
pub static THREAD_NAME_CONTROL_CLIENT: &'static str = "bloom-control-client";

lazy_static! {
    static ref APP_ARGS: AppArgs = make_app_args();
    static ref APP_CONF: Config = ConfigReader::make();
    static ref APP_CACHE_STORE: CacheStore = CacheStoreBuilder::new();
}

fn make_app_args() -> AppArgs {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .about(crate_description!())
        .arg(
            Arg::with_name("config")
                .short("c")
                .long("config")
                .help("Path to configuration file")
                .default_value("./config.cfg")
                .takes_value(true),
        )
        .get_matches();

    // Generate owned app arguments
    AppArgs { config: String::from(matches.value_of("config").expect("invalid config value")) }
}

fn ensure_states() {
    // Ensure all statics are valid (a `deref` is enough to lazily initialize them)
    APP_ARGS.deref();
    APP_CONF.deref();
    APP_CACHE_STORE.deref();
}

fn spawn_worker() {
    let worker = thread::Builder::new()
        .name(THREAD_NAME_WORKER.to_string())
        .spawn(|| ServerListenBuilder::new().run());

    // Block on worker thread (join it)
    let has_error = if let Ok(worker_thread) = worker {
        worker_thread.join().is_err()
    } else {
        true
    };

    // Worker thread crashed?
    if has_error == true {
        error!("worker thread crashed, setting it up again");

        // Prevents thread start loop floods
        thread::sleep(Duration::from_secs(1));

        spawn_worker();
    }
}

fn main() {
    let _logger = ConfigLogger::init(
        LevelFilter::from_str(&APP_CONF.server.log_level).expect("invalid log level"),
    );

    info!("starting up");

    // Ensure all states are bound
    ensure_states();

    // Run control interface (in its own thread)
    ControlListenBuilder::new().run();

    // Run server (from main thread, maintain thread active if down)
    spawn_worker();

    error!("could not start");
}
<commit_msg>Format<commit_after>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate httparse;
extern crate hyper;
extern crate tokio_core;
extern crate farmhash;
extern crate futures;
extern crate futures_cpupool;
extern crate rand;
extern crate r2d2;
extern crate r2d2_redis;
extern crate redis;
extern crate unicase;

mod config;
mod header;
mod proxy;
mod cache;
mod control;
mod server;

use std::thread;
use std::ops::Deref;
use std::time::Duration;
use std::str::FromStr;

use clap::{App, Arg};
use log::LevelFilter;

use config::config::Config;
use config::logger::ConfigLogger;
use config::reader::ConfigReader;
use cache::store::{CacheStore, CacheStoreBuilder};
use control::listen::ControlListenBuilder;
use server::listen::ServerListenBuilder;

struct AppArgs {
    config: String,
}

pub static LINE_FEED: &'static str = "\r\n";

pub static THREAD_NAME_WORKER: &'static str = "bloom-worker";
pub static THREAD_NAME_CONTROL_MASTER: &'static str = "bloom-control-master";
pub static THREAD_NAME_CONTROL_CLIENT: &'static str = "bloom-control-client";

lazy_static! {
    static ref APP_ARGS: AppArgs = make_app_args();
    static ref APP_CONF: Config = ConfigReader::make();
    static ref APP_CACHE_STORE: CacheStore = CacheStoreBuilder::new();
}

fn make_app_args() -> AppArgs {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .about(crate_description!())
        .arg(
            Arg::with_name("config")
                .short("c")
                .long("config")
                .help("Path to configuration file")
                .default_value("./config.cfg")
                .takes_value(true),
        )
        .get_matches();

    // Generate owned app arguments
    AppArgs { config: String::from(matches.value_of("config").expect("invalid config value")) }
}

fn ensure_states() {
    // Ensure all statics are valid (a `deref` is enough to lazily initialize them)
    APP_ARGS.deref();
    APP_CONF.deref();
    APP_CACHE_STORE.deref();
}

fn spawn_worker() {
    let worker = thread::Builder::new()
        .name(THREAD_NAME_WORKER.to_string())
        .spawn(|| ServerListenBuilder::new().run());

    // Block on worker thread (join it)
    let has_error = if let Ok(worker_thread) = worker {
        worker_thread.join().is_err()
    } else {
        true
    };

    // Worker thread crashed?
    if has_error == true {
        error!("worker thread crashed, setting it up again");

        // Prevents thread start loop floods
        thread::sleep(Duration::from_secs(1));

        spawn_worker();
    }
}

fn main() {
    let _logger = ConfigLogger::init(LevelFilter::from_str(&APP_CONF.server.log_level).expect(
        "invalid log level",
    ));

    info!("starting up");

    // Ensure all states are bound
    ensure_states();

    // Run control interface (in its own thread)
    ControlListenBuilder::new().run();

    // Run server (from main thread, maintain thread active if down)
    spawn_worker();

    error!("could not start");
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

#[macro_use] extern crate quick_error;
extern crate regex;
extern crate toml;
extern crate semver;

use std::process::exit;

use semver::Identifier;

mod config;
mod error;
mod cmd;
mod git;
mod cargo;

fn execute() -> Result<i32, error::FatalError> {

    // STEP 0: Check if working directory is clean
    let git_clean = try!(git::status());
    if git_clean {
        println!("Working directory is clean");
    } else {
        println!("Uncommitted changes detected, please commit before release");
        return Ok(128);
    }

    // STEP 1: Read version from Cargo.toml and remove
    let result = try!(config::parse_cargo_config());

    let mut version = result.get("package")
        .and_then(|f| f.as_table())
        .and_then(|f| f.get("version"))
        .and_then(|f| f.as_str())
        .and_then(|f| config::parse_version(f).ok())
        .unwrap();

    // STEP 2: Remove pre extension, save and commit
    if version.is_prerelease() {
        version.pre.clear();
        let new_version_string = version.to_string();
        try!(config::rewrite_cargo_version(&new_version_string));
        // sync Cargo.toml and Cargo.lock
        try!(cargo::update());

        let commit_msg = format!("(cargo-release) version {}", new_version_string);
        if !try!(git::commit_all(&commit_msg)) {
            // commit failed, abort release
            return Ok(128);
        }
    }

    // STEP 3: cargo publish
    if !try!(cargo::publish()) {
        return Ok(128);
    }

    // STEP 4: Tag
    let current_version = version.to_string();
    let tag_message = format!("(cargo-release) version {}", current_version);
    if !try!(git::tag(&current_version, &tag_message)) {
        // tag failed, abort release
        return Ok(128);
    }

    // STEP 5: bump version
    version.increment_patch();
    version.pre.push(Identifier::AlphaNumeric("pre".to_owned()));
    println!("Starting next development cycle {}", version);
    let updated_version_string = version.to_string();
    try!(config::rewrite_cargo_version(&updated_version_string));
    try!(cargo::update());
    let commit_msg = format!("(cargo-release) start next development cycle {}",
                             updated_version_string);
    if !try!(git::commit_all(&commit_msg)) {
        return Ok(128);
    }

    // STEP 6: git push
    if !try!(git::push()) {
        return Ok(128);
    }
    Ok(0)
}

fn main() {
    match execute() {
        Ok(code) => exit(code),
        Err(e) => {
            println!("Fatal: {}", e);
            exit(128);
        }
    }
}
<commit_msg>(feat) remove output when directory is clean<commit_after>#![allow(dead_code)]

#[macro_use] extern crate quick_error;
extern crate regex;
extern crate toml;
extern crate semver;

use std::process::exit;

use semver::Identifier;

mod config;
mod error;
mod cmd;
mod git;
mod cargo;

fn execute() -> Result<i32, error::FatalError> {

    // STEP 0: Check if working directory is clean
    if !try!(git::status()) {
        println!("Uncommitted changes detected, please commit before release");
        return Ok(128);
    }

    // STEP 1: Read version from Cargo.toml and remove
    let result = try!(config::parse_cargo_config());

    let mut version = result.get("package")
        .and_then(|f| f.as_table())
        .and_then(|f| f.get("version"))
        .and_then(|f| f.as_str())
        .and_then(|f| config::parse_version(f).ok())
        .unwrap();

    // STEP 2: Remove pre extension, save and commit
    if version.is_prerelease() {
        version.pre.clear();
        let new_version_string = version.to_string();
        try!(config::rewrite_cargo_version(&new_version_string));
        // sync Cargo.toml and Cargo.lock
        try!(cargo::update());

        let commit_msg = format!("(cargo-release) version {}", new_version_string);
        if !try!(git::commit_all(&commit_msg)) {
            // commit failed, abort release
            return Ok(128);
        }
    }

    // STEP 3: cargo publish
    if !try!(cargo::publish()) {
        return Ok(128);
    }

    // STEP 4: Tag
    let current_version = version.to_string();
    let tag_message = format!("(cargo-release) version {}", current_version);
    if !try!(git::tag(&current_version, &tag_message)) {
        // tag failed, abort release
        return Ok(128);
    }

    // STEP 5: bump version
    version.increment_patch();
    version.pre.push(Identifier::AlphaNumeric("pre".to_owned()));
    println!("Starting next development cycle {}", version);
    let updated_version_string = version.to_string();
    try!(config::rewrite_cargo_version(&updated_version_string));
    try!(cargo::update());
    let commit_msg = format!("(cargo-release) start next development cycle {}",
                             updated_version_string);
    if !try!(git::commit_all(&commit_msg)) {
        return Ok(128);
    }

    // STEP 6: git push
    if !try!(git::push()) {
        return Ok(128);
    }
    Ok(0)
}

fn main() {
    match execute() {
        Ok(code) => exit(code),
        Err(e) => {
            println!("Fatal: {}", e);
            exit(128);
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
pub mod parser;

fn evaluate(input: &str, env: &mut HashMap<String, f64>) -> f64 {
    let mut p = parser::Parser::new(input);
    p.parse().eval(env)
}

pub fn main() {
    use std::f64;
    let mut env = HashMap::new();
    env.insert("wow".to_string(), 35.0f64);
    env.insert("pi".to_string(), f64::consts::PI);

    let mut stdin = io::stdin();

    loop {
        print!(">> ");
        io::stdout().flush().ok();

        let mut input = String::new();

        match stdin.read_line(&mut input) {
            Ok(_) => {
                println!("=> {}", evaluate(&input.trim_right(), &mut env));
                io::stdout().flush().ok();
            }
            Err(_) => {
                println!("");
                break;
            }
        }
    }
}
<commit_msg>remove unnecessary mut<commit_after>use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
pub mod parser;

fn evaluate(input: &str, env: &mut HashMap<String, f64>) -> f64 {
    let mut p = parser::Parser::new(input);
    p.parse().eval(env)
}

pub fn main() {
    use std::f64;
    let mut env = HashMap::new();
    env.insert("wow".to_string(), 35.0f64);
    env.insert("pi".to_string(), f64::consts::PI);

    let stdin = io::stdin();

    loop {
        print!(">> ");
        io::stdout().flush().ok();

        let mut input = String::new();

        match stdin.read_line(&mut input) {
            Ok(_) => {
                println!("=> {}", evaluate(&input.trim_right(), &mut env));
                io::stdout().flush().ok();
            }
            Err(_) => {
                println!("");
                break;
            }
        }
    }
}
<|endoftext|>