<commit_before>extern crate actix_web;
extern crate cookie;
extern crate get_if_addrs;
extern crate hyper;
extern crate itertools;
extern crate json;
extern crate log4rs;
#[macro_use]
extern crate log;
extern crate mustache;
#[cfg(feature = "mocking")]
extern crate reqwest_mock;
#[macro_use]
extern crate serde_derive;
extern crate time;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;

#[cfg(test)]
extern crate crossbeam;
extern crate env_logger;

#[cfg(feature = "lambda")]
extern crate reqwest;
#[cfg(feature = "lambda")]
use reqwest::Client;

#[cfg(feature = "lambda")]
#[macro_use]
extern crate lambda_http;
#[cfg(feature = "lambda")]
use lambda_http::RequestExt;
#[cfg(feature = "lambda")]
extern crate aws_lambda_events;
#[cfg(test)]
#[macro_use]
extern crate maplit;

use actix_web::{http::Method, server, App, HttpRequest, HttpResponse};
#[cfg(not(feature = "lambda"))]
use std::env;

#[cfg(feature = "lambda")]
use std::thread;

mod arrivals;
mod common;
mod favourite;
mod id;
mod nearby;
mod search;

fn root_handler(request: HttpRequest) -> HttpResponse {
    let data = common::mustache_favourites(&request);
    common::render_to_response("resources/templates/root.mustache", &data)
}

fn app() -> App {
    return App::new()
        .route("/", Method::GET, root_handler)
        .route("/search", Method::GET, search::search_handler)
        .route("/id/{id}", Method::GET, id::id_handler)
        .route("/nearby", Method::GET, nearby::nearby_handler)
        .route("/favourites", Method::GET, favourite::list_favourites)
        .route("/favourites", Method::POST, favourite::add_favourite)
        .route("/favourite-remove", Method::POST, favourite::remove_favourite)
        .route("/arrivals/{stopid}", Method::GET, arrivals::arrivals_handler);
}

#[cfg(not(feature = "lambda"))]
fn main() {
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let port = env::var("PORT")
        .unwrap_or("8000".to_string())
        .parse::<u16>()
        .unwrap();
    let mut server = server::new(|| app().finish());
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        let ip = iface.ip();
        info!("Listening on {}:{} for {}", ip, port, iface.name);
        server = server.bind((ip, port)).unwrap();
    }
    server.run();
}

#[cfg(feature = "lambda")]
fn main() {
    env_logger::init();
    thread::spawn(move || server::new(|| app().finish()).bind("0.0.0.0:3457").unwrap().run());
    let client = Client::new();
    lambda!(|request: lambda_http::Request, _context| {
        debug!("Req to inner: {:?}", request);
        let uri = &format!(
            "http://127.0.0.1:3457{}",
            &request
                .uri()
                .clone()
                .into_parts()
                .path_and_query
                .unwrap()
                .as_str()
        );
        debug!("Uri for inner: {}", uri);
        let mut req = client.clone().request(request.method().clone(), uri);
        for (key, value) in request.headers() {
            req = req.header(key, value);
        }
        for (key, value) in request.query_string_parameters().iter() {
            req = req.query(&[(key, value)]);
        }
        match request.body() {
            lambda_http::Body::Empty => {}
            lambda_http::Body::Text(val) => {
                req = req.body(val.clone());
            }
            lambda_http::Body::Binary(val) => {
                req = req.body(val.clone());
            }
        }
        debug!("New req: {:?}", req);
        let mut res = req.send().unwrap();
        debug!("Res: {:?}", res);
        let mut lambda_res = lambda_http::Response::builder();
        lambda_res.status(res.status());
        for (key, value) in res.headers() {
            lambda_res.header(key, value);
        }
        Ok(lambda_res.body(res.text().unwrap()).unwrap())
    });
}

#[cfg(test)]
mod tests {
    use super::{app, common, main};
    use actix_web::actix::System;
    use actix_web::{http, server, test, App, HttpMessage, HttpRequest, HttpResponse, State};
    #[cfg(feature = "lambda")]
    use aws_lambda_events::event::alb;
    use crossbeam::channel::unbounded;
    use crossbeam::{Receiver, Sender};
    use std::collections::HashMap;
    use std::{env, thread};

    #[derive(Debug, Clone)]
    struct AppState {
        req: Receiver<Result<(i32, serde_json::Value), ()>>,
        res: Sender<String>,
    }

    fn test_app(req: Receiver<Result<(i32, serde_json::Value), ()>>, res: Sender<String>) -> App<AppState> {
        App::with_state(AppState { req, res })
            .resource("/2018-06-01/runtime/invocation/1234/response", |r| {
                r.method(http::Method::POST)
                    .with(|(body, state): (String, State<AppState>)| {
                        debug!("Response body: {}", body);
                        state.res.send(body).unwrap();
                        HttpResponse::Ok()
                    })
            })
            .resource("/2018-06-01/runtime/invocation/next", |r| {
                r.method(http::Method::GET).with(|state: State<AppState>| {
                    let (id, body) = match state.req.clone().recv().unwrap() {
                        Ok(val) => val,
                        Err(_) => {
                            System::current().stop();
                            thread::park();
                            unimplemented!();
                        }
                    };
                    debug!("Got req: {}", id);
                    HttpResponse::Ok()
                        .header("Lambda-Runtime-Aws-Request-Id", id.to_string())
                        .header("Lambda-Runtime-Invoked-Function-Arn", "an-arn")
                        .header("Lambda-Runtime-Deadline-Ms", "1000")
                        .json(body)
                })
            })
            .default_resource(|r| {
                r.route().with(|(r, body): (HttpRequest<AppState>, String)| {
                    warn!("{:?}", r);
                    warn!("Body: {}", body);
                    HttpResponse::NotFound()
                })
            })
    }

    #[cfg(feature = "lambda")]
    #[test]
    fn lambda_test() {
        let (req_send, req_recv) = unbounded();
        let (res_send, res_recv) = unbounded();
        req_send.send(Ok((1234,
            serde_json::to_value(
            alb::AlbTargetGroupRequest {
                request_context: alb::AlbTargetGroupRequestContext {
                    elb: alb::ElbContext {
                        target_group_arn: Some("arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09".to_string())
                    }
                },
                http_method: Some("GET".to_string()),
                path: Some("/".to_string()),
                query_string_parameters: HashMap::new(),
                multi_value_query_string_parameters: HashMap::new(),
                headers: hashmap!{
                    "accept".to_string() => "text/html,application/xhtml+xml".to_string(),
                    "accept-language".to_string() => "en-US,en;q=0.8".to_string(),
                    "content-type".to_string() => "text/plain".to_string(),
                    "cookie".to_string() => "cookies".to_string(),
                    "host".to_string() => "lambda-846800462-us-east-2.elb.amazonaws.com".to_string(),
                    "user-agent".to_string() => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)".to_string(),
                    "x-amzn-trace-id".to_string() => "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520".to_string(),
                    "x-forwarded-for".to_string() => "72.21.198.66".to_string(),
                    "x-forwarded-port".to_string() => "443".to_string(),
                    "x-forwarded-proto".to_string() => "https".to_string()
                },
                multi_value_headers: HashMap::new(),
                is_base64_encoded: false,
                body: Some("request_body".to_string())
}).unwrap()))).unwrap();
        env_logger::init();
        thread::spawn(move || {
            server::new(move || test_app(req_recv.clone(), res_send.clone()).finish())
                .bind("0.0.0.0:3456")
                .unwrap()
                .run()
        });
        env::set_var("AWS_LAMBDA_FUNCTION_NAME", "foo");
        env::set_var("AWS_LAMBDA_FUNCTION_VERSION", "1");
        env::set_var("AWS_LAMBDA_LOG_STREAM_NAME", "log");
        env::set_var("AWS_LAMBDA_LOG_GROUP_NAME", "lg");
        env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128");
        env::set_var("AWS_LAMBDA_RUNTIME_API", "127.0.0.1:3456");
        thread::spawn(|| main());
        let resp_raw = res_recv.recv().unwrap();
        let resp: alb::AlbTargetGroupResponse = serde_json::from_str(&resp_raw).unwrap();
        debug!("Response to main: {:#?}", resp);
        // shutdown
        req_send.send(Err(())).unwrap();
    }

    #[test]
    fn simple_search() {
        let mut srv = test::TestServer::with_factory(app);
        common::set_client(common::ClientType::TESTING);
        let request = srv
            .client(http::Method::GET, "/search?query=foo")
            .finish()
            .unwrap();
        let response = srv.execute(request.send()).unwrap();
        let body: String = String::from_utf8(srv.execute(response.body()).unwrap().to_vec()).unwrap();
        assert!(body.find("<title>Search: foo</title>").is_some(), body);
        assert!(response.status().is_success(), response.status());
    }
}
<commit_msg>Decode query parameters<commit_after>extern crate actix_web;
extern crate cookie;
extern crate get_if_addrs;
extern crate hyper;
extern crate itertools;
extern crate json;
extern crate log4rs;
#[macro_use]
extern crate log;
extern crate mustache;
#[cfg(feature = "mocking")]
extern crate reqwest_mock;
#[macro_use]
extern crate serde_derive;
extern crate time;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;

#[cfg(test)]
extern crate crossbeam;
extern crate env_logger;

#[cfg(feature = "lambda")]
extern crate reqwest;
#[cfg(feature = "lambda")]
use reqwest::Client;

#[cfg(feature = "lambda")]
#[macro_use]
extern crate lambda_http;
#[cfg(feature = "lambda")]
use lambda_http::RequestExt;
#[cfg(feature = "lambda")]
use url::percent_encoding::percent_decode;
#[cfg(feature = "lambda")]
extern crate aws_lambda_events;
#[cfg(test)]
#[macro_use]
extern crate maplit;

use actix_web::{http::Method, server, App, HttpRequest, HttpResponse};
#[cfg(not(feature = "lambda"))]
use std::env;

#[cfg(feature = "lambda")]
use std::thread;

mod arrivals;
mod common;
mod favourite;
mod id;
mod nearby;
mod search;

fn root_handler(request: HttpRequest) -> HttpResponse {
    let data = common::mustache_favourites(&request);
    common::render_to_response("resources/templates/root.mustache", &data)
}

fn app() -> App {
    return App::new()
        .route("/", Method::GET, root_handler)
        .route("/search", Method::GET, search::search_handler)
        .route("/id/{id}", Method::GET, id::id_handler)
        .route("/nearby", Method::GET, nearby::nearby_handler)
        .route("/favourites", Method::GET, favourite::list_favourites)
        .route("/favourites", Method::POST, favourite::add_favourite)
        .route("/favourite-remove", Method::POST, favourite::remove_favourite)
        .route("/arrivals/{stopid}", Method::GET, arrivals::arrivals_handler);
}

#[cfg(not(feature = "lambda"))]
fn main() {
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let port = env::var("PORT")
        .unwrap_or("8000".to_string())
        .parse::<u16>()
        .unwrap();
    let mut server = server::new(|| app().finish());
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        let ip = iface.ip();
        info!("Listening on {}:{} for {}", ip, port, iface.name);
        server = server.bind((ip, port)).unwrap();
    }
    server.run();
}

#[cfg(feature = "lambda")]
fn main() {
    env_logger::init();
    thread::spawn(move || server::new(|| app().finish()).bind("0.0.0.0:3457").unwrap().run());
    let client = Client::new();
    lambda!(|request: lambda_http::Request, _context| {
        debug!("Req to inner: {:?}", request);
        let uri = &format!(
            "http://127.0.0.1:3457{}",
            &request
                .uri()
                .clone()
                .into_parts()
                .path_and_query
                .unwrap()
                .as_str()
        );
        debug!("Uri for inner: {}", uri);
        let mut req = client.clone().request(request.method().clone(), uri);
        for (key, value) in request.headers() {
            req = req.header(key, value);
        }
        for (key, value) in request.query_string_parameters().iter() {
            // ALB encodes the query parameters, and reqwest will do it again, so need to stop doing it twice!
            let mut value = percent_decode(value.as_bytes()).decode_utf8().unwrap().to_string();
            value = value.replace("+", " "); // Also need to decode the + characters, which percent_decode doesn't
            debug!("Query param: '{}' = '{}'", key, value);
            req = req.query(&[(key, value)]);
        }
        match request.body() {
            lambda_http::Body::Empty => {}
            lambda_http::Body::Text(val) => {
                req = req.body(val.clone());
            }
            lambda_http::Body::Binary(val) => {
                req = req.body(val.clone());
            }
        }
        debug!("New req: {:?}", req);
        let mut res = req.send().unwrap();
        debug!("Res: {:?}", res);
        let mut lambda_res = lambda_http::Response::builder();
        lambda_res.status(res.status());
        for (key, value) in res.headers() {
            lambda_res.header(key, value);
        }
        Ok(lambda_res.body(res.text().unwrap()).unwrap())
    });
}

#[cfg(test)]
mod tests {
    use super::{app, common, main};
    use actix_web::actix::System;
    use actix_web::{http, server, test, App, HttpMessage, HttpRequest, HttpResponse, State};
    #[cfg(feature = "lambda")]
    use aws_lambda_events::event::alb;
    use crossbeam::channel::unbounded;
    use crossbeam::{Receiver, Sender};
    use std::collections::HashMap;
    use std::{env, thread};

    #[derive(Debug, Clone)]
    struct AppState {
        req: Receiver<Result<(i32, serde_json::Value), ()>>,
        res: Sender<String>,
    }

    fn test_app(req: Receiver<Result<(i32, serde_json::Value), ()>>, res: Sender<String>) -> App<AppState> {
        App::with_state(AppState { req, res })
            .resource("/2018-06-01/runtime/invocation/1234/response", |r| {
                r.method(http::Method::POST)
                    .with(|(body, state): (String, State<AppState>)| {
                        debug!("Response body: {}", body);
                        state.res.send(body).unwrap();
                        HttpResponse::Ok()
                    })
            })
            .resource("/2018-06-01/runtime/invocation/next", |r| {
                r.method(http::Method::GET).with(|state: State<AppState>| {
                    let (id, body) = match state.req.clone().recv().unwrap() {
                        Ok(val) => val,
                        Err(_) => {
                            System::current().stop();
                            thread::park();
                            unimplemented!();
                        }
                    };
                    debug!("Got req: {}", id);
                    HttpResponse::Ok()
                        .header("Lambda-Runtime-Aws-Request-Id", id.to_string())
                        .header("Lambda-Runtime-Invoked-Function-Arn", "an-arn")
                        .header("Lambda-Runtime-Deadline-Ms", "1000")
                        .json(body)
                })
            })
            .default_resource(|r| {
                r.route().with(|(r, body): (HttpRequest<AppState>, String)| {
                    warn!("{:?}", r);
                    warn!("Body: {}", body);
                    HttpResponse::NotFound()
                })
            })
    }

    #[cfg(feature = "lambda")]
    #[test]
    fn lambda_test() {
        let (req_send, req_recv) = unbounded();
        let (res_send, res_recv) = unbounded();
        req_send.send(Ok((1234,
            serde_json::to_value(
            alb::AlbTargetGroupRequest {
                request_context: alb::AlbTargetGroupRequestContext {
                    elb: alb::ElbContext {
                        target_group_arn: Some("arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09".to_string())
                    }
                },
                http_method: Some("GET".to_string()),
                path: Some("/".to_string()),
                query_string_parameters: HashMap::new(),
                multi_value_query_string_parameters: HashMap::new(),
                headers: hashmap!{
                    "accept".to_string() => "text/html,application/xhtml+xml".to_string(),
                    "accept-language".to_string() => "en-US,en;q=0.8".to_string(),
                    "content-type".to_string() => "text/plain".to_string(),
                    "cookie".to_string() => "cookies".to_string(),
                    "host".to_string() => "lambda-846800462-us-east-2.elb.amazonaws.com".to_string(),
                    "user-agent".to_string() => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)".to_string(),
                    "x-amzn-trace-id".to_string() => "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520".to_string(),
                    "x-forwarded-for".to_string() => "72.21.198.66".to_string(),
                    "x-forwarded-port".to_string() => "443".to_string(),
                    "x-forwarded-proto".to_string() => "https".to_string()
                },
                multi_value_headers: HashMap::new(),
                is_base64_encoded: false,
                body: Some("request_body".to_string())
}).unwrap()))).unwrap();
        env_logger::init();
        thread::spawn(move || {
            server::new(move || test_app(req_recv.clone(), res_send.clone()).finish())
                .bind("0.0.0.0:3456")
                .unwrap()
                .run()
        });
        env::set_var("AWS_LAMBDA_FUNCTION_NAME", "foo");
        env::set_var("AWS_LAMBDA_FUNCTION_VERSION", "1");
        env::set_var("AWS_LAMBDA_LOG_STREAM_NAME", "log");
        env::set_var("AWS_LAMBDA_LOG_GROUP_NAME", "lg");
        env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128");
        env::set_var("AWS_LAMBDA_RUNTIME_API", "127.0.0.1:3456");
        thread::spawn(|| main());
        let resp_raw = res_recv.recv().unwrap();
        let resp: alb::AlbTargetGroupResponse = serde_json::from_str(&resp_raw).unwrap();
        debug!("Response to main: {:#?}", resp);
        // shutdown
        req_send.send(Err(())).unwrap();
    }

    #[test]
    fn simple_search() {
        let mut srv = test::TestServer::with_factory(app);
        common::set_client(common::ClientType::TESTING);
        let request = srv
            .client(http::Method::GET, "/search?query=foo")
            .finish()
            .unwrap();
        let response = srv.execute(request.send()).unwrap();
        let body: String = String::from_utf8(srv.execute(response.body()).unwrap().to_vec()).unwrap();
        assert!(body.find("<title>Search: foo</title>").is_some(), body);
        assert!(response.status().is_success(), response.status());
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate termios;

use libc::STDIN_FILENO;
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::io::{self, Read, ErrorKind};

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

struct TermReset {
    orig_term: Termios
}

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

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

    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)
}

fn iscntrl(c: u8) -> bool {
    unsafe {
        libc::iscntrl(c as i32) != 0
    }
}

fn main() {
    let _reset = enable_raw_mode().unwrap();

    let mut stdin = io::stdin();
    let mut buf = [0; 1];

    loop {
        buf[0] = 0;
        let n = stdin.read(&mut buf)
                     .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
                     .expect("read");
        if n > 0 {
            let c = buf[0];
            if c == ctrl_key!(b'q') {
                break;
            }
            if iscntrl(c) {
                print!("{}\r\n", &c);
            } else {
                print!("{} ({})\r\n", &c, c as char);
            }
        }
    }
}
<commit_msg>Refactor keyboard input<commit_after>extern crate libc;
extern crate termios;

use libc::STDIN_FILENO;
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::io::{self, Stdin, Read, ErrorKind};

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

struct TermReset {
    orig_term: Termios
}

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

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

    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)
}

fn editor_read_key(stdin: &mut Stdin) -> u8 {
    let mut buf = [0; 1];
    loop {
        let n = stdin.read(&mut buf)
                 .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
                 .expect("read");
        if n == 1 {
            return buf[0];
        }
    }
}

fn editor_process_keypress(stdin: &mut Stdin) -> bool {
    let c = editor_read_key(stdin);

    match c {
        k if k == ctrl_key!(b'q') => {
            false
        },
        _ => true
    }
}

fn main() {
    let _reset = enable_raw_mode().unwrap();

    let mut stdin = io::stdin();
    while editor_process_keypress(&mut stdin) {
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Gomez Guillaume
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs::{self, File, OpenOptions};
use std::io::{BufRead, Write, Read};
use std::fmt::{Display, Formatter, Error};
use std::ops::Deref;

enum EventType {
    Comment(String),
    FileComment(String),
    Type(TypeStruct),
    InScope,
    OutScope,
}

struct TypeStruct {
    ty: Type,
    parent: Option<Box<TypeStruct>>,
    name: String,
    args: Vec<String>,
}

impl TypeStruct {
    fn new(ty: Type, name: &str) -> TypeStruct {
        TypeStruct {
            ty: ty,
            name: name.to_owned(),
            args: vec!(),
            parent: None,
        }
    }

    fn from_args(ty: Type, args: Vec<String>) -> TypeStruct {
        TypeStruct {
            ty: ty,
            name: String::new(),
            args: args,
            parent: None,
        }
    }

    fn empty() -> TypeStruct {
        TypeStruct {
            ty: Type::Unknown,
            name: String::new(),
            args: Vec::new(),
            parent: None,
        }
    }
}

impl Clone for TypeStruct {
    fn clone(&self) -> TypeStruct {
        TypeStruct {
            ty: self.ty,
            name: self.name.clone(),
            args: self.args.clone(),
            parent: match self.parent {
                Some(ref p) => Some(Box::new(p.deref().clone())),
                None => None,
            }
        }
    }

    fn clone_from(&mut self, source: &TypeStruct) {
        self.ty = source.ty;
        self.name = source.name.clone();
        self.args = source.args.clone();
        self.parent = match source.parent {
            Some(ref p) => Some(Box::new(p.deref().clone())),
            None => None,
        };
    }
}

impl Display for TypeStruct {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let parent = &self.parent;
        match parent {
            &Some(ref p) => write!(f, "{}({} {}{})", p, self.ty, self.name, self.args.join(" ")),
            &None => write!(f, "{} {}{}", self.ty, self.name, self.args.join(" ")),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum Type {
    Struct,
    Mod,
    Enum,
    Fn,
    Const,
    Static,
    Type,
    Variant,
    Impl,
    Use,
    Unknown,
}

impl Type {
    fn from(s: &str) -> Type {
        match s {
            "struct" => Type::Struct,
            "mod" => Type::Mod,
            "enum" => Type::Enum,
            "fn" => Type::Fn,
            "const" => Type::Const,
            "static" => Type::Static,
            "type" => Type::Type,
            "impl" => Type::Impl,
            "use" => Type::Use,
            _ => Type::Variant,
        }
    }
}

impl Display for Type {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match *self {
            Type::Struct => write!(f, "struct"),
            Type::Mod => write!(f, "mod"),
            Type::Enum => write!(f, "enum"),
            Type::Fn => write!(f, "fn"),
            Type::Const => write!(f, "const"),
            Type::Static => write!(f, "static"),
            Type::Type => write!(f, "type"),
            Type::Variant => write!(f, "variant"),
            Type::Impl => write!(f, "impl"),
            Type::Use => write!(f, "use"),
            _ => write!(f, "?"),
        }
    }
}

fn loop_over_files(path: &str, f: &mut File) {
    match fs::read_dir(path) {
        Ok(it) => {
            for entry in it {
                check_path_type(entry.unwrap().path().to_str().unwrap(), f);
            }
        }
        Err(e) => {
            println!("Error while trying to iterate over {}: {}", path, e);
        }
    }
}

fn move_to(words: &[&str], it: &mut usize, limit: &str, line: &mut usize) {
    while (*it + 1) < words.len() && limit.contains(words[*it + 1]) == false {
        if words[*it] == "\n" {
            *line += 1;
        }
        *it += 1;
    }
    if words[*it] == "\n" {
        *line += 1;
    }
}

fn move_until(words: &[&str], it: &mut usize, limit: &str, line: &mut usize) {
    while (*it + 1) < words.len() && limit != words[*it + 1] {
        if words[*it] == "\n" {
            *line += 1;
        }
        *it += 1;
    }
    if words[*it] == "\n" {
        *line += 1;
    }
}

fn get_impl(words: &[&str], it: &mut usize, line: &mut usize) -> Vec<String> {
    let mut v = vec!();

    while (*it + 1) < words.len() {
        if words[*it] == "\n" {
            *line += 1;
        }
        if words[(*it + 1)] == "{" || words[(*it + 1)] == ";" {
            break;
        }
        *it += 1;
        v.push(words[*it].to_owned());
    }
    v
}

fn add_to_type_scope(current: &Option<TypeStruct>, e: &Option<TypeStruct>) -> Option<TypeStruct> {
    match current {
        &Some(ref c) => {
            match e {
                &Some(ref t) => {
                    let mut tmp = t.clone();
                    tmp.parent = Some(Box::new(c.clone()));
                    Some(tmp)
                }
                &None => {
                    let mut tmp = TypeStruct::empty();
                    tmp.parent = Some(Box::new(c.clone()));
                    Some(tmp)
                }
            }
        },
        &None => match e {
            &Some(ref t) => Some(t.clone()),
            &None => None,
        }
    }
}

fn type_out_scope(current: &Option<TypeStruct>) -> Option<TypeStruct> {
    match current {
        &Some(ref c) => match c.parent {
            Some(ref p) => Some(p.deref().clone()),
            None => None,
        },
        &None => None,
    }
}

fn strip_comments(path: &str, out_file: &mut File) {
    match File::open(path) {
        Ok(mut f) => {
            let mut b_content = String::new();
            f.read_to_string(&mut b_content).unwrap();
            let content = b_content.replace("{", " { ")
                                   .replace("}", " } ")
                                   .replace("///", "/// ")
                                   .replace("//!", "//! ")
                                   .replace("/*!", "/*! ")
                                   .replace("*/", " */")
                                   .replace("\n", " \n ")
                                   .replace("(", " (");
            let b_content : Vec<&str> = b_content.split('\n').collect();
            let words : Vec<&str> = content.split(' ').filter(|s| s.len() > 0).collect();
            let mut it = 0;
            let mut line = 0;
            let mut event_list = vec!();

            while it < words.len() {
                match words[it] {
                    "///" => {
                        event_list.push(EventType::Comment(b_content[line].to_owned()));
                        move_to(&words, &mut it, "\n", &mut line);
                    }
                    "//!" => {
                        event_list.push(EventType::FileComment(b_content[line].to_owned()));
                        move_to(&words, &mut it, "\n", &mut line);
                    }
                    "/*!" => {
                        event_list.push(EventType::FileComment(b_content[line].to_owned()));
                        move_until(&words, &mut it, "*/", &mut line);
                    }
                    "struct" | "mod" | "fn" | "enum" | "const" | "static" | "type" | "use" => {
                        event_list.push(EventType::Type(TypeStruct::new(Type::from(words[it]), words[it + 1])));
                        it += 1;
                    }
                    "impl" => {
                        event_list.push(EventType::Type(TypeStruct::from_args(Type::Impl, get_impl(&words, &mut it, &mut line))));
                    }
                    "{" => {
                        event_list.push(EventType::InScope);
                    }
                    "}" => {
                        event_list.push(EventType::OutScope);
                    }
                    "\n" => {
                        line += 1;
                    }
                    _ => {
                        event_list.push(EventType::Type(TypeStruct::new(Type::Unknown, words[it])));
                    }
                }
                it += 1;
            }
            writeln!(out_file, "=! {}", path).unwrap();
            let mut current : Option<TypeStruct> = None;
            let mut waiting_type : Option<TypeStruct> = None;

            it = 0;
            while it < event_list.len() {
                match event_list[it] {
                    EventType::Type(ref t) => {
                        if t.ty != Type::Unknown {
                            waiting_type = Some(t.clone());
                            //println!("current : {}", t);
                        }
                    }
                    EventType::InScope => {
                        current = add_to_type_scope(&current, &waiting_type);
                        /*if waiting_type.is_some() {
                            println!("in : {}", waiting_type.unwrap());
                        }*/
                        waiting_type = None;
                    }
                    EventType::OutScope => {
                        /*if current.is_some() {
                            println!("out : {}", current.clone().unwrap());
                        }*/
                        current = type_out_scope(&current);
                        waiting_type = None;
                    }
                    EventType::FileComment(ref c) => {
                        let mut comments = format!("{}\n", c);

                        it += 1;
                        while match event_list[it] {
                            EventType::FileComment(ref c) => {
                                comments.push_str(&format!("=/ {}\n", c));
                                true
                            }
                            _ => false,
                        } {
                            it += 1;
                        }
                        write!(out_file, "{}", comments).unwrap();
                    }
                    EventType::Comment(ref c) => {
                        let mut comments = format!("{}\n", c);

                        it += 1;
                        while match event_list[it] {
                            EventType::Comment(ref c) => {
                                comments.push_str(&format!("{}\n", c));
                                true
                            }
                            EventType::Type(_) => {
                                false
                            }
                            _ => panic!("Comments cannot be written everywhere"),
                        } {
                            it += 1;
                        }
                        while match event_list[it] {
                            EventType::Type(ref t) => {
                                match t.ty {
                                    Type::Unknown => true,
                                    _ => {
                                        let tmp = add_to_type_scope(&current, &Some(t.clone()));
                                        write!(out_file, "=| {}\n{}", tmp.unwrap(), comments).unwrap();
                                        false
                                    }
                                }
                            }
                            _ => panic!("An item was expected for this comment: {}", comments),
                        } {
                            it += 1;
                        }
                        it -= 1;
                    }
                }
                it += 1;
            }
        }
        Err(e) => {
            println!("Unable to open \"{}\": {}", path, e);
        }
    }
}

fn check_path_type(path: &str, f: &mut File) {
    match fs::metadata(path) {
        Ok(m) => {
            if m.is_dir() {
                if path == ".." || path == "." {
                    return;
                }
                loop_over_files(path, f);
            } else {
                if path == "./comments.cmts" {
                    return;
                }
                println!("-> {}", path);
                strip_comments(path, f);
            }
        }
        Err(e) => {
            println!("An error occurred: {}", e);
        }
    }
}

fn main() {
    println!("Starting...");
    match fs::remove_file("comments.cmts") { _ => {} }
    match OpenOptions::new().write(true).create(true).truncate(true).open("comments.cmts") {
        Ok(mut f) => {
            loop_over_files(".", &mut f);
            /*for com_entry in comments {
                write!(f, "{}", com_entry).unwrap();
            }*/
            println!("Done !");
        }
        Err(e) => {
            println!("Error while opening \"{}\": {}", "comments.cmts", e);
        }
    }
}

// au lieu de splitter le fichier via '\n', lis le sur "une seule ligne".
// quand tu croises un "///", tu stockes ça comme un commentaire, un "mod", comme un module, etc...
// il faudra compter les '{' / '}' pour voir si t'es toujours danns le truc courant ou non<commit_msg>Handle properly "/*!" file comments<commit_after>// Copyright 2015 Gomez Guillaume
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs::{self, File, OpenOptions};
use std::io::{BufRead, Write, Read};
use std::fmt::{Display, Formatter, Error};
use std::ops::Deref;

enum EventType {
    Comment(String),
    FileComment(String),
    Type(TypeStruct),
    InScope,
    OutScope,
}

struct TypeStruct {
    ty: Type,
    parent: Option<Box<TypeStruct>>,
    name: String,
    args: Vec<String>,
}

impl TypeStruct {
    fn new(ty: Type, name: &str) -> TypeStruct {
        TypeStruct {
            ty: ty,
            name: name.to_owned(),
            args: vec!(),
            parent: None,
        }
    }

    fn from_args(ty: Type, args: Vec<String>) -> TypeStruct {
        TypeStruct {
            ty: ty,
            name: String::new(),
            args: args,
            parent: None,
        }
    }

    fn empty() -> TypeStruct {
        TypeStruct {
            ty: Type::Unknown,
            name: String::new(),
            args: Vec::new(),
            parent: None,
        }
    }
}

impl Clone for TypeStruct {
    fn clone(&self) -> TypeStruct {
        TypeStruct {
            ty: self.ty,
            name: self.name.clone(),
            args: self.args.clone(),
            parent: match self.parent {
                Some(ref p) => Some(Box::new(p.deref().clone())),
                None => None,
            }
        }
    }

    fn clone_from(&mut self, source: &TypeStruct) {
        self.ty = source.ty;
        self.name = source.name.clone();
        self.args = source.args.clone();
        self.parent = match source.parent {
            Some(ref p) => Some(Box::new(p.deref().clone())),
            None => None,
        };
    }
}

impl Display for TypeStruct {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let parent = &self.parent;
        match parent {
            &Some(ref p) => write!(f, "{}({} {}{})", p, self.ty, self.name, self.args.join(" ")),
            &None => write!(f, "{} {}{}", self.ty, self.name, self.args.join(" ")),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum Type {
    Struct,
    Mod,
    Enum,
    Fn,
    Const,
    Static,
    Type,
    Variant,
    Impl,
    Use,
    Unknown,
}

impl Type {
    fn from(s: &str) -> Type {
        match s {
            "struct" => Type::Struct,
            "mod" => Type::Mod,
            "enum" => Type::Enum,
            "fn" => Type::Fn,
            "const" => Type::Const,
            "static" => Type::Static,
            "type" => Type::Type,
            "impl" => Type::Impl,
            "use" => Type::Use,
            _ => Type::Variant,
        }
    }
}

impl Display for Type {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match *self {
            Type::Struct => write!(f, "struct"),
            Type::Mod => write!(f, "mod"),
            Type::Enum => write!(f, "enum"),
            Type::Fn => write!(f, "fn"),
            Type::Const => write!(f, "const"),
            Type::Static => write!(f, "static"),
            Type::Type => write!(f, "type"),
            Type::Variant => write!(f, "variant"),
            Type::Impl => write!(f, "impl"),
            Type::Use => write!(f, "use"),
            _ => write!(f, "?"),
        }
    }
}

fn loop_over_files(path: &str, f: &mut File) {
    match fs::read_dir(path) {
        Ok(it) => {
            for entry in it {
                check_path_type(entry.unwrap().path().to_str().unwrap(), f);
            }
        }
        Err(e) => {
            println!("Error while trying to iterate over {}: {}", path, e);
        }
    }
}

fn move_to(words: &[&str], it: &mut usize, limit: &str, line: &mut usize) {
    while (*it + 1) < words.len() && limit.contains(words[*it + 1]) == false {
        if words[*it] == "\n" {
            *line += 1;
        }
        *it += 1;
    }
    if words[*it] == "\n" {
        *line += 1;
    }
}

fn move_until(words: &[&str], it: &mut usize, limit: &str, line: &mut usize) {
    while (*it + 1) < words.len() && limit != words[*it] {
        if words[*it] == "\n" {
            *line += 1;
        }
        *it += 1;
    }
}

fn get_impl(words: &[&str], it: &mut usize, line: &mut usize) -> Vec<String> {
    let mut v = vec!();

    while (*it + 1) < words.len() {
        if words[*it] == "\n" {
            *line += 1;
        }
        if words[(*it + 1)] == "{" || words[(*it + 1)] == ";" {
            break;
        }
        *it += 1;
        v.push(words[*it].to_owned());
    }
    v
}

fn add_to_type_scope(current: &Option<TypeStruct>, e: &Option<TypeStruct>) -> Option<TypeStruct> {
    match current {
        &Some(ref c) => {
            match e {
                &Some(ref t) => {
                    let mut tmp = t.clone();
                    tmp.parent = Some(Box::new(c.clone()));
                    Some(tmp)
                }
                &None => {
                    let mut tmp = TypeStruct::empty();
                    tmp.parent = Some(Box::new(c.clone()));
                    Some(tmp)
                }
            }
        },
        &None => match e {
            &Some(ref t) => Some(t.clone()),
            &None => None,
        }
    }
}

fn type_out_scope(current: &Option<TypeStruct>) -> Option<TypeStruct> {
    match current {
        &Some(ref c) => match c.parent {
            Some(ref p) => Some(p.deref().clone()),
            None => None,
        },
        &None => None,
    }
}

fn strip_comments(path: &str, out_file: &mut File) {
    match File::open(path) {
        Ok(mut f) => {
            let mut b_content = String::new();
            f.read_to_string(&mut b_content).unwrap();
            let content = b_content.replace("{", " { ")
                                   .replace("}", " } ")
                                   .replace("///", "/// ")
                                   .replace("//!", "//! ")
                                   .replace("/*!", "/*! ")
                                   .replace("*/", " */")
                                   .replace("\n", " \n ")
                                   .replace("(", " (");
            let b_content : Vec<&str> = b_content.split('\n').collect();
            let words : Vec<&str> = content.split(' ').filter(|s| s.len() > 0).collect();
            let mut it = 0;
            let mut line = 0;
            let mut event_list = vec!();

            while it < words.len() {
                match words[it] {
                    "///" => {
                        event_list.push(EventType::Comment(b_content[line].to_owned()));
                        move_to(&words, &mut it, "\n", &mut line);
                    }
                    "//!" => {
                        event_list.push(EventType::FileComment(b_content[line].to_owned()));
                        move_to(&words, &mut it, "\n", &mut line);
                    }
                    "/*!" => {
                        let mark = line;
                        move_until(&words, &mut it, "*/", &mut line);
                        for pos in mark..line {
                            event_list.push(EventType::FileComment(b_content[pos].to_owned()));
                        }
                        event_list.push(EventType::FileComment("*/".to_owned()));
                    }
                    "struct" | "mod" | "fn" | "enum" | "const" | "static" | "type" | "use" => {
                        event_list.push(EventType::Type(TypeStruct::new(Type::from(words[it]), words[it + 1])));
                        it += 1;
                    }
                    "impl" => {
                        event_list.push(EventType::Type(TypeStruct::from_args(Type::Impl, get_impl(&words, &mut it, &mut line))));
                    }
                    "{" => {
                        event_list.push(EventType::InScope);
                    }
                    "}" => {
                        event_list.push(EventType::OutScope);
                    }
                    "\n" => {
                        line += 1;
                    }
                    _ => {
                        event_list.push(EventType::Type(TypeStruct::new(Type::Unknown, words[it])));
                    }
                }
                it += 1;
            }
            writeln!(out_file, "=! {}", path).unwrap();
            let mut current : Option<TypeStruct> = None;
            let mut waiting_type : Option<TypeStruct> = None;

            it = 0;
            while it < event_list.len() {
                match event_list[it] {
                    EventType::Type(ref t) => {
                        if t.ty != Type::Unknown {
                            waiting_type = Some(t.clone());
                            //println!("current : {}", t);
                        }
                    }
                    EventType::InScope => {
                        current = add_to_type_scope(&current, &waiting_type);
                        /*if waiting_type.is_some() {
                            println!("in : {}", waiting_type.unwrap());
                        }*/
                        waiting_type = None;
                    }
                    EventType::OutScope => {
                        /*if current.is_some() {
                            println!("out : {}", current.clone().unwrap());
                        }*/
                        current = type_out_scope(&current);
                        waiting_type = None;
                    }
                    EventType::FileComment(ref c) => {
                        let mut comments = format!("=/ {}\n", c);

                        it += 1;
                        while match event_list[it] {
                            EventType::FileComment(ref c) => {
                                comments.push_str(&format!("=/ {}\n", c));
                                true
                            }
                            _ => false,
                        } {
                            it += 1;
                        }
                        write!(out_file, "{}", comments).unwrap();
                    }
                    EventType::Comment(ref c) => {
                        let mut comments = format!("{}\n", c);

                        it += 1;
                        while match event_list[it] {
                            EventType::Comment(ref c) => {
                                comments.push_str(&format!("{}\n", c));
                                true
                            }
                            EventType::Type(_) => {
                                false
                            }
                            _ => panic!("Comments cannot be written everywhere"),
                        } {
                            it += 1;
                        }
                        while match event_list[it] {
                            EventType::Type(ref t) => {
                                match t.ty {
                                    Type::Unknown => true,
                                    _ => {
                                        let tmp = add_to_type_scope(&current, &Some(t.clone()));
                                        write!(out_file, "=| {}\n{}", tmp.unwrap(), comments).unwrap();
                                        false
                                    }
                                }
                            }
                            _ => panic!("An item was expected for this comment: {}", comments),
                        } {
                            it += 1;
                        }
                        it -= 1;
                    }
                }
                it += 1;
            }
        }
        Err(e) => {
            println!("Unable to open \"{}\": {}", path, e);
        }
    }
}

fn check_path_type(path: &str, f: &mut File) {
    match fs::metadata(path) {
        Ok(m) => {
            if m.is_dir() {
                if path == ".." || path == "." {
                    return;
                }
                loop_over_files(path, f);
            } else {
                if path == "./comments.cmts" {
                    return;
                }
                println!("-> {}", path);
                strip_comments(path, f);
            }
        }
        Err(e) => {
            println!("An error occurred: {}", e);
        }
    }
}

fn main() {
    println!("Starting...");
    match fs::remove_file("comments.cmts") { _ => {} }
    match OpenOptions::new().write(true).create(true).truncate(true).open("comments.cmts") {
        Ok(mut f) => {
            loop_over_files(".", &mut f);
            /*for com_entry in comments {
                write!(f, "{}", com_entry).unwrap();
            }*/
            println!("Done !");
        }
        Err(e) => {
            println!("Error while opening \"{}\": {}", "comments.cmts", e);
        }
    }
}

// au lieu de splitter le fichier via '\n', lis le sur "une seule ligne".
// quand tu croises un "///", tu stockes ça comme un commentaire, un "mod", comme un module, etc...
// il faudra compter les '{' / '}' pour voir si t'es toujours danns le truc courant ou non<|endoftext|>


<commit_before>#![feature(box_syntax)]

extern crate bins as lib;
extern crate hyper;
extern crate hyper_openssl;
extern crate url;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate clap;
extern crate toml;
extern crate flate2;
#[macro_use]
extern crate log;
extern crate time;

macro_rules! option {
  ($e: expr) => {{
    match $e {
      Some(x) => x,
      None => return None
    }
  }}
}

mod bins;
mod config;
mod logger;

use config::*;

use lib::*;
use lib::error::*;
use lib::files::{Paste, UploadFile};

use clap::{App, Arg, ArgMatches};
use flate2::read::GzDecoder;

use std::path::{Path, PathBuf};
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom};
use std::io::{Read, Write};
use std::io::Result as IoResult;
use std::error::Error;
use std::collections::HashMap;
use std::sync::Arc;

use log::LogLevel;

use url::Url;

fn main() {
  std::process::exit(inner());
}

fn inner() -> i32 {
  let config = match get_config() {
    Ok(c) => c,
    Err(e) => {
      println!("could not create or load bins config file: {}", e);
      for error in error_parents(&e) {
        println!("{}", error);
      }
      return 1;
    }
  };

  let matches = App::new("bins")
    .about("A tool for pasting from the terminal")
    .author(crate_authors!())
    .version(crate_version!())
    .version_message("print version information and exit")
    .version_short("v")
    .help_message("print help information and exit")
    .arg(Arg::with_name("inputs")
      .help("inputs to the program, either files or URLs")
      .takes_value(true)
      .value_name("input")
      .multiple(true))
    .arg(Arg::with_name("debug")
      .long("debug")
      .short("d")
      .help("enable debug output"))
    .arg(Arg::with_name("bin")
      .long("bin")
      .short("b")
      .help("specify the upload bin")
      .required(config.defaults.bin.is_none())
      .takes_value(true)
      .value_name("bin")
      .possible_values(&["hastebin", "sprunge", "gist"]))
    .arg(Arg::with_name("public")
      .long("public")
      .short("P")
      .help("set the paste to be public")
      .conflicts_with("private"))
    .arg(Arg::with_name("private")
      .long("private")
      .short("p")
      .help("set the paste to be private"))
    .arg(Arg::with_name("authed")
      .long("authed")
      .short("a")
      .help("set the paste to be uploaded while authenticated")
      .conflicts_with("anonymous"))
    .arg(Arg::with_name("anonymous")
      .long("anonymous")
      .short("A")
      .help("set the paste to be uploaded while not authenticated"))
    .arg(Arg::with_name("json")
      .long("json")
      .short("j")
      .help("make bins output JSON information"))
    .get_matches();

  let level = if matches.is_present("debug") {
    LogLevel::Debug
  } else {
    LogLevel::Info
  };
  if let Err(e) = logger::Logger::new(level).init() {
    println!("could not initialize logger: {}", e);
  }

  let mut cli_options = CommandLineOptions::default();

  if matches.is_present("public") {
    cli_options.private = Some(false);
  } else if matches.is_present("private") {
    cli_options.private = Some(true);
  }

  if matches.is_present("authed") {
    cli_options.authed = Some(true);
  } else if matches.is_present("anonymous") {
    cli_options.authed = Some(false);
  }

  if matches.is_present("json") {
    cli_options.json = Some(true);
  }

  let config = Arc::new(config);
  let cli_options = Arc::new(cli_options);

  let bins: HashMap<&str, Box<Bin>> = {
    let mut map: HashMap<&str, Box<Bin>> = HashMap::new();
    map.insert("sprunge", box bins::Sprunge::new(config.clone(), cli_options.clone()));
    map.insert("hastebin", box bins::Hastebin::new(config.clone(), cli_options.clone()));
    map.insert("gist", box bins::Gist::new(config.clone(), cli_options.clone()));
    map
  };

  let b = Bins {
    bins: bins,
    config: config,
    cli_options: cli_options,
    matches: matches
  };

  b.main()
}

struct Bins<'a> {
  bins: HashMap<&'static str, Box<Bin>>,
  config: Arc<Config>,
  cli_options: Arc<CommandLineOptions>,
  matches: ArgMatches<'a>
}

impl<'a> Bins<'a> {
  fn main(&self) -> i32 {
    let inputs: Option<Vec<&str>> = self.matches.values_of("inputs").map(|x| x.collect());
    if let Some(ref is) = inputs {
      if !is.is_empty() {
        let url: Result<Url> = Url::parse(&is[0]).map_err(BinsError::UrlParse);
        if let Ok(u) = url {
          return self.download(u, if is.len() > 1 { Some(&is[1..]) } else { None });
        }
      }
    }
    self.upload(inputs)
  }

  fn upload(&self, inputs: Option<Vec<&str>>) -> i32 {
    let bin_name = self.matches.value_of("bin").map(|x| x.to_owned()).or_else(|| self.config.defaults.bin.clone()).expect("no bin specified");
    let possible_bin = self.bins.get(bin_name.as_str());
    let bin = match possible_bin {
      Some(b) => b,
      None => {
        error!("there is no bin called \"{}\"", bin_name);
        return 1;
      }
    };

    let bin_features = bin.features();
    let features = {
      let mut map = HashMap::new();
      map.insert(BinFeature::Private, self.cli_options.private);
      map.insert(BinFeature::Public, self.cli_options.private.map(|x| !x));
      map.insert(BinFeature::Authed, self.cli_options.authed);
      map.insert(BinFeature::Anonymous, self.cli_options.authed.map(|x| !x));
      map
    };
    for (feature, status) in features {
      if let Some(true) = status {
        if !bin_features.contains(&feature) {
          if let Some(true) = self.config.safety.warn_on_unsupported {
            warn!("{} does not support {} pastes", bin.name(), feature);
          }
          if let Some(true) = self.config.safety.cancel_on_unsupported {
            error!("bins stopped because an unsupported feature was used with {}", bin.name());
            return 1;
          }
        }
      }
    }

    let upload_files = match inputs {
      Some(v) => get_upload_files(v),
      None => get_stdin().map(|x| vec![x])
    };
    let upload_files = match upload_files {
      Ok(u) => u,
      Err(e) => {
        error!("could not get input: {}", e);
        return 1;
      }
    };
    match bin.upload(&upload_files) {
      Err(e) => {
        error!("error uploading to {}: {}", bin.name(), e);
        for error in error_parents(&e) {
          error!("{}", error);
        }
        return 1;
      },
      Ok(u) => println!("{}", u)
    }
    0
  }

  fn download(&self, url: Url, names: Option<&[&str]>) -> i32 {
    let host = match url.host_str() {
      Some(h) => h,
      None => {
        error!("invalid url (no host): {}", url.as_str());
        return 1;
      }
    };
    let (is_html_url, bin) = match self.bins.iter().find(|&(_, b)| b.raw_host() == host) {
      Some(b) => (false, b.1),
      None => {
        match self.bins.iter().find(|&(_, b)| b.html_host() == host) {
          Some(b) => (true, b.1),
          None => {
            error!("no bin uses the hostname {}", host);
            return 1;
          }
        }
      }
    };
    let id = if is_html_url {
      bin.id_from_html_url(url.as_str())
    } else {
      bin.id_from_raw_url(url.as_str())
    };
    let id = match id {
      Some(i) => i,
      None => {
        error!("could not extract paste ID from {}", url.as_str());
        return 1;
      }
    };
    let download = match bin.download(&id, names) {
      Ok(d) => d,
      Err(e) => {
        error!("could not download ID {}: {}", id, e);
        return 1;
      }
    };
    if let Some(true) = self.cli_options.json {
      match serde_json::to_string(&download) {
        Ok(j) => println!("{}", j),
        Err(e) => {
          error!("error converting download to json: {}", e);
          return 1;
        }
      }
    } else {
      match download {
        Paste::Single(f) => {
          println!("{}", f.content);
        },
        Paste::MultiDownloaded(fs) => {
          for f in fs {
            println!("==> {} <==\n\n{}", f.name.name(), f.content);
          }
        }
      }
    }
    0
  }
}

fn get_stdin() -> Result<UploadFile> {
  let mut content = String::new();
  let mut stdin = std::io::stdin();
  stdin.read_to_string(&mut content).map_err(BinsError::Io)?;
  Ok(UploadFile::new("stdin".to_owned(), content))
}

fn get_upload_files(inputs: Vec<&str>) -> Result<Vec<UploadFile>> {
  let files: Vec<(&str, File)> = inputs.into_iter()
    .map(|f| File::open(f).map(|x| (f, x)))
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  let contents: Vec<(&str, String)> = files.into_iter()
    .map(|(n, mut f)| {
      let mut c = String::new();
      f.read_to_string(&mut c).map(|_| (n, c))
    })
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  Ok(contents.into_iter().map(|(n, c)| UploadFile::new(n.to_owned(), c)).collect())
}

fn error_parents(error: &Error) -> Vec<&Error> {
  let mut parents = Vec::new();
  let mut last_error = error;
  loop {
    match last_error.cause() {
      None => break,
      Some(e) => {
        parents.push(e);
        last_error = e;
      }
    }
  }
  parents
}

fn get_config() -> Result<Config> {
  let mut f = match find_config_path() {
    Some(p) => File::open(p).map_err(BinsError::Io)?,
    None => create_config_file()?
  };
  let mut content = String::new();
  f.read_to_string(&mut content).map_err(BinsError::Io)?;
  toml::from_str(&content).map_err(BinsError::Toml)
}

fn create_xdg_config_file() -> Result<File> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_path = Path::new(&xdg_dir);
    let xdg_config_path = xdg_path.join("bins.cfg");
    if xdg_path.exists() && xdg_path.is_dir() && !xdg_config_path.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(xdg_config_path)
        .map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_home_config_file() -> Result<File> {
  if let Ok(home_dir) = std::env::var("HOME") {
    let home = Path::new(&home_dir);
    let home_folder = home.join(".config");
    let home_folder_config = home_folder.join("bins.cfg");
    if home_folder.exists() && home_folder.is_dir() && !home_folder_config.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(home_folder_config)
        .map_err(BinsError::Io);
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home.exists() && home.is_dir() && !home_config.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(home_config)
        .map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_config_file() -> Result<File> {
  let mut f = match create_xdg_config_file() {
    Ok(f) => f,
    Err(_) => match create_home_config_file() {
      Ok(hf) => hf,
      Err(_) => return Err(BinsError::Config)
    }
  };
  let mut default_config = String::new();
  GzDecoder::new(config::DEFAULT_CONFIG_GZIP)
    .map_err(BinsError::Io)?
    .read_to_string(&mut default_config)
    .map_err(BinsError::Io)?;
  f.write_all(default_config.as_bytes()).map_err(BinsError::Io)?;
  f.seek(SeekFrom::Start(0)).map_err(BinsError::Io)?;
  Ok(f)
}

fn find_config_path() -> Option<PathBuf> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_config_path = Path::new(&xdg_dir).join("bins.cfg");
    if xdg_config_path.exists() {
      return Some(xdg_config_path.to_owned());
    }
  }
  if let Ok(home_dir) = std::env::var("HOME") {
    let home_config_folder = Path::new(&home_dir).join(".config").join("bins.cfg");
    if home_config_folder.exists() {
      return Some(home_config_folder.to_owned());
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home_config.exists() {
      return Some(home_config.to_owned());
    }
  }
  None
}
<commit_msg>fix: use file name, not entire path<commit_after>#![feature(box_syntax)]

extern crate bins as lib;
extern crate hyper;
extern crate hyper_openssl;
extern crate url;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate clap;
extern crate toml;
extern crate flate2;
#[macro_use]
extern crate log;
extern crate time;

macro_rules! option {
  ($e: expr) => {{
    match $e {
      Some(x) => x,
      None => return None
    }
  }}
}

mod bins;
mod config;
mod logger;

use config::*;

use lib::*;
use lib::error::*;
use lib::files::{Paste, UploadFile};

use clap::{App, Arg, ArgMatches};
use flate2::read::GzDecoder;

use std::path::{Path, PathBuf};
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom};
use std::io::{Read, Write};
use std::io::Result as IoResult;
use std::error::Error;
use std::collections::HashMap;
use std::sync::Arc;

use log::LogLevel;

use url::Url;

fn main() {
  std::process::exit(inner());
}

fn inner() -> i32 {
  let config = match get_config() {
    Ok(c) => c,
    Err(e) => {
      println!("could not create or load bins config file: {}", e);
      for error in error_parents(&e) {
        println!("{}", error);
      }
      return 1;
    }
  };

  let matches = App::new("bins")
    .about("A tool for pasting from the terminal")
    .author(crate_authors!())
    .version(crate_version!())
    .version_message("print version information and exit")
    .version_short("v")
    .help_message("print help information and exit")
    .arg(Arg::with_name("inputs")
      .help("inputs to the program, either files or URLs")
      .takes_value(true)
      .value_name("input")
      .multiple(true))
    .arg(Arg::with_name("debug")
      .long("debug")
      .short("d")
      .help("enable debug output"))
    .arg(Arg::with_name("bin")
      .long("bin")
      .short("b")
      .help("specify the upload bin")
      .required(config.defaults.bin.is_none())
      .takes_value(true)
      .value_name("bin")
      .possible_values(&["hastebin", "sprunge", "gist"]))
    .arg(Arg::with_name("public")
      .long("public")
      .short("P")
      .help("set the paste to be public")
      .conflicts_with("private"))
    .arg(Arg::with_name("private")
      .long("private")
      .short("p")
      .help("set the paste to be private"))
    .arg(Arg::with_name("authed")
      .long("authed")
      .short("a")
      .help("set the paste to be uploaded while authenticated")
      .conflicts_with("anonymous"))
    .arg(Arg::with_name("anonymous")
      .long("anonymous")
      .short("A")
      .help("set the paste to be uploaded while not authenticated"))
    .arg(Arg::with_name("json")
      .long("json")
      .short("j")
      .help("make bins output JSON information"))
    .get_matches();

  let level = if matches.is_present("debug") {
    LogLevel::Debug
  } else {
    LogLevel::Info
  };
  if let Err(e) = logger::Logger::new(level).init() {
    println!("could not initialize logger: {}", e);
  }

  let mut cli_options = CommandLineOptions::default();

  if matches.is_present("public") {
    cli_options.private = Some(false);
  } else if matches.is_present("private") {
    cli_options.private = Some(true);
  }

  if matches.is_present("authed") {
    cli_options.authed = Some(true);
  } else if matches.is_present("anonymous") {
    cli_options.authed = Some(false);
  }

  if matches.is_present("json") {
    cli_options.json = Some(true);
  }

  let config = Arc::new(config);
  let cli_options = Arc::new(cli_options);

  let bins: HashMap<&str, Box<Bin>> = {
    let mut map: HashMap<&str, Box<Bin>> = HashMap::new();
    map.insert("sprunge", box bins::Sprunge::new(config.clone(), cli_options.clone()));
    map.insert("hastebin", box bins::Hastebin::new(config.clone(), cli_options.clone()));
    map.insert("gist", box bins::Gist::new(config.clone(), cli_options.clone()));
    map
  };

  let b = Bins {
    bins: bins,
    config: config,
    cli_options: cli_options,
    matches: matches
  };

  b.main()
}

struct Bins<'a> {
  bins: HashMap<&'static str, Box<Bin>>,
  config: Arc<Config>,
  cli_options: Arc<CommandLineOptions>,
  matches: ArgMatches<'a>
}

impl<'a> Bins<'a> {
  fn main(&self) -> i32 {
    let inputs: Option<Vec<&str>> = self.matches.values_of("inputs").map(|x| x.collect());
    if let Some(ref is) = inputs {
      if !is.is_empty() {
        let url: Result<Url> = Url::parse(&is[0]).map_err(BinsError::UrlParse);
        if let Ok(u) = url {
          return self.download(u, if is.len() > 1 { Some(&is[1..]) } else { None });
        }
      }
    }
    self.upload(inputs)
  }

  fn upload(&self, inputs: Option<Vec<&str>>) -> i32 {
    let bin_name = self.matches.value_of("bin").map(|x| x.to_owned()).or_else(|| self.config.defaults.bin.clone()).expect("no bin specified");
    let possible_bin = self.bins.get(bin_name.as_str());
    let bin = match possible_bin {
      Some(b) => b,
      None => {
        error!("there is no bin called \"{}\"", bin_name);
        return 1;
      }
    };

    let bin_features = bin.features();
    let features = {
      let mut map = HashMap::new();
      map.insert(BinFeature::Private, self.cli_options.private);
      map.insert(BinFeature::Public, self.cli_options.private.map(|x| !x));
      map.insert(BinFeature::Authed, self.cli_options.authed);
      map.insert(BinFeature::Anonymous, self.cli_options.authed.map(|x| !x));
      map
    };
    for (feature, status) in features {
      if let Some(true) = status {
        if !bin_features.contains(&feature) {
          if let Some(true) = self.config.safety.warn_on_unsupported {
            warn!("{} does not support {} pastes", bin.name(), feature);
          }
          if let Some(true) = self.config.safety.cancel_on_unsupported {
            error!("bins stopped because an unsupported feature was used with {}", bin.name());
            return 1;
          }
        }
      }
    }

    let upload_files = match inputs {
      Some(v) => get_upload_files(v),
      None => get_stdin().map(|x| vec![x])
    };
    let upload_files = match upload_files {
      Ok(u) => u,
      Err(e) => {
        error!("could not get input: {}", e);
        return 1;
      }
    };
    match bin.upload(&upload_files) {
      Err(e) => {
        error!("error uploading to {}: {}", bin.name(), e);
        for error in error_parents(&e) {
          error!("{}", error);
        }
        return 1;
      },
      Ok(u) => println!("{}", u)
    }
    0
  }

  fn download(&self, url: Url, names: Option<&[&str]>) -> i32 {
    let host = match url.host_str() {
      Some(h) => h,
      None => {
        error!("invalid url (no host): {}", url.as_str());
        return 1;
      }
    };
    let (is_html_url, bin) = match self.bins.iter().find(|&(_, b)| b.raw_host() == host) {
      Some(b) => (false, b.1),
      None => {
        match self.bins.iter().find(|&(_, b)| b.html_host() == host) {
          Some(b) => (true, b.1),
          None => {
            error!("no bin uses the hostname {}", host);
            return 1;
          }
        }
      }
    };
    let id = if is_html_url {
      bin.id_from_html_url(url.as_str())
    } else {
      bin.id_from_raw_url(url.as_str())
    };
    let id = match id {
      Some(i) => i,
      None => {
        error!("could not extract paste ID from {}", url.as_str());
        return 1;
      }
    };
    let download = match bin.download(&id, names) {
      Ok(d) => d,
      Err(e) => {
        error!("could not download ID {}: {}", id, e);
        return 1;
      }
    };
    if let Some(true) = self.cli_options.json {
      match serde_json::to_string(&download) {
        Ok(j) => println!("{}", j),
        Err(e) => {
          error!("error converting download to json: {}", e);
          return 1;
        }
      }
    } else {
      match download {
        Paste::Single(f) => {
          println!("{}", f.content);
        },
        Paste::MultiDownloaded(fs) => {
          for f in fs {
            println!("==> {} <==\n\n{}", f.name.name(), f.content);
          }
        }
      }
    }
    0
  }
}

fn get_stdin() -> Result<UploadFile> {
  let mut content = String::new();
  let mut stdin = std::io::stdin();
  stdin.read_to_string(&mut content).map_err(BinsError::Io)?;
  Ok(UploadFile::new("stdin".to_owned(), content))
}

fn get_upload_files(inputs: Vec<&str>) -> Result<Vec<UploadFile>> {
  let files: Option<Vec<(&str, File)>> = inputs.into_iter()
    .map(|f| File::open(f).map(|x| Path::new(f).file_name().and_then(|f| f.to_str()).map(|of| (of, x))))
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  let files = match files {
    Some(f) => f,
    None => {
      error!("one or more inputs did not have a file name or did not have a valid utf-8 file name");
      return Err(BinsError::Other);
    }
  };
  let contents: Vec<(&str, String)> = files.into_iter()
    .map(|(n, mut f)| {
      let mut c = String::new();
      f.read_to_string(&mut c).map(|_| (n, c))
    })
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  Ok(contents.into_iter().map(|(n, c)| UploadFile::new(n.to_owned(), c)).collect())
}

fn error_parents(error: &Error) -> Vec<&Error> {
  let mut parents = Vec::new();
  let mut last_error = error;
  loop {
    match last_error.cause() {
      None => break,
      Some(e) => {
        parents.push(e);
        last_error = e;
      }
    }
  }
  parents
}

fn get_config() -> Result<Config> {
  let mut f = match find_config_path() {
    Some(p) => File::open(p).map_err(BinsError::Io)?,
    None => create_config_file()?
  };
  let mut content = String::new();
  f.read_to_string(&mut content).map_err(BinsError::Io)?;
  toml::from_str(&content).map_err(BinsError::Toml)
}

fn create_xdg_config_file() -> Result<File> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_path = Path::new(&xdg_dir);
    let xdg_config_path = xdg_path.join("bins.cfg");
    if xdg_path.exists() && xdg_path.is_dir() && !xdg_config_path.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(xdg_config_path)
        .map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_home_config_file() -> Result<File> {
  if let Ok(home_dir) = std::env::var("HOME") {
    let home = Path::new(&home_dir);
    let home_folder = home.join(".config");
    let home_folder_config = home_folder.join("bins.cfg");
    if home_folder.exists() && home_folder.is_dir() && !home_folder_config.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(home_folder_config)
        .map_err(BinsError::Io);
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home.exists() && home.is_dir() && !home_config.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(home_config)
        .map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_config_file() -> Result<File> {
  let mut f = match create_xdg_config_file() {
    Ok(f) => f,
    Err(_) => match create_home_config_file() {
      Ok(hf) => hf,
      Err(_) => return Err(BinsError::Config)
    }
  };
  let mut default_config = String::new();
  GzDecoder::new(config::DEFAULT_CONFIG_GZIP)
    .map_err(BinsError::Io)?
    .read_to_string(&mut default_config)
    .map_err(BinsError::Io)?;
  f.write_all(default_config.as_bytes()).map_err(BinsError::Io)?;
  f.seek(SeekFrom::Start(0)).map_err(BinsError::Io)?;
  Ok(f)
}

fn find_config_path() -> Option<PathBuf> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_config_path = Path::new(&xdg_dir).join("bins.cfg");
    if xdg_config_path.exists() {
      return Some(xdg_config_path.to_owned());
    }
  }
  if let Ok(home_dir) = std::env::var("HOME") {
    let home_config_folder = Path::new(&home_dir).join(".config").join("bins.cfg");
    if home_config_folder.exists() {
      return Some(home_config_folder.to_owned());
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home_config.exists() {
      return Some(home_config.to_owned());
    }
  }
  None
}
<|endoftext|>


<commit_before>use std::net::*;
use std::io::prelude::*;
use std::mem::*;
use std::vec::Vec;

/*
TODO:
1. разобраться с модулями
2. реализовать оставшиеся операции
3. преобразовать к структуре с методами
3. демо-приложение
*/

#[repr(C)]
enum E {
    I(i32),
    D(f64),
    S(String),
    None,
}

impl E {
	fn println(&self) {
		match self {
			&E::I(ref i) => println!("Int: {}", i),
			&E::D(ref d) => println!("Double: {}", d),
			&E::S(ref s) => println!("String: {}", s),
			&E::None => println!("Wildcard"),
		}
	}
}

const PUT: i32 = 0;
const GET: i32 = 1;
const READ: i32 = 2;
const GET_NB: i32 = 3;
const READ_NB: i32 = 4;
const DUMP: i32 = 5;
const COUNT: i32 = 6;
const REPLACE: i32 = 8;
/*
const LOG: i32 = 7;
*/

const asciiS: i32 = 115;
const asciiI: i32 = 105;
const asciiD: i32 = 100;
const asciiQ: i32 = 63;
const asciiT: i32 = 116;

fn ti(i: i32) -> [u8; 4] {
	let buff: [u8; 4];
	unsafe {
		buff = transmute(i);
	}
	return buff;
}

fn ctoi(arr: &mut [u8; 24], mut val: i32, offset: isize) {
	unsafe {
		std::ptr::copy_nonoverlapping(&mut val as *mut _ as *mut u8, arr.as_ptr().offset(offset) as *mut u8, std::mem::size_of::<i32>());
	}
}

fn ciot(arr:&[u8; 24], offset:isize) -> i32 {
	let mut val: i32 = 0;

	unsafe {
		std::ptr::copy_nonoverlapping(arr.as_ptr().offset(offset) as *mut u8, &mut val as *mut _ as *mut u8, std::mem::size_of::<i32>());
	}

	return val;
}

fn cdot(arr:&[u8; 24], offset:isize) -> f64 {
	let mut val: f64 = 0.;
	unsafe {
		std::ptr::copy_nonoverlapping(arr.as_ptr().offset(offset) as *mut u8, &mut val as *mut _ as *mut u8, std::mem::size_of::<f64>());
	}
	return val;
}

fn ctod(arr: &mut [u8; 24], mut val: f64, offset: isize) {
	unsafe {
		std::ptr::copy_nonoverlapping(&mut val as *mut _ as *mut u8, arr.as_ptr().offset(offset) as *mut u8, std::mem::size_of::<f64>());
	}
}

fn send_tuple(tuple: &Vec<E>, stream: &mut TcpStream) {

    stream.write_all(& (ti(tuple.len() as i32)));

    let mut string_length:i32 = 0;

    for elem in tuple {
    	match elem {
    		&E::S(ref s) => string_length += s.len() as i32,
    		_ => {}
    	}
    }

    stream.write_all(&ti(string_length));

    string_length = 0;

    let mut buff: [u8; 24] = [0; 24];

    for elem in tuple {
    	match elem {
    		&E::I(ref i) => {
				//tag
   	 			ctoi(&mut buff, asciiI, 0);
   	 			//union
   	 			ctoi(&mut buff, *i, 8);

   	 			stream.write_all(&buff);
    		},
    		&E::D(ref d) => {
    			//tag
   	 			ctoi(&mut buff, asciiD, 0);
   	 			//union
   	 			ctod(&mut buff, *d, 8);
   	 			stream.write_all(&buff);

    		},
    		&E::S(ref s) => {
    			//tag
   	 			ctoi(&mut buff, asciiS, 0);

   	 			//union
   	 			ctoi(&mut buff, string_length, 8);
   	 			ctoi(&mut buff, s.len() as i32, 16);

   	 			//tuple pointer
   	 			stream.write_all(&buff);

   	 			string_length += s.len() as i32;
    		}
    		&E::None => {
    			ctoi(&mut buff, asciiQ, 0);

    			stream.write_all(&buff);
    		}
    	}
    }

    for elem in tuple {
    	match elem {
    		&E::S(ref s) => {
    			stream.write_all(s.as_bytes());
    		}
    		_ => {}
    	}
    }
}

fn recv_tuple(stream: &mut TcpStream) -> Vec<E> {

	struct str_desc {
		used: bool,
		offset: i32,
		len: i32,
	}

	let mut tuple: Vec<E> = Vec::<E>::new();
	let mut str_descs: Vec<str_desc> = Vec::<str_desc>::new();

	let mut buff_4:[u8; 4] = [0; 4];
	let mut buff_8:[u8; 8] = [0; 8];
	let mut buff:[u8; 24] = [0; 24];

	stream.read_exact(&mut buff_4);
	let num_elements:i32 = unsafe { transmute(buff_4) };

	//non-blocking reads/writes
	if num_elements == -1 {
		return tuple;
	}

	stream.read_exact(&mut buff_4);
	let string_length:i32 = unsafe { transmute(buff_4) };

	for i in 0..num_elements {

		stream.read_exact(&mut buff);

		let tag: i32 = ciot(&buff, 0);

		match tag {
			asciiI => {
				tuple.push(E::I(ciot(&buff, 8)));
				str_descs.push(str_desc { used: false, offset: 0, len: 0} );
			},
			asciiD => {
				tuple.push(E::D(cdot(&buff, 8)));
				str_descs.push(str_desc { used: false, offset: 0, len: 0} );
			},
			asciiS => {
				str_descs.push(str_desc {
						used: true,
						offset: ciot(&buff, 8),
						len: ciot(&buff, 16),
					});
				tuple.push(E::S("".to_string()));
			},
			asciiQ => {
				str_descs.push(str_desc { used: false, offset: 0, len: 0} );
			},
			_ => {

			}
		}
	}

	if string_length != 0 {
		let mut string_space:Vec<u8> = vec![0; string_length as usize];
		stream.read_exact(&mut string_space);

		let mut str_slice = string_space.as_slice();
		for i in 0..num_elements {
			if str_descs[i as usize].used == true {
				tuple[i as usize] = E::S(String::from_utf8(
					string_space[
						(str_descs[i as usize].offset as usize)..
						(str_descs[i as usize].offset + str_descs[i as usize].len) as usize].to_vec()).unwrap());
			}
		}

	}

	return tuple;
}

fn put_tuple(tuple: &Vec<E>, connection: &SocketAddr) {
    let mut stream_err = TcpStream::connect(connection);
    match stream_err {
    	Ok(mut stream) => {
    		stream.write_all(&ti(PUT));
			send_tuple(tuple, &mut stream);
			stream.shutdown(Shutdown::Write);
    	}
    	Err(why) => {
    		println!("{}", why);
    	}
    }
}

fn get_tuple(tuple: &Vec<E>, connection: &SocketAddr) -> Vec<E>
{
	let mut stream_err = TcpStream::connect(connection);
	match stream_err {
		Ok(mut stream) => {
			stream.write_all(&ti(GET));

			send_tuple(tuple, &mut stream);

			stream.shutdown(Shutdown::Write);

			return recv_tuple(&mut stream);
		}
		Err(why) => {
			println!("{}", why);
			return Vec::<E>::new();
		}
	}
}

fn read_tuple(tuple: &Vec<E>, connection: &SocketAddr) -> Vec<E>
{
	let mut stream_err = TcpStream::connect(connection);
	match stream_err {
		Ok(mut stream) => {
			stream.write_all(&ti(READ));

			send_tuple(tuple, &mut stream);

			stream.shutdown(Shutdown::Write);

			return recv_tuple(&mut stream);
		}
		Err(why) => {
			println!("{}", why);
			return Vec::<E>::new();
		}
	}
}

fn get_nb_tuple(tuple: &Vec<E>, connection: &SocketAddr) -> Vec<E>
{
	let mut stream_err = TcpStream::connect(connection);
	match stream_err {
		Ok(mut stream) => {
			stream.write_all(&ti(GET_NB));

			send_tuple(tuple, &mut stream);

			stream.shutdown(Shutdown::Write);

			return recv_tuple(&mut stream);
		}
		Err(why) => {
			println!("{}", why);
			return Vec::<E>::new();
		}
	}
}

fn read_nb_tuple(tuple: &Vec<E>, connection: &SocketAddr) -> Vec<E>
{
	let mut stream_err = TcpStream::connect(connection);
	match stream_err {
		Ok(mut stream) => {
			stream.write_all(&ti(READ_NB));

			send_tuple(tuple, &mut stream);

			stream.shutdown(Shutdown::Write);

			return recv_tuple(&mut stream);
		}
		Err(why) => {
			println!("{}", why);
			return Vec::<E>::new();
		}
	}
}

fn read_all_tuples(tuples: &Vec<Vec<E>>, connection: &SocketAddr) -> Vec<Vec<E>>
{
	let mut stream_err = TcpStream::connect(connection);
	match stream_err {
		Ok(mut stream) => {
			stream.write_all(&ti(DUMP));
			let count: i32 = tuples.len() as i32;
			stream.write_all(&ti(count));

			for tuple in tuples {
				send_tuple(tuple, &mut stream);
			}

			let mut buff_4:[u8; 4] = [0; 4];

			stream.read_exact(&mut buff_4);

			let recv_count = unsafe { transmute(buff_4) };

			let mut result:Vec<Vec<E>> = Vec::<Vec<E>>::new();

			for i in 0..recv_count {
				result.push(recv_tuple(&mut stream));
			}
			return result;
		}
		Err(why) => {
			println!("{}", why);
			return Vec::<Vec<E>>::new();
		}
	}
}

fn number_of_tuples(tuples: &Vec<Vec<E>>, connection: &SocketAddr) -> i32
{
	let mut stream_err = TcpStream::connect(connection);
	match stream_err {
		Ok(mut stream) => {
			stream.write_all(&ti(COUNT));
			let count: i32 = tuples.len() as i32;
			stream.write_all(&ti(count));

			for tuple in tuples {
				send_tuple(tuple, &mut stream);
			}

			let mut buff_4:[u8; 4] = [0; 4];

			stream.read_exact(&mut buff_4);

			let recv_count = unsafe { transmute(buff_4) };

			return recv_count;
		}
		Err(why) => {
			println!("{}", why);
			return -1;
		}
	}
}

fn replace_tuple(tuple: &Vec<E>, replacement: &Vec<E>, connection: &SocketAddr) {
		let mut stream_err = TcpStream::connect(connection);
		match stream_err {
			Ok(mut stream) => {
				stream.write_all(&ti(REPLACE));
				send_tuple(tuple, &mut stream);
				send_tuple(replacement, &mut stream);
				let mut buff_4:[u8; 4] = [0; 4];
				stream.read_exact(&mut buff_4);

				let op:i32 = unsafe { transmute(buff_4) };
				if op != REPLACE {
					println!("Replacement error");
				}
			}
			Err(why) => {
				println!("{}", why);
			}
		}
}

fn main() {
    let connection: SocketAddr = ("127.0.0.1:5000").parse().unwrap();
    let tuple = vec![E::I(10), E::D(10.1), E::S("123".to_string())];
    let tuple2 = vec![E::S("12789".to_string()), E::I(10), E::D(5.), E::S("1234".to_string())];
    let tuple3 = vec![E::S("12abv".to_string()), E::I(11), E::D(5.), E::S("123".to_string())];

    put_tuple(&tuple, &connection);
    put_tuple(&tuple2, &connection);
    put_tuple(&tuple3, &connection);

    let templates: Vec<Vec<E>> = vec![];
	let tuples_dump = read_all_tuples(&templates, &connection);

	for tuple in tuples_dump {
		for item in tuple {
			item.println();
		}
		println!("");
	}

	println!("Count: {}", number_of_tuples(&templates, &connection));

	replace_tuple(&tuple2, &tuple3, &connection);

	let tuples_dump = read_all_tuples(&templates, &connection);

	for tuple in tuples_dump {
		for item in tuple {
			item.println();
		}
		println!("");
	}

	println!("Count: {}", number_of_tuples(&templates, &connection));

}
<commit_msg>Refactored to structure<commit_after>use std::net::*;
use std::io::prelude::*;
use std::mem::*;
use std::vec::Vec;

/*
TODO:
1. разобраться с модулями
2. реализовать оставшиеся операции
3. демо-приложение
*/

#[repr(C)]
enum E {
    I(i32),
    D(f64),
    S(String),
    None,
}

impl E {
	fn println(&self) {
		match self {
			&E::I(ref i) => println!("Int: {}", i),
			&E::D(ref d) => println!("Double: {}", d),
			&E::S(ref s) => println!("String: {}", s),
			&E::None => println!("Wildcard"),
		}
	}
}

struct LinuxTuplesConnection {
	connection: SocketAddr,
}

const PUT: i32 = 0;
const GET: i32 = 1;
const READ: i32 = 2;
const GET_NB: i32 = 3;
const READ_NB: i32 = 4;
const DUMP: i32 = 5;
const COUNT: i32 = 6;
const REPLACE: i32 = 8;
/*
const LOG: i32 = 7;
*/

const asciiS: i32 = 115;
const asciiI: i32 = 105;
const asciiD: i32 = 100;
const asciiQ: i32 = 63;
const asciiT: i32 = 116;

fn ti(i: i32) -> [u8; 4] {
	let buff: [u8; 4];
	unsafe {
		buff = transmute(i);
	}
	return buff;
}

fn ctoi(arr: &mut [u8; 24], mut val: i32, offset: isize) {
	unsafe {
		std::ptr::copy_nonoverlapping(&mut val as *mut _ as *mut u8, arr.as_ptr().offset(offset) as *mut u8, std::mem::size_of::<i32>());
	}
}

fn ciot(arr:&[u8; 24], offset:isize) -> i32 {
	let mut val: i32 = 0;

	unsafe {
		std::ptr::copy_nonoverlapping(arr.as_ptr().offset(offset) as *mut u8, &mut val as *mut _ as *mut u8, std::mem::size_of::<i32>());
	}

	return val;
}

fn cdot(arr:&[u8; 24], offset:isize) -> f64 {
	let mut val: f64 = 0.;
	unsafe {
		std::ptr::copy_nonoverlapping(arr.as_ptr().offset(offset) as *mut u8, &mut val as *mut _ as *mut u8, std::mem::size_of::<f64>());
	}
	return val;
}

fn ctod(arr: &mut [u8; 24], mut val: f64, offset: isize) {
	unsafe {
		std::ptr::copy_nonoverlapping(&mut val as *mut _ as *mut u8, arr.as_ptr().offset(offset) as *mut u8, std::mem::size_of::<f64>());
	}
}

fn send_tuple(tuple: &Vec<E>, stream: &mut TcpStream) {

    stream.write_all(& (ti(tuple.len() as i32)));

    let mut string_length:i32 = 0;

    for elem in tuple {
    	match elem {
    		&E::S(ref s) => string_length += s.len() as i32,
    		_ => {}
    	}
    }

    stream.write_all(&ti(string_length));

    string_length = 0;

    let mut buff: [u8; 24] = [0; 24];

    for elem in tuple {
    	match elem {
    		&E::I(ref i) => {
				//tag
   	 			ctoi(&mut buff, asciiI, 0);
   	 			//union
   	 			ctoi(&mut buff, *i, 8);

   	 			stream.write_all(&buff);
    		},
    		&E::D(ref d) => {
    			//tag
   	 			ctoi(&mut buff, asciiD, 0);
   	 			//union
   	 			ctod(&mut buff, *d, 8);
   	 			stream.write_all(&buff);

    		},
    		&E::S(ref s) => {
    			//tag
   	 			ctoi(&mut buff, asciiS, 0);

   	 			//union
   	 			ctoi(&mut buff, string_length, 8);
   	 			ctoi(&mut buff, s.len() as i32, 16);

   	 			//tuple pointer
   	 			stream.write_all(&buff);

   	 			string_length += s.len() as i32;
    		}
    		&E::None => {
    			ctoi(&mut buff, asciiQ, 0);

    			stream.write_all(&buff);
    		}
    	}
    }

    for elem in tuple {
    	match elem {
    		&E::S(ref s) => {
    			stream.write_all(s.as_bytes());
    		}
    		_ => {}
    	}
    }
}

fn recv_tuple(stream: &mut TcpStream) -> Vec<E> {

	struct str_desc {
		used: bool,
		offset: i32,
		len: i32,
	}

	let mut tuple: Vec<E> = Vec::<E>::new();
	let mut str_descs: Vec<str_desc> = Vec::<str_desc>::new();

	let mut buff_4:[u8; 4] = [0; 4];
	let mut buff_8:[u8; 8] = [0; 8];
	let mut buff:[u8; 24] = [0; 24];

	stream.read_exact(&mut buff_4);
	let num_elements:i32 = unsafe { transmute(buff_4) };

	//non-blocking reads/writes
	if num_elements == -1 {
		return tuple;
	}

	stream.read_exact(&mut buff_4);
	let string_length:i32 = unsafe { transmute(buff_4) };

	for i in 0..num_elements {

		stream.read_exact(&mut buff);

		let tag: i32 = ciot(&buff, 0);

		match tag {
			asciiI => {
				tuple.push(E::I(ciot(&buff, 8)));
				str_descs.push(str_desc { used: false, offset: 0, len: 0} );
			},
			asciiD => {
				tuple.push(E::D(cdot(&buff, 8)));
				str_descs.push(str_desc { used: false, offset: 0, len: 0} );
			},
			asciiS => {
				str_descs.push(str_desc {
						used: true,
						offset: ciot(&buff, 8),
						len: ciot(&buff, 16),
					});
				tuple.push(E::S("".to_string()));
			},
			asciiQ => {
				str_descs.push(str_desc { used: false, offset: 0, len: 0} );
			},
			_ => {

			}
		}
	}

	if string_length != 0 {
		let mut string_space:Vec<u8> = vec![0; string_length as usize];
		stream.read_exact(&mut string_space);

		let mut str_slice = string_space.as_slice();
		for i in 0..num_elements {
			if str_descs[i as usize].used == true {
				tuple[i as usize] = E::S(String::from_utf8(
					string_space[
						(str_descs[i as usize].offset as usize)..
						(str_descs[i as usize].offset + str_descs[i as usize].len) as usize].to_vec()).unwrap());
			}
		}

	}

	return tuple;
}

impl LinuxTuplesConnection {
	fn put_tuple(&self, tuple: &Vec<E>) {
	    let mut stream_err = TcpStream::connect(&self.connection);
	    match stream_err {
	    	Ok(mut stream) => {
	    		stream.write_all(&ti(PUT));
				send_tuple(tuple, &mut stream);
				stream.shutdown(Shutdown::Write);
	    	}
	    	Err(why) => {
	    		println!("{}", why);
	    	}
	    }
	}

	fn get_tuple(&self, tuple: &Vec<E>) -> Vec<E>
	{
		let mut stream_err = TcpStream::connect(&self.connection);
		match stream_err {
			Ok(mut stream) => {
				stream.write_all(&ti(GET));

				send_tuple(tuple, &mut stream);

				stream.shutdown(Shutdown::Write);

				return recv_tuple(&mut stream);
			}
			Err(why) => {
				println!("{}", why);
				return Vec::<E>::new();
			}
		}
	}

	fn read_tuple(&self, tuple: &Vec<E>) -> Vec<E>
	{
		let mut stream_err = TcpStream::connect(&self.connection);
		match stream_err {
			Ok(mut stream) => {
				stream.write_all(&ti(READ));

				send_tuple(tuple, &mut stream);

				stream.shutdown(Shutdown::Write);

				return recv_tuple(&mut stream);
			}
			Err(why) => {
				println!("{}", why);
				return Vec::<E>::new();
			}
		}
	}

	fn get_nb_tuple(&self, tuple: &Vec<E>) -> Vec<E>
	{
		let mut stream_err = TcpStream::connect(&self.connection);
		match stream_err {
			Ok(mut stream) => {
				stream.write_all(&ti(GET_NB));

				send_tuple(tuple, &mut stream);

				stream.shutdown(Shutdown::Write);

				return recv_tuple(&mut stream);
			}
			Err(why) => {
				println!("{}", why);
				return Vec::<E>::new();
			}
		}
	}

	fn read_nb_tuple(&self, tuple: &Vec<E>) -> Vec<E>
	{
		let mut stream_err = TcpStream::connect(&self.connection);
		match stream_err {
			Ok(mut stream) => {
				stream.write_all(&ti(READ_NB));

				send_tuple(tuple, &mut stream);

				stream.shutdown(Shutdown::Write);

				return recv_tuple(&mut stream);
			}
			Err(why) => {
				println!("{}", why);
				return Vec::<E>::new();
			}
		}
	}

	fn read_all_tuples(&self, tuples: &Vec<Vec<E>>) -> Vec<Vec<E>>
	{
		let mut stream_err = TcpStream::connect(&self.connection);
		match stream_err {
			Ok(mut stream) => {
				stream.write_all(&ti(DUMP));
				let count: i32 = tuples.len() as i32;
				stream.write_all(&ti(count));

				for tuple in tuples {
					send_tuple(tuple, &mut stream);
				}

				let mut buff_4:[u8; 4] = [0; 4];

				stream.read_exact(&mut buff_4);

				let recv_count = unsafe { transmute(buff_4) };

				let mut result:Vec<Vec<E>> = Vec::<Vec<E>>::new();

				for i in 0..recv_count {
					result.push(recv_tuple(&mut stream));
				}
				return result;
			}
			Err(why) => {
				println!("{}", why);
				return Vec::<Vec<E>>::new();
			}
		}
	}

	fn number_of_tuples(&self, tuples: &Vec<Vec<E>>) -> i32
	{
		let mut stream_err = TcpStream::connect(&self.connection);
		match stream_err {
			Ok(mut stream) => {
				stream.write_all(&ti(COUNT));
				let count: i32 = tuples.len() as i32;
				stream.write_all(&ti(count));

				for tuple in tuples {
					send_tuple(tuple, &mut stream);
				}

				let mut buff_4:[u8; 4] = [0; 4];

				stream.read_exact(&mut buff_4);

				let recv_count = unsafe { transmute(buff_4) };

				return recv_count;
			}
			Err(why) => {
				println!("{}", why);
				return -1;
			}
		}
	}

	fn replace_tuple(&self, tuple: &Vec<E>, replacement: &Vec<E>) {
			let mut stream_err = TcpStream::connect(&self.connection);
			match stream_err {
				Ok(mut stream) => {
					stream.write_all(&ti(REPLACE));
					send_tuple(tuple, &mut stream);
					send_tuple(replacement, &mut stream);
					let mut buff_4:[u8; 4] = [0; 4];
					stream.read_exact(&mut buff_4);

					let op:i32 = unsafe { transmute(buff_4) };
					if op != REPLACE {
						println!("Replacement error");
					}
				}
				Err(why) => {
					println!("{}", why);
				}
			}
	}
}

fn main() {
    let conn: SocketAddr = ("127.0.0.1:5000").parse().unwrap();
    let tuple = vec![E::I(10), E::D(10.1), E::S("123".to_string())];
    let tuple2 = vec![E::S("12789".to_string()), E::I(10), E::D(5.), E::S("1234".to_string())];
    let tuple3 = vec![E::S("12abv".to_string()), E::I(11), E::D(5.), E::S("123".to_string())];

    let serv = LinuxTuplesConnection {connection : conn};

    serv.put_tuple(&tuple);
    serv.put_tuple(&tuple2);
    serv.put_tuple(&tuple3);

    let templates: Vec<Vec<E>> = vec![];
	let tuples_dump = serv.read_all_tuples(&templates);

	for tuple in tuples_dump {
		for item in tuple {
			item.println();
		}
		println!("");
	}

	println!("Count: {}", serv.number_of_tuples(&templates));

	serv.replace_tuple(&tuple2, &tuple3);

	let tuples_dump = serv.read_all_tuples(&templates);

	for tuple in tuples_dump {
		for item in tuple {
			item.println();
		}
		println!("");
	}

	println!("Count: {}", serv.number_of_tuples(&templates));

}
<|endoftext|>