<commit_before>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;
extern crate chrono;
extern crate kuchiki;
use kuchiki::traits::*;

use std::default::Default;

use rustbox::{Color, RustBox, Key};
use rustc_serialize::json;
use rustc_serialize::json::Json;

use chrono::*;

use hkg::utility::cache;
use hkg::model::ListTopicItem;
use hkg::model::ShowItem;
use hkg::model::UrlQueryItem;

use std::path::Path;

#[derive(PartialEq, Eq, Copy, Clone)]
enum Status {
    List,
    Show,
}

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let title = String::from("高登");
    let s = cache::readfile(String::from("data/topics.json"));
    let collection: Vec<ListTopicItem> = json::decode(&s).unwrap();

    // initialize show with empty page
    let mut show_file;
    let mut show_item = ShowItem {
        url_query: UrlQueryItem { message: String::from("") },
        replies: vec![],
        page: 0,
        max_page: 0,
        reply_count: String::from(""),
        title: String::from(""),
    };

    let mut status = String::from("> ");

    let mut state = Status::List;
    let mut prev_state = state;
    let mut prev_width = rustbox.width();

    let mut list = hkg::screen::list::List::new(&rustbox);
    let mut show = hkg::screen::show::Show::new(&rustbox);

    loop {

        // show UI
        if prev_state != state {
            hkg::screen::common::clear(&rustbox); // clear screen when switching state
            prev_state = state;
        }

        let url = &collection[1].title.url;

        rustbox.print(1, 3, rustbox::RB_NORMAL, Color::White, Color::Black, url);

        let p = "data/html/6360604/show_1.html";
        let document = kuchiki::parse_html().from_utf8().from_file(&p).unwrap();

        let replies_data = document.select(".repliers tr[userid][username]")
                               .unwrap()
                               .collect::<Vec<_>>();

        for (index, tr) in replies_data.iter().enumerate() {
            let tr_attrs = (&tr.attributes).borrow();
            let userid = tr_attrs.get("userid").unwrap();
            let username = tr_attrs.get("username").unwrap();

            let content = tr.as_node().select(".repliers_right .ContentGrid").unwrap()
                                .next().unwrap() // first
                                .as_node().text_contents(); // text

            rustbox.print(1,
                          index + 4,
                          rustbox::RB_NORMAL,
                          Color::White,
                          Color::Black,
                          &format!("[{:<2}]={:?}", index, content));

        }

        // match state {
        //     Status::List => {
        //         list.print(&title, &collection);
        //     }
        //     Status::Show => {
        //         show.print(&title, &show_item);
        //     }
        // }

        print_status(&rustbox, &status);

        rustbox.present();

        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {

                if prev_width != rustbox.width() {
                    hkg::screen::common::clear(&rustbox);
                    prev_width = rustbox.width();
                }

                match key {
                    Key::Char('q') => {
                        break;
                    }
                    Key::PageUp => {
                        let w = rustbox.width();
                        status = format_status(status, w, " PU");

                        match state {
                            Status::List => {}
                            Status::Show => {
                                let bh = show.body_height();
                                if show.scrollUp(bh) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::PageDown => {
                        let w = rustbox.width();
                        status = format_status(status, w, " PD");

                        match state {
                            Status::List => {}
                            Status::Show => {
                                let bh = show.body_height();
                                if show.scrollDown(bh) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Up => {
                        let w = rustbox.width();
                        status = format_status(status, w, "U");

                        match state {
                            Status::List => {
                                let tmp = list.get_selected_topic();
                                if tmp > 1 {
                                    list.select_topic(tmp - 1);
                                }
                            }
                            Status::Show => {
                                if show.scrollUp(2) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }

                    }
                    Key::Down => {
                        let w = rustbox.width();
                        status = format_status(status, w, "D");

                        match state {
                            Status::List => {
                                let tmp = list.get_selected_topic();
                                if tmp < list.body_height() {
                                    list.select_topic(tmp + 1);
                                }
                            }
                            Status::Show => {
                                if show.scrollDown(2) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Left => {
                        match state {
                            Status::List => {}
                            Status::Show => {
                                if show_item.page > 1 {
                                    let show_file_path = format!("data/{postid}/show_{page}.json",
                                                                 postid = show_item.url_query
                                                                                .message,
                                                                 page = show_item.page - 1);

                                    show_file = cache::readfile(String::from(show_file_path));
                                    show_item = json::decode(&show_file).unwrap();
                                    show.resetY();
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Right => {
                        match state {
                            Status::List => {}
                            Status::Show => {
                                if show_item.max_page > show_item.page {
                                    let show_file_path = format!("data/{postid}/show_{page}.json",
                                                                 postid = show_item.url_query
                                                                                .message,
                                                                 page = show_item.page + 1);

                                    show_file = cache::readfile(String::from(show_file_path));
                                    show_item = json::decode(&show_file).unwrap();
                                    show.resetY();
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Enter => {
                        let w = rustbox.width();
                        status = format_status(status, w, "E");
                        match state {
                            Status::List => {

                                let index = list.get_selected_topic();
                                if index > 0 {
                                    let topic_item = &collection[index - 1];

                                    let postid = &topic_item.title.url_query.message;

                                    let show_file_path = format!("data/{postid}/show_{page}.json",
                                                                 postid = postid,
                                                                 page = 1);

                                    if Path::new(&show_file_path).exists() {
                                        show_file = cache::readfile(String::from(show_file_path));
                                        show_item = json::decode(&show_file).unwrap();
                                        show.resetY();
                                        hkg::screen::common::clear(&rustbox);
                                        state = Status::Show;
                                    } else {
                                        let w = rustbox.width();
                                        status = format_status(status,
                                                               w,
                                                               &format!(" postid {} not found.",
                                                                        postid));
                                    }
                                }
                            }
                            Status::Show => {}
                        }
                    }
                    Key::Backspace => {
                        let w = rustbox.width();
                        status = format_status(status, w, "B");
                        match state {
                            Status::List => {}
                            Status::Show => {
                                state = Status::List;
                            }
                        }
                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_status(rustbox: &rustbox::RustBox, status: &str) {
    // for status bar only
    let w = rustbox.width();
    let h = rustbox.height();

    let status_width = if w > status.len() {
        w - status.len()
    } else {
        0
    };
    let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

    rustbox.print(0,
                  h - 1,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  &format!("{status}{status_spacing}",
                           status = status,
                           status_spacing = status_spacing));

}

fn format_status(status: String, w: usize, s: &str) -> String {
    if status.len() >= w {
        String::from(format!("{}{}", &"> ", s))
    } else {
        String::from(format!("{}{}", &status, s))
    }
}

// for (jndex, elm) in tr.as_node().select(".repliers_right .ContentGrid").unwrap().enumerate() {
//     let content = elm.as_node().text_contents();
//     let name = &elm.name;
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}][{:<2}]={:?}", index, jndex, content));
// }

// let content = tr.text_contents();
// rustbox.print(1,
//               index + 4,
//               rustbox::RB_NORMAL,
//               Color::White,
//               Color::Black,
//               &format!("[{:<2}]={:?}", index, content));

// for (jndex, div) in tr.as_node().children().enumerate() {
//     let content = div.as_text();
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index + jndex, content));
// }

// for (jndex, div) in tr.as_node().select(".repliers_right").unwrap().enumerate() {
//     let content = div.as_node().as_text();
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index + jndex, content));
// }

// for (jndex, div) in tr.as_node().select(".repliers_right").unwrap().collect::<Vec<_>>().iter().enumerate() {
//     let content = div.as_node().as_text();
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index + jndex, content));
// }

// let c = &tr.as_node().select(".repliers_right .ContentGrid").unwrap().collect::<Vec<_>>()[0];
// let content = c.as_node().as_text();
// rustbox.print(1, index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index, content));

// fn date_operation_example(rustbox: &rustbox::RustBox) {
//     let now = Local::now();
//
//     let dt1 = match Local.datetime_from_str("30/4/2016 9:22", "%d/%m/%Y %H:%M") {
//         Ok(v) => v,
//         Err(e) => Local::now(),
//     };
//
//     let dt2 = now.checked_sub(Duration::seconds(46)).unwrap();
//     let dt3 = now.checked_sub(Duration::minutes(6)).unwrap();
//     let dt4 = now.checked_sub(Duration::days(17)).unwrap();
//     let dt5 = now.checked_sub(Duration::weeks(9)).unwrap();
//
//     rustbox.print(0,
//                   0,
//                   rustbox::RB_BOLD,
//                   Color::White,
//                   Color::Black,
//                   &format!("{} {} {} {}",
//                    duration_format(&(now - dt2)),
//                    duration_format(&(now - dt3)),
//                    duration_format(&(now - dt4)),
//                    duration_format(&(now - dt5))
//               ));
//
// }

// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<commit_msg>parse content and write to file<commit_after>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;
extern crate chrono;
extern crate kuchiki;

use kuchiki::traits::*;

use std::default::Default;

use rustbox::{Color, RustBox, Key};
use rustc_serialize::json;
use rustc_serialize::json::Json;

use chrono::*;

use hkg::utility::cache;
use hkg::model::ListTopicItem;
use hkg::model::ShowItem;
use hkg::model::UrlQueryItem;

use std::path::Path;

use std::io::prelude::*;
use std::fs::File;

#[derive(PartialEq, Eq, Copy, Clone)]
enum Status {
    List,
    Show,
}

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    rustbox.print(1,
                  1,
                  rustbox::RB_NORMAL,
                  Color::White,
                  Color::Black,
                  &format!("start => {}", Local::now()));

    let title = String::from("高登");
    let s = cache::readfile(String::from("data/topics.json"));
    let collection: Vec<ListTopicItem> = json::decode(&s).unwrap();

    // initialize show with empty page
    let mut show_file;
    let mut show_item = ShowItem {
        url_query: UrlQueryItem { message: String::from("") },
        replies: vec![],
        page: 0,
        max_page: 0,
        reply_count: String::from(""),
        title: String::from(""),
    };

    let mut status = String::from("> ");

    let mut state = Status::List;
    let mut prev_state = state;
    let mut prev_width = rustbox.width();

    let mut list = hkg::screen::list::List::new(&rustbox);
    let mut show = hkg::screen::show::Show::new(&rustbox);

    loop {

        // show UI
        if prev_state != state {
            hkg::screen::common::clear(&rustbox); // clear screen when switching state
            prev_state = state;
        }

        let url = &collection[1].title.url;

        rustbox.print(1, 2, rustbox::RB_NORMAL, Color::White, Color::Black, url);

        rustbox.print(1,
                      3,
                      rustbox::RB_NORMAL,
                      Color::White,
                      Color::Black,
                      &format!("before parse => {}", Local::now()));

        let p = "data/html/6360604/show_1.html";
        let document = kuchiki::parse_html().from_utf8().from_file(&p).unwrap();

        let replies_data = document.select(".repliers tr[userid][username]")
                                   .unwrap()
                                   .collect::<Vec<_>>();

        let mut ss = String::new();
        for (index, tr) in replies_data.iter().enumerate() {
            let tr_attrs = (&tr.attributes).borrow();
            let userid = tr_attrs.get("userid").unwrap();
            let username = tr_attrs.get("username").unwrap();

            let content = tr.as_node().select(".repliers_right .ContentGrid").unwrap()
                                .next().unwrap() // first
                                .as_node().text_contents(); // text

            let tmp_str = format!("[{:0<2}]={}\n", index, content.trim());
            ss.push_str(&tmp_str);
            rustbox.print(1,
                          index + 5,
                          rustbox::RB_NORMAL,
                          Color::White,
                          Color::Black,
                          &tmp_str);
        }

        let mut f = File::create("foo.txt").unwrap();
        // let uu :Vec<u8> = ss.chars;
        f.write_all(ss.as_bytes());

        rustbox.print(1,
                      4,
                      rustbox::RB_NORMAL,
                      Color::White,
                      Color::Black,
                      &format!("after parse => {}", Local::now()));

        // match state {
        //     Status::List => {
        //         list.print(&title, &collection);
        //     }
        //     Status::Show => {
        //         show.print(&title, &show_item);
        //     }
        // }

        print_status(&rustbox, &status);

        rustbox.present();

        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {

                if prev_width != rustbox.width() {
                    hkg::screen::common::clear(&rustbox);
                    prev_width = rustbox.width();
                }

                match key {
                    Key::Char('q') => {
                        break;
                    }
                    Key::PageUp => {
                        let w = rustbox.width();
                        status = format_status(status, w, " PU");

                        match state {
                            Status::List => {}
                            Status::Show => {
                                let bh = show.body_height();
                                if show.scrollUp(bh) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::PageDown => {
                        let w = rustbox.width();
                        status = format_status(status, w, " PD");

                        match state {
                            Status::List => {}
                            Status::Show => {
                                let bh = show.body_height();
                                if show.scrollDown(bh) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Up => {
                        let w = rustbox.width();
                        status = format_status(status, w, "U");

                        match state {
                            Status::List => {
                                let tmp = list.get_selected_topic();
                                if tmp > 1 {
                                    list.select_topic(tmp - 1);
                                }
                            }
                            Status::Show => {
                                if show.scrollUp(2) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }

                    }
                    Key::Down => {
                        let w = rustbox.width();
                        status = format_status(status, w, "D");

                        match state {
                            Status::List => {
                                let tmp = list.get_selected_topic();
                                if tmp < list.body_height() {
                                    list.select_topic(tmp + 1);
                                }
                            }
                            Status::Show => {
                                if show.scrollDown(2) {
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Left => {
                        match state {
                            Status::List => {}
                            Status::Show => {
                                if show_item.page > 1 {
                                    let show_file_path = format!("data/{postid}/show_{page}.json",
                                                                 postid = show_item.url_query
                                                                                .message,
                                                                 page = show_item.page - 1);

                                    show_file = cache::readfile(String::from(show_file_path));
                                    show_item = json::decode(&show_file).unwrap();
                                    show.resetY();
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Right => {
                        match state {
                            Status::List => {}
                            Status::Show => {
                                if show_item.max_page > show_item.page {
                                    let show_file_path = format!("data/{postid}/show_{page}.json",
                                                                 postid = show_item.url_query
                                                                                .message,
                                                                 page = show_item.page + 1);

                                    show_file = cache::readfile(String::from(show_file_path));
                                    show_item = json::decode(&show_file).unwrap();
                                    show.resetY();
                                    hkg::screen::common::clear(&rustbox);
                                }
                            }
                        }
                    }
                    Key::Enter => {
                        let w = rustbox.width();
                        status = format_status(status, w, "E");
                        match state {
                            Status::List => {

                                let index = list.get_selected_topic();
                                if index > 0 {
                                    let topic_item = &collection[index - 1];

                                    let postid = &topic_item.title.url_query.message;

                                    let show_file_path = format!("data/{postid}/show_{page}.json",
                                                                 postid = postid,
                                                                 page = 1);

                                    if Path::new(&show_file_path).exists() {
                                        show_file = cache::readfile(String::from(show_file_path));
                                        show_item = json::decode(&show_file).unwrap();
                                        show.resetY();
                                        hkg::screen::common::clear(&rustbox);
                                        state = Status::Show;
                                    } else {
                                        let w = rustbox.width();
                                        status = format_status(status,
                                                               w,
                                                               &format!(" postid {} not found.",
                                                                        postid));
                                    }
                                }
                            }
                            Status::Show => {}
                        }
                    }
                    Key::Backspace => {
                        let w = rustbox.width();
                        status = format_status(status, w, "B");
                        match state {
                            Status::List => {}
                            Status::Show => {
                                state = Status::List;
                            }
                        }
                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_status(rustbox: &rustbox::RustBox, status: &str) {
    // for status bar only
    let w = rustbox.width();
    let h = rustbox.height();

    let status_width = if w > status.len() {
        w - status.len()
    } else {
        0
    };
    let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

    rustbox.print(0,
                  h - 1,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  &format!("{status}{status_spacing}",
                           status = status,
                           status_spacing = status_spacing));

}

fn format_status(status: String, w: usize, s: &str) -> String {
    if status.len() >= w {
        String::from(format!("{}{}", &"> ", s))
    } else {
        String::from(format!("{}{}", &status, s))
    }
}

// for (jndex, elm) in tr.as_node().select(".repliers_right .ContentGrid").unwrap().enumerate() {
//     let content = elm.as_node().text_contents();
//     let name = &elm.name;
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}][{:<2}]={:?}", index, jndex, content));
// }

// let content = tr.text_contents();
// rustbox.print(1,
//               index + 4,
//               rustbox::RB_NORMAL,
//               Color::White,
//               Color::Black,
//               &format!("[{:<2}]={:?}", index, content));

// for (jndex, div) in tr.as_node().children().enumerate() {
//     let content = div.as_text();
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index + jndex, content));
// }

// for (jndex, div) in tr.as_node().select(".repliers_right").unwrap().enumerate() {
//     let content = div.as_node().as_text();
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index + jndex, content));
// }

// for (jndex, div) in tr.as_node().select(".repliers_right").unwrap().collect::<Vec<_>>().iter().enumerate() {
//     let content = div.as_node().as_text();
//     rustbox.print(1, jndex + index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index + jndex, content));
// }

// let c = &tr.as_node().select(".repliers_right .ContentGrid").unwrap().collect::<Vec<_>>()[0];
// let content = c.as_node().as_text();
// rustbox.print(1, index + 4, rustbox::RB_NORMAL, Color::White, Color::Black, &format!("[{:<2}]={:?}", index, content));

// fn date_operation_example(rustbox: &rustbox::RustBox) {
//     let now = Local::now();
//
//     let dt1 = match Local.datetime_from_str("30/4/2016 9:22", "%d/%m/%Y %H:%M") {
//         Ok(v) => v,
//         Err(e) => Local::now(),
//     };
//
//     let dt2 = now.checked_sub(Duration::seconds(46)).unwrap();
//     let dt3 = now.checked_sub(Duration::minutes(6)).unwrap();
//     let dt4 = now.checked_sub(Duration::days(17)).unwrap();
//     let dt5 = now.checked_sub(Duration::weeks(9)).unwrap();
//
//     rustbox.print(0,
//                   0,
//                   rustbox::RB_BOLD,
//                   Color::White,
//                   Color::Black,
//                   &format!("{} {} {} {}",
//                    duration_format(&(now - dt2)),
//                    duration_format(&(now - dt3)),
//                    duration_format(&(now - dt4)),
//                    duration_format(&(now - dt5))
//               ));
//
// }

// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<|endoftext|>


<commit_before>use macroquad::prelude::*;

use grid::Grid;
use graph::Graph;

mod colour;
mod grid;
mod graph;

#[macroquad::main("BasicShapes")]
async fn main() {
    let grid = Grid::generate(8);
    let mut graph = Graph::create(&grid);

    loop {
        clear_background(BLACK);

        draw_line(40.0, 40.0, 100.0, 200.0, 15.0, BLUE);
        draw_rectangle(screen_width() / 2.0 - 60.0, 100.0, 120.0, 60.0, GREEN);
        draw_circle(screen_width() - 30.0, screen_height() - 30.0, 15.0, YELLOW);
        draw_text("HELLO", 20.0, 20.0, 20.0, DARKGRAY);

        next_frame().await
    }
}
<commit_msg>Draw rectangle the size of the grid<commit_after>use macroquad::prelude::*;

use grid::Grid;
use graph::Graph;

mod colour;
mod grid;
mod graph;

#[macroquad::main("BasicShapes")]
async fn main() {
    let grid = Grid::generate(8);
    let mut graph = Graph::create(&grid);

    loop {
        clear_background(BLACK);

        let rendering_size = screen_width().min(screen_height());

        draw_rectangle((screen_width() - rendering_size) / 2.0, (screen_height() - rendering_size) / 2.0, rendering_size, rendering_size, GREEN);

        next_frame().await
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate lazy_static;
extern crate termion;

mod prolog;
#[macro_use] mod test_utils;

use prolog::io::*;
use prolog::machine::*;

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[cfg(test)]
mod tests;

fn process_buffer(wam: &mut Machine, buffer: &str)
{
    match parse_code(wam, buffer) {
        Ok(tl) => {
            let result = compile(wam, &tl);
            print(wam, result);
        },
        Err(s) => println!("{:?}", s)
    };
}

fn prolog_repl() {
    let mut wam = Machine::new();

    load_init_file(&mut wam, "lists.pl");

    loop {
        print!("prolog> ");

        let buffer = read();

        if buffer == "quit\n" {
            break;
        } else if buffer == "clear\n" {
            wam.clear();
            continue;
        }

        process_buffer(&mut wam, buffer.as_str());
        wam.reset();
    }
}

fn main() {
    prolog_repl();
}
<commit_msg>prepare for batch processing<commit_after>#[macro_use] extern crate lazy_static;
extern crate termion;

mod prolog;
#[macro_use] mod test_utils;

use prolog::io::*;
use prolog::machine::*;

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

#[cfg(test)]
mod tests;

fn process_buffer(wam: &mut Machine, buffer: &str)
{
    match parse_code(wam, buffer) {
        Ok(tl) => {
            let result = compile(wam, &tl);
            print(wam, result);
        },
        Err(s) => println!("{:?}", s)
    };
}

fn prolog_repl() {
    let mut wam = Machine::new();

    loop {
        print!("prolog> ");

        let buffer = read();

        if buffer == "quit\n" {
            break;
        } else if buffer == "clear\n" {
            wam.clear();
            continue;
        }

        process_buffer(&mut wam, buffer.as_str());
        wam.reset();
    }
}

fn main() {
    prolog_repl();
}
<|endoftext|>


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

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
  * the function names for the corresponding addresses (in the "stacks" array) and returns JSON
  * such as:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

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

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

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

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

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

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

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

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

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

fn stacks_to_stack_map(decoded_stacks: Vec<Vec<(i8,u64)>>) -> HashMap<i8, Vec<u64>> {
    debug!("decoded.stacks: {:?}", decoded_stacks);

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

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

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

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

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

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

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

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

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let stack_map = stacks_to_stack_map(decoded.stacks);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map);
            res.write_all(symbol_response.as_bytes()).unwrap();

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

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

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

        let stack_map_copy = stack_map.clone();

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

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

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

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

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

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

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

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

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

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

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

    let mut symbolicated_stacks = vec!();

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

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

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

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

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<commit_msg>use rust-style doc comments<commit_after>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

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

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

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

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

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

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

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

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

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

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

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

fn stacks_to_stack_map(decoded_stacks: Vec<Vec<(i8,u64)>>) -> HashMap<i8, Vec<u64>> {
    debug!("decoded.stacks: {:?}", decoded_stacks);

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

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

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

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

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

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

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

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

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let stack_map = stacks_to_stack_map(decoded.stacks);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map);
            res.write_all(symbol_response.as_bytes()).unwrap();

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

    debug!("finished serving request");
}

///
/// Creates multiple client connections and aggregates result.
///
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

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

        let stack_map_copy = stack_map.clone();

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

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

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

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

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

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

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

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

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

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

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

    let mut symbolicated_stacks = vec!();

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

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

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

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

    json::encode(&result).unwrap()
}

///
/// Returns individual values from the configuration file.
///
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>use std::{thread};
extern crate sysinfo;
extern crate libc;
extern crate time;

fn kill_procs() {
    // Nobody aint got time for that
    let blocklist = ["firefox", "termite", "gimp", "thunderbird", "process_viewer"];
    let blocked = blocklist.iter();
    // TODO: Make config not hard coded

    let sys = sysinfo::System::new();
    let procs = sys.get_process_list();

    for (_, i) in procs {
        if blocked.clone().any(|x| x == &i.name) {
            i.kill(sysinfo::Signal::Kill);
        }
    }
}

fn condition() -> bool {
    // Time where blocking is enforced
    time::now().tm_hour < 18
}

fn check() {
    if condition() {
        thread::spawn(move || {
            println!("Checking thread started");
            kill_procs();
            thread::sleep_ms(2000);
            check();
        }).join().unwrap();
    } else {
        thread::sleep_ms(240000);
        check();
    }
}

fn main() {
    check();
}
<commit_msg>Bug fix :(<commit_after>use std::{thread};
extern crate sysinfo;
extern crate time;

fn kill_procs() {
    // Nobody aint got time for that
    let blocklist = ["firefox", "termite", "gimp", "thunderbird", "process_viewer"];
    let blocked = blocklist.iter();
    // TODO: Make config not hard coded

    let sys = sysinfo::System::new();
    let procs = sys.get_process_list();

    for (_, i) in procs {
        if blocked.clone().any(|x| x == &i.name) {
            i.kill(sysinfo::Signal::Kill);
        }
    }
}

fn condition() -> bool {
    // Time where blocking is enforced
    time::now().tm_hour < 18
}

fn check() {
    if condition() {
        thread::spawn(move || {
            println!("Checking thread started");
            kill_procs();
            thread::sleep_ms(2000);
            check();
        }).join().unwrap();
    } else {
        thread::sleep_ms(240000);
        check();
    }
}

fn main() {
    check();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate capnp;
extern crate capnp_rpc;
extern crate cassandra;

use std::collections::HashMap;
use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;
use cassandra::*;

pub mod killranswers_capnp {
  include!(concat!(env!("OUT_DIR"), "/killranswers_capnp.rs"));
}

lazy_static! {
    static ref QUERIES: HashMap<String, String> = {
        let mut m = HashMap::new();
        m.insert("create_user".to_string(), "insert into user (user_id), values (?)".to_string());
        m
    };
}

use killranswers_capnp::killr_answers;

struct KillrAnswersImpl;

impl killr_answers::Server for KillrAnswersImpl {
    // fn ask(&mut self, mut context: killr_answers::AskContext) {
    //     println!("Asking");
    // }
    fn register_user(&mut self, mut context: killr_answers::RegisterUserContext) {
        context.done();
    }

}

fn main() {
    println!("Starting up...");
    let rpc_server = EzRpcServer::new("127.0.0.1:6000").unwrap();
    let ka = Box::new(killr_answers::ServerDispatch { server : Box::new(KillrAnswersImpl)});
    //
    rpc_server.serve(ka);

    println!("Done");
}
<commit_msg>static string to create user<commit_after>#[macro_use]
extern crate lazy_static;
extern crate capnp;
extern crate capnp_rpc;
extern crate cassandra;

use std::collections::HashMap;
use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;
use cassandra::*;

pub mod killranswers_capnp {
  include!(concat!(env!("OUT_DIR"), "/killranswers_capnp.rs"));
}

static CREATE_USER : &'static str = "insert into user (user_id), values (?)";

lazy_static! {

}

use killranswers_capnp::killr_answers;

struct KillrAnswersImpl;

impl killr_answers::Server for KillrAnswersImpl {
    // fn ask(&mut self, mut context: killr_answers::AskContext) {
    //     println!("Asking");
    // }
    fn register_user(&mut self, mut context: killr_answers::RegisterUserContext) {
        context.done();
    }

}

fn main() {
    println!("Starting up...");
    let rpc_server = EzRpcServer::new("127.0.0.1:6000").unwrap();
    let ka = Box::new(killr_answers::ServerDispatch { server : Box::new(KillrAnswersImpl)});
    //
    rpc_server.serve(ka);

    println!("Done");
}
<|endoftext|>


<commit_before>#![feature(globs)]
extern crate terminal;
extern crate libc;
extern crate serialize;
extern crate collections;
extern crate getopts;

use getopts::{reqopt,getopts};
use std::os;
use std::io::IoResult;
use std::io::EndOfFile;
use terminal::{Screen,Vte,ScreenError};
use terminal::c_bits::libtsm::*;
use libc::{c_uint,c_void,size_t,uint32_t};
use std::char::from_u32;
use std::str::from_char;
use serialize::{Encodable,Encoder};
use serialize::json;

#[deriving(Show,Copy)]
struct State {
  rendered: Vec<Vec<(String, Attribute)>>
}

#[deriving(Show,Encodable)]
struct CursorState {
  x: u32,
  y: u32,
  visible: bool
}

#[deriving(PartialEq,Show)]
struct Attribute {
  fg: Option<u8>,
  bg: Option<u8>,
  bold: bool,
  underline: bool,
  inverse: bool,
  blink: bool
}

fn main() {
  let args: Vec<String> = os::args();

  let opts = [
      reqopt("h", "height", "the height of the console", "HEIGHT"),
      reqopt("w", "width", "the width of the console", "WIDTH")
  ];
  let matches = match getopts(args.tail(), opts) {
     Ok(m) => { m }
     Err(f) => { fail!("{}", f) }
  };
  let width: u32 = from_str(matches.opt_str("w").unwrap().as_slice()).unwrap();
  let height: u32 = from_str(matches.opt_str("h").unwrap().as_slice()).unwrap();

  let (screen, vte) = match setup_screen(width, height) {
    Err(reason) => { fail!("screen setup failed for {}", reason) }
    Ok(v) => v
  };

  match run(screen, vte) {
    Ok(()) => {},
    Err(reason) => {
      if !(reason.kind == EndOfFile) {
        fail!("runloop failed for {}", reason)
      }
    }
  }
}

fn setup_screen(width: u32, height: u32) -> Result<(Screen,Vte), ScreenError> {
  let screen = try!(Screen::open());
  try!(screen.resize(width,height));
  let vte = try!(screen.vte());
  Ok((screen,vte))
}

fn run(screen: Screen, vte: Vte) -> IoResult<()> {
  let mut stdin = std::io::stdio::stdin();

  loop {
    let line = try!(stdin.read_line());

    match line.as_slice().chars().next() {
      Some('d') => {
        let next_line = try!(stdin.read_line());
        let n: uint = from_str(next_line.as_slice().trim()).expect("d must be followed by int");
        let result = try!(stdin.read_exact(n));
        vte.feed(result.as_slice())
      }
      Some('p') => {
        let mut state = State { rendered: vec![] };
        let state_ptr: *mut c_void = &mut state as *mut _ as *mut c_void;
        screen.draw(draw_cb, state_ptr);
        let encoded = json::encode(&state);
        println!("{}", encoded)
      }
      Some('c') => {
        let (x,y) = screen.cursor_pos();
        let visible = screen.cursor_visible();
        let c_state = CursorState { x: x, y: y, visible: visible };
        let encoded = json::encode(&c_state);
        println!("{}", encoded)
      }
      None => { fail!("empty input!") }
      _ => { fail!("unknown command!") }
    }
  }
}

impl State {
  fn push_if_same(&mut self, ch: char, attr: Attribute) -> bool {
    let current_line  = self.rendered.mut_last().unwrap();
    let current_stream = current_line.mut_last().unwrap();
    if current_stream.ref1() == &attr {
      current_stream.mut0().push_char(ch);
      true
    } else {
      false
    }
  }

  fn push_new_cell(&mut self, ch: char, attr: Attribute) {
    let current_line  = self.rendered.mut_last().unwrap();
    let element = (from_char(ch), attr);
    current_line.push(element);
  }
}

impl<S: Encoder<E>, E> Encodable<S, E> for State {
  fn encode(&self, s: &mut S) -> Result<(), E> {
    self.rendered.encode(s)
  }
}

impl Attribute {
  fn new(attr: tsm_screen_attr) -> Attribute {
    Attribute {
      fg: attr.get_fg(),
      bg: attr.get_bg(),
      bold: attr.get_flag(Bold),
      underline: attr.get_flag(Underline),
      inverse: attr.get_flag(Inverse),
      blink: attr.get_flag(Blink)
    }
  }
}

impl<S: Encoder<E>, E> Encodable<S, E> for Attribute {
  fn encode(&self, e: &mut S) -> Result<(), E> {
    let fields_to_emit = vec![self.fg.is_some(), self.bg.is_some(), self.bold, self.underline, self.inverse, self.blink];
    let no_of_fields_to_emit = fields_to_emit.iter().filter(|val| **val).count();

    e.emit_map(no_of_fields_to_emit, |e| {
      let mut key_pos = 0;
      if self.fg.is_some() {
        try!(e.emit_map_elt_key(key_pos, |e| "fg".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.fg.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.bg.is_some() {
        try!(e.emit_map_elt_key(key_pos, |e| "bg".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.bg.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.bold {
        try!(e.emit_map_elt_key(key_pos, |e| "bold".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.bold.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.underline {
        try!(e.emit_map_elt_key(key_pos, |e| "underline".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.underline.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.inverse {
        try!(e.emit_map_elt_key(key_pos, |e| "inverse".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.inverse.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.blink {
        try!(e.emit_map_elt_key(key_pos, |e| "blink".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.blink.encode(e)));
      }

      Ok(())
    })
  }
}

extern "C" fn draw_cb(
  _: *const tsm_screen,
  _: u32,
  ch: *const uint32_t,
  _: size_t,
  _: c_uint,
  _: c_uint,
  posy: c_uint,
  attr: *const tsm_screen_attr,
  _: tsm_age_t,
  data: *mut c_void
) {
  unsafe {
    let data: &mut State = &mut *(data as *mut State);
    let mut current_char;
    let attribute = Attribute::new(*attr);

    if *ch == 0 {
      current_char = ' ';
    } else {
      current_char = from_u32(*ch).unwrap();
    }

    if data.rendered.len() == (posy as uint) {
      data.rendered.push(vec![(from_char(current_char), attribute)]);
      return
    }

    let added = data.push_if_same(current_char, attribute);
    if !added {
      data.push_new_cell(current_char, attribute);
    }
  };
}
<commit_msg>Remove deprecations<commit_after>#![feature(globs)]
extern crate terminal;
extern crate libc;
extern crate serialize;
extern crate collections;
extern crate getopts;

use getopts::{reqopt,getopts};
use std::os;
use std::io::IoResult;
use std::io::EndOfFile;
use terminal::{Screen,Vte,ScreenError};
use terminal::c_bits::libtsm::*;
use libc::{c_uint,c_void,size_t,uint32_t};
use std::char::from_u32;
use std::str::from_char;
use serialize::{Encodable,Encoder};
use serialize::json;

#[deriving(Show,Copy)]
struct State {
  rendered: Vec<Vec<(String, Attribute)>>
}

#[deriving(Show,Encodable)]
struct CursorState {
  x: u32,
  y: u32,
  visible: bool
}

#[deriving(PartialEq,Show)]
struct Attribute {
  fg: Option<u8>,
  bg: Option<u8>,
  bold: bool,
  underline: bool,
  inverse: bool,
  blink: bool
}

fn main() {
  let args: Vec<String> = os::args();

  let opts = [
      reqopt("h", "height", "the height of the console", "HEIGHT"),
      reqopt("w", "width", "the width of the console", "WIDTH")
  ];
  let matches = match getopts(args.tail(), opts) {
     Ok(m) => { m }
     Err(f) => { fail!("{}", f) }
  };
  let width: u32 = from_str(matches.opt_str("w").unwrap().as_slice()).unwrap();
  let height: u32 = from_str(matches.opt_str("h").unwrap().as_slice()).unwrap();

  let (screen, vte) = match setup_screen(width, height) {
    Err(reason) => { fail!("screen setup failed for {}", reason) }
    Ok(v) => v
  };

  match run(screen, vte) {
    Ok(()) => {},
    Err(reason) => {
      if !(reason.kind == EndOfFile) {
        fail!("runloop failed for {}", reason)
      }
    }
  }
}

fn setup_screen(width: u32, height: u32) -> Result<(Screen,Vte), ScreenError> {
  let screen = try!(Screen::open());
  try!(screen.resize(width,height));
  let vte = try!(screen.vte());
  Ok((screen,vte))
}

fn run(screen: Screen, vte: Vte) -> IoResult<()> {
  let mut stdin = std::io::stdio::stdin();

  loop {
    let line = try!(stdin.read_line());

    match line.as_slice().chars().next() {
      Some('d') => {
        let next_line = try!(stdin.read_line());
        let n: uint = from_str(next_line.as_slice().trim()).expect("d must be followed by int");
        let result = try!(stdin.read_exact(n));
        vte.feed(result.as_slice())
      }
      Some('p') => {
        let mut state = State { rendered: vec![] };
        let state_ptr: *mut c_void = &mut state as *mut _ as *mut c_void;
        screen.draw(draw_cb, state_ptr);
        let encoded = json::encode(&state);
        println!("{}", encoded)
      }
      Some('c') => {
        let (x,y) = screen.cursor_pos();
        let visible = screen.cursor_visible();
        let c_state = CursorState { x: x, y: y, visible: visible };
        let encoded = json::encode(&c_state);
        println!("{}", encoded)
      }
      None => { fail!("empty input!") }
      _ => { fail!("unknown command!") }
    }
  }
}

impl State {
  fn push_if_same(&mut self, ch: char, attr: Attribute) -> bool {
    let current_line  = self.rendered.mut_last().unwrap();
    let current_stream = current_line.mut_last().unwrap();
    if current_stream.ref1() == &attr {
      current_stream.mut0().push_char(ch);
      true
    } else {
      false
    }
  }

  fn push_new_cell(&mut self, ch: char, attr: Attribute) {
    let current_line  = self.rendered.mut_last().unwrap();
    let element = (ch.to_string(), attr);
    current_line.push(element);
  }
}

impl<S: Encoder<E>, E> Encodable<S, E> for State {
  fn encode(&self, s: &mut S) -> Result<(), E> {
    self.rendered.encode(s)
  }
}

impl Attribute {
  fn new(attr: tsm_screen_attr) -> Attribute {
    Attribute {
      fg: attr.get_fg(),
      bg: attr.get_bg(),
      bold: attr.get_flag(Bold),
      underline: attr.get_flag(Underline),
      inverse: attr.get_flag(Inverse),
      blink: attr.get_flag(Blink)
    }
  }
}

impl<S: Encoder<E>, E> Encodable<S, E> for Attribute {
  fn encode(&self, e: &mut S) -> Result<(), E> {
    let fields_to_emit = vec![self.fg.is_some(), self.bg.is_some(), self.bold, self.underline, self.inverse, self.blink];
    let no_of_fields_to_emit = fields_to_emit.iter().filter(|val| **val).count();

    e.emit_map(no_of_fields_to_emit, |e| {
      let mut key_pos = 0;
      if self.fg.is_some() {
        try!(e.emit_map_elt_key(key_pos, |e| "fg".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.fg.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.bg.is_some() {
        try!(e.emit_map_elt_key(key_pos, |e| "bg".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.bg.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.bold {
        try!(e.emit_map_elt_key(key_pos, |e| "bold".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.bold.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.underline {
        try!(e.emit_map_elt_key(key_pos, |e| "underline".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.underline.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.inverse {
        try!(e.emit_map_elt_key(key_pos, |e| "inverse".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.inverse.encode(e)));
        key_pos = key_pos + 1;
      }
      if self.blink {
        try!(e.emit_map_elt_key(key_pos, |e| "blink".encode(e)));
        try!(e.emit_map_elt_val(key_pos, |e| self.blink.encode(e)));
      }

      Ok(())
    })
  }
}

extern "C" fn draw_cb(
  _: *const tsm_screen,
  _: u32,
  ch: *const uint32_t,
  _: size_t,
  _: c_uint,
  _: c_uint,
  posy: c_uint,
  attr: *const tsm_screen_attr,
  _: tsm_age_t,
  data: *mut c_void
) {
  unsafe {
    let data: &mut State = &mut *(data as *mut State);
    let mut current_char;
    let attribute = Attribute::new(*attr);

    if *ch == 0 {
      current_char = ' ';
    } else {
      current_char = from_u32(*ch).unwrap();
    }

    if data.rendered.len() == (posy as uint) {
      data.rendered.push(vec![(from_char(current_char), attribute)]);
      return
    }

    let added = data.push_if_same(current_char, attribute);
    if !added {
      data.push_new_cell(current_char, attribute);
    }
  };
}
<|endoftext|>


<commit_before>use std::fs;
use std::path::{Path,PathBuf};
use std::fmt;

struct Entry {
    path: PathBuf,
    self_size: u64,
    children: Vec<Entry>,
}

impl Entry {
    fn from_metadata(path: PathBuf, metadata: &fs::Metadata) -> Option<Entry> {
        let children = if metadata.is_dir() {
            Entry::in_directory(&path)
        } else if metadata.is_file() {
            vec![]
        } else {
            return None;
        };

        Some(Entry {children: children, path: path, self_size: metadata.len()})
    }

    fn for_path(path: PathBuf) -> Option<Entry> {
        match fs::metadata(&path) {
            Ok(metadata) => Entry::from_metadata(path, &metadata),
            _ => None
        }
    }

    fn in_directory(dir: &Path) -> Vec<Entry> {
        match fs::read_dir(dir) {
            Ok(read_dir) => {
                read_dir.filter_map(|child| {
                    match child {
                        Ok(child) => Entry::for_path(child.path()),
                        Err(..) => None,
                    }
                }).collect()
            },
            Err(..) => Vec::new()
        }
    }

    fn size(&self) -> u64 {
        self.self_size + self.descendent_size()
    }

    fn descendent_size(&self) -> u64 {
        self.children.iter().map(|child| child.size()).fold(0, |a, n| a + n)
    }

    fn file_name(&self) -> &str {
        // Converting paths to strings might fail. We start by trying to convert the
        // filename. If the path is something like ".", it will fail as there is no "file
        // name" in that path. We fall back to the entire path in that case. If that also
        // fails, we fall back to hardcoded representation.
        let file_name = self.path.file_name().and_then(|s| s.to_str());
        file_name.or_else(|| self.path.to_str()).unwrap_or("(no name)")
    }
}

trait SizeDisplay {
    fn as_size_display(&self) -> String;
}

const KILO: f64 = 1e3 as f64;
const MEGA: f64 = 1e6 as f64;
const GIGA: f64 = 1e9 as f64;

const KILO_CUTOFF: u64 = (0.6 * KILO) as u64;
const MEGA_CUTOFF: u64 = (1.4 * MEGA) as u64;
const GIGA_CUTOFF: u64 = (1.4 * GIGA) as u64;

impl SizeDisplay for u64 {
    fn as_size_display(&self) -> String {
        if *self < KILO_CUTOFF {
            return format!("{} B", *self)
        }

        let (scaled, unit) = match *self {
            KILO_CUTOFF...MEGA_CUTOFF => (*self as f64 / KILO, "kB"),
            MEGA_CUTOFF...GIGA_CUTOFF => (*self as f64 / MEGA, "MB"),
            _ => (*self as f64 / GIGA, "GB"),
        };
        format!("{:.2} {}", scaled, unit)
    }
}

impl fmt::Display for Entry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", self.file_name(), self.size().as_size_display())
    }
}

fn print_tree(dir_entry: Entry) {
    print_indented_tree(dir_entry, 0);
}

fn print_indented_tree(entry: Entry, indent: usize) {
    println!("{0:1$}{2}", "", indent * 2, entry);
    for child in entry.children {
        print_indented_tree(child, indent + 1);
    }
}

fn main() {
    let root = Entry::for_path(PathBuf::from(".")).unwrap();
    print_tree(root);
}
<commit_msg>Extract entry display into a trait<commit_after>use std::fs;
use std::path::{Path,PathBuf};
use std::fmt;
use std::slice::Iter;

struct Entry {
    path: PathBuf,
    self_size: u64,
    children: Vec<Entry>,
}

impl Entry {
    fn from_metadata(path: PathBuf, metadata: &fs::Metadata) -> Option<Entry> {
        let children = if metadata.is_dir() {
            Entry::in_directory(&path)
        } else if metadata.is_file() {
            vec![]
        } else {
            return None;
        };

        Some(Entry {children: children, path: path, self_size: metadata.len()})
    }

    fn for_path(path: PathBuf) -> Option<Entry> {
        match fs::metadata(&path) {
            Ok(metadata) => Entry::from_metadata(path, &metadata),
            _ => None
        }
    }

    fn in_directory(dir: &Path) -> Vec<Entry> {
        match fs::read_dir(dir) {
            Ok(read_dir) => {
                read_dir.filter_map(|child| {
                    match child {
                        Ok(child) => Entry::for_path(child.path()),
                        Err(..) => None,
                    }
                }).collect()
            },
            Err(..) => Vec::new()
        }
    }

    fn descendent_size(&self) -> u64 {
        self.children.iter().map(|child| child.size()).fold(0, |a, n| a + n)
    }
}

trait SizeDisplay {
    fn as_size_display(&self) -> String;
}

const KILO: f64 = 1e3 as f64;
const MEGA: f64 = 1e6 as f64;
const GIGA: f64 = 1e9 as f64;

const KILO_CUTOFF: u64 = (0.6 * KILO) as u64;
const MEGA_CUTOFF: u64 = (1.4 * MEGA) as u64;
const GIGA_CUTOFF: u64 = (1.4 * GIGA) as u64;

impl SizeDisplay for u64 {
    fn as_size_display(&self) -> String {
        if *self < KILO_CUTOFF {
            return format!("{} B", *self)
        }

        let (scaled, unit) = match *self {
            KILO_CUTOFF...MEGA_CUTOFF => (*self as f64 / KILO, "kB"),
            MEGA_CUTOFF...GIGA_CUTOFF => (*self as f64 / MEGA, "MB"),
            _ => (*self as f64 / GIGA, "GB"),
        };
        format!("{:.2} {}", scaled, unit)
    }
}

trait DisplayableEntry : fmt::Display + Sized {
    fn size(&self) -> u64;
    fn name(&self) -> &str;
    fn children_iter(&self) -> Iter<Self>;
}

impl DisplayableEntry for Entry {
    fn size(&self) -> u64 {
        self.self_size + self.descendent_size()
    }

    fn name(&self) -> &str {
        // Converting paths to strings might fail. We start by trying to convert the
        // filename. If the path is something like ".", it will fail as there is no "file
        // name" in that path. We fall back to the entire path in that case. If that also
        // fails, we fall back to hardcoded representation.
        let file_name = self.path.file_name().and_then(|s| s.to_str());
        file_name.or_else(|| self.path.to_str()).unwrap_or("(no name)")
    }

    fn children_iter(&self) -> Iter<Entry> {
        self.children.iter()
    }
}

impl fmt::Display for Entry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", self.name(), self.size().as_size_display())
    }
}

fn print_tree<T: DisplayableEntry>(entry: &T) {
    print_indented_tree(entry, 0);
}

fn print_indented_tree<T: DisplayableEntry>(entry: &T, indent: usize) {
    println!("{0:1$}{2}", "", indent * 2, entry);
    for child in entry.children_iter() {
        print_indented_tree(child, indent + 1);
    }
}

fn main() {
    let root = Entry::for_path(PathBuf::from(".")).unwrap();
    print_tree(&root);
}
<|endoftext|>


<commit_before>pub mod api;
pub mod db;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use crate::{
    api::{
        Error, GameRow, GameRowWithUci, PersonalMoveRow, PersonalQuery, PersonalQueryFilter,
        PersonalResponse,
    },
    db::Database,
    indexer::{IndexerOpt, IndexerStub},
    model::{AnnoLichess, PersonalKeyBuilder, PersonalKeyPrefix},
    opening::{Opening, Openings},
    util::NdJson,
};
use axum::{
    extract::{Extension, Path, Query},
    handler::get,
    http::StatusCode,
    AddExtensionLayer, Router,
};
use clap::Clap;
use futures_util::stream::Stream;
use shakmaty::{fen::Fen, variant::VariantPosition, zobrist::Zobrist, CastlingMode};
use std::{mem, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
use tokio::sync::oneshot;

#[derive(Clap)]
struct Opt {
    #[clap(long = "bind", default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long = "db", default_value = "_db")]
    db: PathBuf,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::init_from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    );

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handle) = IndexerStub::spawn(db.clone(), opt.indexer);

    let app = Router::new()
        .route("/admin/:prop", get(db_property))
        .route("/admin/game/:prop", get(game_property))
        .route("/admin/personal/:prop", get(personal_property))
        .route("/personal", get(personal))
        .route("/test", get(test))
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(indexer));

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .with_graceful_shutdown(async {
            tokio::signal::ctrl_c().await.expect("wait for ctrl-c");
        })
        .await
        .expect("bind");

    join_handle.await.expect("indexer");
}

async fn test() -> NdJson<impl futures_util::stream::Stream<Item = u32>> {
    NdJson::new(futures_util::stream::unfold(0, |state| async move {
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        Some((state, state + 1))
    }))
}

async fn db_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .db_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn game_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .game_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .personal_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

struct PersonalStreamState {
    indexing: Option<oneshot::Receiver<()>>,
    key: PersonalKeyPrefix,
    db: Arc<Database>,
    filter: PersonalQueryFilter,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<NdJson<impl Stream<Item = PersonalResponse>>, Error> {
    let indexing = if query.update {
        Some(indexer.index_player(query.player.clone()).await)
    } else {
        None
    };

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = PersonalKeyBuilder::with_user_pov(&query.player.into(), query.color)
        .with_zobrist(variant, pos.zobrist_hash());

    let state = PersonalStreamState {
        filter: query.filter,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson::new(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing => true,
                        _ = tokio::time::sleep(if first { Duration::from_millis(200) } else { Duration::from_secs(5) } ) => false,
                    }
                }
                None => true,
            };
            let queryable = state.db.queryable();
            let filtered = queryable
                .get_personal(&state.key, AnnoLichess::from_year(state.filter.since))
                .expect("get personal")
                .prepare(&state.pos, &state.filter);

            Some((
                PersonalResponse {
                    total: filtered.total,
                    moves: filtered
                        .moves
                        .into_iter()
                        .map(|row| PersonalMoveRow {
                            uci: row.uci,
                            san: row.san,
                            stats: row.stats,
                            game: row.game.and_then(|id| {
                                queryable
                                    .get_game_info(id)
                                    .expect("get game")
                                    .map(|info| GameRow { id, info })
                            }),
                        })
                        .collect(),
                    recent_games: filtered
                        .recent_games
                        .into_iter()
                        .flat_map(|(uci, id)| {
                            queryable.get_game_info(id).expect("get game").map(|info| {
                                GameRowWithUci {
                                    uci,
                                    row: GameRow { id, info },
                                }
                            })
                        })
                        .collect(),
                    opening: state.opening,
                },
                state,
            ))
        },
    )))
}
<commit_msg>shorten line<commit_after>pub mod api;
pub mod db;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use crate::{
    api::{
        Error, GameRow, GameRowWithUci, PersonalMoveRow, PersonalQuery, PersonalQueryFilter,
        PersonalResponse,
    },
    db::Database,
    indexer::{IndexerOpt, IndexerStub},
    model::{AnnoLichess, PersonalKeyBuilder, PersonalKeyPrefix},
    opening::{Opening, Openings},
    util::NdJson,
};
use axum::{
    extract::{Extension, Path, Query},
    handler::get,
    http::StatusCode,
    AddExtensionLayer, Router,
};
use clap::Clap;
use futures_util::stream::Stream;
use shakmaty::{fen::Fen, variant::VariantPosition, zobrist::Zobrist, CastlingMode};
use std::{mem, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
use tokio::sync::oneshot;

#[derive(Clap)]
struct Opt {
    #[clap(long = "bind", default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long = "db", default_value = "_db")]
    db: PathBuf,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::init_from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    );

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handle) = IndexerStub::spawn(db.clone(), opt.indexer);

    let app = Router::new()
        .route("/admin/:prop", get(db_property))
        .route("/admin/game/:prop", get(game_property))
        .route("/admin/personal/:prop", get(personal_property))
        .route("/personal", get(personal))
        .route("/test", get(test))
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(indexer));

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .with_graceful_shutdown(async {
            tokio::signal::ctrl_c().await.expect("wait for ctrl-c");
        })
        .await
        .expect("bind");

    join_handle.await.expect("indexer");
}

async fn test() -> NdJson<impl futures_util::stream::Stream<Item = u32>> {
    NdJson::new(futures_util::stream::unfold(0, |state| async move {
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        Some((state, state + 1))
    }))
}

async fn db_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .db_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn game_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .game_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn personal_property(
    Extension(db): Extension<Arc<Database>>,
    Path(prop): Path<String>,
) -> Result<String, StatusCode> {
    db.queryable()
        .personal_property(&prop)
        .expect("get property")
        .ok_or(StatusCode::NOT_FOUND)
}

struct PersonalStreamState {
    indexing: Option<oneshot::Receiver<()>>,
    key: PersonalKeyPrefix,
    db: Arc<Database>,
    filter: PersonalQueryFilter,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<NdJson<impl Stream<Item = PersonalResponse>>, Error> {
    let indexing = if query.update {
        Some(indexer.index_player(query.player.clone()).await)
    } else {
        None
    };

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = PersonalKeyBuilder::with_user_pov(&query.player.into(), query.color)
        .with_zobrist(variant, pos.zobrist_hash());

    let state = PersonalStreamState {
        filter: query.filter,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson::new(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing => true,
                        _ = tokio::time::sleep(Duration::from_millis(if first { 200 } else { 5000 })) => false,
                    }
                }
                None => true,
            };
            let queryable = state.db.queryable();
            let filtered = queryable
                .get_personal(&state.key, AnnoLichess::from_year(state.filter.since))
                .expect("get personal")
                .prepare(&state.pos, &state.filter);

            Some((
                PersonalResponse {
                    total: filtered.total,
                    moves: filtered
                        .moves
                        .into_iter()
                        .map(|row| PersonalMoveRow {
                            uci: row.uci,
                            san: row.san,
                            stats: row.stats,
                            game: row.game.and_then(|id| {
                                queryable
                                    .get_game_info(id)
                                    .expect("get game")
                                    .map(|info| GameRow { id, info })
                            }),
                        })
                        .collect(),
                    recent_games: filtered
                        .recent_games
                        .into_iter()
                        .flat_map(|(uci, id)| {
                            queryable.get_game_info(id).expect("get game").map(|info| {
                                GameRowWithUci {
                                    uci,
                                    row: GameRow { id, info },
                                }
                            })
                        })
                        .collect(),
                    opening: state.opening,
                },
                state,
            ))
        },
    )))
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate regex;

use std::fs;
use std::io;
use std::io::Write;
use std::path::Path;
use docopt::Docopt;
use regex::Regex;

const USAGE: &'static str = "
Usage: r3name --pattern <pattern> --replacement <replacement> <path>...
       r3name -h | --help
       r3name --version

Options:
    -h --help                    Show this screen.
    --version                    Show version.
    --pattern <pattern>          Pattern to match in the provided paths
    --replacement <replacement>  String used to replace pattern matches
";

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");

fn rename_path(path: &str, pattern: &Regex, replacement: &str) {
    if !pattern.is_match(path) {
        println!("Skipping `{}`", path);
        return;
    }

    if !Path::new(path).exists() {
        writeln!(io::stderr(), "Source path `{}` doesn't exist", path).unwrap();
        return;
    }

    let new_path = pattern.replace(path, replacement);

    if Path::new(&new_path).exists() {
        writeln!(io::stderr(), "Failed to rename `{}`: destination path `{}` already exists", path, new_path).unwrap();
        return;
    }

    match fs::rename(path, &new_path) {
        Ok(_) => println!("Renamed `{}` -> `{}`", path, new_path),
        Err(error) => {
            writeln!(io::stderr(), "Failed to rename `{}` -> `{}`: {}", path, new_path, error).unwrap();
            return;
        }
    };
}

fn main() {
    let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());

    if args.get_bool("--version") {
        println!("r3name {}", VERSION.unwrap_or("unknown"));
        return;
    }

    let pattern = args.get_str("--pattern");
    let pattern = match Regex::new(pattern) {
        Ok(regex) => regex,
        Err(error) => {
            writeln!(io::stderr(), "Invalid pattern: {}", error).unwrap();
            return;
        }
    };

    let replacement = args.get_str("--replacement");

    for path in args.get_vec("<path>") {
        rename_path(path, &pattern, replacement);
    }
}
<commit_msg>Add a --dry-run switch to the program<commit_after>extern crate docopt;
extern crate regex;

use std::fs;
use std::io;
use std::io::Write;
use std::path::Path;
use docopt::Docopt;
use regex::Regex;

const USAGE: &'static str = "
Usage: r3name --pattern <pattern> --replacement <replacement> [--dry-run] <path>...
       r3name -h | --help
       r3name --version

Options:
    -h --help                    Show this screen.
    --version                    Show version.
    --pattern <pattern>          Pattern to match in the provided paths
    --replacement <replacement>  String used to replace pattern matches
    --dry-run                    Show what would be renamed, but don't rename anything.
";

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");

fn rename_path(path: &str, pattern: &Regex, replacement: &str, dry_run: bool) {
    if !pattern.is_match(path) {
        println!("Skipping `{}`", path);
        return;
    }

    if !Path::new(path).exists() {
        writeln!(io::stderr(), "Source path `{}` doesn't exist", path).unwrap();
        return;
    }

    let new_path = pattern.replace(path, replacement);

    if Path::new(&new_path).exists() {
        writeln!(io::stderr(), "Failed to rename `{}`: destination path `{}` already exists", path, new_path).unwrap();
        return;
    }

    if dry_run {
        println!("Would rename `{}` -> `{}`", path, new_path);
        return;
    }

    match fs::rename(path, &new_path) {
        Ok(_) => println!("Renamed `{}` -> `{}`", path, new_path),
        Err(error) => {
            writeln!(io::stderr(), "Failed to rename `{}` -> `{}`: {}", path, new_path, error).unwrap();
            return;
        }
    };
}

fn main() {
    let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());

    if args.get_bool("--version") {
        println!("r3name {}", VERSION.unwrap_or("unknown"));
        return;
    }

    let pattern = args.get_str("--pattern");
    let pattern = match Regex::new(pattern) {
        Ok(regex) => regex,
        Err(error) => {
            writeln!(io::stderr(), "Invalid pattern: {}", error).unwrap();
            return;
        }
    };

    let replacement = args.get_str("--replacement");
    let dry_run = args.get_bool("--dry-run");

    for path in args.get_vec("<path>") {
        rename_path(path, &pattern, replacement, dry_run);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate rocket;

#[macro_use]
extern crate rocket_contrib;

mod airport_data;
mod path;
mod server_route;

use anyhow::Result;
use anyhow::{anyhow, Context};
use rocket::config::{Config, Environment};
use rocket_contrib::serve::StaticFiles;

#[rocket::main]
async fn main() -> Result<()> {
    if let Err(err) = airport_data::ensure_updated() {
        return Err(anyhow!("airport data update failed: {}", err));
    }

    println!("loading airport data..");

    let airports = match airport_data::Airport::load_all() {
        Ok(airports) => airports,
        Err(err) => return Err(anyhow!("error loading airport data: {}", err)),
    };

    println!("finished loading airport data..");

    let config = {
        let env = Environment::active().context("failed to get Rocket config")?;

        Config::build(env)
            .workers(1)
            .finalize()
            .context("failed to build Rocket config")?
    };

    rocket::custom(config)
        .manage(airports)
        .mount("/", StaticFiles::from("frontend/public/"))
        .mount("/api", routes![server_route::search_routes::search_routes])
        .launch()
        .await
        .with_context(|| anyhow!("failed to initialize Rocket"))
}
<commit_msg>backend: Simplify error handling in main module<commit_after>#[macro_use]
extern crate rocket;

#[macro_use]
extern crate rocket_contrib;

mod airport_data;
mod path;
mod server_route;

use anyhow::{Context, Result};
use rocket::config::{Config, Environment};
use rocket_contrib::serve::StaticFiles;

#[rocket::main]
async fn main() -> Result<()> {
    airport_data::ensure_updated().context("failed to update airport data")?;

    println!("loading airport data..");
    let airports = airport_data::Airport::load_all().context("failed to load airport data")?;
    println!("finished loading airport data");

    let config = {
        let env = Environment::active().context("failed to get Rocket config")?;

        Config::build(env)
            .workers(1)
            .finalize()
            .context("failed to build Rocket config")?
    };

    rocket::custom(config)
        .manage(airports)
        .mount("/", StaticFiles::from("frontend/public/"))
        .mount("/api", routes![server_route::search_routes::search_routes])
        .launch()
        .await
        .context("failed to initialize Rocket")
}
<|endoftext|>