<commit_before>extern crate hkg;
extern crate termion;
extern crate rustc_serialize;
extern crate kuchiki;
extern crate chrono;
extern crate hyper;
extern crate cancellation;
extern crate time;
extern crate url;

use std::io::{stdout, stdin, Write};
use std::thread;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use cancellation::{CancellationToken, CancellationTokenSource, OperationCanceled};
use kuchiki::traits::*;
use rustc_serialize::json;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::event::Key;
use hkg::status::*;
use hkg::model::IconItem;
use hkg::model::ListTopicItem;
use hkg::state_manager::*;
use hkg::screen_manager::*;
use hkg::resources::common::*;
use hkg::caches::file_cache::*;
use hkg::net::*;
use hkg::net::web_resource::*;

fn main() {

    // Initialize
    let stdout = stdout();
    let mut stdout = stdout.lock().into_raw_mode().unwrap();

    // Clear the screen.
    hkg::screen::common::clear_screen();

    let mut builder = hkg::builder::Builder::new();

    let mut state_manager = StateManager::new();
    let mut screen_manager = ScreenManager::new();

    let icon_manifest_string = hkg::utility::readfile(String::from("data/icon.manifest.json"));
    let icon_collection: Box<Vec<IconItem>> = Box::new(json::decode(&icon_manifest_string).unwrap());

    // initialize empty page
    let mut list_topic_items: Vec<ListTopicItem> = vec![];
    let mut show_item = builder.default_show_item();

    let mut status_bar = hkg::screen::status_bar::StatusBar::new();
    let mut index = hkg::screen::index::Index::new();
    let mut show = hkg::screen::show::Show::new(icon_collection);

    let (tx_req, rx_req) = channel::<ChannelItem>();
    let (tx_res, rx_res) = channel::<ChannelItem>();

    // web client
    thread::spawn(move || {
        let mut wr = WebResource::new();
        let mut fc = Box::new(FileCache::new());
        let ct = CancellationTokenSource::new();
        ct.cancel_after(std::time::Duration::new(10, 0));
        loop {
            match rx_req.recv() {
                Ok(item) => {

                    let th = thread::current();
                    ct.run(|| {
                               th.unpark();
                           },
                           || {
                                match item.extra.clone() {
                                    ChannelItemType::Index(_) => {
                                        let mut index_resource = hkg::resources::index_resource::IndexResource::new(&mut wr, &ct, &mut fc);
                                        tx_res.send(index_resource.fetch(&item)).unwrap();
                                    },
                                    ChannelItemType::Show(_) => {
                                        let mut show_resource = hkg::resources::show_resource::ShowResource::new(&mut wr, &ct, &mut fc);
                                        tx_res.send(show_resource.fetch(&item)).unwrap();
                                    },
                                    ChannelItemType::Image(_) => {
                                        let mut image_resource = hkg::resources::image_resource::ImageResource::new(&mut wr, &ct, &mut fc);
                                        tx_res.send(image_resource.fetch(&item)).unwrap();
                                    }
                                }
                           });

                    if ct.is_canceled() {
                        thread::park_timeout(std::time::Duration::new(0, 250));
                        // Err(OperationCanceled)
                    } else {
                        // Ok(())
                    }
                }
                Err(_) => {}
            }
        }
    });

    // topics request
    let status_message = list_page(&mut state_manager, &tx_req);
    status_bar.append(&screen_manager, &status_message);

    loop {

        match rx_res.try_recv() {
            Ok(item) => {
                match item.extra {
                    ChannelItemType::Show(extra) => {
                        let document = kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                        let posturl = get_posturl(&extra.postid, extra.page);
                        show_item = builder.show_item(&document, &posturl);

                        status_bar.append(&screen_manager, &format!("[{}-{}:ROK][{}]",
                                 show_item.url_query.message,
                                 show_item.page,
                                 state_manager.isWebRequest()));

                        show.resetY();
                        hkg::screen::common::clear_screen();
                        state_manager.updateState(Status::Show); //state = Status::Show;
                        state_manager.setWebRequest(false); // is_web_requesting = false;
                    },
                    ChannelItemType::Index(_) => {
                        let document = kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                        list_topic_items = builder.list_topic_items(&document);

                        status_bar.append(&screen_manager,
                                               &format!("[TOPICS:ROK]"));

                        hkg::screen::common::clear_screen();

                        state_manager.updateState(Status::List); // state = Status::List;
                        state_manager.setWebRequest(false); // is_web_requesting = false;

                    },
                    ChannelItemType::Image(extra) => {
                    }
                }
            }
            Err(_) => {}
        }

        match state_manager.getState() {
            Status::Startup => {

            },
            Status::List => {
                index.print(&mut stdout, &list_topic_items);
            }
            Status::Show => {
                show.print(&mut stdout, &show_item);
            }
        }

        status_bar.print(&screen_manager);

        stdout.flush().unwrap();

        if !state_manager.isWebRequest() {

            let stdin = stdin();

            for c in stdin.keys() {

                if screen_manager.isWidthChanged() {
                    hkg::screen::common::clear_screen();
                }

                match state_manager.getState() {
                    Status::Startup => {},
                    Status::List => {
                        match c.unwrap() {
                            Key::Char('q') => {
                                hkg::screen::common::reset_screen();
                                return
                            },
                            Key::Char('\n') => {
                                status_bar.append(&screen_manager, "ENTER");
                                let i = index.get_selected_topic();
                                if i > 0 {
                                    let topic_item = &list_topic_items[i - 1];
                                    let postid = &topic_item.title.url_query.message;
                                    let page = 1;
                                    let status_message = show_page(&postid, page, &mut state_manager, &tx_req);

                                    status_bar.append(&screen_manager,
                                                           &get_show_page_status_message(postid, page, &status_message));
                                }
                                break
                            },
                            Key::PageUp => {
                                status_bar.append(&screen_manager, "↑");
                                let tmp = index.get_selected_topic();
                                status_bar.append(&screen_manager, &format!("{}", tmp));

                                if tmp > 1 {
                                    index.select_topic(tmp - 1);
                                }
                                break
                            },
                            Key::Up => {
                                status_bar.append(&screen_manager, "↑");
                                let tmp = index.get_selected_topic();
                                status_bar.append(&screen_manager, &format!("{}", tmp));

                                if tmp > 1 {
                                    index.select_topic(tmp - 1);
                                }
                                break
                            },
                            Key::Down => {
                                status_bar.append(&screen_manager, "↓");
                                let tmp = index.get_selected_topic();
                                status_bar.append(&screen_manager, &format!("{}", tmp));

                                if tmp < index.body_height() {
                                    index.select_topic(tmp + 1);
                                }
                                break
                            },
                            _ => {},
                        }
                    },
                    Status::Show => {
                            match c.unwrap() {
                                Key::Char('q') => {
                                    hkg::screen::common::reset_screen(); // print!("{}{}{}", termion::clear::All, style::Reset, termion::cursor::Show);
                                    return
                                },
                                Key::Left => {
                                    status_bar.append(&screen_manager, &format!("←"));
                                    if show_item.page > 1 {
                                        let postid = &show_item.url_query.message;
                                        let page = &show_item.page - 1;
                                        let status_message = show_page(&postid, page, &mut state_manager, &tx_req);

                                        status_bar.append(&screen_manager,
                                                               &get_show_page_status_message(postid, page, &status_message));
                                    }
                                    break
                                }
                                Key::Right => {
                                    status_bar.append(&screen_manager, &format!("→"));
                                    if show_item.max_page > show_item.page {
                                        let postid = &show_item.url_query.message;
                                        let page = &show_item.page + 1;
                                        let status_message = show_page(&postid, page, &mut state_manager, &tx_req);

                                        status_bar.append(&screen_manager,
                                                               &get_show_page_status_message(postid, page, &status_message));
                                    }
                                    break
                                },
                                Key::PageUp => {
                                    status_bar.append(&screen_manager, "↑");
                                    let bh = show.body_height();
                                    if show.scrollUp(bh) {
                                    hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::PageDown => {
                                    status_bar.append(&screen_manager, "↓");
                                    let bh = show.body_height();
                                    if show.scrollDown(bh) {
                                        hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::Up => {
                                    status_bar.append(&screen_manager, "↑");
                                    if show.scrollUp(2) {
                                        hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::Down => {
                                    status_bar.append(&screen_manager, "↓");
                                    if show.scrollDown(2) {
                                        hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::Backspace => {
                                    status_bar.append(&screen_manager, "B");
                                    state_manager.updateState(Status::List); // state = Status::List;
                                    hkg::screen::common::clear_screen();
                                    break
                                },
                                _ => {},
                            }
                        }
                }

            }
        }

    }
}
fn get_posturl(postid: &String, page: usize) -> String {
    let base_url = "http://forum1.hkgolden.com/view.aspx";
    let posturl = format!("{base_url}?type=BW&message={postid}&page={page}",
                          base_url = base_url,
                          postid = postid,
                          page = page);
    posturl
}

fn list_page(state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Index(ChannelIndexItem { }),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.setWebRequest(true);    // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn show_page(postid: &String, page: usize, state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Show(ChannelShowItem { postid: postid.clone(), page: page }),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.setWebRequest(true); // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn image_request(url: &String, state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Image(ChannelImageItem { url: url.to_string(), bytes: Vec::new() }),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.setWebRequest(true); // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn get_show_page_status_message(postid: &String, page: usize, status_message: &String) -> String {
    format!("[{}-{}:{}]", postid, page, status_message)
}
<commit_msg>main - handle images download request in show state + display response status for images request<commit_after>extern crate hkg;
extern crate termion;
extern crate rustc_serialize;
extern crate kuchiki;
extern crate chrono;
extern crate hyper;
extern crate cancellation;
extern crate time;
extern crate url;

use std::io::{stdout, stdin, Write};
use std::thread;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use cancellation::{CancellationToken, CancellationTokenSource, OperationCanceled};
use kuchiki::traits::*;
use rustc_serialize::json;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::event::Key;
use hkg::status::*;
use hkg::model::IconItem;
use hkg::model::ListTopicItem;
use hkg::state_manager::*;
use hkg::screen_manager::*;
use hkg::resources::common::*;
use hkg::caches::file_cache::*;
use hkg::net::*;
use hkg::net::web_resource::*;
use std::sync::{Arc, Mutex};

fn main() {

    // Initialize
    let stdout = stdout();
    let mut stdout = stdout.lock().into_raw_mode().unwrap();

    // Clear the screen.
    hkg::screen::common::clear_screen();

    let mut builder = hkg::builder::Builder::new();

    let mut state_manager = StateManager::new();
    let mut screen_manager = ScreenManager::new();

    let icon_manifest_string = hkg::utility::readfile(String::from("data/icon.manifest.json"));
    let icon_collection: Box<Vec<IconItem>> = Box::new(json::decode(&icon_manifest_string).unwrap());

    // initialize empty page
    let mut list_topic_items: Vec<ListTopicItem> = vec![];
    let mut show_item = builder.default_show_item();

    let mut status_bar = hkg::screen::status_bar::StatusBar::new();
    let mut index = hkg::screen::index::Index::new();
    let mut show = hkg::screen::show::Show::new(icon_collection);

    let (tx_req, rx_req) = channel::<ChannelItem>();
    let (tx_res, rx_res) = channel::<ChannelItem>();

    let mut image_request_count_lock = Arc::new(Mutex::new(0));
    let mut image_request_count_lock2 = image_request_count_lock.clone();
    let mut isBgRequest = false;

    // web client
    thread::spawn(move || {
        let mut wr = WebResource::new();
        let mut fc = Box::new(FileCache::new());
        let ct = CancellationTokenSource::new();
        ct.cancel_after(std::time::Duration::new(10, 0));
        loop {
            match rx_req.recv() {
                Ok(item) => {

                    let th = thread::current();
                    ct.run(|| {
                               th.unpark();
                           },
                           || {
                                match item.extra.clone() {
                                    ChannelItemType::Index(_) => {
                                        let mut index_resource = hkg::resources::index_resource::IndexResource::new(&mut wr, &ct, &mut fc);
                                        tx_res.send(index_resource.fetch(&item)).unwrap();
                                    },
                                    ChannelItemType::Show(_) => {
                                        let mut show_resource = hkg::resources::show_resource::ShowResource::new(&mut wr, &ct, &mut fc);
                                        tx_res.send(show_resource.fetch(&item)).unwrap();
                                    },
                                    ChannelItemType::Image(_) => {
                                        let mut image_resource = hkg::resources::image_resource::ImageResource::new(&mut wr, &ct, &mut fc);
                                        tx_res.send(image_resource.fetch(&item)).unwrap();
                                    }
                                }
                           });

                    if ct.is_canceled() {
                        thread::park_timeout(std::time::Duration::new(0, 250));
                        // Err(OperationCanceled)
                    } else {
                        // Ok(())
                    }
                }
                Err(_) => {}
            }
        }
    });

    // topics request
    let status_message = list_page(&mut state_manager, &tx_req);
    status_bar.append(&screen_manager, &status_message);

    loop {

        match rx_res.try_recv() {
            Ok(item) => {
                match item.extra {
                    ChannelItemType::Show(extra) => {
                        let document = kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                        let posturl = get_posturl(&extra.postid, extra.page);
                        show_item = builder.show_item(&document, &posturl);

                        status_bar.append(&screen_manager, &format!("[{}-{}:ROK][{}]",
                                 show_item.url_query.message,
                                 show_item.page,
                                 state_manager.isWebRequest()));

                        // get all images links in an array, and send to background download
                        let maps = show_item.replies.iter().flat_map(|reply|
                            {
                                 let f = reply.body.iter().filter(|node| {
                                     let node2 = node.clone();
                                     match *node2 {
                                         hkg::reply_model::NodeType::Image(ref n) => (n.data.starts_with("http") || n.data.starts_with("https")) && n.alt.starts_with("[img]") && n.alt.ends_with("[/img]"),
                                         _ => false
                                     }
                                 }).collect::<Vec<_>>();
                                 return f;
                            }
                        ).collect::<Vec<_>>();

                        let mut count = image_request_count_lock.lock().unwrap();
                        *count = maps.len();
                        isBgRequest = true;
                        status_bar.append(&screen_manager,
                                                &format!("[SIMG:{count}]", count = *count ));

                        for node in &maps {
                            let node2 = node.clone();
                            match *node2 {
                                 hkg::reply_model::NodeType::Image(ref n) => {
                                     let status_message = image_request(&n.data, &mut state_manager, &tx_req);
                                     status_bar.append(&screen_manager,&status_message);
                                 },
                                 _ => {}
                            }
                        }

                        show.resetY();
                        hkg::screen::common::clear_screen();
                        state_manager.updateState(Status::Show); //state = Status::Show;
                        state_manager.setWebRequest(false); // is_web_requesting = false;
                    },
                    ChannelItemType::Index(_) => {
                        let document = kuchiki::parse_html().from_utf8().one(item.result.as_bytes());

                        list_topic_items = builder.list_topic_items(&document);

                        status_bar.append(&screen_manager,
                                               &format!("[TOPICS:ROK]"));

                        hkg::screen::common::clear_screen();

                        state_manager.updateState(Status::List); // state = Status::List;
                        state_manager.setWebRequest(false); // is_web_requesting = false;

                    },
                    ChannelItemType::Image(extra) => {
                        match image_request_count_lock2.lock() {
                            Ok(mut count) => {
                                if *count == 0 {
                                    status_bar.append(&screen_manager,
                                                           &format!("[RIMG:CERR]"));
                                } else {
                                    *count -= 1;
                                    if (item.result != "") {
                                        status_bar.append(&screen_manager,
                                                           &format!("[RIMG:E-{count}-{error}]", count = *count, error = item.result ));
                                    } else {
                                        status_bar.append(&screen_manager,
                                                           &format!("[RIMG:S-{count}]", count = *count ));
                                    }
                                }

                                if *count <= 0 {
                                    isBgRequest = false;
                                    hkg::screen::common::clear_screen();
                                    state_manager.setWebRequest(false); // is_web_requesting = false;
                                }
                            },
                            Err(poisoned) => { status_bar.append(&screen_manager,
                                                   &format!("[IMAGES:LOCKERR]")); }
                        };
                    }
                }
            }
            Err(_) => {}
        }

        match state_manager.getState() {
            Status::Startup => {

            },
            Status::List => {
                index.print(&mut stdout, &list_topic_items);
            }
            Status::Show => {
                show.print(&mut stdout, &show_item);
            }
        }

        status_bar.print(&screen_manager);

        stdout.flush().unwrap();

        if !state_manager.isWebRequest() {

            let stdin = stdin();

            for c in stdin.keys() {

                if screen_manager.isWidthChanged() {
                    hkg::screen::common::clear_screen();
                }

                match state_manager.getState() {
                    Status::Startup => {},
                    Status::List => {
                        match c.unwrap() {
                            Key::Char('q') => {
                                hkg::screen::common::reset_screen();
                                return
                            },
                            Key::Char('\n') => {
                                status_bar.append(&screen_manager, "ENTER");
                                let i = index.get_selected_topic();
                                if i > 0 {
                                    let topic_item = &list_topic_items[i - 1];
                                    let postid = &topic_item.title.url_query.message;
                                    let page = 1;
                                    let status_message = show_page(&postid, page, &mut state_manager, &tx_req);

                                    status_bar.append(&screen_manager,
                                                           &get_show_page_status_message(postid, page, &status_message));
                                }
                                break
                            },
                            Key::PageUp => {
                                status_bar.append(&screen_manager, "↑");
                                let tmp = index.get_selected_topic();
                                status_bar.append(&screen_manager, &format!("{}", tmp));

                                if tmp > 1 {
                                    index.select_topic(tmp - 1);
                                }
                                break
                            },
                            Key::Up => {
                                status_bar.append(&screen_manager, "↑");
                                let tmp = index.get_selected_topic();
                                status_bar.append(&screen_manager, &format!("{}", tmp));

                                if tmp > 1 {
                                    index.select_topic(tmp - 1);
                                }
                                break
                            },
                            Key::Down => {
                                status_bar.append(&screen_manager, "↓");
                                let tmp = index.get_selected_topic();
                                status_bar.append(&screen_manager, &format!("{}", tmp));

                                if tmp < index.body_height() {
                                    index.select_topic(tmp + 1);
                                }
                                break
                            },
                            _ => {},
                        }
                    },
                    Status::Show => {
                            match c.unwrap() {
                                Key::Char('q') => {
                                    hkg::screen::common::reset_screen(); // print!("{}{}{}", termion::clear::All, style::Reset, termion::cursor::Show);
                                    return
                                },
                                Key::Left => {
                                    status_bar.append(&screen_manager, &format!("←"));
                                    if show_item.page > 1 {
                                        let postid = &show_item.url_query.message;
                                        let page = &show_item.page - 1;
                                        let status_message = show_page(&postid, page, &mut state_manager, &tx_req);

                                        status_bar.append(&screen_manager,
                                                               &get_show_page_status_message(postid, page, &status_message));
                                    }
                                    break
                                }
                                Key::Right => {
                                    status_bar.append(&screen_manager, &format!("→"));
                                    if show_item.max_page > show_item.page {
                                        let postid = &show_item.url_query.message;
                                        let page = &show_item.page + 1;
                                        let status_message = show_page(&postid, page, &mut state_manager, &tx_req);

                                        status_bar.append(&screen_manager,
                                                               &get_show_page_status_message(postid, page, &status_message));
                                    }
                                    break
                                },
                                Key::PageUp => {
                                    status_bar.append(&screen_manager, "↑");
                                    let bh = show.body_height();
                                    if show.scrollUp(bh) {
                                    hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::PageDown => {
                                    status_bar.append(&screen_manager, "↓");
                                    let bh = show.body_height();
                                    if show.scrollDown(bh) {
                                        hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::Up => {
                                    status_bar.append(&screen_manager, "↑");
                                    if show.scrollUp(2) {
                                        hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::Down => {
                                    status_bar.append(&screen_manager, "↓");
                                    if show.scrollDown(2) {
                                        hkg::screen::common::clear_screen();
                                    }
                                    break
                                },
                                Key::Backspace => {
                                    status_bar.append(&screen_manager, "B");
                                    state_manager.updateState(Status::List); // state = Status::List;
                                    hkg::screen::common::clear_screen();
                                    break
                                },
                                _ => {},
                            }
                        }
                }

            }
        }

    }
}
fn get_posturl(postid: &String, page: usize) -> String {
    let base_url = "http://forum1.hkgolden.com/view.aspx";
    let posturl = format!("{base_url}?type=BW&message={postid}&page={page}",
                          base_url = base_url,
                          postid = postid,
                          page = page);
    posturl
}

fn list_page(state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Index(ChannelIndexItem { }),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.setWebRequest(true);    // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn show_page(postid: &String, page: usize, state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Show(ChannelShowItem { postid: postid.clone(), page: page }),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.setWebRequest(true); // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn image_request(url: &String, state_manager: &mut StateManager, tx_req: &Sender<ChannelItem>) -> String {

    let ci = ChannelItem {
        extra: ChannelItemType::Image(ChannelImageItem { url: url.to_string(), bytes: Vec::new() }),
        result: String::from(""),
    };

    let status_message = match tx_req.send(ci) {
        Ok(()) => {
            state_manager.setWebRequest(true); // *is_web_requesting = true;
            "SOK".to_string()
        }
        Err(e) => format!("{}:{}", "SFAIL", e).to_string(),
    };

    status_message
}

fn get_show_page_status_message(postid: &String, page: usize, status_message: &String) -> String {
    format!("[{}-{}:{}]", postid, page, status_message)
}
<|endoftext|>


<commit_before>fn main() {

	//let mut array_A = [31i, 41, 59, 26, 41, 58];
	let mut array_A = [5i,2,4,6,1,3];
	let mut i: int;
	let mut key: int;

	// It is not necessary to explicitly declare j
	for j in range(1u, array_A.len()) { //arrays only work with uint (unsigned)
		key = array_A[j];
		i = j as int - 1;

		while i >= 0 && array_A[i as uint] > key {
			array_A[i as uint + 1] = array_A[i as uint];
			i = i - 1;
		};
		array_A[i as uint+1]  = key;

	};

	//Just for printing purposes
	for j in range(0u, array_A.len()) {
		println!("{}", array_A[j]);
	};

}<commit_msg>improve code readabilitty by introduccing a bridge variable i_u to avoid casting back and forth from int to uint<commit_after>fn main() {

	let mut array_A = [31i, 41, 59, 26, 41, 58];
	//let mut array_A = [5i,2,4,6,1,3];
	let mut i: int;

	//utility variable. Uggly hack to avoid casting back and foward from int to uint
	let mut i_u: uint;
	let mut key: int;

	// It is not necessary to explicitly declare j
	for j in range(1u, array_A.len()) { //arrays only work with uint (unsigned)
		key = array_A[j];
		i_u = j - 1;
		i = i_u as int;

		while i >= 0 && array_A[i_u] > key {
			array_A[i_u + 1] = array_A[i_u];

			i = i - 1;
			i_u = i as uint;
		};
		array_A[i_u + 1]  = key;

	};

	//Just for printing purposes
	for j in range(0u, array_A.len()) {
		println!("{}", array_A[j]);
	};

}<|endoftext|>


<commit_before>//! Coredump implementation of the Space API server.
//!
//! Start this via the commandline:
//!
//!     ./coredump_status [-p PORT] [-i IP]

use docopt::Docopt;
use env_logger;
use serde_derive::Deserialize;
use spaceapi_server::api;
use spaceapi_server::api::sensors::{PeopleNowPresentSensorTemplate, TemperatureSensorTemplate};
use spaceapi_server::modifiers::StateFromPeopleNowPresent;
use spaceapi_server::SpaceapiServerBuilder;
use std::env;

static USAGE: &str = "
Usage: coredump_status [-p PORT] [-i IP]

Options:
    -p PORT  The port to listen on [default: 3000].
    -i IP    The IP address to listen on [default: 127.0.0.1].
";

#[derive(Deserialize, Debug)]
struct Args {
    flag_p: u16,
    flag_i: std::net::IpAddr,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    env_logger::init();

    // Parse arguments
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
    let host = args.flag_i;
    let port = args.flag_p;

    // Create new Status instance
    let mut status = api::StatusBuilder::new("coredump")
        .logo("https://www.coredump.ch/wp-content/uploads/2016/11/logo.png")
        .url("https://www.coredump.ch/")
        .location(api::Location {
            address: Some("Holzwiesstrasse 50, 8645 Jona, Switzerland".into()),
            lat: 47.235_960_7,
            lon: 8.841_005_7,
        })
        .contact(api::Contact {
            irc: Some("irc://freenode.net/#coredump".into()),
            twitter: Some("@coredump_ch".into()),
            email: Some("vorstand@lists.coredump.ch".into()),
            ..Default::default()
        })
        .add_issue_report_channel(api::IssueReportChannel::Email)
        .add_issue_report_channel(api::IssueReportChannel::Twitter)
        .spacefed(api::Spacefed {
            spacenet: false,
            spacesaml: false,
            spacephone: false,
        })
        .feeds(api::Feeds {
            blog: Some(api::Feed {
                type_: Some("rss".into()),
                url: "https://www.coredump.ch/feed/".into(),
            }),
            wiki: None,
            calendar: Some(api::Feed {
                type_: Some("ical".into()),
                url: "https://www.coredump.ch/events/?ical=1".into(),
            }),
            flickr: None,
        })
        .add_project("https://www.coredump.ch/projekte/")
        .add_project("https://forum.coredump.ch/c/projects")
        .add_project("https://github.com/coredump-ch/")
        .add_cam("https://webcam.coredump.ch/cams/ultimaker_0.jpg")
        .add_extension("ccc", "chaostreff")
        .build()
        .expect("Couldn't create status object");

    // Set state message
    status.state.message = Some("Open Mondays from 20:00".into());

    // Redis connection info
    let redis_host: String = env::var("REDIS_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
    let redis_port: u16 = env::var("REDIS_PORT")
        .unwrap_or_else(|_| "6379".to_string())
        .parse()
        .unwrap_or(6379);
    let redis_db: i64 = env::var("REDIS_DB")
        .unwrap_or_else(|_| "0".to_string())
        .parse()
        .unwrap_or(0);
    let redis_url = format!("redis://{}:{}/{}", redis_host, redis_port, redis_db);

    // Create server
    let server = SpaceapiServerBuilder::new(status)
        .redis_connection_info(&*redis_url)
        .add_status_modifier(StateFromPeopleNowPresent)
        .add_sensor(
            TemperatureSensorTemplate {
                unit: "°C".into(),
                location: "Hackerspace".into(),
                name: Some("Raspberry CPU".into()),
                description: None,
            },
            "temperature_raspi".into(),
        )
        .add_sensor(
            TemperatureSensorTemplate {
                unit: "°C".into(),
                location: "Hackerspace".into(),
                name: Some("Room Temperature (Entrance)".into()),
                description: None,
            },
            "temperature_entrance".into(),
        )
        .add_sensor(
            TemperatureSensorTemplate {
                unit: "°C".into(),
                location: "Hackerspace".into(),
                name: Some("Room Temperature (Tables)".into()),
                description: None,
            },
            "temperature_tables".into(),
        )
        .add_sensor(
            PeopleNowPresentSensorTemplate {
                location: Some("Hackerspace".into()),
                name: None,
                description: None,
                names: None,
            },
            "people_now_present".into(),
        )
        .add_sensor(
            PeopleNowPresentSensorTemplate {
                location: Some("Cyberspace".into()),
                name: None,
                description: Some("Our virtual hackerspace meeting through Jitsi (because of COVID)".into()),
                names: None,
            },
            "people_in_cyberspace".into(),
        )
        .build()
        .expect("Could not build server");

    server.serve((host, port)).expect("Could not start the server");
}
<commit_msg>Fix postal address (#134)<commit_after>//! Coredump implementation of the Space API server.
//!
//! Start this via the commandline:
//!
//!     ./coredump_status [-p PORT] [-i IP]

use docopt::Docopt;
use env_logger;
use serde_derive::Deserialize;
use spaceapi_server::api;
use spaceapi_server::api::sensors::{PeopleNowPresentSensorTemplate, TemperatureSensorTemplate};
use spaceapi_server::modifiers::StateFromPeopleNowPresent;
use spaceapi_server::SpaceapiServerBuilder;
use std::env;

static USAGE: &str = "
Usage: coredump_status [-p PORT] [-i IP]

Options:
    -p PORT  The port to listen on [default: 3000].
    -i IP    The IP address to listen on [default: 127.0.0.1].
";

#[derive(Deserialize, Debug)]
struct Args {
    flag_p: u16,
    flag_i: std::net::IpAddr,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    env_logger::init();

    // Parse arguments
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
    let host = args.flag_i;
    let port = args.flag_p;

    // Create new Status instance
    let mut status = api::StatusBuilder::new("coredump")
        .logo("https://www.coredump.ch/wp-content/uploads/2016/11/logo.png")
        .url("https://www.coredump.ch/")
        .location(api::Location {
            address: Some("Neue Jonastrasse 107, 8640 Rapperswil, Switzerland".into()),
            lat: 47.235_960_7,
            lon: 8.841_005_7,
        })
        .contact(api::Contact {
            irc: Some("irc://freenode.net/#coredump".into()),
            twitter: Some("@coredump_ch".into()),
            email: Some("vorstand@lists.coredump.ch".into()),
            ..Default::default()
        })
        .add_issue_report_channel(api::IssueReportChannel::Email)
        .add_issue_report_channel(api::IssueReportChannel::Twitter)
        .spacefed(api::Spacefed {
            spacenet: false,
            spacesaml: false,
            spacephone: false,
        })
        .feeds(api::Feeds {
            blog: Some(api::Feed {
                type_: Some("rss".into()),
                url: "https://www.coredump.ch/feed/".into(),
            }),
            wiki: None,
            calendar: Some(api::Feed {
                type_: Some("ical".into()),
                url: "https://www.coredump.ch/events/?ical=1".into(),
            }),
            flickr: None,
        })
        .add_project("https://www.coredump.ch/projekte/")
        .add_project("https://forum.coredump.ch/c/projects")
        .add_project("https://github.com/coredump-ch/")
        .add_cam("https://webcam.coredump.ch/cams/ultimaker_0.jpg")
        .add_extension("ccc", "chaostreff")
        .build()
        .expect("Couldn't create status object");

    // Set state message
    status.state.message = Some("Open Mondays from 20:00".into());

    // Redis connection info
    let redis_host: String = env::var("REDIS_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
    let redis_port: u16 = env::var("REDIS_PORT")
        .unwrap_or_else(|_| "6379".to_string())
        .parse()
        .unwrap_or(6379);
    let redis_db: i64 = env::var("REDIS_DB")
        .unwrap_or_else(|_| "0".to_string())
        .parse()
        .unwrap_or(0);
    let redis_url = format!("redis://{}:{}/{}", redis_host, redis_port, redis_db);

    // Create server
    let server = SpaceapiServerBuilder::new(status)
        .redis_connection_info(&*redis_url)
        .add_status_modifier(StateFromPeopleNowPresent)
        .add_sensor(
            TemperatureSensorTemplate {
                unit: "°C".into(),
                location: "Hackerspace".into(),
                name: Some("Raspberry CPU".into()),
                description: None,
            },
            "temperature_raspi".into(),
        )
        .add_sensor(
            TemperatureSensorTemplate {
                unit: "°C".into(),
                location: "Hackerspace".into(),
                name: Some("Room Temperature (Entrance)".into()),
                description: None,
            },
            "temperature_entrance".into(),
        )
        .add_sensor(
            TemperatureSensorTemplate {
                unit: "°C".into(),
                location: "Hackerspace".into(),
                name: Some("Room Temperature (Tables)".into()),
                description: None,
            },
            "temperature_tables".into(),
        )
        .add_sensor(
            PeopleNowPresentSensorTemplate {
                location: Some("Hackerspace".into()),
                name: None,
                description: None,
                names: None,
            },
            "people_now_present".into(),
        )
        .add_sensor(
            PeopleNowPresentSensorTemplate {
                location: Some("Cyberspace".into()),
                name: None,
                description: Some("Our virtual hackerspace meeting through Jitsi (because of COVID)".into()),
                names: None,
            },
            "people_in_cyberspace".into(),
        )
        .build()
        .expect("Could not build server");

    server.serve((host, port)).expect("Could not start the server");
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate glob;
extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate structopt;
extern crate tokio_core;
extern crate toml;

mod ghclient;
mod statics;
mod structs;
#[macro_use]
mod syslog;

use statics::CONF_PATH;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
enum Command {
    /// Gets user public key
    #[structopt(name = "key")]
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    #[structopt(name = "pam")]
    Pam,
    /// Check configuration
    #[structopt(name = "check")]
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(name = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(name = "ratelimit")]
    RateLimit,
    /// Displays version details
    #[structopt(name = "version")]
    Version,
}

fn main() {
    let command = Command::from_args();

    use std::env;
    use std::process;
    use ghclient::GithubClient;

    match command {
        Command::Check { confpath } => match structs::Config::new(&confpath) {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(11),
        },
        Command::Key { user } => {
            match structs::Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                  .and_then(|client| client.print_user_public_key(&user))
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(21),
            }
        }
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match structs::Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                              .and_then(|client| client.check_pam(&user))
            {
                Ok(true) => process::exit(0),
                Ok(false) => process::exit(1),
                Err(_) => process::exit(31),
            },
            Err(_) => process::exit(41),
        },
        Command::CleanUp => {
            match structs::Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                  .and_then(|client| client.clear_all_caches())
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(51),
            }
        }
        Command::RateLimit => {
            match structs::Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                  .and_then(|client| client.print_rate_limit())
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(61),
            }
        }
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
    };
}
<commit_msg>Change to use structs::Config directly<commit_after>extern crate futures;
extern crate glob;
extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate structopt;
extern crate tokio_core;
extern crate toml;

mod ghclient;
mod statics;
mod structs;
#[macro_use]
mod syslog;

use statics::CONF_PATH;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
enum Command {
    /// Gets user public key
    #[structopt(name = "key")]
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    #[structopt(name = "pam")]
    Pam,
    /// Check configuration
    #[structopt(name = "check")]
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(name = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(name = "ratelimit")]
    RateLimit,
    /// Displays version details
    #[structopt(name = "version")]
    Version,
}

fn main() {
    let command = Command::from_args();

    use ghclient::GithubClient;
    use std::env;
    use std::process;
    use structs::Config;

    match command {
        Command::Check { confpath } => match Config::new(&confpath) {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(11),
        },
        Command::Key { user } => {
            match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                         .and_then(|client| client.print_user_public_key(&user))
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(21),
            }
        }
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                     .and_then(|client| client.check_pam(&user))
            {
                Ok(true) => process::exit(0),
                Ok(false) => process::exit(1),
                Err(_) => process::exit(31),
            },
            Err(_) => process::exit(41),
        },
        Command::CleanUp => {
            match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                         .and_then(|client| client.clear_all_caches())
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(51),
            }
        }
        Command::RateLimit => {
            match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                         .and_then(|client| client.print_rate_limit())
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(61),
            }
        }
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
    };
}
<|endoftext|>


<commit_before>#![feature(globs)]
#![feature(phase)]
#![feature(unboxed_closures)]
//extern crate regex_macros;
//extern crate regex;
//extern crate parsers;

use std::collections::HashMap;
use parsers::*;

pub mod parsers;

fn main() {

  //let parser = CharP
  /*
  let prog = vec![
    Assign("x", Num(3)),
    Output(Variable("x")),
    Assign("x", Plus(box Variable("x"), box Num(6))),
    Assign("y", Variable("x")),
    Assign("z", Plus(box Variable("x"), box Variable("y"))),
    Output(Variable("z"))
  ];

  run(&prog);
  */

  /*
  fn simplify<'a, I, O>(b: Box<Parser<'a, I, O> + 'a>) -> ParserGenerator<'a, I, O> {
    box |&:| b
  }
  */

  /*
  let rep_eq   = RepParser{
    reps: 3,
    parser: &OrParser{a: box |&:| box match_num as Box<Parser<&[Token], int>>, b: box |&:| box LiteralParser{literal: Equals} as Box<Parser<&[Token], Token>>}
  };
  */
  type TokenParser<'a> = Parser<'a, &'a[Token], Expr> + 'a;

  fn expr<'a>() -> Box<Parser<'a, &'a [Token], Expr> + 'a> {

    fn match_num<'a>() -> MatchParser<'a Token, Expr> {
      MatchParser{matcher: box |&: input: &Token| -> Result<Expr, String> match *input {
        Number(num) => Ok(Num(num)),
        _ => Err(format!("wrong type"))
      }}
    }

    fn plus<'a>() -> MapParser<'a, &'a[Token], Token, Expr, Parser<'a, &'a[Token], Expr> + 'a> {
      MapParser{
        parser: RepSepParser{
          rep: match_num(),
          sep: literal(PlusSign),
          min_reps : 2
        },
        mapper: box |&: ops: Vec<Expr>| Plus(ops)
      }
    }

    fn simple_expr<'a>() -> OrParser<'a, &'a[Token], Expr, TokenParser<'a>, TokenParser<'a>> {
      OrParser{
        b: box |&:| match_num() ,
        a: box |&:| plus()
      }
    }

    fn mult<'a>() -> MapParser<'a, &'a[Token], Token, Expr, TokenParser<'a>> {
      MapParser{
        parser: RepSepParser{
          rep: simple_expr.call(()).call(()),
          sep: literal(MultSign),
          min_reps : 2
        },
        mapper: box |&: ops: Vec<Expr>| Mult(ops)
      }
    }

    let expr = OrParser{
      a: box |&:| mult(),
      b: box |&:| simple_expr(),
    };

    box expr
  }

  //let e = [Number(11), MultSign, Number(13), MultSign, Number(17), PlusSign, Number(14)];

  let p2 = "3 + 4 + 5 + 6";
  let parser = expr();
  let tokens = tokenize(p2);
  match parser.parse(tokens.as_slice()) {
    Ok((exp, _)) => {println!("{}", eval(&exp, &HashMap::new()));}
    Err(err) => {println!("Parse Error: {}", err);}
  };

}

fn tokenize<'a>(input: &'a str) -> Vec<Token> {
  let mut lines = input.split('\n');
  let mut build: Vec<Token> = Vec::new();
  for line in lines {
    let mut toks = line.split(' ');
    for tok in toks {
      match tok.trim() {
        "=" => {build.push(Equals);}
        "+" => {build.push(PlusSign);}
        "*" => {build.push(MultSign);}
        "out" => {build.push(OutputCmd)}
        "" => {}
        other => { build.push(Ident(String::from_str(other)))}
      };
    }
    build.push(NewLine);
  }
  build
}

/*
trait TokenParser<T> {
  fn parse<'a>(tokens: &'a [Token]>)
*/

fn run(prog: &Vec<Statement>) {
  let mut env: HashMap<&str, int> = HashMap::new();
  for s in prog.iter() {
    match *s {
      Assign(var, ref expr) => {
        match eval(expr, &env) {
          Ok(res)   => {env.insert(var, res);}
          Err(err)  => {
            println!("ERROR: {}", err);
            return;
          }
        }
      }
      Output(ref expr) => match eval(expr, &env) {
        Ok(val) => {println!("{}", val)}
        Err(err) => {
          println!("ERROR: {}", err)
          return;
        }
      }
    }
  }
}

fn eval(expr: &Expr, env: &HashMap<&str, int>) -> Result<int, String> {
  match *expr {
    Variable(var) => match env.find(&var) {
      Some(val) => Ok(*val),
      None => Err(format!("Undefined var {}", var)),
    },
    Num(val) => Ok(val),
    Plus(ref ops) => {
      let mut sum = 0i;
      for op in ops.iter() {
        match eval(op, env) {
          Ok(value) => {
            sum += value;
          }
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(sum)
    },
    Mult(ref ops) => {
      let mut total = 1i;
      for op in ops.iter() {
        match eval(op, env) {
          Ok(value) => {
            total += value;
          }
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(total)
    }
  }
}

#[deriving(Show)]
enum Expr {
  Variable(&'static str),
  Num(int),
  Plus(Vec<Expr>), //a + b + c + d
  Mult(Vec<Expr>)
}

enum Statement {
  Assign(&'static str, Expr),
  Output(Expr),
}

#[deriving(Show)]
#[deriving(Eq)]
#[deriving(PartialEq)]
#[deriving(Clone)]
enum Token {
  Equals,
  Ident(String),
  Number(int),
  PlusSign,
  MultSign,
  OutputCmd,
  NewLine,
}
<commit_msg>works, but uses macros<commit_after>#![feature(globs)]
#![feature(phase)]
#![feature(unboxed_closures)]
#![feature(macro_rules)]
//extern crate regex_macros;
//extern crate regex;
//extern crate parsers;

use std::collections::HashMap;
use parsers::*;

pub mod parsers;

fn main() {

  //let parser = CharP
  /*
  let prog = vec![
    Assign("x", Num(3)),
    Output(Variable("x")),
    Assign("x", Plus(box Variable("x"), box Num(6))),
    Assign("y", Variable("x")),
    Assign("z", Plus(box Variable("x"), box Variable("y"))),
    Output(Variable("z"))
  ];

  run(&prog);
  */

  /*
  fn simplify<'a, I, O>(b: Box<Parser<'a, I, O> + 'a>) -> ParserGenerator<'a, I, O> {
    box |&:| b
  }
  */

  /*
  let rep_eq   = RepParser{
    reps: 3,
    parser: &OrParser{a: box |&:| box match_num as Box<Parser<&[Token], int>>, b: box |&:| box LiteralParser{literal: Equals} as Box<Parser<&[Token], Token>>}
  };
  */

  fn expr<'a>() -> Box<Parser<'a, &'a [Token], Expr> + 'a> {

    fn match_num<'a>() -> MatchParser<'a Token, Expr> {
      MatchParser{matcher: box |&: input: &Token| -> Result<Expr, String> match input {
        &Number(num) => Ok(Num(num)),
        other => Err(format!("wrong type, expected number, got {}", other))
      }}
    }

    macro_rules! plus {
      () => {
        MapParser{
          parser: RepSepParser{
            rep: match_num(),
            sep: literal(PlusSign),
            min_reps : 2
          },
          mapper: box |&: ops: Vec<Expr>| Plus(ops)
        }
      }
    };

    macro_rules! simple_expr{
      () => {
        OrParser{
          b: box |&:| match_num() ,
          a: box |&:| plus!()
        }
      }
    };

    let mult = box |&:| MapParser{
      parser: RepSepParser{
        rep: simple_expr!(),
        sep: literal(MultSign),
        min_reps : 2
      },
      mapper: box |&: ops: Vec<Expr>| Mult(ops)
    };

    let expr = box OrParser{
      a: mult,
      b: box |&:| simple_expr!(),
    };

    expr
  }

  //let e = [Number(11), MultSign, Number(13), MultSign, Number(17), PlusSign, Number(14)];

  let p2 = "4 * 6 + 3 + * 5 * 2";
  let parser = expr();
  let tokens = tokenize(p2);
  match parser.parse(tokens.as_slice()) {
    Ok((exp, rest)) => {
      if rest.len() > 0 {
        println!("Error: unpexpected token {}", rest[0]);
      } else {
        println!("{}", eval(&exp, &HashMap::new()));
      }
    }
    Err(err) => {println!("Parse Error: {}", err);}
  };

}

fn tokenize<'a>(input: &'a str) -> Vec<Token> {
  let mut lines = input.split('\n');
  let mut build: Vec<Token> = Vec::new();
  for line in lines {
    let mut toks = line.split(' ');
    for tok in toks {
      match tok.trim() {
        "=" => {build.push(Equals);}
        "+" => {build.push(PlusSign);}
        "*" => {build.push(MultSign);}
        "out" => {build.push(OutputCmd)}
        "" => {}
        other => {
          match from_str(other) {
            Some(num) => {
              build.push(Number(num));
            }
            None => {
              build.push(Ident(String::from_str(other)));
            }
          }
        }
      };
    }
    build.push(NewLine);
  }
  build
}

/*
trait TokenParser<T> {
  fn parse<'a>(tokens: &'a [Token]>)
*/

fn run(prog: &Vec<Statement>) {
  let mut env: HashMap<&str, int> = HashMap::new();
  for s in prog.iter() {
    match *s {
      Assign(var, ref expr) => {
        match eval(expr, &env) {
          Ok(res)   => {env.insert(var, res);}
          Err(err)  => {
            println!("ERROR: {}", err);
            return;
          }
        }
      }
      Output(ref expr) => match eval(expr, &env) {
        Ok(val) => {println!("{}", val)}
        Err(err) => {
          println!("ERROR: {}", err)
          return;
        }
      }
    }
  }
}

fn eval(expr: &Expr, env: &HashMap<&str, int>) -> Result<int, String> {
  match *expr {
    Variable(var) => match env.find(&var) {
      Some(val) => Ok(*val),
      None => Err(format!("Undefined var {}", var)),
    },
    Num(val) => Ok(val),
    Plus(ref ops) => {
      let mut sum = 0i;
      for op in ops.iter() {
        match eval(op, env) {
          Ok(value) => {
            sum += value;
          }
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(sum)
    },
    Mult(ref ops) => {
      let mut total = 1i;
      for op in ops.iter() {
        match eval(op, env) {
          Ok(value) => {
            total *= value;
          }
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(total)
    }
  }
}

#[deriving(Show)]
enum Expr {
  Variable(&'static str),
  Num(int),
  Plus(Vec<Expr>), //a + b + c + d
  Mult(Vec<Expr>)
}

enum Statement {
  Assign(&'static str, Expr),
  Output(Expr),
}

#[deriving(Show)]
#[deriving(Eq)]
#[deriving(PartialEq)]
#[deriving(Clone)]
enum Token {
  Equals,
  Ident(String),
  Number(int),
  PlusSign,
  MultSign,
  OutputCmd,
  NewLine,
}
<|endoftext|>


<commit_before>#![feature(plugin)]

#[plugin]
extern crate clippy;

extern crate cli;
extern crate getopts;
extern crate hyper;

use std::os;
use std::io::fs;

fn main() {
    let opts = &[
        cli::helpopt(),
        cli::versionopt(),
        getopts::optflag("l", "list", "List installed fonts"),
        getopts::optopt("s", "search", "Search font", "FONTNAME"),
        getopts::optmulti("i", "install", "Install font(s) require a --source", "FONTNAME"),
        getopts::optmulti("d", "delete", "Delete font(s)", "FONTNAME"),
        getopts::optmulti("s", "source", "Source file to use", "FILENAME"),
    ];

    let matches = cli::parse_args(opts);
    if matches.opt_present("help") {
        println!("{}", cli::usage_string(opts));
        return;
    }
    if matches.opt_present("version") {
        println!("{}", cli::version_string("0.0.1"));
        return;
    }
    if matches.opt_present("list") {
        list_installed_fonts();
    }
    if matches.opt_present("search") {
        search_font(&*matches.opt_str("search").unwrap());
    }
    if matches.opt_present("install") {
        if matches.opt_present("source") {
            install_font(&*matches.opt_str("source").unwrap(), &*matches.opt_str("install").unwrap());
        } else {
            println!("{}", cli::usage_string(opts));
        }
    }
    if matches.opt_present("delete") {
        delete_font(&*matches.opt_str("delete").unwrap());
    }
}

fn get_font_dir() -> Path {
    match os::consts::SYSNAME {
        "linux" => {
            match os::homedir() {
                Some(ref p) => p.join(".fonts"),
                None => panic!("Impossible to get your home dir!")
            }
        },
        "macos" => {
            match os::homedir() {
                Some(ref p) => p.join(".fonts"),
                None => panic!("Impossible to get your home dir!")
            }
        },
        _ => unreachable!(),
    }
}

fn get_font_path(name: &str) -> Path {
    get_font_dir().join(name)
}

fn list_installed_fonts() {
    let font_dir : Path = get_font_dir();
    let font_paths : Vec<Path> = fs::readdir(&font_dir).unwrap();
    for font in font_paths.iter() {
        println!("{}", font.filename_str().unwrap());
    }
}

fn search_font(name: &str) {
    let mut client = hyper::Client::new();
    let resp = client
        .get(&*format!("http://api.github.com/search/repositories?q={}+in:name&sort=stars&order=desc", name))
        .send().unwrap();
    println!("body={}", resp.status);
}

fn install_font(source: &str, name: &str) {
    let font_path = get_font_path(name);
    fs::copy(&Path::new(source), &font_path).unwrap();
}

fn delete_font(name: &str) {
    let font_path = get_font_path(name);
    fs::unlink(&font_path).unwrap();
}

<commit_msg>Hamdle some io errors<commit_after>#![feature(plugin)]

#[plugin]
extern crate clippy;

extern crate cli;
extern crate getopts;
extern crate hyper;

use std::os;
use std::io::fs;

fn main() {
    let opts = &[
        cli::helpopt(),
        cli::versionopt(),
        getopts::optflag("l", "list", "List installed fonts"),
        getopts::optopt("s", "search", "Search font", "FONTNAME"),
        getopts::optmulti("i", "install", "Install font(s) require a --source", "FONTNAME"),
        getopts::optmulti("d", "delete", "Delete font(s)", "FONTNAME"),
        getopts::optmulti("s", "source", "Source file to use", "FILENAME"),
    ];

    let matches = cli::parse_args(opts);
    if matches.opt_present("help") {
        println!("{}", cli::usage_string(opts));
        return;
    }
    if matches.opt_present("version") {
        println!("{}", cli::version_string("0.0.1"));
        return;
    }
    if matches.opt_present("list") {
        list_installed_fonts();
    }
    if matches.opt_present("search") {
        search_font(&*matches.opt_str("search").unwrap());
    }
    if matches.opt_present("install") {
        if matches.opt_present("source") {
            install_font(&*matches.opt_str("source").unwrap(), &*matches.opt_str("install").unwrap());
        } else {
            println!("{}", cli::usage_string(opts));
        }
    }
    if matches.opt_present("delete") {
        delete_font(&*matches.opt_str("delete").unwrap());
    }
}

fn get_font_dir() -> Path {
    match os::consts::SYSNAME {
        "linux" => {
            match os::homedir() {
                Some(ref p) => p.join(".fonts"),
                None => panic!("Impossible to get your home dir!")
            }
        },
        "macos" => {
            match os::homedir() {
                Some(ref p) => p.join(".fonts"),
                None => panic!("Impossible to get your home dir!")
            }
        },
        _ => unreachable!(),
    }
}

fn get_font_path(font_name: &str) -> Path {
    get_font_dir().join(font_name)
}

fn list_installed_fonts() {
    let font_dir = get_font_dir();
    match fs::readdir(&font_dir) {
        Ok(fonts) => for font in fonts.iter() {
            if let Some(font_name) = font.filename_str() {
                println!("{}", font_name);
            }
        },
        Err(msg) => println!("{}", msg)
    }
}

fn search_font(font_name: &str) {
    let mut client = hyper::Client::new();
    let url = format!("http://api.github.com/search/repositories?q={}+in:name&sort=stars&order=desc", font_name);
    println!("{}", url);
    match client.get(&*url).send() {
        Ok(resp) => println!("body={}", resp.status),
        Err(msg) => println!("{}", msg)
    }
}

fn install_font(source: &str, font_name: &str) {
    let dest_font_path = get_font_path(font_name);
    match fs::copy(&Path::new(source), &dest_font_path) {
        Ok(_) => println!("Font {} installed successfully", font_name),
        Err(msg) => println!("{}", msg)
    }
}

fn delete_font(font_name: &str) {
    let font_path = get_font_path(font_name);
    match fs::unlink(&font_path) {
        Ok(_) => println!("Font {} deleted successfully", font_name),
        Err(msg) => println!("{}", msg)
    }
}

<|endoftext|>


<commit_before>extern crate clap;

use std::io::{Write, Read, ErrorKind, Error};
use std::net::{TcpListener, TcpStream, SocketAddrV4};
use std::thread::{spawn, JoinHandle, sleep};
use std::time::Duration;
use std::str;
use clap::{Arg, App};

fn main() {
    let matches = App::new("Simple TCP echo server")
        .version("1.0")
        .author("Corentin H. <corentinhenry@gmail.com>")
        .about("Quickly test TCP connections")
        .arg(Arg::with_name("server")
            .short("s")
            .long("server")
            .number_of_values(1)
            .multiple(true)
            .help("Start a server listen on the specified address")
            .takes_value(true))
        .arg(Arg::with_name("client")
            .short("c")
            .long("client")
            .number_of_values(1)
            .multiple(true)
            .help("Start a client on the specified address")
            .takes_value(true))
        .get_matches();

    let mut handles = Vec::<JoinHandle<()>>::new();
    if let Some(addresses) = matches.values_of("server") {
        let mut servers: Vec<SocketAddrV4> = vec![];
        for addr in addresses {
            servers.push(addr.trim().parse().expect("not a value port number"));
        }

        for address in servers.into_iter() {
            handles.push(spawn(move || listen(address) ));
        }
    }

    if let Some(addresses) = matches.values_of("client") {
        let mut clients: Vec<SocketAddrV4> = vec![];
        for addr in addresses {
            clients.push(addr.trim().parse().expect("not a value port number"));
        }

        for address in clients.into_iter() {
            handles.push(spawn(move || ping(address)));
        }
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

fn get_client_error_string(error: Error) -> &'static str {
    match error.kind() {
        ErrorKind::ConnectionRefused => "Connection refused",
        ErrorKind::ConnectionAborted => "Connection aborted",
        ErrorKind::ConnectionReset => "Connection reset",
        _ => "Unhandled error",
    }
}

fn ping(server: SocketAddrV4) {
    let mut i: u64 = 0;
    println!("Establishing session to {}", server);
    loop {
        match TcpStream::connect(server) {
            Err(e) => {
                println!("Connection to {} failed: {}", server, get_client_error_string(e));
                i += 1;
                sleep(Duration::from_secs(1));
            },
            Ok(mut stream) => {
                let client = stream.local_addr().unwrap();
                stream.set_read_timeout(Some(Duration::from_millis(100))).unwrap();
                stream.set_write_timeout(Some(Duration::from_millis(100))).unwrap();
                let mut read_buf: [u8; 128] = [0; 128];
                loop {
                    // FIXME: do we really need to allocate a string here? I could not find how to
                    // do this directly with byte literals.
                    let msg = format!("ping {}", i);
                    i += 1;
                    match stream.write(msg.as_bytes()) {
                        Err(e) => {
                            println!("{}: failed to write ping message for {} ({})", client, server, get_client_error_string(e));
                            sleep(Duration::from_secs(1));
                            break;
                        },
                        Ok(_) => {
                            println!("{} >>> {}: {}", client, server, msg);
                            match stream.read(&mut read_buf) {
                                Ok(n) => {
                                    if n > 0 {
                                        println!("{} <<< {}: {}", client, server, str::from_utf8(&read_buf[0..n]).unwrap());
                                    } else {
                                        println!("{}: connection with {} closed (received EOF)", client, server);
                                        sleep(Duration::from_secs(1));
                                        break;
                                    }
                                    sleep(Duration::from_secs(1));
                                }
                                Err(e) => {
                                    println!("{}: failed to read ping response from {} ({})", client, server, get_client_error_string(e));
                                }
                            }
                        },
                    }
                }
            },
        }
    }
}

fn listen(address: SocketAddrV4) {
    let listener = TcpListener::bind(&address).unwrap();
    println!("listening started on {}, ready to accept", address);
    for stream in listener.incoming() {
        spawn(|| {
            handle_connection(stream.unwrap());
        });
    }
}

fn handle_connection(mut stream: TcpStream) {
    let server = stream.local_addr().unwrap();
    let peer = stream.peer_addr().unwrap();
    println!("{}: connection established from {}", server, peer);

    // We'll wait for 100 milliseconds to receive a message Concretely, a ErrorKind::WouldBlock
    // io::Error is returned after 100ms if there is nothing to read.
    stream.set_read_timeout(Some(Duration::from_millis(100))).unwrap();

    // We'll wait for 100 milliseconds to send a message
    stream.set_write_timeout(Some(Duration::from_millis(100))).unwrap();

    let mut read_buf: [u8; 128] = [0; 128];
    loop {
        match stream.read(&mut read_buf) {
            Ok(n) => {
                if n > 0 {
                    handle_message(&read_buf, n, &mut stream);
                } else {
                    // according to rust docs[1], n == 0 can indicate one of two scenarios:
                    //
                    // 1. This reader has reached its "end of file" and will likely no longer be
                    //    able to produce bytes. Note that this does not mean that the reader will
                    //    always no longer be able to produce bytes.
                    //
                    // 2. The buffer specified was 0 bytes in length.
                    //
                    // https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read
                    //
                    // We assume the first scenario since we know our buffer length in not 0.
                    // This means the connection is closed and we can exit.
                    println!("{}: connection with {} is closed (received EOF).", server, peer);
                    return;
                }
            }
            Err(e) => {
                match e.kind() {
                    // We are using a non-blocking socket and there was nothing to read, so Linux
                    // return an EAGAIN error which translates in rust into a "WouldBlock" error.
                    ErrorKind::WouldBlock => continue,
                    _ => panic!("{}: An unknown error occured while reading the socket for {}: {}", server, peer, e),
                }
            }
        }
    }
}

fn handle_message(buffer: &[u8], length: usize, stream: &mut TcpStream) {
    let server = stream.local_addr().unwrap();
    let peer = stream.peer_addr().unwrap();
    if let Ok(msg) = str::from_utf8(&buffer[0..length]) {
        println!("{} <<< {} {}", server, peer, msg);
        match stream.write(&buffer[0..length]) {
            Ok(n) => {
                if length < n {
                    panic!("*** {}: Incomplete answer: {} bytes answered instead of {}",
                           peer,
                           n,
                           length);
                } else {
                    println!("{} >>> {} {}", server, peer, msg);
                }
            }
            Err(e) => {
                panic!("*** {}: {:?}", peer, e);
            }
        }
    } else {
        println!("<<< {} {:?}", peer, buffer);
        println!("Received message is not a valid UTF-8 sequence. Ignoring");
    }
}
<commit_msg>run rustfmt<commit_after>extern crate clap;

use std::io::{Write, Read, ErrorKind, Error};
use std::net::{TcpListener, TcpStream, SocketAddrV4};
use std::thread::{spawn, JoinHandle, sleep};
use std::time::Duration;
use std::str;
use clap::{Arg, App};

fn main() {
    let matches = App::new("Simple TCP echo server")
        .version("1.0")
        .author("Corentin H. <corentinhenry@gmail.com>")
        .about("Quickly test TCP connections")
        .arg(Arg::with_name("server")
            .short("s")
            .long("server")
            .number_of_values(1)
            .multiple(true)
            .help("Start a server listen on the specified address")
            .takes_value(true))
        .arg(Arg::with_name("client")
            .short("c")
            .long("client")
            .number_of_values(1)
            .multiple(true)
            .help("Start a client on the specified address")
            .takes_value(true))
        .get_matches();

    let mut handles = Vec::<JoinHandle<()>>::new();
    if let Some(addresses) = matches.values_of("server") {
        let mut servers: Vec<SocketAddrV4> = vec![];
        for addr in addresses {
            servers.push(addr.trim().parse().expect("not a value port number"));
        }

        for address in servers.into_iter() {
            handles.push(spawn(move || listen(address)));
        }
    }

    if let Some(addresses) = matches.values_of("client") {
        let mut clients: Vec<SocketAddrV4> = vec![];
        for addr in addresses {
            clients.push(addr.trim().parse().expect("not a value port number"));
        }

        for address in clients.into_iter() {
            handles.push(spawn(move || ping(address)));
        }
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

fn get_client_error_string(error: Error) -> &'static str {
    match error.kind() {
        ErrorKind::ConnectionRefused => "Connection refused",
        ErrorKind::ConnectionAborted => "Connection aborted",
        ErrorKind::ConnectionReset => "Connection reset",
        _ => "Unhandled error",
    }
}

fn ping(server: SocketAddrV4) {
    let mut i: u64 = 0;
    println!("Establishing session to {}", server);
    loop {
        match TcpStream::connect(server) {
            Err(e) => {
                println!("Connection to {} failed: {}",
                         server,
                         get_client_error_string(e));
                i += 1;
                sleep(Duration::from_secs(1));
            }
            Ok(mut stream) => {
                let client = stream.local_addr().unwrap();
                stream.set_read_timeout(Some(Duration::from_millis(100))).unwrap();
                stream.set_write_timeout(Some(Duration::from_millis(100))).unwrap();
                let mut read_buf: [u8; 128] = [0; 128];
                loop {
                    // FIXME: do we really need to allocate a string here? I could not find how to
                    // do this directly with byte literals.
                    let msg = format!("ping {}", i);
                    i += 1;
                    match stream.write(msg.as_bytes()) {
                        Err(e) => {
                            println!("{}: failed to write ping message for {} ({})",
                                     client,
                                     server,
                                     get_client_error_string(e));
                            sleep(Duration::from_secs(1));
                            break;
                        }
                        Ok(_) => {
                            println!("{} >>> {}: {}", client, server, msg);
                            match stream.read(&mut read_buf) {
                                Ok(n) => {
                                    if n > 0 {
                                        println!("{} <<< {}: {}",
                                                 client,
                                                 server,
                                                 str::from_utf8(&read_buf[0..n]).unwrap());
                                    } else {
                                        println!("{}: connection with {} closed (received EOF)",
                                                 client,
                                                 server);
                                        sleep(Duration::from_secs(1));
                                        break;
                                    }
                                    sleep(Duration::from_secs(1));
                                }
                                Err(e) => {
                                    println!("{}: failed to read ping response from {} ({})",
                                             client,
                                             server,
                                             get_client_error_string(e));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

fn listen(address: SocketAddrV4) {
    let listener = TcpListener::bind(&address).unwrap();
    println!("listening started on {}, ready to accept", address);
    for stream in listener.incoming() {
        spawn(|| {
            handle_connection(stream.unwrap());
        });
    }
}

fn handle_connection(mut stream: TcpStream) {
    let server = stream.local_addr().unwrap();
    let peer = stream.peer_addr().unwrap();
    println!("{}: connection established from {}", server, peer);

    // We'll wait for 100 milliseconds to receive a message Concretely, a ErrorKind::WouldBlock
    // io::Error is returned after 100ms if there is nothing to read.
    stream.set_read_timeout(Some(Duration::from_millis(100))).unwrap();

    // We'll wait for 100 milliseconds to send a message
    stream.set_write_timeout(Some(Duration::from_millis(100))).unwrap();

    let mut read_buf: [u8; 128] = [0; 128];
    loop {
        match stream.read(&mut read_buf) {
            Ok(n) => {
                if n > 0 {
                    handle_message(&read_buf, n, &mut stream);
                } else {
                    // according to rust docs[1], n == 0 can indicate one of two scenarios:
                    //
                    // 1. This reader has reached its "end of file" and will likely no longer be
                    //    able to produce bytes. Note that this does not mean that the reader will
                    //    always no longer be able to produce bytes.
                    //
                    // 2. The buffer specified was 0 bytes in length.
                    //
                    // https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read
                    //
                    // We assume the first scenario since we know our buffer length in not 0.
                    // This means the connection is closed and we can exit.
                    println!("{}: connection with {} is closed (received EOF).",
                             server,
                             peer);
                    return;
                }
            }
            Err(e) => {
                match e.kind() {
                    // We are using a non-blocking socket and there was nothing to read, so Linux
                    // return an EAGAIN error which translates in rust into a "WouldBlock" error.
                    ErrorKind::WouldBlock => continue,
                    _ => {
                        panic!("{}: An unknown error occured while reading the socket for {}: {}",
                               server,
                               peer,
                               e)
                    }
                }
            }
        }
    }
}

fn handle_message(buffer: &[u8], length: usize, stream: &mut TcpStream) {
    let server = stream.local_addr().unwrap();
    let peer = stream.peer_addr().unwrap();
    if let Ok(msg) = str::from_utf8(&buffer[0..length]) {
        println!("{} <<< {} {}", server, peer, msg);
        match stream.write(&buffer[0..length]) {
            Ok(n) => {
                if length < n {
                    panic!("*** {}: Incomplete answer: {} bytes answered instead of {}",
                           peer,
                           n,
                           length);
                } else {
                    println!("{} >>> {} {}", server, peer, msg);
                }
            }
            Err(e) => {
                panic!("*** {}: {:?}", peer, e);
            }
        }
    } else {
        println!("<<< {} {:?}", peer, buffer);
        println!("Received message is not a valid UTF-8 sequence. Ignoring");
    }
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]

use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};

use futures::FutureExt;
use std::error::Error;
use std::{env, io};

use nix::sys::socket::{getsockopt, sockopt, InetAddr};
use std::net::SocketAddr;
use std::os::unix::io::AsRawFd;

use bytes::BytesMut;
use tokio::net::tcp::{ReadHalf, WriteHalf};
use tokio::time;

const BUF_SIZE: usize = 1024 * 1024;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let listen_addr = env::args()
        .nth(1)
        .unwrap_or_else(|| "127.0.0.1:1080".to_string());

    let timeout = env::args()
        .nth(2)
        .unwrap_or_else(|| "3600".to_string()).parse::<u64>().unwrap();

    let timeout = time::Duration::from_secs(timeout);

    println!("Listening on: {}", listen_addr);

    let listener = TcpListener::bind(listen_addr).await?;

    while let Ok((inbound, client_addr)) = listener.accept().await {
        let server_addr = getsockopt(inbound.as_raw_fd(), sockopt::OriginalDst).unwrap();
        let server_addr = InetAddr::V4(server_addr).to_std();
        eprintln!(
            "[INFO]{} -> {}: connection incoming",
            client_addr, server_addr
        );
        if inbound.local_addr().unwrap() == server_addr {
            eprintln!(
                "[WARN]{} -> {}: connection from local network",
                client_addr, server_addr
            );
            continue;
        }
        let transfer = transfer(inbound, server_addr, timeout).map(move |r| {
            if let Err(e) = r {
                println!(
                    "[INFO]{} -> {}: connection closed with error; {}",
                    client_addr, server_addr, e
                );
            }
        });
        tokio::spawn(transfer);
    }

    Ok(())
}

async fn transfer(mut inbound: TcpStream, proxy_addr: SocketAddr, timeout: time::Duration) -> Result<(), Box<dyn Error>> {
    let mut outbound = TcpStream::connect(proxy_addr).await?;

    inbound.set_nodelay(true)?;
    outbound.set_nodelay(true)?;

    let client_addr = inbound.peer_addr().unwrap();
    let server_addr = outbound.peer_addr().unwrap();

    let (mut ri, mut wi) = inbound.split();
    let (mut ro, mut wo) = outbound.split();

    tokio::try_join!(
        copy_one_direction(&mut ro, &mut wi, timeout),
        copy_one_direction(&mut ri, &mut wo, timeout)
    )?;
    eprintln!(
        "[INFO]{} -> {}: connection closed",
        client_addr, server_addr
    );

    Ok(())
}

async fn copy_one_direction(
    from: &mut ReadHalf<'_>,
    to: &mut WriteHalf<'_>,
    timeout: time::Duration,
) -> std::io::Result<()> {
    loop {
        if (time::timeout(timeout,from.readable()).await).is_err() {
            break
        }
        {
            let mut buf = BytesMut::with_capacity(BUF_SIZE);
            match from.try_read_buf(&mut buf) {
                Ok(0) => {
                    break;
                }
                Ok(_n) => {
                    to.write_all_buf(&mut buf).await?;
                }
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                    continue;
                }
                Err(e) => {
                    return Err(e);
                }
            }
        }
    }
    to.shutdown().await
}
<commit_msg>Timeout when both direction is silent<commit_after>#![warn(rust_2018_idioms)]

use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};

use futures::FutureExt;
use std::error::Error;
use std::{env, io};

use nix::sys::socket::{getsockopt, sockopt, InetAddr};
use std::net::SocketAddr;
use std::os::unix::io::AsRawFd;

use bytes::BytesMut;
use tokio::net::tcp::{ReadHalf, WriteHalf};
use tokio::sync::{broadcast, mpsc};

use tokio::{select, time};

const BUF_SIZE: usize = 1024 * 1024;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let listen_addr = env::args()
        .nth(1)
        .unwrap_or_else(|| "127.0.0.1:1080".to_string());

    let timeout = env::args()
        .nth(2)
        .unwrap_or_else(|| "3600".to_string())
        .parse::<u64>()
        .unwrap();

    let timeout = time::Duration::from_secs(timeout);

    println!("Listening on: {}", listen_addr);

    let listener = TcpListener::bind(listen_addr).await?;

    while let Ok((inbound, client_addr)) = listener.accept().await {
        let server_addr = getsockopt(inbound.as_raw_fd(), sockopt::OriginalDst).unwrap();
        let server_addr = InetAddr::V4(server_addr).to_std();
        eprintln!(
            "[INFO]{} -> {}: connection incoming",
            client_addr, server_addr
        );
        if inbound.local_addr().unwrap() == server_addr {
            eprintln!(
                "[WARN]{} -> {}: connection from local network",
                client_addr, server_addr
            );
            continue;
        }
        let transfer = transfer(inbound, server_addr, timeout).map(move |r| {
            if let Err(e) = r {
                println!(
                    "[INFO]{} -> {}: connection closed with error; {}",
                    client_addr, server_addr, e
                );
            }
        });
        tokio::spawn(transfer);
    }

    Ok(())
}

async fn transfer(
    mut inbound: TcpStream,
    proxy_addr: SocketAddr,
    timeout: time::Duration,
) -> Result<(), Box<dyn Error>> {
    let mut outbound = TcpStream::connect(proxy_addr).await?;

    inbound.set_nodelay(true)?;
    outbound.set_nodelay(true)?;

    let client_addr = inbound.peer_addr().unwrap();
    let server_addr = outbound.peer_addr().unwrap();

    let (mut ri, mut wi) = inbound.split();
    let (mut ro, mut wo) = outbound.split();

    let (stat_tx, stat_rx) = mpsc::channel(32);
    let (timeout_tx, timeout_rx) = broadcast::channel(1);

    tokio::join!(
        copy_one_direction(&mut ro, &mut wi, timeout_rx, stat_tx.clone()),
        copy_one_direction(&mut ri, &mut wo, timeout_tx.subscribe(), stat_tx.clone()),
        stat_and_timeout(stat_rx, timeout_tx, timeout)
    );
    eprintln!(
        "[INFO]{} -> {}: connection closed",
        client_addr, server_addr
    );

    Ok(())
}

async fn stat_and_timeout(
    mut stat_rx: mpsc::Receiver<usize>,
    timeout_tx: broadcast::Sender<()>,
    timeout: time::Duration,
) -> Result<usize, broadcast::error::SendError<()>> {
    let mut traffic_total: usize = 0;
    loop {
        match time::timeout(timeout, stat_rx.recv()).await {
            Ok(Some(traffic)) => {
                traffic_total += traffic;
            }
            Ok(None) => continue,
            Err(_) => break,
        }
    }
    eprintln!("total traffic on both sides: {}", traffic_total);
    timeout_tx.send(())
}

async fn copy_one_direction(
    from: &mut ReadHalf<'_>,
    to: &mut WriteHalf<'_>,
    mut timeout_rx: broadcast::Receiver<()>,
    stat_tx: mpsc::Sender<usize>,
) -> std::io::Result<()> {
    loop {
        select! {
            _ = from.readable() => {
                let mut buf = BytesMut::with_capacity(BUF_SIZE);
                match from.try_read_buf(&mut buf) {
                    Ok(0) => {
                        break;
                    }
                    Ok(n) => {
                        stat_tx.send(n).await.ok();
                        to.write_all_buf(&mut buf).await?;
                    }
                    Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                        continue;
                    }
                    Err(e) => {
                        return Err(e);
                    }
                }
            }
            _ = timeout_rx.recv() => {
                break
            }
        }
    }
    to.shutdown().await
}
<|endoftext|>


<commit_before>struct Item(&'static str);

struct Location {
    description: &'static str,
    items: Vec<Item>,
}

// how to do actions?
// as an enum? sounds reasonable.
// what properties do they have?
// a keyword which is typed in
// one or more arguments, possibly a variable number
// something which happens when they are invoked

// "look": print the area description
// "take + item": move the item to the player's inventory
// "go + direction": move to the area in that direction
// "use + item (+ object)": maybe remove the item, invoke a (targeted?) effect

// alternatively, instantiate actions at runtime
// write a struct for each one with arity and synonyms
// problem: each one is associated with a function of some sort
// do not want to go adding these at runtime if it can be avoided

fn parse(command: String) {
    let tokens = command.split(' ');
    match tokens[0] {
        "look" => println!("You look around."),
        "take" => println!("You take something."),
        "go" => println!("You go somewhere."),
        "use" => println!("You use something."),
        _ => println!("Unknown command!"),
    }
    // look should just check for arity 0 invoke an associated function
    // take should parse rest of the command, see if there is an item with a name equal to the rest of the command
    // go should parse exactly one more token and go in that direction... how?
    // use should parse either one or two tokens and act accordingly
}

fn main() {
    let item1 = Item("Little key");
    let item2 = Item("Big key");
    let hall = Location{ description: "A wide open space. Streams of lava flow on both sides of the entry gate.",
                         items: vec![item1, item2] };
}
<commit_msg>For some reason it is required to make tokens mutable here. No idea why yet.<commit_after>struct Item(&'static str);

struct Location {
    description: &'static str,
    items: Vec<Item>,
}

// how to do actions?
// as an enum? sounds reasonable.
// what properties do they have?
// a keyword which is typed in
// one or more arguments, possibly a variable number
// something which happens when they are invoked

// "look": print the area description
// "take + item": move the item to the player's inventory
// "go + direction": move to the area in that direction
// "use + item (+ object)": maybe remove the item, invoke a (targeted?) effect

// alternatively, instantiate actions at runtime
// write a struct for each one with arity and synonyms
// problem: each one is associated with a function of some sort
// do not want to go adding these at runtime if it can be avoided

fn parse(command: String) {
    let mut tokens = command.split(' ');
    let verb = tokens.nth(0);
    //   match tokens[0] {
    //       "look" => println!("You look around."),
    //       "take" => println!("You take something."),
    //       "go" => println!("You go somewhere."),
    //       "use" => println!("You use something."),
    //       _ => println!("Unknown command!"),
    //   }
    // look should just check for arity 0 invoke an associated function
    // take should parse rest of the command, see if there is an item with a name equal to the rest of the command
    // go should parse exactly one more token and go in that direction... how?
    // use should parse either one or two tokens and act accordingly
}

fn main() {
    let item1 = Item("Little key");
    let item2 = Item("Big key");
    let hall = Location{ description: "A wide open space. Streams of lava flow on both sides of the entry gate.",
                         items: vec![item1, item2] };
}
<|endoftext|>