<commit_before>/*
Copyright 2017 Aron Heinecke

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

extern crate reqwest;
extern crate json;
extern crate flate2;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate quick_error;
extern crate mysql;
extern crate regex;
extern crate chrono;
extern crate toml;
extern crate timer;
#[macro_use]
extern crate serde_derive;
extern crate serde;

mod http;
mod error;
mod parser;
mod db;
mod config;

use http::HeaderType;

use std::fs::{File,metadata};
use std::env::current_exe;
use std::fs::DirBuilder;
use std::io::Write;

use std::sync::Arc;

use chrono::naive::NaiveTime;
use chrono::offset::Local;
use chrono::Timelike;

use mysql::Pool;

use error::Error;

use config::Config;

const USER_AGENT: &'static str = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0";
const REFERER: &'static str = "http://crossfire.z8games.com/";
const CONFIG_PATH: &'static str = "config/config.toml";
const LOG_PATH: &'static str = "config/log.yml";
const INTERVALL_H: i64 = 24; // execute intervall

fn main() {
    match init_log() {
        Err(e) => println!("Error on config initialization: {}",e),
        Ok(_) => println!("Initialized log")
    }
    info!("Starting clan tools crawler v0.0.1");
    let config = Arc::new(config::init_config());
    let pool = match db::new(config.db.ip.clone(),
        config.db.port,
        config.db.user.clone(),
        config.db.password.clone(),
        config.db.db.clone()) {
        Err(e) => {error!("Can't connect to DB! {}",e); panic!();},
        Ok(v) => Arc::new(v)
    };
    let timer = timer::Timer::new();
    run_timer(pool.clone(), config.clone(),&timer);
    //let local_pool = &*pool;
    //let local_config = &*config;
    //debug!("Result: {:?}",run_update(local_pool,local_config));

    loop {
        std::thread::sleep(std::time::Duration::from_millis(1000));
    }
}

/// Initialize timed task
fn run_timer<'a>(pool: Arc<Pool>, config: Arc<Config>, timer: &'a timer::Timer) {
    let date_time = Local::now(); // get current datetime
    let today = Local::today();
    let target_naive_time = match NaiveTime::parse_from_str(&config.main.time, "%H:%M") {
        Ok(v) => v,
        Err(e) => {error!("Unable to parse config time!: {}",e); panic!();}
    };

	// get retry time
	let retry_time: NaiveTime = match NaiveTime::parse_from_str(&config.main.retry_interval, "%H:%M") {
		Ok(v) => v,
		Err(e) => {error!("Unable to parse config retry time!: {}",e); panic!();}
	};

    let schedule_time;
    trace!("Parsed time: {}",target_naive_time);
    if target_naive_time < date_time.time() {
        debug!("Target time is tinier then current time");
        // create from chrono::Duration, convert to std::time::Duration to add
        let c_duration = chrono::Duration::hours(INTERVALL_H);
        let tomorrow = today.checked_add_signed(c_duration).unwrap();
        schedule_time = tomorrow.and_time(target_naive_time).unwrap();
    } else {
        schedule_time = today.and_time(target_naive_time).unwrap();
    }
    info!("First execution will be on {}",schedule_time);

	let a = timer.schedule(schedule_time,Some(chrono::Duration::hours(INTERVALL_H)), move || {
        trace!("performing crawler");
		let local_pool = &*pool;
		let local_config = &*config;
		for x in 1..local_config.main.retries+1 {
			match run_update(local_pool,local_config) {
				Ok(_) => { debug!("Crawling successfull."); break;
					},
				Err(e) => { error!("Error at update: {}: {}",x,e);
					if x == local_config.main.retries {
						warn!("No dataset for this schedule, all retries failed!");
					}else{
                    std::thread::sleep(std::time::Duration::from_secs(retry_time.num_seconds_from_midnight().into()));
                    }
                }
            }
        }
    });
    a.ignore();
}

/// get member url for ajax request
fn get_member_url(site: &u8, config: &Config) -> String {
    let _site = format!("{}",site);
    let end_row = site*config.main.clan_ajax_exptected_per_site;
    let start_row = end_row - (config.main.clan_ajax_exptected_per_site -1);
    let _start_row = format!("{}", start_row);
    let _end_row = format!("{}", end_row);
    let mut output = config.main.clan_ajax_url.replace(&config.main.clan_ajax_site_key, &_site);
    output = output.replace(&config.main.clan_ajax_start_row_key, &_start_row);
    output = output.replace(&config.main.clan_ajax_end_row_key, &_end_row);
    debug!("Start: {} end: {} site: {}",start_row,end_row,site);
    output
}

/// Run crawl + db update
fn run_update(pool: &Pool, config: &Config) -> Result<(),Error> {
    let time = Local::now().naive_local();
    let mut conn = try!(pool.get_conn());
    let raw_http_clan = try!(http::get(&config.main.clan_url,HeaderType::Html));
    let clan = try!(parser::parse_clan(&raw_http_clan));

    let mut site = 0;
    let mut members = Vec::new();
    let mut to_receive = 100;
    while members.len() < to_receive {
        site += 1;
        if site > config.main.clan_ajax_max_sites {
            error!("Reaching site {}, aborting.",site);
            return Err(Error::Other("Site over limit."));
        }
        let raw_members_json = try!(http::get(&get_member_url(&site,config),HeaderType::Ajax));
        let (mut members_temp,t_total) = try!(parser::parse_all_member(&raw_members_json));
        to_receive = t_total as usize;
        members.append(&mut members_temp);
        trace!("fetched site {}",site);
    }
    debug!("Fetched {} member entries",members.len());
    try!(db::insert_members(&mut conn,&members,&time));
    try!(db::insert_clan_update(&mut conn,&clan,&time));
    Ok(())
}

/// Init log system
/// Creating a default log file if not existing
fn init_log() -> Result<(),Error> {
    let mut log_path = try!(get_executable_folder());
    log_path.push(LOG_PATH);
    let mut log_dir = log_path.clone();
    println!("LogPath: {:?}",&log_path);
    log_dir.pop();
    try!(DirBuilder::new()
    .recursive(true)
    .create(log_dir));

    if !metadata(&log_path).is_ok() {
        let config = include_str!("../log_config.yml");
        let mut file = try!(File::create(&log_path));
        try!(file.write_all(config.as_bytes()));
    }
    try!(log4rs::init_file(log_path, Default::default()));
    Ok(())
}

/// Returns the current executable folder
pub fn get_executable_folder() -> Result<std::path::PathBuf, Error> {
    let mut folder = try!(current_exe());
    folder.pop();
    Ok(folder)
}

/// Clan data structure
#[derive(Debug,PartialEq, PartialOrd)]
pub struct Clan {
    members: u8,
    wins: u16,
    losses: u16,
    draws: u16
}

/// Member data structure
#[derive(Debug,PartialEq, PartialOrd,Clone)]
pub struct Member {
    name: String,
    id: u32,
    exp: u32,
    contribution: u32
}

#[cfg(test)]
mod tests {

    use chrono::naive::NaiveTime;
    use chrono::Local;

    #[test]
    fn test_chrono() {
        let date = Local::today();
        let parsed_time = NaiveTime::parse_from_str("12:00","%H:%M").unwrap();
        let parsed_time_2 = NaiveTime::parse_from_str("12:01","%H:%M").unwrap();
        let datetime_1 = date.and_time(parsed_time).unwrap();
        let datetime_2 = date.and_time(parsed_time_2).unwrap();
        //assert_eq!(datetime_1.cmp(datetime_2),Ordering::Less);
        assert_eq!(parsed_time_2 > parsed_time,true);
        assert_eq!(datetime_2 > datetime_1,true);
    }
}
<commit_msg>tabs->spaces<commit_after>/*
Copyright 2017 Aron Heinecke

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

extern crate reqwest;
extern crate json;
extern crate flate2;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate quick_error;
extern crate mysql;
extern crate regex;
extern crate chrono;
extern crate toml;
extern crate timer;
#[macro_use]
extern crate serde_derive;
extern crate serde;

mod http;
mod error;
mod parser;
mod db;
mod config;

use http::HeaderType;

use std::fs::{File,metadata};
use std::env::current_exe;
use std::fs::DirBuilder;
use std::io::Write;

use std::sync::Arc;

use chrono::naive::NaiveTime;
use chrono::offset::Local;
use chrono::Timelike;

use mysql::Pool;

use error::Error;

use config::Config;

const USER_AGENT: &'static str = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0";
const REFERER: &'static str = "http://crossfire.z8games.com/";
const CONFIG_PATH: &'static str = "config/config.toml";
const LOG_PATH: &'static str = "config/log.yml";
const INTERVALL_H: i64 = 24; // execute intervall

fn main() {
    match init_log() {
        Err(e) => println!("Error on config initialization: {}",e),
        Ok(_) => println!("Initialized log")
    }
    info!("Starting clan tools crawler v0.0.1");
    let config = Arc::new(config::init_config());
    let pool = match db::new(config.db.ip.clone(),
        config.db.port,
        config.db.user.clone(),
        config.db.password.clone(),
        config.db.db.clone()) {
        Err(e) => {error!("Can't connect to DB! {}",e); panic!();},
        Ok(v) => Arc::new(v)
    };
    let timer = timer::Timer::new();
    run_timer(pool.clone(), config.clone(),&timer);
    //let local_pool = &*pool;
    //let local_config = &*config;
    //debug!("Result: {:?}",run_update(local_pool,local_config));

    loop {
        std::thread::sleep(std::time::Duration::from_millis(1000));
    }
}

/// Initialize timed task
fn run_timer<'a>(pool: Arc<Pool>, config: Arc<Config>, timer: &'a timer::Timer) {
    let date_time = Local::now(); // get current datetime
    let today = Local::today();
    let target_naive_time = match NaiveTime::parse_from_str(&config.main.time, "%H:%M") {
        Ok(v) => v,
        Err(e) => {error!("Unable to parse config time!: {}",e); panic!();}
    };

    // get retry time
    let retry_time: NaiveTime = match NaiveTime::parse_from_str(&config.main.retry_interval, "%H:%M") {
        Ok(v) => v,
        Err(e) => {error!("Unable to parse config retry time!: {}",e); panic!();}
    };

    let schedule_time;
    trace!("Parsed time: {}",target_naive_time);
    if target_naive_time < date_time.time() {
        debug!("Target time is tinier then current time");
        // create from chrono::Duration, convert to std::time::Duration to add
        let c_duration = chrono::Duration::hours(INTERVALL_H);
        let tomorrow = today.checked_add_signed(c_duration).unwrap();
        schedule_time = tomorrow.and_time(target_naive_time).unwrap();
    } else {
        schedule_time = today.and_time(target_naive_time).unwrap();
    }
    info!("First execution will be on {}",schedule_time);

    let a = timer.schedule(schedule_time,Some(chrono::Duration::hours(INTERVALL_H)), move || {
        trace!("performing crawler");
        let local_pool = &*pool;
        let local_config = &*config;
        for x in 1..local_config.main.retries+1 {
            match run_update(local_pool,local_config) {
                Ok(_) => { debug!("Crawling successfull."); break;
                    },
                Err(e) => { error!("Error at update: {}: {}",x,e);
                    if x == local_config.main.retries {
                        warn!("No dataset for this schedule, all retries failed!");
                    }else{
                    std::thread::sleep(std::time::Duration::from_secs(retry_time.num_seconds_from_midnight().into()));
                    }
                }
            }
        }
    });
    a.ignore();
}

/// get member url for ajax request
fn get_member_url(site: &u8, config: &Config) -> String {
    let _site = format!("{}",site);
    let end_row = site*config.main.clan_ajax_exptected_per_site;
    let start_row = end_row - (config.main.clan_ajax_exptected_per_site -1);
    let _start_row = format!("{}", start_row);
    let _end_row = format!("{}", end_row);
    let mut output = config.main.clan_ajax_url.replace(&config.main.clan_ajax_site_key, &_site);
    output = output.replace(&config.main.clan_ajax_start_row_key, &_start_row);
    output = output.replace(&config.main.clan_ajax_end_row_key, &_end_row);
    debug!("Start: {} end: {} site: {}",start_row,end_row,site);
    output
}

/// Run crawl + db update
fn run_update(pool: &Pool, config: &Config) -> Result<(),Error> {
    let time = Local::now().naive_local();
    let mut conn = try!(pool.get_conn());
    let raw_http_clan = try!(http::get(&config.main.clan_url,HeaderType::Html));
    let clan = try!(parser::parse_clan(&raw_http_clan));

    let mut site = 0;
    let mut members = Vec::new();
    let mut to_receive = 100;
    while members.len() < to_receive {
        site += 1;
        if site > config.main.clan_ajax_max_sites {
            error!("Reaching site {}, aborting.",site);
            return Err(Error::Other("Site over limit."));
        }
        let raw_members_json = try!(http::get(&get_member_url(&site,config),HeaderType::Ajax));
        let (mut members_temp,t_total) = try!(parser::parse_all_member(&raw_members_json));
        to_receive = t_total as usize;
        members.append(&mut members_temp);
        trace!("fetched site {}",site);
    }
    debug!("Fetched {} member entries",members.len());
    try!(db::insert_members(&mut conn,&members,&time));
    try!(db::insert_clan_update(&mut conn,&clan,&time));
    Ok(())
}

/// Init log system
/// Creating a default log file if not existing
fn init_log() -> Result<(),Error> {
    let mut log_path = try!(get_executable_folder());
    log_path.push(LOG_PATH);
    let mut log_dir = log_path.clone();
    println!("LogPath: {:?}",&log_path);
    log_dir.pop();
    try!(DirBuilder::new()
    .recursive(true)
    .create(log_dir));

    if !metadata(&log_path).is_ok() {
        let config = include_str!("../log_config.yml");
        let mut file = try!(File::create(&log_path));
        try!(file.write_all(config.as_bytes()));
    }
    try!(log4rs::init_file(log_path, Default::default()));
    Ok(())
}

/// Returns the current executable folder
pub fn get_executable_folder() -> Result<std::path::PathBuf, Error> {
    let mut folder = try!(current_exe());
    folder.pop();
    Ok(folder)
}

/// Clan data structure
#[derive(Debug,PartialEq, PartialOrd)]
pub struct Clan {
    members: u8,
    wins: u16,
    losses: u16,
    draws: u16
}

/// Member data structure
#[derive(Debug,PartialEq, PartialOrd,Clone)]
pub struct Member {
    name: String,
    id: u32,
    exp: u32,
    contribution: u32
}

#[cfg(test)]
mod tests {

    use chrono::naive::NaiveTime;
    use chrono::Local;

    #[test]
    fn test_chrono() {
        let date = Local::today();
        let parsed_time = NaiveTime::parse_from_str("12:00","%H:%M").unwrap();
        let parsed_time_2 = NaiveTime::parse_from_str("12:01","%H:%M").unwrap();
        let datetime_1 = date.and_time(parsed_time).unwrap();
        let datetime_2 = date.and_time(parsed_time_2).unwrap();
        //assert_eq!(datetime_1.cmp(datetime_2),Ordering::Less);
        assert_eq!(parsed_time_2 > parsed_time,true);
        assert_eq!(datetime_2 > datetime_1,true);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
extern crate ncurses;
extern crate rand;
use ncurses::*;
use std::io::Write;

mod addr;
mod byte;
mod dungeon;
mod item;
mod monster;
mod player;
mod point;
mod spell;
mod sprite;
mod tile;
mod timer;
mod util;

fn main() {
    initscr();
    raw();
    noecho();
    curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
    let result = std::panic::catch_unwind(|| {
        printw("Hello, world!");
        refresh();
        getch();
    });
    endwin();

    std::process::exit(match result {
        Ok(_) => 0,
        Err(e) => {
            if let Some(s) = e.downcast_ref::<&'static str>() {
                writeln!(std::io::stderr(), "{}", s).unwrap();
            }
            1
        }
    });
}
<commit_msg>Print io::Error panics, too<commit_after>#![allow(dead_code)]
extern crate ncurses;
extern crate rand;
use ncurses::*;
use std::io::Write;

mod addr;
mod byte;
mod dungeon;
mod item;
mod monster;
mod player;
mod point;
mod spell;
mod sprite;
mod tile;
mod timer;
mod util;

fn main() {
    initscr();
    raw();
    noecho();
    curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
    let result = std::panic::catch_unwind(|| {
        printw("Hello, world!");
        refresh();
        getch();
    });
    endwin();

    std::process::exit(match result {
        Ok(_) => 0,
        Err(e) => {
            if let Some(s) = e.downcast_ref::<&'static str>() {
                writeln!(std::io::stderr(), "{}", s).unwrap();
            }
            if let Some(s) = e.downcast_ref::<std::io::Error>() {
                writeln!(std::io::stderr(), "{}", s).unwrap();
            }
            1
        }
    });
}
<|endoftext|>


<commit_before>extern crate osmio;
extern crate image;
extern crate gif;
extern crate clap;

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

use std::fs;
use osmio::OSMReader;
use osmio::pbf::PBFReader;
use std::io::{Read, Write, BufReader, BufRead, BufWriter};
use std::collections::HashMap;

use gif::SetParameter;

// (frame_no, (pixel_index, num_changes))
type Frames = Vec<(u32, Vec<(u32, u16)>)>;

struct ColourRamp {
    empty_colour: (u8, u8, u8),
    steps: Vec<(u32, (u8, u8, u8))>,
}

impl ColourRamp {
    fn new_from_filename(filename: &str) -> Self {
        let mut contents = String::new();
        let mut file = fs::File::open(filename).unwrap();
        file.read_to_string(&mut contents).expect("Couldn't read colour ramp file");
        Self::new_from_text(&contents)

    }
    fn new_from_text(source: &str) -> Self {
        let lines: Vec<_> = source.lines().collect();
        let empty_vec = lines[0].split(",").filter_map(|x| x.parse::<u8>().ok()).take(3).collect::<Vec<_>>();
        let empty = (empty_vec[0], empty_vec[1], empty_vec[2]);

        let mut steps = Vec::new();
        for line in lines.iter().skip(1) {
            let line = line.split(",").filter_map(|x| x.parse::<u32>().ok()).take(4).collect::<Vec<_>>();
            let age = line[0];
            let colour = (line[1] as u8, line[2] as u8, line[3] as u8);
            steps.push((age, colour));
        }

        if steps.len() > 254 {
            panic!("Too many steps");
        }

        ColourRamp{ empty_colour: empty, steps: steps }
    }

    fn palette(&self) -> Vec<u8> {
        let mut results = Vec::with_capacity((self.steps.len()+1)*3);
        results.push(self.empty_colour.0);
        results.push(self.empty_colour.1);
        results.push(self.empty_colour.2);

        for &(_, (r, g, b)) in self.steps.iter() {
            results.push(r);
            results.push(g);
            results.push(b);
        }

        results
    }

    fn index_for_magnitude(&self, magnitude: Option<u32>) -> u8 {
        match magnitude {
            None => 0,
            Some(magnitude) => {
                if magnitude > 255 {
                    1
                } else {
                    (255 - magnitude) as u8
                }
            },
            //Some(age) => match self.steps.iter().position(|&(x, _)| x == age) {
            //    None => 0,
            //    Some(i) => (i+1) as u8,
            //}
        }
    }
}

fn read_pbf(filename: &str, height: u32, sec_per_frame: u32, bbox: &[f32; 4]) -> Frames {
    let file = BufReader::new(fs::File::open(&filename).unwrap());
    let mut node_reader = PBFReader::new(file);
    let node_reader = node_reader.nodes();

    let mut results: HashMap<u32, HashMap<u32, u16>> = HashMap::new();

    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;
    let width = ((bbox_width / bbox_height) * (height as f32)) as u32;

    // 1st April 2005, midnight GMT. We presume no OSM editing before then.
    let osm_epoch = 1109635200;

    // FIXME use a btreemap?

    let mut first_frame_no = std::u32::MAX;
    let mut last_frame_no = 0;

    let mut num_nodes: u64 = 0;
    for node in node_reader {
        if let (Some(lat), Some(lon)) = (node.lat, node.lon) {
            // FIXME should be able to do non-equals but it fails for point at south pole
            if lat >= top || lat <= bottom || lon >= right || lon <= left {
                continue;
            }

            let timestamp = node.timestamp.to_epoch_number() as u64;
            if timestamp < osm_epoch {
                panic!("timestamp before epoch. Change code. {}", timestamp);
            }
            let offset = (timestamp - osm_epoch) as u32;
            // TODO double check that this rounds down
            let frame_no = offset / sec_per_frame;

            if frame_no < first_frame_no {
                first_frame_no = frame_no;
            }
            if frame_no > last_frame_no {
                last_frame_no = frame_no;
            }

            let pixel_idx = latlon_to_pixel_index(lat, lon, width, height, &bbox);
            let curr_val = results.entry(frame_no).or_insert(HashMap::new()).entry(pixel_idx).or_insert(0);
            if *curr_val < std::u16::MAX {
                *curr_val += 1;
            }

            num_nodes += 1;
            if num_nodes % 50_000_000 == 0 {
                println!("Done {} million points", num_nodes/1_000_000);
            }
        }
    }
    println!("Done {} points", num_nodes);
    let num_frames = last_frame_no - first_frame_no + 1;
    println!("There are {} frames, which is {} sec", num_frames, num_frames as f32/30.);

    let mut sorted_results = Vec::with_capacity((last_frame_no-first_frame_no+1) as usize);

    for frame_no in 0..num_frames {
        match results.remove(&(frame_no+first_frame_no)) {
            None => { sorted_results.push((frame_no, Vec::with_capacity(0))) },
            Some(pixels) => {
                let pixels = pixels.into_iter().collect();
                sorted_results.push((frame_no, pixels));
            }
        }
    }

    sorted_results
}

fn write_frames(frames: Frames, filename: &str, height: u32, sec_per_frame: u32, bbox: &[f32; 4]) {
    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;
    let width = ((bbox_width / bbox_height) * (height as f32)) as u32;

    let mut file = BufWriter::new(fs::File::create(&filename).unwrap());

    writeln!(file, "metadata version {}", env!("CARGO_PKG_VERSION")).expect("Couldn't write metadata");
    writeln!(file, "metadata height {}", height).expect("Couldn't write metadata");
    writeln!(file, "metadata width {}", width).expect("Couldn't write metadata");
    writeln!(file, "metadata sec_per_frame {}", sec_per_frame).expect("Couldn't write metadata");
    writeln!(file, "metadata left {}", left).expect("Couldn't write metadata");
    writeln!(file, "metadata bottom {}", bottom).expect("Couldn't write metadata");
    writeln!(file, "metadata right {}", right).expect("Couldn't write metadata");
    writeln!(file, "metadata top {}", top).expect("Couldn't write metadata");
    writeln!(file, "").expect("Couldn't write metadata");

    for (frame_no, pixels) in frames.into_iter() {
        write!(file, "{}", frame_no).unwrap();
        for p in pixels {
            write!(file, ",{},{}", p.0, p.1).unwrap();
        }
        write!(file, "\n").unwrap()
    }
}

fn read_frames(filename: &str) -> Frames {
    let file = BufReader::new(fs::File::open(&filename).unwrap());
    let mut results = Frames::new();

    //let metadata: Vec<(String, String)> = file.lines().filter_map(|x| x.ok()).take_while(|x| x.len() > 0).map(|x| { let words: Vec<_> = x.split(" ").skip(1).take(2).collect(); (words[0].clone(), words[1].clone())}).collect();

    for line in file.lines().filter_map(|x| x.ok()).skip_while(|x| x.starts_with("metadata ") || x.len() == 0) {
        let frame_no = line.split(",").nth(0).unwrap().parse().unwrap();
        let pixels: Vec<(u32, u16)> = line.split(",").skip(1).collect::<Vec<_>>().chunks(2).map(|pair| (pair[0].parse().unwrap(), pair[1].parse().unwrap())).collect();
        results.push((frame_no, pixels))
    }

    results
}

fn latlon_to_pixel_index(lat: f32, lon: f32, width: u32, height: u32, bbox: &[f32; 4]) -> u32 {
    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;

    let lat0 = top - lat;
    let lon0 = lon - left;

    let x = ((lon0/bbox_width)*(width as f32)) as u32;
    let y = ((lat0/bbox_height)*(height as f32)) as u32;

    let i = y * width + x;

    assert!(i < width*height, "{} L{}, lat = {} lon = {} width = {} height = {} bbox = {:?} x = {} y = {} i = {}", file!(), line!(), lat, lon, width, height, bbox, x, y, i);

    i
}

fn create_gif(frames: Frames, output_image_filename: &str, height: u32, bbox: &[f32; 4], colour_ramp: &ColourRamp) {
    let mut output_file = fs::File::create(output_image_filename).expect("Can't create image");

    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;
    let width = ((bbox_width / bbox_height) * (height as f32)) as u32;

    // FIXME change width/height to u16?
    let mut encoder = gif::Encoder::new(&mut output_file, width as u16, height as u16, &colour_ramp.palette()).expect("Couldn't create encoder");
    encoder.set(gif::Repeat::Infinite).expect("Couldn't get inf repeat");

    // TODO have a global palette

    let mut image = vec![None; (width*height) as usize];

    for (frame_no, pixels) in frames.into_iter() {

        for i in 0..image.len() {
            if image[i].is_some() && image[i].unwrap() > 0 {
                image[i] = image[i].map(|x| (x as f32*0.9) as u32);
            }
        }

        for (i, magnitude) in pixels {
            // FIXME sometimes the value is invalid
            //assert!(i < width*height, "{} L{}, width = {} height = {} i = {}", file!(), line!(), width, height, i);
            if i < width*height {
                image[i as usize] = Some(magnitude as u32);
            }
        }

        let mut pixels = Vec::with_capacity(image.len() * 4);
        for p in image.iter().cloned() {
            let index = colour_ramp.index_for_magnitude(p);
            pixels.push(index);
        }

        let mut frame = gif::Frame::from_indexed_pixels(width as u16, height as u16, pixels.as_mut_slice(), None);
        // 30 fps, and delay is in units of 10ms.
        frame.delay = 100 / 30;

        encoder.write_frame(&frame).expect("Couldn't write frame");

        if frame_no % 30 == 0 {
            println!("Wrote frame {}", frame_no);
        }

    }

}

fn main() {
    let matches = App::new("osm-history-animation")
                          .arg(Arg::with_name("input").long("input").short("i")
                               .takes_value(true))
                          .arg(Arg::with_name("output") .long("output").short("o")
                               .takes_value(true))
                          .arg(Arg::with_name("height") .long("height").short("h")
                               .takes_value(true))
                          .arg(Arg::with_name("spf") .long("sec-per-frame").short("s")
                               .takes_value(true))
                          .arg(Arg::with_name("colour_ramp") .long("colour-ramp")
                               .takes_value(true))
                          .arg(Arg::with_name("save-intermediate").long("save-intermediate"))
                          .arg(Arg::with_name("load-intermediate").long("load-intermediate"))
                          .arg(Arg::with_name("bbox").long("bbox").takes_value(true).short("b"))
                          .setting(AppSettings::AllowLeadingHyphen)
                          .get_matches();

    let input_filename = matches.value_of("input").unwrap();
    let output_filename = matches.value_of("output").unwrap();
    let height: u32 = matches.value_of("height").unwrap().parse().unwrap();
    let sec_per_frame: u32 = matches.value_of("spf").unwrap().parse().unwrap();

    let bbox = match matches.value_of("bbox") {
        None => [-180., -90., 180., 90.],
        Some(text) => {
            let coords: Vec<f32> = text.split(",").map(|x| x.parse().unwrap() ).collect();
            [coords[0], coords[1], coords[2], coords[3]]
        }
    };

    let frames = if matches.is_present("load-intermediate") {
        println!("Reading frames from {}", input_filename);
        read_frames(&input_filename)
    } else {
        println!("Reading PBF file {}", input_filename);
        read_pbf(&input_filename, height, sec_per_frame, &bbox)
    };

    if matches.is_present("save-intermediate") {
        println!("Saving frame details to {}", output_filename);
        write_frames(frames, &output_filename, height, sec_per_frame, &bbox);
    } else {
        let colour_ramp = ColourRamp::new_from_filename(matches.value_of("colour_ramp").unwrap());
        println!("Creating image {}", output_filename);
        create_gif(frames, &output_filename, height, &bbox, &colour_ramp);
    }
    println!("\nFinished");
}
<commit_msg>more fading<commit_after>extern crate osmio;
extern crate image;
extern crate gif;
extern crate clap;

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

use std::fs;
use osmio::OSMReader;
use osmio::pbf::PBFReader;
use std::io::{Read, Write, BufReader, BufRead, BufWriter};
use std::collections::HashMap;

use gif::SetParameter;

// (frame_no, (pixel_index, num_changes))
type Frames = Vec<(u32, Vec<(u32, u16)>)>;

struct ColourRamp {
    empty_colour: (u8, u8, u8),
    steps: Vec<(u32, (u8, u8, u8))>,
}

impl ColourRamp {
    fn new_from_filename(filename: &str) -> Self {
        let mut contents = String::new();
        let mut file = fs::File::open(filename).unwrap();
        file.read_to_string(&mut contents).expect("Couldn't read colour ramp file");
        Self::new_from_text(&contents)

    }
    fn new_from_text(source: &str) -> Self {
        let lines: Vec<_> = source.lines().collect();
        let empty_vec = lines[0].split(",").filter_map(|x| x.parse::<u8>().ok()).take(3).collect::<Vec<_>>();
        let empty = (empty_vec[0], empty_vec[1], empty_vec[2]);

        let mut steps = Vec::new();
        for line in lines.iter().skip(1) {
            let line = line.split(",").filter_map(|x| x.parse::<u32>().ok()).take(4).collect::<Vec<_>>();
            let age = line[0];
            let colour = (line[1] as u8, line[2] as u8, line[3] as u8);
            steps.push((age, colour));
        }

        if steps.len() > 254 {
            panic!("Too many steps");
        }

        ColourRamp{ empty_colour: empty, steps: steps }
    }

    fn palette(&self) -> Vec<u8> {
        let mut results = Vec::with_capacity((self.steps.len()+1)*3);
        results.push(self.empty_colour.0);
        results.push(self.empty_colour.1);
        results.push(self.empty_colour.2);

        for &(_, (r, g, b)) in self.steps.iter() {
            results.push(r);
            results.push(g);
            results.push(b);
        }

        results
    }

    fn index_for_magnitude(&self, magnitude: Option<u32>) -> u8 {
        match magnitude {
            None => 0,
            Some(magnitude) => {
                if magnitude > 255 {
                    1
                } else {
                    (255 - magnitude) as u8
                }
            },
            //Some(age) => match self.steps.iter().position(|&(x, _)| x == age) {
            //    None => 0,
            //    Some(i) => (i+1) as u8,
            //}
        }
    }
}

fn read_pbf(filename: &str, height: u32, sec_per_frame: u32, bbox: &[f32; 4]) -> Frames {
    let file = BufReader::new(fs::File::open(&filename).unwrap());
    let mut node_reader = PBFReader::new(file);
    let node_reader = node_reader.nodes();

    let mut results: HashMap<u32, HashMap<u32, u16>> = HashMap::new();

    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;
    let width = ((bbox_width / bbox_height) * (height as f32)) as u32;

    // 1st April 2005, midnight GMT. We presume no OSM editing before then.
    let osm_epoch = 1109635200;

    // FIXME use a btreemap?

    let mut first_frame_no = std::u32::MAX;
    let mut last_frame_no = 0;

    let mut num_nodes: u64 = 0;
    for node in node_reader {
        if let (Some(lat), Some(lon)) = (node.lat, node.lon) {
            // FIXME should be able to do non-equals but it fails for point at south pole
            if lat >= top || lat <= bottom || lon >= right || lon <= left {
                continue;
            }

            let timestamp = node.timestamp.to_epoch_number() as u64;
            if timestamp < osm_epoch {
                panic!("timestamp before epoch. Change code. {}", timestamp);
            }
            let offset = (timestamp - osm_epoch) as u32;
            // TODO double check that this rounds down
            let frame_no = offset / sec_per_frame;

            if frame_no < first_frame_no {
                first_frame_no = frame_no;
            }
            if frame_no > last_frame_no {
                last_frame_no = frame_no;
            }

            let pixel_idx = latlon_to_pixel_index(lat, lon, width, height, &bbox);
            let curr_val = results.entry(frame_no).or_insert(HashMap::new()).entry(pixel_idx).or_insert(0);
            if *curr_val < std::u16::MAX {
                *curr_val += 1;
            }

            num_nodes += 1;
            if num_nodes % 50_000_000 == 0 {
                println!("Done {} million points", num_nodes/1_000_000);
            }
        }
    }
    println!("Done {} points", num_nodes);
    let num_frames = last_frame_no - first_frame_no + 1;
    println!("There are {} frames, which is {} sec", num_frames, num_frames as f32/30.);

    let mut sorted_results = Vec::with_capacity((last_frame_no-first_frame_no+1) as usize);

    for frame_no in 0..num_frames {
        match results.remove(&(frame_no+first_frame_no)) {
            None => { sorted_results.push((frame_no, Vec::with_capacity(0))) },
            Some(pixels) => {
                let pixels = pixels.into_iter().collect();
                sorted_results.push((frame_no, pixels));
            }
        }
    }

    sorted_results
}

fn write_frames(frames: Frames, filename: &str, height: u32, sec_per_frame: u32, bbox: &[f32; 4]) {
    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;
    let width = ((bbox_width / bbox_height) * (height as f32)) as u32;

    let mut file = BufWriter::new(fs::File::create(&filename).unwrap());

    writeln!(file, "metadata version {}", env!("CARGO_PKG_VERSION")).expect("Couldn't write metadata");
    writeln!(file, "metadata height {}", height).expect("Couldn't write metadata");
    writeln!(file, "metadata width {}", width).expect("Couldn't write metadata");
    writeln!(file, "metadata sec_per_frame {}", sec_per_frame).expect("Couldn't write metadata");
    writeln!(file, "metadata left {}", left).expect("Couldn't write metadata");
    writeln!(file, "metadata bottom {}", bottom).expect("Couldn't write metadata");
    writeln!(file, "metadata right {}", right).expect("Couldn't write metadata");
    writeln!(file, "metadata top {}", top).expect("Couldn't write metadata");
    writeln!(file, "").expect("Couldn't write metadata");

    for (frame_no, pixels) in frames.into_iter() {
        write!(file, "{}", frame_no).unwrap();
        for p in pixels {
            write!(file, ",{},{}", p.0, p.1).unwrap();
        }
        write!(file, "\n").unwrap()
    }
}

fn read_frames(filename: &str) -> Frames {
    let file = BufReader::new(fs::File::open(&filename).unwrap());
    let mut results = Frames::new();

    //let metadata: Vec<(String, String)> = file.lines().filter_map(|x| x.ok()).take_while(|x| x.len() > 0).map(|x| { let words: Vec<_> = x.split(" ").skip(1).take(2).collect(); (words[0].clone(), words[1].clone())}).collect();

    for line in file.lines().filter_map(|x| x.ok()).skip_while(|x| x.starts_with("metadata ") || x.len() == 0) {
        let frame_no = line.split(",").nth(0).unwrap().parse().unwrap();
        let pixels: Vec<(u32, u16)> = line.split(",").skip(1).collect::<Vec<_>>().chunks(2).map(|pair| (pair[0].parse().unwrap(), pair[1].parse().unwrap())).collect();
        results.push((frame_no, pixels))
    }

    results
}

fn latlon_to_pixel_index(lat: f32, lon: f32, width: u32, height: u32, bbox: &[f32; 4]) -> u32 {
    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;

    let lat0 = top - lat;
    let lon0 = lon - left;

    let x = ((lon0/bbox_width)*(width as f32)) as u32;
    let y = ((lat0/bbox_height)*(height as f32)) as u32;

    let i = y * width + x;

    assert!(i < width*height, "{} L{}, lat = {} lon = {} width = {} height = {} bbox = {:?} x = {} y = {} i = {}", file!(), line!(), lat, lon, width, height, bbox, x, y, i);

    i
}

fn create_gif(frames: Frames, output_image_filename: &str, height: u32, bbox: &[f32; 4], colour_ramp: &ColourRamp) {
    let mut output_file = fs::File::create(output_image_filename).expect("Can't create image");

    let left = bbox[0]; let bottom = bbox[1]; let right = bbox[2]; let top = bbox[3];
    let bbox_width = right - left;
    let bbox_height = top - bottom;
    let width = ((bbox_width / bbox_height) * (height as f32)) as u32;

    // FIXME change width/height to u16?
    let mut encoder = gif::Encoder::new(&mut output_file, width as u16, height as u16, &colour_ramp.palette()).expect("Couldn't create encoder");
    encoder.set(gif::Repeat::Infinite).expect("Couldn't get inf repeat");

    // TODO have a global palette

    let mut image = vec![None; (width*height) as usize];

    for (frame_no, pixels) in frames.into_iter() {

        for i in 0..image.len() {
            if image[i].is_some() && image[i].unwrap() > 0 {
                image[i] = image[i].map(|x| (x as f32*0.9) as u32);
            }
        }

        for (i, magnitude) in pixels {
            // FIXME sometimes the value is invalid
            //assert!(i < width*height, "{} L{}, width = {} height = {} i = {}", file!(), line!(), width, height, i);
            if i < width*height {
                let new_value: u32 = image[i as usize].unwrap_or(0) + magnitude as u32;
                image[i as usize] = Some(new_value);
            }
        }

        let mut pixels = Vec::with_capacity(image.len() * 4);
        for p in image.iter().cloned() {
            let index = colour_ramp.index_for_magnitude(p);
            pixels.push(index);
        }

        let mut frame = gif::Frame::from_indexed_pixels(width as u16, height as u16, pixels.as_mut_slice(), None);
        // 30 fps, and delay is in units of 10ms.
        frame.delay = 100 / 30;

        encoder.write_frame(&frame).expect("Couldn't write frame");

        if frame_no % 30 == 0 {
            println!("Wrote frame {}", frame_no);
        }

    }

}

fn main() {
    let matches = App::new("osm-history-animation")
                          .arg(Arg::with_name("input").long("input").short("i")
                               .takes_value(true))
                          .arg(Arg::with_name("output") .long("output").short("o")
                               .takes_value(true))
                          .arg(Arg::with_name("height") .long("height").short("h")
                               .takes_value(true))
                          .arg(Arg::with_name("spf") .long("sec-per-frame").short("s")
                               .takes_value(true))
                          .arg(Arg::with_name("colour_ramp") .long("colour-ramp")
                               .takes_value(true))
                          .arg(Arg::with_name("save-intermediate").long("save-intermediate"))
                          .arg(Arg::with_name("load-intermediate").long("load-intermediate"))
                          .arg(Arg::with_name("bbox").long("bbox").takes_value(true).short("b"))
                          .setting(AppSettings::AllowLeadingHyphen)
                          .get_matches();

    let input_filename = matches.value_of("input").unwrap();
    let output_filename = matches.value_of("output").unwrap();
    let height: u32 = matches.value_of("height").unwrap().parse().unwrap();
    let sec_per_frame: u32 = matches.value_of("spf").unwrap().parse().unwrap();

    let bbox = match matches.value_of("bbox") {
        None => [-180., -90., 180., 90.],
        Some(text) => {
            let coords: Vec<f32> = text.split(",").map(|x| x.parse().unwrap() ).collect();
            [coords[0], coords[1], coords[2], coords[3]]
        }
    };

    let frames = if matches.is_present("load-intermediate") {
        println!("Reading frames from {}", input_filename);
        read_frames(&input_filename)
    } else {
        println!("Reading PBF file {}", input_filename);
        read_pbf(&input_filename, height, sec_per_frame, &bbox)
    };

    if matches.is_present("save-intermediate") {
        println!("Saving frame details to {}", output_filename);
        write_frames(frames, &output_filename, height, sec_per_frame, &bbox);
    } else {
        let colour_ramp = ColourRamp::new_from_filename(matches.value_of("colour_ramp").unwrap());
        println!("Creating image {}", output_filename);
        create_gif(frames, &output_filename, height, &bbox, &colour_ramp);
    }
    println!("\nFinished");
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::iter::repeat;
use std::str::FromStr;

enum Hamming { Encode, Decode }

fn main() {
    let title = "RUSTYHAM: A HAMMING CODE GENERATOR IN RUST";
    let border: String = repeat('=').take(title.len()).collect::<>();
    println!("{}", border);
    println!("{}", title);
    println!("{}", border);

    loop {
        let mut linebuf = String::new();
        println!("(1) Encode ASCII");
        println!("*(2) Encode binary");
        println!("(3) Decode to ASCII");
        println!("*(4) Decode to binary");
        println!("Note: options marked as * are not supported yet.");
        print!("Enter your choice: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut linebuf).unwrap();

        let num = i32::from_str(linebuf.trim());
        match num.ok() {
            Some(n) => {
                let mut good = true;
                println!("{}", match n {
                    1 => hamming(Hamming::Encode),
                    3 => hamming(Hamming::Decode),
                    2 | 4 => "Sorry, not implemented yet.".to_string(),
                    _ => { good = false; "Invalid input.".to_string() }
                });
                if good { break; }
            }
            None => {
                println!("Invalid input.");
            }
        };
    }
}

fn hamming(action: Hamming) -> String {
    match action {
        Hamming::Encode => {
            // prompt for ASCII input
            let mut message = String::new();
            print!("Enter string to encode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut message).unwrap();

            // compute block and message length
            message = message.trim().to_string();
            let mlen = message.len() as u32 * 7;
            let lenpow = (2..).find(|&r| 2u32.pow(r) - r - 1 >= mlen).unwrap();
            let len = 2us.pow(lenpow) - 1;

            // the thing we're storing the hamming code in
            let mut code: Vec<bool> = repeat(false).take(len).collect::<>();

            // convert ASCII string to binary
            // IMPORTANT NOTE: the following line takes ownership of the
            // `message' variable. We no longer have access to the original
            // string from this point onwards.
            let bytes = message.into_bytes();
            let bytes_str = bytes.iter().map(|&c| format!("{:0>1$b}", c, 7))
                .collect::<Vec<String>>().concat();

            // set data bits
            let mut bytes_iter = bytes_str.chars();
            for i in 1..len {
                if (i & (i - 1)) != 0 {  // if i is not a power of 2
                    code[i-1] = bytes_iter.next().unwrap_or('0') == '1';
                }
            }

            // set parity bits
            for i in 0..lenpow {
                let bi = 2us.pow(i) - 1;
                let (mut parity, mut ignore, mut counter) = (false, false, 0);
                for j in bi..len {
                    if !ignore && code[j] { parity = !parity; }
                    counter += 1;
                    if counter >= 2u32.pow(i) {
                        ignore = !ignore;
                        counter = 0;
                    }
                }
                code[bi] = parity;
            }

            code.into_iter().map(|x| if x {"1"} else {"0"}).collect::<Vec<_>>().concat()
        },
        Hamming::Decode => {
            // prompt for binary input
            let mut code_str = String::new();
            print!("Enter Hamming code to decode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut code_str).unwrap();

            // verify parity bits, fix 1-bit-flipped errors if any
            // TODO avoid code repetition in Hamming::Encode =>
            let len = code_str.len() - 1;
            let lenpow = ((len + 1) as f32).sqrt().round() as u32;
            let chars = code_str.chars().map(|x| x == '1').collect::<Vec<bool>>();
            for i in 0..lenpow {
                println!("{:?}", calc_parity(chars, i));
            }

            // collect all bits at non-powers-of-2
            let data = &code_str.trim().chars().enumerate()
                .filter(|x| ((x.0 + 1) & x.0) != 0).map(|x| x.1)
                .collect::<Vec<char>>()[..];
            let chars = data.chunks(7)
                .map(|x| u8::from_str_radix(&x.iter().cloned().collect::<String>()[..], 2).unwrap())
                .collect::<Vec<u8>>();

            String::from_utf8(chars).unwrap()
        }
    }
}

fn calc_parity(code: Vec<bool>, i: u32) -> bool {
    let bi = 2us.pow(i) - 1;
    let (mut parity, mut ignore, mut counter) = (false, false, 0);
    for j in bi..code.len() {
        if !ignore && code[j] { parity = !parity }
        counter += 1;
        if counter >= 2u32.pow(i) {
            ignore = !ignore;
            counter = 0;
        }
    }
    parity
}
<commit_msg>detect errors (but do not fix them, yet)<commit_after>use std::io;
use std::io::prelude::*;
use std::iter::repeat;
use std::str::FromStr;

enum Hamming { Encode, Decode }

fn main() {
    let title = "RUSTYHAM: A HAMMING CODE GENERATOR IN RUST";
    let border: String = repeat('=').take(title.len()).collect::<>();
    println!("{}", border);
    println!("{}", title);
    println!("{}", border);

    loop {
        let mut linebuf = String::new();
        println!("(1) Encode ASCII");
        println!("*(2) Encode binary");
        println!("(3) Decode to ASCII");
        println!("*(4) Decode to binary");
        println!("Note: options marked as * are not supported yet.");
        print!("Enter your choice: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut linebuf).unwrap();

        let num = i32::from_str(linebuf.trim());
        match num.ok() {
            Some(n) => {
                let mut good = true;
                println!("{}", match n {
                    1 => hamming(Hamming::Encode),
                    3 => hamming(Hamming::Decode),
                    2 | 4 => "Sorry, not implemented yet.".to_string(),
                    _ => { good = false; "Invalid input.".to_string() }
                });
                if good { break; }
            }
            None => {
                println!("Invalid input.");
            }
        };
    }
}

fn hamming(action: Hamming) -> String {
    match action {
        Hamming::Encode => {
            // prompt for ASCII input
            let mut message = String::new();
            print!("Enter string to encode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut message).unwrap();

            // compute block and message length
            message = message.trim().to_string();
            let mlen = message.len() as u32 * 7;
            let lenpow = (2..).find(|&r| 2u32.pow(r) - r - 1 >= mlen).unwrap();
            let len = 2us.pow(lenpow) - 1;

            // the thing we're storing the hamming code in
            let mut code: Vec<bool> = repeat(false).take(len).collect::<>();

            // convert ASCII string to binary
            // IMPORTANT NOTE: the following line takes ownership of the
            // `message' variable. We no longer have access to the original
            // string from this point onwards.
            let bytes = message.into_bytes();
            let bytes_str = bytes.iter().map(|&c| format!("{:0>1$b}", c, 7))
                .collect::<Vec<String>>().concat();

            // set data bits
            let mut bytes_iter = bytes_str.chars();
            for i in 1..len {
                if (i & (i - 1)) != 0 {  // if i is not a power of 2
                    code[i-1] = bytes_iter.next().unwrap_or('0') == '1';
                }
            }

            // set parity bits
            for i in 0..lenpow {
                let bi = 2us.pow(i) - 1;
                let (mut parity, mut ignore, mut counter) = (false, false, 0);
                for j in bi..len {
                    if !ignore && code[j] { parity = !parity; }
                    counter += 1;
                    if counter >= 2u32.pow(i) {
                        ignore = !ignore;
                        counter = 0;
                    }
                }
                code[bi] = parity;
            }

            code.into_iter().map(|x| if x {"1"} else {"0"}).collect::<Vec<_>>().concat()
        },
        Hamming::Decode => {
            // prompt for binary input
            let mut code_str = String::new();
            print!("Enter Hamming code to decode: ");
            io::stdout().flush().unwrap();
            io::stdin().read_line(&mut code_str).unwrap();

            // verify parity bits, fix 1-bit-flipped errors if any
            // TODO avoid code repetition in Hamming::Encode =>
            let len = code_str.len() - 1;
            let lenpow = ((len + 1) as f32).sqrt().round() as u32;
            let chars = code_str.chars().map(|x| x == '1').collect::<Vec<bool>>();
            if (0..lenpow).any(|i| calc_parity(&chars, i)) {
                println!("There is an error!");
            }

            // collect all bits at non-powers-of-2
            let data = &code_str.trim().chars().enumerate()
                .filter(|x| ((x.0 + 1) & x.0) != 0).map(|x| x.1)
                .collect::<Vec<char>>()[..];
            let chars = data.chunks(7)
                .map(|x| u8::from_str_radix(&x.iter().cloned().collect::<String>()[..], 2).unwrap())
                .collect::<Vec<u8>>();

            String::from_utf8(chars).unwrap()
        }
    }
}

fn calc_parity(code: &Vec<bool>, i: u32) -> bool {
    let bi = 2us.pow(i) - 1;
    let (mut parity, mut ignore, mut counter) = (false, false, 0);
    for j in bi..code.len() {
        if !ignore && code[j] { parity = !parity }
        counter += 1;
        if counter >= 2u32.pow(i) {
            ignore = !ignore;
            counter = 0;
        }
    }
    parity
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate chan;
extern crate crypto;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;
use walkdir::WalkDir;
use std::thread;
// use chan::Receiver;

fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

fn do_it(root: &Path) -> io::Result<()> {
    let jobs = {
        let (s, r) = chan::sync(0);
        let pb = root.to_path_buf();
        thread::spawn(move || {
            for e in WalkDir::new(pb) {
                let e = match e {
                    Ok(e) => e,
                    Err(_) => continue, //? TODO: send Result with error
                };
                if !e.file_type().is_file() {
                    continue;
                }
                s.send(e);
            }
        });
        // This extra lexical scope will drop the initial
        // sender we created. Thus, the channel will be
        // closed when all threads spawned above has completed.
        r
    };
    let wg = chan::WaitGroup::new();
    for _ in 0..8 {
        wg.add(1);
        let wg = wg.clone();
        let jobs = jobs.clone();
        thread::spawn(move || {
            let mut hasher = Sha1::new();
            let mut buf = [0; 1024 * 8];
            for e in jobs {
                let hex = match calc_hash(e.path(), &mut hasher, &mut buf) {
                    Ok(h) => h,
                    Err(_) => continue, //? TODO: Handle error
                };
                println!("{}\t{}", hex, e.path().display());
            }
            wg.done();
        });
    }
    wg.wait();
    Ok(())
}

use walkdir::Iter;

fn fun(e: walkdir::Result<walkdir::DirEntry>) -> i32 {
    let e = match e {
        Ok(e) => e,
        Err(_) => return 0, //? TODO: send Result with error
    };
    if !e.file_type().is_file() {
        return 0;
    }
    1
}

fn take_iterator_test<T, W, I>(it: T, worker: W) -> i32
    where T: IntoIterator<Item = I>,
          T: std::marker::Send + 'static,
          W: std::marker::Send + 'static,
          W: Fn(I) -> i32
{
    let mut i = 0;
    let t = thread::spawn(move || {
        for e in it {
            i += worker(e);
        }
    });
    t.join();
    i
}

fn do_it_2(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let it = WalkDir::new(pb).into_iter();
    let i = take_iterator_test(it, fun);
    println!("{}", i);
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    do_it(root).expect("Hello error 1!");
    do_it_2(root).expect("Hello error 2!");
}
<commit_msg>More threading and lifetime adventures.<commit_after>#![deny(warnings)]

extern crate chan;
extern crate crypto;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;
use walkdir::WalkDir;
use std::thread;
// use chan::Receiver;

fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

fn do_it(root: &Path) -> io::Result<()> {
    let jobs = {
        let (s, r) = chan::sync(0);
        let pb = root.to_path_buf();
        thread::spawn(move || {
            for e in WalkDir::new(pb) {
                let e = match e {
                    Ok(e) => e,
                    Err(_) => continue, //? TODO: send Result with error
                };
                if !e.file_type().is_file() {
                    continue;
                }
                s.send(e);
            }
        });
        // This extra lexical scope will drop the initial
        // sender we created. Thus, the channel will be
        // closed when all threads spawned above has completed.
        r
    };
    let wg = chan::WaitGroup::new();
    for _ in 0..8 {
        wg.add(1);
        let wg = wg.clone();
        let jobs = jobs.clone();
        thread::spawn(move || {
            let mut hasher = Sha1::new();
            let mut buf = [0; 1024 * 8];
            for e in jobs {
                let hex = match calc_hash(e.path(), &mut hasher, &mut buf) {
                    Ok(h) => h,
                    Err(_) => continue, //? TODO: Handle error
                };
                println!("{}\t{}", hex, e.path().display());
            }
            wg.done();
        });
    }
    wg.wait();
    Ok(())
}

fn process_dir_entry(e: walkdir::Result<walkdir::DirEntry>) -> i32 {
    let e = match e {
        Ok(e) => e,
        Err(_) => return 0, //? TODO: send Result with error
    };
    if !e.file_type().is_file() {
        return 0;
    }
    1
}

/// factory is needed to allow iterators missing std::marker::Send
/// TODO: spawn worker threads
/// TODO: use channels
/// TODO: return channel Receiver as Iterator
fn fan_out_in<I, F, T, W>(factory: F, worker: W) -> i32
    where F: 'static + std::marker::Send + FnOnce() -> Box<T>,
          W: 'static + std::marker::Send + Fn(I) -> i32,
          T: IntoIterator<Item = I>
{
    let t = thread::spawn(move || {
        let mut i = 0;
        let it = factory();
        for e in it.into_iter() {
            i += worker(e);
        }
        i
    });
    t.join().unwrap()
}

fn do_it_2(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let iterator_factory = || Box::new(WalkDir::new(pb));
    let i = fan_out_in(iterator_factory, process_dir_entry);
    println!("{}", i);
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    do_it(root).expect("Hello error 1!");
    do_it_2(root).expect("Hello error 2!");
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals, dead_code)]
#![feature(box_syntax)]
#![feature(slice_patterns)]

mod gamestate;
use gamestate::*;

extern crate rand;
//use rand::{Rng, SeedableRng, XorShiftRng};

extern crate piston;
extern crate image;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::rc::Rc;
use std::cell::RefCell;

use opengl_graphics::{ GlGraphics, OpenGL, Texture };
use sdl2_window::Sdl2Window;
use image::GenericImage;
use piston::input::{ MouseButton };
use piston::window::{WindowSettings, AdvancedWindow};
use piston::event::*;

fn main() {
    let mut game = GameState::new();

    let mut paused = true;
    let mut raining = true;
    let mut redraw_needed = true;

    let mut mouse_buttons_pressed = (false, false); //left, right

    let opengl = OpenGL::_3_2;
    let (width, height) = (WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32);
    let window = Sdl2Window::new(
        opengl,
        WindowSettings::new("Falling Sand", (width, height))
        .exit_on_esc(true)
        //what those do?
        .vsync(true)
        .samples(1)
        .fullscreen(true)
    );
    let window = Rc::new(RefCell::new(window));
    let mut image = image::ImageBuffer::from_pixel(width, height, image::Rgba([0, 0, 0, 255]));
    let blank = image.clone().into_raw();
    let mut texture = Texture::from_image(&image);
    let ref mut gl = GlGraphics::new(opengl);
    for e in window.clone().events().max_fps(120).ups(60) {
        use piston::event::*;
        use piston::input::{Button, Key};

        if let Some(Button::Keyboard(key)) = e.press_args() {
            match key {
                Key::Space => {
                    paused = !paused;
                    window.borrow_mut().set_capture_cursor(!paused); //remove cursor when game is running
                },
                Key::R => {raining = !raining},
                _ => {}
            }
        }

        if let Some(_) = e.update_args() {
            //Update game and image on every loop
            if !paused {
                if raining {game.rain()}
                game.update();
                redraw_needed = true;
            }//if !paused {
        }

        if paused {
            if let Some(button) = e.press_args() {
                match button {
                    Button::Mouse(MouseButton::Left) => {mouse_buttons_pressed.0 = true},
                    Button::Mouse(MouseButton::Right) => {mouse_buttons_pressed.1 = true},
                    _ => {}
                }
            }
            if let Some(button) = e.release_args() {
                match button {
                    Button::Mouse(MouseButton::Left) => {mouse_buttons_pressed.0 = false},
                    Button::Mouse(MouseButton::Right) => {mouse_buttons_pressed.1 = false},
                    _ => {}
                }
            }
            if let Some(pos) = e.mouse_cursor_args() {
                redraw_needed = true;
                //get mouse coordinates and draw something if there is place
                let (mouse_x, mouse_y) = (pos[0] as i16, pos[1] as i16);

                match mouse_buttons_pressed {
                   (true, false) => {
                       game.paint_square_obstacles(mouse_x, mouse_y, 30, 30);
                   }
                   (false, true) => {
                       //remove everything from map and both lists
                       game.remove_square(mouse_x, mouse_y, 30, 30);
                   }
                   _ => {}
               }
            }
        } //if paused

        if redraw_needed {
            if let Some(args) = e.render_args() {
                image = image::ImageBuffer::from_vec(width, height, blank.clone()).unwrap();
                for particle in game.particles.iter() {
                    image.put_pixel(particle.x as u32, particle.y as u32,
                                    image::Rgba([238,232,170,255]));
                }
                for obstacle in game.obstacles.iter() {
                    image.put_pixel(obstacle.x as u32, obstacle.y as u32,
                                    image::Rgba([128,0,0,255]));
                }
                texture.update(&image);
                gl.draw(args.viewport(), |c, gl| {
                    graphics::clear([1.0; 4], gl);
                    graphics::image(&texture, c.transform, gl);
                });

                redraw_needed = false;
            }
        };
    }
}
<commit_msg>Don't clear the GL buffer<commit_after>#![allow(non_upper_case_globals, dead_code)]
#![feature(box_syntax)]
#![feature(slice_patterns)]

mod gamestate;
use gamestate::*;

extern crate rand;
//use rand::{Rng, SeedableRng, XorShiftRng};

extern crate piston;
extern crate image;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::rc::Rc;
use std::cell::RefCell;

use opengl_graphics::{ GlGraphics, OpenGL, Texture };
use sdl2_window::Sdl2Window;
use image::GenericImage;
use piston::input::{ MouseButton };
use piston::window::{WindowSettings, AdvancedWindow};
use piston::event::*;

fn main() {
    let mut game = GameState::new();

    let mut paused = true;
    let mut raining = true;
    let mut redraw_needed = true;

    let mut mouse_buttons_pressed = (false, false); //left, right

    let opengl = OpenGL::_3_2;
    let (width, height) = (WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32);
    let window = Sdl2Window::new(
        opengl,
        WindowSettings::new("Falling Sand", (width, height))
        .exit_on_esc(true)
        //what those do?
        .vsync(true)
        .samples(1)
        .fullscreen(true)
    );
    let window = Rc::new(RefCell::new(window));
    let mut image = image::ImageBuffer::from_pixel(width, height, image::Rgba([0, 0, 0, 255]));
    let blank = image.clone().into_raw();
    let mut texture = Texture::from_image(&image);
    let ref mut gl = GlGraphics::new(opengl);
    for e in window.clone().events().max_fps(120).ups(60) {
        use piston::event::*;
        use piston::input::{Button, Key};

        if let Some(Button::Keyboard(key)) = e.press_args() {
            match key {
                Key::Space => {
                    paused = !paused;
                    window.borrow_mut().set_capture_cursor(!paused); //remove cursor when game is running
                },
                Key::R => {raining = !raining},
                _ => {}
            }
        }

        if let Some(_) = e.update_args() {
            //Update game and image on every loop
            if !paused {
                if raining {game.rain()}
                game.update();
                redraw_needed = true;
            }//if !paused {
        }

        if paused {
            if let Some(button) = e.press_args() {
                match button {
                    Button::Mouse(MouseButton::Left) => {mouse_buttons_pressed.0 = true},
                    Button::Mouse(MouseButton::Right) => {mouse_buttons_pressed.1 = true},
                    _ => {}
                }
            }
            if let Some(button) = e.release_args() {
                match button {
                    Button::Mouse(MouseButton::Left) => {mouse_buttons_pressed.0 = false},
                    Button::Mouse(MouseButton::Right) => {mouse_buttons_pressed.1 = false},
                    _ => {}
                }
            }
            if let Some(pos) = e.mouse_cursor_args() {
                redraw_needed = true;
                //get mouse coordinates and draw something if there is place
                let (mouse_x, mouse_y) = (pos[0] as i16, pos[1] as i16);

                match mouse_buttons_pressed {
                   (true, false) => {
                       game.paint_square_obstacles(mouse_x, mouse_y, 30, 30);
                   }
                   (false, true) => {
                       //remove everything from map and both lists
                       game.remove_square(mouse_x, mouse_y, 30, 30);
                   }
                   _ => {}
               }
            }
        } //if paused

        if redraw_needed {
            if let Some(args) = e.render_args() {
                image = image::ImageBuffer::from_vec(width, height, blank.clone()).unwrap();
                for particle in game.particles.iter() {
                    image.put_pixel(particle.x as u32, particle.y as u32,
                                    image::Rgba([238,232,170,255]));
                }
                for obstacle in game.obstacles.iter() {
                    image.put_pixel(obstacle.x as u32, obstacle.y as u32,
                                    image::Rgba([128,0,0,255]));
                }
                texture.update(&image);
                gl.draw(args.viewport(), |c, gl| {
                    graphics::image(&texture, c.transform, gl);
                });

                redraw_needed = false;
            }
        };
    }
}
<|endoftext|>


<commit_before>#![feature(globs)]

extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate serialize;

use neovim::nvim_main;
use rgtk::*;
use std::collections::HashSet;
use std::{io, str, time};

mod projects;
mod ui;
mod utils;

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut proj_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    proj_btns.set_size_request(-1, -1);
    proj_btns.add(&new_button);
    proj_btns.add(&import_button);
    proj_btns.add(&rename_button);
    proj_btns.add(&remove_button);

    let mut proj_tree = gtk::TreeView::new().unwrap();
    let selection = proj_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    proj_tree.set_model(&model);
    proj_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&proj_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    proj_tree.append_column(&column);

    let mut proj_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    proj_pane.set_size_request(-1, -1);
    proj_pane.pack_start(&proj_btns, false, true, 0);
    proj_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_btns.set_size_request(-1, -1);
    build_btns.add(&run_button);
    build_btns.add(&build_button);
    build_btns.add(&test_button);
    build_btns.add(&clean_button);
    build_btns.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_btns, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&proj_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::create_data_dir();
    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut proj_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::new_project(&mut state, &mut proj_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::import_project(&mut state, &mut proj_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(|| {
        ::projects::update_selection(&mut state);
    }));
    proj_tree.connect(gtk::signals::RowCollapsed::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    proj_tree.connect(gtk::signals::RowExpanded::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn main() {
    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_from_gui : [ffi::c_int, ..2] = [0, ..2];
    let mut gui_from_nvim : [ffi::c_int, ..2] = [0, ..2];
    unsafe {
        ffi::pipe(nvim_from_gui.as_mut_ptr());
        ffi::pipe(gui_from_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        // listen for messages from nvim
        spawn(proc() {
            let mut buf : [ffi::c_uchar, ..100] = [0, ..100];
            unsafe {
                loop {
                    let buf_ptr = buf.as_mut_ptr() as *mut ffi::c_void;
                    let n = ffi::read(gui_from_nvim[0], buf_ptr, 100);
                    if n < 0 { break; }
                    let msg = str::from_utf8(buf.slice_to(n as uint)).unwrap();
                    println!("Received: {}", msg);
                }
            }
        });

        // start the gui
        gui_main(&mut pty, gui_from_nvim[0], gui_from_nvim[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // listen for messages from the gui
        spawn(proc() {
            io::timer::sleep(time::Duration::seconds(1));

            let mut ch = neovim::Channel::new(nvim_from_gui[0], gui_from_nvim[1]);
            ch.subscribe("test");

            unsafe {
                let msg = "Hello, world!";
                let msg_c = msg.to_c_str();
                let msg_ptr = msg_c.as_ptr() as *const ffi::c_void;
                ffi::write(gui_from_nvim[1], msg_ptr, msg_c.len() as ffi::size_t);
            }
        });

        // start nvim
        nvim_main();
    }
}
<commit_msg>Show version number in title bar<commit_after>#![feature(globs)]

extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate serialize;

use neovim::nvim_main;
use rgtk::*;
use std::collections::HashSet;
use std::{io, str, time};

mod projects;
mod ui;
mod utils;

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let version = format!("SolidOak {}.{}.{}",
                          option_env!("CARGO_PKG_VERSION_MAJOR").unwrap(),
                          option_env!("CARGO_PKG_VERSION_MINOR").unwrap(),
                          option_env!("CARGO_PKG_VERSION_PATCH").unwrap());
    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title(version.as_slice());
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut proj_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    proj_btns.set_size_request(-1, -1);
    proj_btns.add(&new_button);
    proj_btns.add(&import_button);
    proj_btns.add(&rename_button);
    proj_btns.add(&remove_button);

    let mut proj_tree = gtk::TreeView::new().unwrap();
    let selection = proj_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    proj_tree.set_model(&model);
    proj_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&proj_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    proj_tree.append_column(&column);

    let mut proj_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    proj_pane.set_size_request(-1, -1);
    proj_pane.pack_start(&proj_btns, false, true, 0);
    proj_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_btns.set_size_request(-1, -1);
    build_btns.add(&run_button);
    build_btns.add(&build_button);
    build_btns.add(&test_button);
    build_btns.add(&clean_button);
    build_btns.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_btns, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&proj_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::create_data_dir();
    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut proj_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::new_project(&mut state, &mut proj_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::import_project(&mut state, &mut proj_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(|| {
        ::projects::update_selection(&mut state);
    }));
    proj_tree.connect(gtk::signals::RowCollapsed::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    proj_tree.connect(gtk::signals::RowExpanded::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn main() {
    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_from_gui : [ffi::c_int, ..2] = [0, ..2];
    let mut gui_from_nvim : [ffi::c_int, ..2] = [0, ..2];
    unsafe {
        ffi::pipe(nvim_from_gui.as_mut_ptr());
        ffi::pipe(gui_from_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        // listen for messages from nvim
        spawn(proc() {
            let mut buf : [ffi::c_uchar, ..100] = [0, ..100];
            unsafe {
                loop {
                    let buf_ptr = buf.as_mut_ptr() as *mut ffi::c_void;
                    let n = ffi::read(gui_from_nvim[0], buf_ptr, 100);
                    if n < 0 { break; }
                    let msg = str::from_utf8(buf.slice_to(n as uint)).unwrap();
                    println!("Received: {}", msg);
                }
            }
        });

        // start the gui
        gui_main(&mut pty, gui_from_nvim[0], gui_from_nvim[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // listen for messages from the gui
        spawn(proc() {
            io::timer::sleep(time::Duration::seconds(1));

            let mut ch = neovim::Channel::new(nvim_from_gui[0], gui_from_nvim[1]);
            ch.subscribe("test");

            unsafe {
                let msg = "Hello, world!";
                let msg_c = msg.to_c_str();
                let msg_ptr = msg_c.as_ptr() as *const ffi::c_void;
                ffi::write(gui_from_nvim[1], msg_ptr, msg_c.len() as ffi::size_t);
            }
        });

        // start nvim
        nvim_main();
    }
}
<|endoftext|>