<commit_before>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use git2::build::CheckoutBuilder;
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let exit_code = real_main();
    std::process::exit(exit_code);
}

fn real_main() -> i32 {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          // TODO: get this in synch with Cargo.toml and README
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            return E_NO_GIT_REPO;
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodule_path = Path::new(submodule_dir);
    let submodule = repo.find_submodule(&submodule_dir)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // TODO: make sure "topological" here means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|t| t.get_path(submodule_path))
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&submodule_commit_id);

                // rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    // It's safe to do force-reset because we checked at the beginning and the repo was clean
    let mut checkoutbuilder = CheckoutBuilder::new();
    checkoutbuilder.force();

    let updated_id = old_id_to_new[&head_id];
    let object = repo.find_object(updated_id, None).expect("Couldn't look up an object at which HEAD points");
    repo.reset(&object, git2::ResetType::Hard, Some(&mut checkoutbuilder)).expect("Couldn't run force-reset");

    0 // An exit code indicating success
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    // Maybe use remote_anonymous()
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<commit_msg>Check my assumption about topo commit order<commit_after>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use git2::build::CheckoutBuilder;
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let exit_code = real_main();
    std::process::exit(exit_code);
}

fn real_main() -> i32 {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          // TODO: get this in synch with Cargo.toml and README
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            return E_NO_GIT_REPO;
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodule_path = Path::new(submodule_dir);
    let submodule = repo.find_submodule(&submodule_dir)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // "Topological" and reverse means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|t| t.get_path(submodule_path))
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&submodule_commit_id);

                // rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    // It's safe to do force-reset because we checked at the beginning and the repo was clean
    let mut checkoutbuilder = CheckoutBuilder::new();
    checkoutbuilder.force();

    let updated_id = old_id_to_new[&head_id];
    let object = repo.find_object(updated_id, None).expect("Couldn't look up an object at which HEAD points");
    repo.reset(&object, git2::ResetType::Hard, Some(&mut checkoutbuilder)).expect("Couldn't run force-reset");

    0 // An exit code indicating success
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    // Maybe use remote_anonymous()
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate chrono;

use std::env;
use std::fs::DirBuilder;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;
use std::io::Read;
use std::io;
use std::io::Seek;
use std::io::SeekFrom;
use std::ops::Sub;
use std::path::PathBuf;
use std::process;
use std::str;

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

use chrono::DateTime;
use chrono::TimeZone;
use chrono::UTC;

const RECORD_LENGTH: usize = 22;

fn main() {
    match ensure_log_file_exists() {
    	Ok(_) => {},
    	Err(e) => {
    		println!("Couldn't create punch log: {}.\nExiting.", e);
    		process::exit(1)
    	}
    }

    let args = App::new("punchcard")
        .about("Time tracker")
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("in"))
        .subcommand(SubCommand::with_name("out"))
        .subcommand(SubCommand::with_name("card"))
        .get_matches();

	if args.is_present("in") {
		write_record_to_log(chrono::UTC::now(), Action::PunchIn);
	}

	if args.is_present("out") {
		write_record_to_log(chrono::UTC::now(), Action::PunchOut);
	}

	if args.is_present("card") {
		print_last_record();
	}
}

#[derive(Debug)]
#[derive(PartialEq)]
enum Action {
	PunchIn,
	PunchOut
}

struct Record {
	timestamp: DateTime<UTC>,
	action: Action
}

fn print_last_record() {

	let mut conf_file = PathBuf::new();
    conf_file.push(env::home_dir().unwrap());
    conf_file.push(".punch");
    conf_file.push("punch.log");

    let mut config_file = OpenOptions::new().read(true).open(conf_file).unwrap();
    let mut record = Record {
    	action: Action::PunchIn,
    	timestamp: chrono::UTC::now()
    };
    seek_to_record_offset(&mut config_file, 0);
    populate_record_at_current_offset(&mut config_file, &mut record);

    if record.action == Action::PunchIn {
    	let current_timestamp = chrono::UTC::now();
    	let time_punched_in = current_timestamp.sub(record.timestamp);
    	println!("Punched in since {} ({})", record.timestamp, time_punched_in)
    }
    else {
    	let mut previous_record = Record {
    		action: Action::PunchIn,
    		timestamp: chrono::UTC::now()
    	};
    	seek_to_record_offset(&mut config_file, 1);
    	populate_record_at_current_offset(&mut config_file, &mut previous_record);

    	let delta = record.timestamp.sub(previous_record.timestamp);
    	println!("Previously punched in between {} and {} ({})",
    		previous_record.timestamp, record.timestamp, delta)
    }
}

fn populate_record_at_offset_from_end(config_file: &mut File, record: &mut Record, offset_from_end: u64) -> Result<(), String> {
	seek_to_record_offset(config_file, 1);
	populate_record_at_current_offset(config_file, record);
	Ok(())
}

fn write_record_to_log(tm: DateTime<UTC>, action: Action) {
	let action_token = match action {
		Action::PunchIn => "I",
		Action::PunchOut => "O"
	};

	let mut conf_file = PathBuf::new();
    conf_file.push(env::home_dir().unwrap());
    conf_file.push(".punch");
    conf_file.push("punch.log");

    let mut config_file = OpenOptions::new().append(true).open(conf_file).unwrap();
    let fmt = tm.format("%FT%T");
	let formatted_timestamp = fmt.to_string();
	do_file_write(formatted_timestamp.as_bytes(), &mut config_file);
	do_file_write("_".as_bytes(), &mut config_file);
	do_file_write(action_token.as_bytes(), &mut config_file);
	do_file_write("\n".as_bytes(), &mut config_file);
}

fn populate_record_at_current_offset(f: &mut File, record: &mut Record) -> bool {
	let mut data = [0 as u8; RECORD_LENGTH];
	let read = f.read(&mut data);
	if read.unwrap() != RECORD_LENGTH {
		panic!("Could not read complete record")
	}
	let (ts_data, rest) = data.split_at(19);
	let timestamp = str::from_utf8(&ts_data).unwrap();
	let parse_result = chrono::UTC.datetime_from_str(&timestamp, "%FT%T");

	let record_ts = parse_result.
		unwrap().with_timezone(&chrono::UTC);
	record.timestamp = record_ts;
	let action_string = str::from_utf8(&rest).unwrap();
	if action_string == "_O\n" {
		record.action = Action::PunchOut;
	}
	else if action_string == "_I\n" {
		record.action = Action::PunchIn;
	}
	else {
		panic!("Could not determine action type from '{}'", action_string)
	}
	return true
}

fn seek_to_record_offset(f: &mut File, record_offset: u64) {
	let m = f.metadata().unwrap();
	let file_len = m.len();

	let record_length_in_bytes = RECORD_LENGTH as u64;
	let seek_offset = file_len - ((record_offset + 1) * record_length_in_bytes);

	if f.seek(SeekFrom::Start(seek_offset)).unwrap() != seek_offset {
		panic!("Could not seek to record offset {}", record_offset)
	}
}

fn do_file_write(data: &[u8], f: &mut File) {
	match f.write_all(data) {
    	Ok(_) => {},
    	Err(e) => println!("Failed to write data to log: {}", e)
    }
}

fn ensure_log_file_exists() -> io::Result<()> {
    let mut conf_dir = PathBuf::new();
    conf_dir.push(env::home_dir().unwrap());
    conf_dir.push(".punch");
    let config_path = conf_dir.as_path();

    let mut conf_file_builder = PathBuf::from(config_path);
    conf_file_builder.push("punch.log");

    let mut dir_builder = DirBuilder::new();
    dir_builder.recursive(true);

    try!(dir_builder.create(config_path));

    let conf_file = conf_file_builder.as_path();
    match OpenOptions::new().create(true).write(true).open(conf_file) {
    	Ok(_) => Ok(()),
    	Err(e) => Err(e)
    }
}<commit_msg>factor out common functions<commit_after>extern crate clap;
extern crate chrono;

use std::env;
use std::fs::DirBuilder;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;
use std::io::Read;
use std::io;
use std::io::Seek;
use std::io::SeekFrom;
use std::ops::Sub;
use std::path::PathBuf;
use std::process;
use std::str;

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

use chrono::DateTime;
use chrono::TimeZone;
use chrono::UTC;

const RECORD_LENGTH: usize = 22;

fn main() {
    match ensure_log_file_exists() {
    	Ok(_) => {},
    	Err(e) => {
    		println!("Couldn't create punch log: {}.\nExiting.", e);
			process::exit(1)
    	}
    }

    let args = App::new("punchcard")
        .about("Time tracker")
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("in"))
        .subcommand(SubCommand::with_name("out"))
        .subcommand(SubCommand::with_name("card"))
        .get_matches();

	if args.is_present("in") {
		ensure_last_record_is_of_action(Action::PunchOut);
		write_record_to_log(chrono::UTC::now(), Action::PunchIn);
	}

	if args.is_present("out") {
		ensure_last_record_is_of_action(Action::PunchIn);
		write_record_to_log(chrono::UTC::now(), Action::PunchOut);
	}

	if args.is_present("card") {
		print_last_record();
	}
}

#[derive(Debug)]
#[derive(PartialEq)]
enum Action {
	PunchIn,
	PunchOut,
	Unset
}

struct Record {
	timestamp: DateTime<UTC>,
	action: Action
}

fn ensure_last_record_is_of_action(expected_action: Action) {

	let mut config_file = get_conf_file(true, false).unwrap();
    let mut record = empty_record();

    match populate_record_at_offset_from_end(&mut config_file, &mut record, 0) {
    	Ok(_) => {},
    	Err(e) => {
    		println!("Couldn't create punch log: {}.\nExiting.", e);
			process::exit(1)
    	}
    }

    if record.action != expected_action {
    	match expected_action {
    		Action::PunchIn => {
    			println!("Already punched out, punch in first!");
				process::exit(1)
    		}
    		Action::PunchOut => {
    			println!("Already punched in, punch out first!");
    			process::exit(1)
    		}
    		Action::Unset => {
    			println!("Found unset action, log file corrupt");
    			process::exit(1)
    		}
    	}

    }
}

fn get_conf_file(read: bool, append: bool) -> io::Result<File> {

	let mut conf_file = PathBuf::new();
    conf_file.push(env::home_dir().unwrap());
    conf_file.push(".punch");
    conf_file.push("punch.log");

    OpenOptions::new().read(read).append(append).open(conf_file)
}

fn empty_record() -> Record {
	Record {
    	action: Action::Unset,
    	timestamp: chrono::UTC::now()
    }
}

fn print_last_record() {
    let mut config_file = get_conf_file(true, false).unwrap();
    let mut record = empty_record();

    match populate_record_at_offset_from_end(&mut config_file, &mut record, 0) {
    	Ok(_) => {},
    	Err(e) => {
    		println!("Couldn't create punch log: {}.\nExiting.", e);
			process::exit(1)
    	}
    }

    if record.action == Action::PunchIn {
    	let current_timestamp = chrono::UTC::now();
    	let time_punched_in = current_timestamp.sub(record.timestamp);
    	println!("Punched in since {} ({})", record.timestamp, time_punched_in)
    }
    else {
    	let mut previous_record = empty_record();
    	match populate_record_at_offset_from_end(&mut config_file, &mut previous_record, 1) {
	    	Ok(_) => {},
	    	Err(e) => {
	    		println!("Couldn't create punch log: {}.\nExiting.", e);
				process::exit(1)
	    	}
	    }

    	let delta = record.timestamp.sub(previous_record.timestamp);
    	println!("Previously punched in between {} and {} ({})",
    		previous_record.timestamp, record.timestamp, delta)
    }
}

fn populate_record_at_offset_from_end(config_file: &mut File, record: &mut Record, offset_from_end: u64) -> Result<(), String> {
	seek_to_record_offset(config_file, offset_from_end);
	populate_record_at_current_offset(config_file, record);
	Ok(())
}

fn write_record_to_log(tm: DateTime<UTC>, action: Action) {
	let action_token = match action {
		Action::PunchIn => "I",
		Action::PunchOut => "O",
		Action::Unset => "U"
	};

    let mut config_file = get_conf_file(false, true).unwrap();
    let fmt = tm.format("%FT%T");
	let formatted_timestamp = fmt.to_string();
	do_file_write(formatted_timestamp.as_bytes(), &mut config_file);
	do_file_write("_".as_bytes(), &mut config_file);
	do_file_write(action_token.as_bytes(), &mut config_file);
	do_file_write("\n".as_bytes(), &mut config_file);
}

fn populate_record_at_current_offset(f: &mut File, record: &mut Record) -> bool {
	let mut data = [0 as u8; RECORD_LENGTH];
	let read = f.read(&mut data);
	if read.unwrap() != RECORD_LENGTH {
		panic!("Could not read complete record")
	}
	let (ts_data, rest) = data.split_at(19);
	let timestamp = str::from_utf8(&ts_data).unwrap();
	let parse_result = chrono::UTC.datetime_from_str(&timestamp, "%FT%T");

	let record_ts = parse_result.
		unwrap().with_timezone(&chrono::UTC);
	record.timestamp = record_ts;
	let action_string = str::from_utf8(&rest).unwrap();
	if action_string == "_O\n" {
		record.action = Action::PunchOut;
	}
	else if action_string == "_I\n" {
		record.action = Action::PunchIn;
	}
	else {
		panic!("Could not determine action type from '{}'", action_string)
	}
	return true
}

fn seek_to_record_offset(f: &mut File, record_offset: u64) {
	let m = f.metadata().unwrap();
	let file_len = m.len();

	let record_length_in_bytes = RECORD_LENGTH as u64;
	let seek_offset = file_len - ((record_offset + 1) * record_length_in_bytes);

	if f.seek(SeekFrom::Start(seek_offset)).unwrap() != seek_offset {
		panic!("Could not seek to record offset {}", record_offset)
	}
}

fn do_file_write(data: &[u8], f: &mut File) {
	match f.write_all(data) {
    	Ok(_) => {},
    	Err(e) => println!("Failed to write data to log: {}", e)
    }
}

fn ensure_log_file_exists() -> io::Result<()> {
    let mut conf_dir = PathBuf::new();
    conf_dir.push(env::home_dir().unwrap());
    conf_dir.push(".punch");
    let config_path = conf_dir.as_path();

    let mut conf_file_builder = PathBuf::from(config_path);
    conf_file_builder.push("punch.log");

    let mut dir_builder = DirBuilder::new();
    dir_builder.recursive(true);

    try!(dir_builder.create(config_path));

    let conf_file = conf_file_builder.as_path();
    match OpenOptions::new().create(true).write(true).open(conf_file) {
    	Ok(_) => Ok(()),
    	Err(e) => Err(e)
    }
}<|endoftext|>


<commit_before>#![recursion_limit = "1024"] // error_chain

extern crate websocket;
extern crate env_logger;
#[macro_use]
extern crate log;
#[macro_use]
extern crate error_chain;
extern crate url;
extern crate clap;

use std::thread;
use std::io::{stdin,stdout};

use websocket::{Message, Sender, Receiver, DataFrame};
use websocket::message::Type;
use websocket::client::request::Url;
use websocket::Client;

use std::borrow::Borrow;
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Write, Read};

error_chain! {
    foreign_links {
        Io(::std::io::Error);
        Log(log::SetLoggerError);
        Url(::url::ParseError);
        Ws(::websocket::result::WebSocketError);
        VarError(::std::env::VarError);
        RE(std::sync::mpsc::RecvError);
    }
}

// Initialize logger with default "info" log level:
fn init_logger() -> Result<()> {
    let mut builder = env_logger::LogBuilder::new();
    builder.filter(None, log::LogLevelFilter::Info);
    if ::std::env::var("RUST_LOG").is_ok() {
       builder.parse(&::std::env::var("RUST_LOG")?);
    }
    builder.init()?;
    Ok(())
}

struct SenderWrapper<T: Sender> (T);

impl<T: Sender> ::std::io::Write for SenderWrapper<T> {
    fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
        let message = Message::binary(buf);
        let ret;
        let len = buf.len();
        if len > 0 {
            debug!("Sending message of {} bytes", len);
            ret = self.0.send_message(&message);
        } else {
            // Interpret zero length buffer is request
            // to close communication

            debug!("Sending the closing message");
            ret = self.0.send_message(&Message::close());
        }
        ret.map_err(|e|IoError::new(IoErrorKind::BrokenPipe, e))?;
        Ok(len)
    }
    fn flush(&mut self) -> ::std::io::Result<()> {
        Ok(())
    }
}

struct ReceiverWrapper<T: Receiver<DataFrame>> (T);

impl<T:Receiver<DataFrame>> ::std::io::Read for ReceiverWrapper<T> {
    fn read(&mut self, buf: &mut [u8]) -> ::std::io::Result<usize> {
        let ret = self.0.recv_message();
        let msg : Message = ret.map_err(|e|IoError::new(IoErrorKind::BrokenPipe, e))?;

        match msg.opcode {
            Type::Close => {
                Ok(0)
            }
            Type::Ping => {
                unimplemented!();
            }
            _ => {
                let msgpayload : &[u8] = msg.payload.borrow();
                let len = msgpayload.len();
                debug!("Received message of {} bytes", len);

                assert!(buf.len() >= len);

                buf[0..len].clone_from_slice(msgpayload);

                Ok(len)
            }
        }
    }
}

struct DataExchangeSession<R1,R2,W1,W2>
    where R1 : Read + Send, R2: Read + Send, W1: Write + Send, W2: Write + Send
{
    reader1 : R1,
    writer1 : W1,
    reader2 : R2,
    writer2 : W2,
}

impl<R1,R2,W1,W2> DataExchangeSession<R1,R2,W1,W2>
    where R1 : Read + Send + 'static, R2: Read + Send, W1: Write + Send, W2: Write + Send + 'static
{
    fn data_exchange(self) -> Result<()> {

        let mut reader1 = self.reader1;
        let mut writer1 = self.writer1;
        let mut reader2 = self.reader2;
        let mut writer2 = self.writer2;

        let receive_loop = thread::spawn(move || -> Result<()> {
            // Actual data transfer happens here
            ::std::io::copy(&mut reader1, &mut writer2)?;
            writer2.write(b"")?;
            Ok(())
        });

        // Actual data transfer happens here
        ::std::io::copy(&mut reader2, &mut writer1)?;
        writer1.write(b"")?; // Signal close

        info!("Waiting for child threads to exit");

        receive_loop.join().map_err(|x|format!("{:?}",x))?
    }
}

fn try_main() -> Result<()> {
    //env_logger::init()?;
    init_logger()?;

    // setup command line arguments
    let matches = ::clap::App::new("WS Command Line Client")
        .version("0.1")
        .author("Vitaly \"_Vi\" Shukela <vi0oss@gmail.com>")
        .about("Send binary data from stdin to a WebSocket and back to stdout.")
        .arg(::clap::Arg::with_name("URL")
             .help("The URL of the WebSocket server.")
             .required(true)
             .index(1)).get_matches();

    let url = Url::parse(matches.value_of("URL").ok_or("no URL")?)?;

    info!("Connecting to {}", url);

    let request = Client::connect(url)?;

    let response = request.send()?; // Send the request and retrieve a response

    info!("Validating response...");

    response.validate()?; // Validate the response

    info!("Successfully connected");

    let (sender, receiver) = response.begin().split();

    let des = DataExchangeSession {
        writer1 : SenderWrapper(sender),
        reader1 : ReceiverWrapper(receiver),
        writer2 : stdout(),
        reader2 : stdin(),
    };

    des.data_exchange()?;

    info!("Exited");
    Ok(())
}

fn main() {
    if let Err(x) = try_main() {
        let _ = writeln!(::std::io::stderr(), "{:?}", x);
    }
}

<commit_msg>mini-refactor: Peer<commit_after>#![recursion_limit = "1024"] // error_chain

extern crate websocket;
extern crate env_logger;
#[macro_use]
extern crate log;
#[macro_use]
extern crate error_chain;
extern crate url;
extern crate clap;

use std::thread;
use std::io::{stdin,stdout};

use websocket::{Message, Sender, Receiver, DataFrame};
use websocket::message::Type;
use websocket::client::request::Url;
use websocket::Client;

use std::borrow::Borrow;
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Write, Read};

error_chain! {
    foreign_links {
        Io(::std::io::Error);
        Log(log::SetLoggerError);
        Url(::url::ParseError);
        Ws(::websocket::result::WebSocketError);
        VarError(::std::env::VarError);
        RE(std::sync::mpsc::RecvError);
    }
}

// Initialize logger with default "info" log level:
fn init_logger() -> Result<()> {
    let mut builder = env_logger::LogBuilder::new();
    builder.filter(None, log::LogLevelFilter::Info);
    if ::std::env::var("RUST_LOG").is_ok() {
       builder.parse(&::std::env::var("RUST_LOG")?);
    }
    builder.init()?;
    Ok(())
}

struct SenderWrapper<T: Sender> (T);

impl<T: Sender> ::std::io::Write for SenderWrapper<T> {
    fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
        let message = Message::binary(buf);
        let ret;
        let len = buf.len();
        if len > 0 {
            debug!("Sending message of {} bytes", len);
            ret = self.0.send_message(&message);
        } else {
            // Interpret zero length buffer is request
            // to close communication

            debug!("Sending the closing message");
            ret = self.0.send_message(&Message::close());
        }
        ret.map_err(|e|IoError::new(IoErrorKind::BrokenPipe, e))?;
        Ok(len)
    }
    fn flush(&mut self) -> ::std::io::Result<()> {
        Ok(())
    }
}

struct ReceiverWrapper<T: Receiver<DataFrame>> (T);

impl<T:Receiver<DataFrame>> ::std::io::Read for ReceiverWrapper<T> {
    fn read(&mut self, buf: &mut [u8]) -> ::std::io::Result<usize> {
        let ret = self.0.recv_message();
        let msg : Message = ret.map_err(|e|IoError::new(IoErrorKind::BrokenPipe, e))?;

        match msg.opcode {
            Type::Close => {
                Ok(0)
            }
            Type::Ping => {
                // Sender used to be in a separate thread with a channel
                // now there's no channel, so trickier to combine ping replies
                // and usual data exchange
                unimplemented!();
            }
            _ => {
                let msgpayload : &[u8] = msg.payload.borrow();
                let len = msgpayload.len();
                debug!("Received message of {} bytes", len);

                assert!(buf.len() >= len);

                buf[0..len].clone_from_slice(msgpayload);

                Ok(len)
            }
        }
    }
}

struct Peer<R, W>
    where R : Read + Send + 'static, W: Write + Send + 'static
{
    reader: R,
    writer: W,
}

struct DataExchangeSession<R1, R2, W1, W2>
    where R1 : Read  + Send + 'static,
          R2 : Read  + Send + 'static,
          W1 : Write + Send + 'static,
          W2 : Write + Send + 'static,
{
    peer1: Peer<R1, W1>,
    peer2: Peer<R2, W2>,
}

impl<R1,R2,W1,W2> DataExchangeSession<R1,R2,W1,W2>
    where R1 : Read  + Send + 'static,
          R2 : Read  + Send + 'static,
          W1 : Write + Send + 'static,
          W2 : Write + Send + 'static,
{
    fn data_exchange(self) -> Result<()> {

        let mut reader1 = self.peer1.reader;
        let mut writer1 = self.peer1.writer;
        let mut reader2 = self.peer2.reader;
        let mut writer2 = self.peer2.writer;

        let receive_loop = thread::spawn(move || -> Result<()> {
            // Actual data transfer happens here
            ::std::io::copy(&mut reader1, &mut writer2)?;
            writer2.write(b"")?; // signal close
            Ok(())
        });

        // Actual data transfer happens here
        ::std::io::copy(&mut reader2, &mut writer1)?;
        writer1.write(b"")?; // Signal close

        info!("Waiting for child threads to exit");

        receive_loop.join().map_err(|x|format!("{:?}",x))?
    }
}

fn try_main() -> Result<()> {
    //env_logger::init()?;
    init_logger()?;

    // setup command line arguments
    let matches = ::clap::App::new("WS Command Line Client")
        .version("0.1")
        .author("Vitaly \"_Vi\" Shukela <vi0oss@gmail.com>")
        .about("Send binary data from stdin to a WebSocket and back to stdout.")
        .arg(::clap::Arg::with_name("URL")
             .help("The URL of the WebSocket server.")
             .required(true)
             .index(1)).get_matches();

    let url = Url::parse(matches.value_of("URL").ok_or("no URL")?)?;

    info!("Connecting to {}", url);

    let request = Client::connect(url)?;

    let response = request.send()?; // Send the request and retrieve a response

    info!("Validating response...");

    response.validate()?; // Validate the response

    info!("Successfully connected");

    let (sender, receiver) = response.begin().split();

    let des = DataExchangeSession {
        peer1 : Peer {
            reader : ReceiverWrapper(receiver),
            writer : SenderWrapper(sender),
        },
        peer2 : Peer {
            reader : stdin(),
            writer : stdout(),
        },
    };

    des.data_exchange()?;

    info!("Exited");
    Ok(())
}

fn main() {
    if let Err(x) = try_main() {
        let _ = writeln!(::std::io::stderr(), "{:?}", x);
    }
}

<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate num;
extern crate lazycell;
extern crate colored;
#[macro_use]
extern crate lazy_static;
extern crate getopts;

mod util;
mod syntax;
mod source;
mod number;
mod printer;
mod elaborate;
mod lowering;

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

use source::{SrcMgr, Source, DiagMgr, Severity};
use printer::PrettyPrint;

// use lexer::TokenKind;
use std::rc::Rc;

fn print_help(opts: &getopts::Options, program: &str) {
    let brief = format!("Usage: {} [options] FILES", program);
    eprint!("{}", opts.usage(&brief));
}

fn main() {
    //
    // Argument parsing
    //
    let args: Vec<String> = std::env::args().collect();

    let mut opts = getopts::Options::new();
    opts.optopt("o", "", "set output file name", "FILE");
    opts.optopt("t", "", "set toplevel module name", "MODULE");
    opts.optflag("", "parse", "parse only, do not elaborate");
    opts.optflag("h", "help", "print this help message");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => {
            eprintln!("{}", f.to_string());
            return print_help(&opts, &args[0])
        }
    };

    if matches.opt_present("h") {
        return print_help(&opts, &args[0])
    }

    // Initailise source manager and diagnostic manager first
    let src_mgr = Rc::new(SrcMgr::new());
    let diag_mgr = DiagMgr::new(src_mgr.clone());

    if matches.free.is_empty() {
        diag_mgr.report_span(Severity::Fatal, "no input files specified", source::Span::none());
        return;
    }

    // Register a new panic handler. If the panic is caused by throwing fatal error, we mute
    // Rust's built-in error message and stack trace.
    {
        let hook = std::panic::take_hook();
        std::panic::set_hook(Box::new(move |info| {
            if info.payload().downcast_ref::<Severity>().is_none() {
                hook(info)
            }
        }));
    }

    // Parse all files together
    let mut files = Vec::new();
    'outer: for filename in &matches.free {
        let mut infile = File::open(filename).unwrap();
        let mut contents = String::new();
        infile.read_to_string(&mut contents).unwrap();

        let src = Rc::new(Source::new((*filename).to_owned(), contents));
        src_mgr.add_source(src.clone());

        let list = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            let tokens = syntax::lex(&src_mgr, &diag_mgr, &src);
            let list = syntax::parse(&diag_mgr, tokens);
            list
        })) {
            Ok(v) => v,
            Err(info) => {
                if info.downcast_ref::<Severity>().is_some() {
                    continue
                } else {
                    return;
                }
            }
        };

        files.push(list);
    }

    // Abort elaboration when there are syntax errors.
    if diag_mgr.has_error() { return; }

    elaborate::resolve(&diag_mgr, &mut files);

    // Abort elaboration when there are syntax errors.
    if diag_mgr.has_error() { return; }

    if matches.opt_present("parse") {
        let mut out: Box<dyn Write> = match matches.opt_str("o") {
            None => Box::new(std::io::stdout()),
            Some(v) => Box::new(File::create(v).unwrap()),
        };

        let mut printer = PrettyPrint::new();
        for list in &files {
            for i in list {
                printer.print_item(&i);
                printer.append("\n");
            }
        }
        writeln!(out, "{}", printer.take()).unwrap();
        return;
    }

    let elaborated = elaborate::elaborate(&diag_mgr, &files, match matches.opt_str("t") {
        None => "chip_top",
        Some(ref v) => v,
    });

    // Abort elaboration when there are syntax errors.
    if diag_mgr.has_error() { return; }

    let elaborated = lowering::loop_gen_elim(elaborated);
    let mut elaborated = lowering::inst_array_elim(elaborated);
    lowering::type_param_elim(&mut elaborated);

    let files = elaborate::reconstruct(&elaborated);

    let mut out: Box<dyn Write> = match matches.opt_str("o") {
        None => Box::new(std::io::stdout()),
        Some(v) => Box::new(File::create(v).unwrap()),
    };

    {
        let list = files.first().unwrap();
        writeln!(out, "/* packages */").unwrap();
        let mut printer = PrettyPrint::new();
        for i in list {
            printer.print_item(&i);
            printer.append("\n");
        }
        writeln!(out, "{}", printer.take()).unwrap();
    }

    for (list, name) in files.iter().skip(1).zip(matches.free.iter()) {
        writeln!(out, "/* file: {} */", name).unwrap();
        if list.is_empty() {

        } else {
            let mut printer = PrettyPrint::new();
            for i in list {
                printer.print_item(&i);
                printer.append("\n");
            }
            writeln!(out, "{}", printer.take()).unwrap();
        }
    }
}
<commit_msg>Add option to specify black boxes<commit_after>#![allow(dead_code)]

extern crate num;
extern crate lazycell;
extern crate colored;
#[macro_use]
extern crate lazy_static;
extern crate getopts;

mod util;
mod syntax;
mod source;
mod number;
mod printer;
mod elaborate;
mod lowering;

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

use source::{SrcMgr, Source, DiagMgr, Severity};
use printer::PrettyPrint;
use syntax::ast;

// use lexer::TokenKind;
use std::rc::Rc;

fn print_help(opts: &getopts::Options, program: &str) {
    let brief = format!("Usage: {} [options] FILES", program);
    eprint!("{}", opts.usage(&brief));
}

fn main() {
    //
    // Argument parsing
    //
    let args: Vec<String> = std::env::args().collect();

    let mut opts = getopts::Options::new();
    opts.optopt("o", "", "set output file name", "FILE");
    opts.optopt("t", "", "set toplevel module name", "MODULE");
    opts.optmulti("b", "", "set a module to be a black box", "MODULE");
    opts.optflag("", "parse", "parse only, do not elaborate");
    opts.optflag("h", "help", "print this help message");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => {
            eprintln!("{}", f.to_string());
            return print_help(&opts, &args[0])
        }
    };

    if matches.opt_present("h") {
        return print_help(&opts, &args[0])
    }

    // Initailise source manager and diagnostic manager first
    let src_mgr = Rc::new(SrcMgr::new());
    let diag_mgr = DiagMgr::new(src_mgr.clone());

    if matches.free.is_empty() {
        diag_mgr.report_span(Severity::Fatal, "no input files specified", source::Span::none());
        return;
    }

    // Register a new panic handler. If the panic is caused by throwing fatal error, we mute
    // Rust's built-in error message and stack trace.
    {
        let hook = std::panic::take_hook();
        std::panic::set_hook(Box::new(move |info| {
            if info.payload().downcast_ref::<Severity>().is_none() {
                hook(info)
            }
        }));
    }

    // Parse all files together
    let mut files = Vec::new();
    'outer: for filename in &matches.free {
        let mut infile = File::open(filename).unwrap();
        let mut contents = String::new();
        infile.read_to_string(&mut contents).unwrap();

        let src = Rc::new(Source::new((*filename).to_owned(), contents));
        src_mgr.add_source(src.clone());

        let list = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            let tokens = syntax::lex(&src_mgr, &diag_mgr, &src);
            let list = syntax::parse(&diag_mgr, tokens);
            list
        })) {
            Ok(v) => v,
            Err(info) => {
                if info.downcast_ref::<Severity>().is_some() {
                    continue
                } else {
                    return;
                }
            }
        };

        files.push(list);
    }

    // Abort elaboration when there are syntax errors.
    if diag_mgr.has_error() { return; }

    elaborate::resolve(&diag_mgr, &mut files);

    // Abort elaboration when there are syntax errors.
    if diag_mgr.has_error() { return; }

    if matches.opt_present("parse") {
        let mut out: Box<dyn Write> = match matches.opt_str("o") {
            None => Box::new(std::io::stdout()),
            Some(v) => Box::new(File::create(v).unwrap()),
        };

        let mut printer = PrettyPrint::new();
        for list in &files {
            for i in list {
                printer.print_item(&i);
                printer.append("\n");
            }
        }
        writeln!(out, "{}", printer.take()).unwrap();
        return;
    }

    let elaborated = elaborate::elaborate(&diag_mgr, &files, match matches.opt_str("t") {
        None => "chip_top",
        Some(ref v) => v,
    });

    // Abort elaboration when there are syntax errors.
    if diag_mgr.has_error() { return; }

    let elaborated = lowering::loop_gen_elim(elaborated);
    let mut elaborated = lowering::inst_array_elim(elaborated);
    lowering::type_param_elim(&mut elaborated);

    let files = elaborate::reconstruct(&elaborated);

    let mut out: Box<dyn Write> = match matches.opt_str("o") {
        None => Box::new(std::io::stdout()),
        Some(v) => Box::new(File::create(v).unwrap()),
    };

    {
        let list = files.first().unwrap();
        writeln!(out, "/* packages */").unwrap();
        let mut printer = PrettyPrint::new();
        for i in list {
            printer.print_item(&i);
            printer.append("\n");
        }
        writeln!(out, "{}", printer.take()).unwrap();
    }

    let blackbox = matches.opt_strs("b");
    for (list, name) in files.iter().skip(1).zip(matches.free.iter()) {
        writeln!(out, "/* file: {} */", name).unwrap();
        if list.is_empty() {

        } else {
            let mut printer = PrettyPrint::new();
            for i in list {
                // If it is a design unit specified in blackbox list, do not print it out.
                if let ast::Item::DesignDecl(ref decl) = i {
                    if blackbox.iter().any(|item| &decl.name.value == item) {
                        continue
                    }
                }
                printer.print_item(&i);
                printer.append("\n");
            }
            writeln!(out, "{}", printer.take()).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
extern crate time;
extern crate env_logger;
extern crate bincode;
#[macro_use]
extern crate rustc_serialize;

mod cli;
mod dir;
mod database;
mod error;
#[macro_use]
mod logger;

use database::{Database};
use cli::{Action};
use error::{ToResult};

fn main() {
    let request = match cli::Request::get() {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    logger::init(request.verbose);
    log!("Logger works!");
    log!("request: {:?}", request);

    let db = match dir::db() {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    log!("db: {:?}", db);

    let mut store = match Database::open(db) {
        Ok(db) => db,
        Err(e) => panic!("failed to open database: {:?}", e),
    };

    log!("store: {:?}", store);

    println!("store {:?}", store);
    let result = match request.action {
        Action::Put => store.put(request.name, request.directory),
        Action::Get => show(store, request.name),
        _ => panic!("NOT IMPLEMENTED!"),
    };

    match result {
        Ok(value) => println!("success! {:?}", value),
        Err(err) => panic!(err),
    }
}

fn show(mut store: Database, key: String) -> ToResult<()> {
    // let bookmark = try!(store.get(key));
    if let Some(bookmark) = store.get(&key) {
        println!("info: {:?}", bookmark);
    } else {
        panic!("NOT FOUND");
    }

    return Ok(());
}
<commit_msg>cleanup<commit_after>#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
extern crate time;
extern crate env_logger;
extern crate bincode;
#[macro_use]
extern crate rustc_serialize;

mod cli;
mod dir;
mod database;
mod error;
#[macro_use]
mod logger;

use database::{Database};
use cli::{Action};
use error::{ToResult};

fn main() {
    let request = match cli::Request::get() {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    logger::init(request.verbose);
    log!("Logger works!");
    log!("request: {:?}", request);

    let db = match dir::db() {
        Ok(value) => value,
        Err(err) => panic!(err),
    };

    log!("db: {:?}", db);

    let mut store = match Database::open(db) {
        Ok(db) => db,
        Err(e) => panic!("failed to open database: {:?}", e),
    };

    log!("store: {:?}", store);

    println!("store {:?}", store);
    let result = match request.action {
        Action::Put => store.put(request.name, request.directory),
        Action::Get => show(store, request.name),
        _ => panic!("NOT IMPLEMENTED!"),
    };

    match result {
        Ok(value) => println!("success! {:?}", value),
        Err(err) => panic!(err),
    }
}

fn show(store: Database, key: String) -> ToResult<()> {
    if let Some(bookmark) = store.get(&key) {
        println!("info: {:?}", bookmark);
    } else {
        panic!("NOT FOUND");
    }

    return Ok(());
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>Using sdl2 for creating window && extend sdl-renderer to fill pixels more simplier<commit_after>extern crate sdl2;

use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::rect::Point;
use sdl2::render::Renderer;

trait TinyRenderer {
    fn set_pixel(&mut self, i32, i32, Color);
}

impl<'a> TinyRenderer for Renderer<'a> {
    fn set_pixel(&mut self, x: i32, y: i32, c: Color) {
        let current_color = self.draw_color();

        self.set_draw_color(c);
        self.draw_point(Point::new(x, y)).unwrap();
        self.set_draw_color(current_color);
    }
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("tinyrenderer-rs", 800, 600)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    renderer.set_draw_color(Color::RGB(0, 0, 0));
    renderer.clear();

    //Draw pixel
    renderer.set_pixel(200, 200, Color::RGB(0, 255, 0));

    renderer.present();

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running
                },
                _ => {}
            }
        }
        //code here
    }
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use zip::write::FileOptions;
use std::fs::File;

extern crate zip;

fn main() {
    let prog_return = create_archive();
    match prog_return {
        Ok(f) => f,
        Err(err) => {
            println!("Error: {}", err);
            std::process::exit(1);

        }
    }

    //Probably not necessary, but we want the
    //process to explicitly return 0 on success
    std::process::exit(0);
}

/*
 * This function creates a zip archive, and returns a Result
 * that indicates if all operations were successful.
 *
 * The first command-line argument is the name and path of the zip file to be created,
 * and all the other arguments are the names and paths of the files to be archived
 *
 */
fn create_archive() -> Result<(), String> {
    //If there are less than 3 command-line arguments, return an error
    if std::env::args().count() < 3 {
        return Err("Usage: zipper <zip file> <file to archive> <file to archive> ...".to_string());
    }

    //The 1st command line argument is the name of the zip file
    let zipfile_name = match std::env::args().nth(1) {
        Some(c) => c,
        None => return Err("Invalid argument for zip file.".to_string()),
    };

    let path_to_zipfile = std::path::Path::new(&zipfile_name);
    let zipfile = match std::fs::File::create(&path_to_zipfile) {
        Err(_) => {
            return Err(format!("Could not create zip file: {}. Check that the file path exists.",
                               zipfile_name))
        }
        Ok(f) => f,
    };
    let mut zip = zip::ZipWriter::new(zipfile);

    //Iterate over the command-line arguments for the names of the
    //files we wish to archive. Skip the first two arguments.
    for arg in std::env::args().skip(2) {
        let filename = &arg;

        //Put this in it's own scope so that the memory for the contents of
        //each file will be freed on each iteration
        {
            //Extract the raw filename out of the full path entered by the user
            //This returns the file name as a type &OsStr slice instead of a &str slice
            let name_of_file = match std::path::Path::new(filename).file_name() {
                Some(c) => c,
                None => return Err(format!("{}: Invalid file name", filename)),
            };

            //Redefine and convert the &OsStr slice to a standard &str slice.
            //Checks if the filename is valid unicode.
            let name_of_file = match name_of_file.to_str() {
                Some(c) => c,
                None => return Err(format!("{}: Invalid file name", filename)),
            };

            //Open the file and check for errors
            let mut f = match File::open(filename) {
                Err(e) => return Err(format!("{}: {}", filename, e)),
                Ok(f) => f,
            };

            //Get the file metadata so we can get the file size. Also check for errors
            let file_metadata = match f.metadata() {
                Err(e) => return Err(format!("{}: {}", filename, e)),
                Ok(f)  => f,
            };

            //Get the exact size of the file from the file metadata
            let file_size = file_metadata.len() as usize;

            //Create a buffer to store the file contents that is exactly the size of the file + 1
            let mut contents: Vec<u8> = Vec::with_capacity(file_size + 1);

            //Read the file and check for errors
            try!(f.read_to_end(&mut contents).map_err(|e| e.to_string()));

            //Now compress the file and write it into the zip archive
            try!(zip.start_file(name_of_file, FileOptions::default())
                     .map_err(|e| e.to_string()));
            try!(zip.write_all(contents.as_slice())
                     .map_err(|e| e.to_string()));

        }

    }

    //Finish the zip file
    try!(zip.finish().map_err(|e| e.to_string()));

    //Return an empty Ok tuple because we had no errors.
    return Ok(());
}
<commit_msg>Cleanup based on feedback<commit_after>use std::io::prelude::*;
use zip::write::FileOptions;
use std::fs::File;

extern crate zip;

fn main() {
    if let Err(err) = create_archive() {
        //If there was a problem creating the zip archive,
        //then print the error and exit(1).
        println!("Error: {}", err);
        std::process::exit(1);
    }
}

/*
 * This function creates a zip archive, and returns a Result
 * that indicates if all operations were successful.
 *
 * The first command-line argument is the name and path of the zip file to be created,
 * and all the other arguments are the names and paths of the files to be archived
 *
 */
fn create_archive() -> Result<(), String> {
    //If there are less than 3 command-line arguments, return an error
    if std::env::args().count() < 3 {
        return Err("Usage: zipper <zip file> <file to archive> <file to archive> ...".to_string());
    }

    //The 1st command line argument is the name of the zip file.
    //It's not necessary to error check because we already checked
    //the number of command-line arguments earlier
    let zipfile_name = std::env::args().nth(1).unwrap();

    let path_to_zipfile = std::path::Path::new(&zipfile_name);
    let zipfile = match std::fs::File::create(&path_to_zipfile) {
        Err(e) => return Err(format!("Error creating {}: {}", zipfile_name, e)),
        Ok(f)  => f,
    };
    let mut zip = zip::ZipWriter::new(zipfile);

    //Iterate over the command-line arguments for the names of the
    //files we wish to archive. Skip the first two arguments.
    for arg in std::env::args().skip(2) {
        let filename = &arg;

        //Extract the raw filename out of the full path entered by the user
        //This returns the file name as a type &OsStr slice instead of a &str slice
        let name_of_file = match std::path::Path::new(filename).file_name() {
            Some(c) => c,
            None    => return Err(format!("{}: Invalid file name", filename)),
        };

        //Redefine and convert the &OsStr slice to a standard &str slice.
        //Checks if the filename is valid unicode.
        let name_of_file = match name_of_file.to_str() {
            Some(c) => c,
            None    => return Err(format!("{}: Invalid file name", filename)),
        };

        //Open the file and check for errors
        let mut f = match File::open(filename) {
            Err(e) => return Err(format!("{}: {}", filename, e)),
            Ok(f)  => f,
        };

        //Get the file metadata so we can get the file size. Also check for errors
        let file_metadata = match f.metadata() {
            Err(e) => return Err(format!("{}: {}", filename, e)),
            Ok(f)  => f,
        };

        //Get the exact size of the file from the file metadata
        let file_size = file_metadata.len() as usize;

        //Create a buffer to store the file contents that is exactly the size of the file + 1
        let mut contents: Vec<u8> = Vec::with_capacity(file_size + 1);

        //Read the file and check for errors
        f.read_to_end(&mut contents).map_err(|e| e.to_string())?;

        //Now compress the file and write it into the zip archive
        zip.start_file(name_of_file, FileOptions::default())
            .map_err(|e| e.to_string())?;
        zip.write_all(contents.as_slice())
            .map_err(|e| e.to_string())?;

    } //end for loop

    //Finish the zip file
    //try!(zip.finish().map_err(|e| e.to_string()));
    zip.finish().map_err(|e| e.to_string())?;

    //Return an empty Ok tuple because we had no errors.
    Ok(())
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate rand;
extern crate "rustc-serialize" as rustc_serialize;

use iron::prelude::*;
use iron::status;
use rand::{ OsRng, Rng };
use rand::distributions::{ IndependentSample, Range };
use rand::distributions::range::SampleRange;
use std::collections::hash_map::{ HashMap, Entry };

#[derive(RustcEncodable, RustcDecodable)]
struct QueryResult {
    high: u64,
    low: u64,
    avg: f64,
    rolls: Box<[u64]>,
}

fn main() {
    let mut rng = match OsRng::new() {
        Ok(rng) => rng,
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };
    let mut range_map = HashMap::new();

    match Iron::new(|req| handler(req, &mut range_map, &mut rng)).listen("localhost:2020") {
        Ok(_) => (),
        Err(_) => println!("srand failed to start"),
    }
}

fn handler<R: Rng>(req: &mut Request, map: &mut HashMap<u64, Range<u64>>, rng: &mut R) -> IronResult<Response> {
    let inputs: Vec<u64> = req.url.path.iter().filter_map(|i| i.parse().ok()).collect();

    build_query_result(&inputs[..], map, rng);
}

fn build_query_result<'a, R: Rng>(inputs: &[u64], map: &mut HashMap<u64, Range<u64>>, rng: &mut R) -> Result<QueryResult, &'a str> {
    let values: Vec<_> = inputs.iter().map(|i| {
        if !map.contains_key(i) {
            map.push(i, Range::new(0, i));
        }

        get_random(map[i], rng)
    }).collect();

    if values.len() > 0 {
        let average = {
            let (count, total) = values.iter().fold((0,0), |(count,total),b| (count + 1, total + b));
            total as f64 / count as f64
        };

        Ok(QueryResult {
            high: values.iter().max(),
            low: values.iter().min(),
            avg: average,
            rolls: Box::new(values),
        })
    } else {
        Err("Not enough inputs.")
    }
}

fn get_random<R: Rng>(range: Range<u64>, rng: &mut R) -> u64 {
    range.ind_sample(rng)
}
<commit_msg>Blocked.<commit_after>#![feature(box_syntax)]

extern crate iron;
extern crate rand;
extern crate "rustc-serialize" as rustc_serialize;

use iron::middleware::Handler;
use iron::prelude::*;
use iron::status;
use rand::{ OsRng, Rng };
use rand::distributions::{ IndependentSample, Range };
use rand::distributions::range::SampleRange;
use rustc_serialize::json;
use std::collections::hash_map::{ HashMap, Entry };

type SrandResult = Result<QueryResult, &'static str>;

#[derive(RustcEncodable, RustcDecodable)]
struct QueryResult {
    high: u64,
    low: u64,
    avg: f64,
    rolls: Box<Vec<u64>>,
}

struct PrngHandler<R: Rng + Send + Sync + 'static> {
    map: HashMap<u64, Range<u64>>,
    rng: R,
}

impl<R: Rng + Send + Sync + 'static> PrngHandler<R> {
    fn build_query_result<'a>(&mut self, inputs: &[u64]) -> SrandResult {
        let values: Vec<_> = inputs.iter().map(|i| {
            if !self.map.contains_key(i) {
                self.map.insert(*i, Range::new(0, *i));
            }

            self.get_random(*i)
        }).collect();

        if values.len() > 0 {
            let average = {
                let (count, total) = values.iter().fold((0,0), |(count,total),b| (count + 1, total + *b));
                total as f64 / count as f64
            };

            Ok(QueryResult {
                high: *values.iter().max().unwrap_or(&0),
                low: *values.iter().min().unwrap_or(&0),
                avg: average,
                rolls: box values,
            })
        } else {
            Err("Invalid request.")
        }
    }

    fn get_random(&mut self, i: u64) -> u64 {
        self.map[i].ind_sample(&mut self.rng)
    }
}

/// It's impossible to write a stateful handler this way because (I'm assuming) the underlying
/// Iron framework is based on the assumption that handlers are stateless, or should behave
/// statelessly, which means that some manner of synchronization will have to be applied to
/// anything that modifies the PrngHandler (and therefore borrows it as mutable).
///
/// I know that there is some kind of way to do this in Rust, but I have no idea what that is,
/// so I'm kind of stymied for the evening. I believe it has something to do with Arcs or some
/// crap along those lines--basically, you have an immutable borrowed reference to something
/// that can (magically) give you (and only you) a mutable reference to itself.
///
/// Damned if I know. I really never thought I'd have to use anything of the kind.
impl<R: Rng + Send + Sync + 'static> Handler for PrngHandler<R> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let inputs: Vec<u64> = req.url.path.iter().filter_map(|i| i.parse().ok()).collect();

        match self.build_query_result(&inputs[..]) {
            Ok(result) => Ok(Response::with((status::Ok, json::encode(&result).unwrap()))),
            Err(e) => Ok(Response::with((status::Ok, e))),
        }
    }
}

fn main() {
    let mut prng = match OsRng::new() {
        Ok(rng) => PrngHandler {
            map: HashMap::new(),
            rng: rng,
        },
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };

    match Iron::new(prng).http("localhost:2020") {
        Ok(_) => (),
        Err(_) => println!("srand failed to start"),
    }
}
<|endoftext|>