<commit_before>extern crate env_logger;
extern crate git2;
#[macro_use]
extern crate log;
extern crate tools;
extern crate unicode_normalization;

use std::borrow::Borrow;
use std::collections::HashMap;
use std::env;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};

use git2::{DiffFindOptions, ObjectType, Oid, Patch, Repository, Sort};
use tools::config::index_blame;
use unicode_normalization::UnicodeNormalization;

fn get_hg_rev(helper: &mut Child, git_oid: &Oid) -> Option<String> {
    write!(helper.stdin.as_mut().unwrap(), "git2hg {}\n", git_oid).unwrap();
    let mut reader = BufReader::new(helper.stdout.as_mut().unwrap());
    loop {
        let mut result = String::new();
        reader.read_line(&mut result).unwrap();
        if result.starts_with("changeset ") {
            return result.split(' ').nth(1).map(str::trim).map(str::to_string);
        }
        if result.trim().chars().all(|c| c == '0') {
            return None;
        }
        // else loop again. cinnabar-helper emits a bunch of other info that we don't care about
    }
}

fn start_cinnabar_helper(git_repo: &Repository) -> Child {
    Command::new("git")
        .arg("cinnabar-helper")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(git_repo.path())
        .spawn()
        .unwrap()
}

fn count_lines(blob: &git2::Blob) -> usize {
    let data = blob.content();
    if data.is_empty() {
        return 0;
    }
    let mut linecount = 0;
    for b in data {
        if *b == b'\n' {
            linecount += 1;
        }
    }
    if data[data.len() - 1] != b'\n' {
        linecount += 1;
    }
    linecount
}

fn unmodified_lines(
    blob: &git2::Blob,
    parent_blob: &git2::Blob,
) -> Result<Vec<(usize, usize)>, git2::Error> {
    let mut unchanged = Vec::new();

    let patch = Patch::from_blobs(parent_blob, None, blob, None, None)?;

    if patch.delta().flags().is_binary() {
        return Ok(unchanged);
    }

    fn add_delta(lineno: usize, delta: i32) -> usize {
        ((lineno as i32) + delta) as usize
    }

    let mut latest_line: usize = 0;
    let mut delta: i32 = 0;

    for hunk_index in 0..patch.num_hunks() {
        for line_index in 0..patch.num_lines_in_hunk(hunk_index)? {
            let line = patch.line_in_hunk(hunk_index, line_index)?;

            if let Some(lineno) = line.new_lineno() {
                let lineno = lineno as usize;
                for i in latest_line..lineno - 1 {
                    unchanged.push((i, add_delta(i, delta)));
                }
                latest_line = (lineno - 1) + 1;
            }

            match line.origin() {
                '+' => delta -= 1,
                '-' => delta += 1,
                ' ' => {
                    assert_eq!(
                        line.old_lineno().unwrap() as usize,
                        add_delta(line.new_lineno().unwrap() as usize, delta)
                    );
                    unchanged.push((
                        (line.new_lineno().unwrap() - 1) as usize,
                        (line.old_lineno().unwrap() - 1) as usize,
                    ));
                }
                _ => (),
            };
        }
    }

    let linecount = count_lines(blob);
    for i in latest_line..linecount {
        unchanged.push((i, add_delta(i, delta)));
    }
    Ok(unchanged)
}

fn blame_for_path(
    file_movement: &HashMap<Oid, HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    path: &Path,
) -> Result<String, git2::Error> {
    let blob = commit
        .tree()?
        .get_path(path)?
        .to_object(git_repo)?
        .peel_to_blob()?;
    let linecount = count_lines(&blob);
    // TODO: drop this author field entirely, I don't think it gets consumed by anything
    let commit_id = commit.id();
    let author = commit
        .author()
        .name()
        .map(|n| n.nfkd().filter(|c| c.is_ascii()).collect::<String>())
        .unwrap_or_default();
    let mut blame = Vec::with_capacity(linecount);
    for line in 1..=linecount {
        blame.push(format!("{}:%:{}:{}", commit_id, line, author));
    }

    for (parent, blame_parent) in commit.parents().zip(blame_parents.iter()).rev() {
        let parent_path = file_movement
            .get(&parent.id())
            .and_then(|m| m.get(&blob.id()))
            .map(|p| p.borrow())
            .unwrap_or(path);
        let parent_blob = match parent.tree()?.get_path(parent_path) {
            Ok(t) if t.kind() == Some(ObjectType::Blob) => t.to_object(git_repo)?.peel_to_blob()?,
            _ => {
                continue;
            }
        };
        let parent_blame_blob = match blame_parent.tree()?.get_path(parent_path) {
            Ok(entry) => entry.to_object(blame_repo)?.peel_to_blob()?,
            _ => continue,
        };
        let parent_blame = std::str::from_utf8(parent_blame_blob.content())
            .unwrap() // We only ever put ascii in the blame blob (for now)
            .lines()
            .collect::<Vec<&str>>();

        let path_unchanged = path == parent_path;
        for (lineno, parent_lineno) in unmodified_lines(&blob, &parent_blob)? {
            if path_unchanged {
                blame[lineno] = String::from(parent_blame[parent_lineno]);
                continue;
            }
            let mut pieces = parent_blame[parent_lineno].splitn(4, ':');
            let p_rev = pieces.next().unwrap();
            let mut p_fname = pieces.next().unwrap();
            let p_lineno = pieces.next().unwrap();
            let p_author = pieces.next().unwrap();
            if p_fname == "%" {
                p_fname = parent_path.to_str().unwrap();
            }
            blame[lineno] = format!("{}:{}:{}:{}", p_rev, p_fname, p_lineno, p_author);
        }
    }
    // Extra entry so the `join` call after adds a trailing newline
    blame.push(String::new());
    Ok(blame.join("\n"))
}

fn build_blame_tree(
    builder: &mut git2::TreeBuilder,
    file_movement: &HashMap<Oid, HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    mut path: PathBuf,
) -> Result<(), git2::Error> {
    let tree_at_path = if path == PathBuf::new() {
        commit.tree()?
    } else {
        commit
            .tree()?
            .get_path(&path)?
            .to_object(git_repo)?
            .peel_to_tree()?
    };
    'outer: for entry in tree_at_path.iter() {
        path.push(entry.name().unwrap());
        for (i, parent) in commit.parents().enumerate() {
            if let Ok(parent_entry) = parent.tree()?.get_path(&path) {
                if parent_entry.id() == entry.id() {
                    // Item at `path` is the same in the tree for `commit` as in the tree
                    // for `parent`, so the blame must be the same too
                    let blame_parent_entry = blame_parents[i].tree()?.get_path(&path)?;
                    builder.insert(
                        entry.name().unwrap(),
                        blame_parent_entry.id(),
                        entry.filemode(),
                    )?;
                    path.pop();
                    continue 'outer;
                }
            }
        }

        match entry.kind() {
            Some(ObjectType::Blob) => {
                let blame_text = blame_for_path(
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    &path,
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    blame_repo.blob(&blame_text.as_bytes())?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Commit) => {
                // This is a submodule, just treat it as an empty dir. We could
                // probably also skip over it entirely.
                let entry_builder = blame_repo.treebuilder(None)?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Tree) => {
                let mut entry_builder = blame_repo.treebuilder(None)?;
                build_blame_tree(
                    &mut entry_builder,
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    path.clone(),
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            _ => {
                panic!(
                    "Unexpected entry kind {:?} found in tree for commit {:?} at path {:?}",
                    entry.kind(),
                    commit.id(),
                    path
                );
            }
        };

        path.pop();
    }

    Ok(())
}

fn main() {
    env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init();

    let args: Vec<_> = env::args().collect();
    let git_repo = Repository::open(&args[1]).unwrap();
    let blame_repo = Repository::open(&args[2]).unwrap();
    let use_cinnabar = env::var("CINNABAR").map_or(true, |v| v != "0");
    let mut hg_helper = if use_cinnabar {
        Some(start_cinnabar_helper(&git_repo))
    } else {
        None
    };
    let blame_ref = env::var("BLAME_REF").ok().unwrap_or("HEAD".to_string());

    info!("Reading existing blame map of ref {}...", blame_ref);
    let mut blame_map = if let Ok(oid) = blame_repo.refname_to_id(&blame_ref) {
        let (blame_map, _) = index_blame(&blame_repo, Some(oid));
        blame_map
    } else {
        HashMap::new()
    };

    let mut refobj = blame_repo
        .find_reference(&blame_ref)
        .ok()
        .and_then(|r| r.resolve().ok());

    let mut walk = git_repo.revwalk().unwrap();
    walk.set_sorting(Sort::TOPOLOGICAL | Sort::REVERSE).unwrap();
    walk.push(git_repo.refname_to_id(&blame_ref).unwrap())
        .unwrap();
    let revs_to_process = walk
        .map(|r| r.unwrap()) // walk produces Result<git2::Oid> so we unwrap to just the Oid
        .filter(|git_oid| !blame_map.contains_key(git_oid))
        .collect::<Vec<_>>();
    let rev_count = revs_to_process.len();

    info!("Transforming new commits...");

    let mut rev_done = 0;
    for git_oid in revs_to_process {
        rev_done += 1;

        let hg_rev = match hg_helper {
            Some(ref mut helper) => get_hg_rev(helper, &git_oid),
            None => None, // we don't support mapfiles any more.
        };

        info!(
            "Transforming {} (hg {:?}) progress {}/{}",
            git_oid, hg_rev, rev_done, rev_count
        );
        let commit = git_repo.find_commit(git_oid).unwrap();
        let blame_parents = commit
            .parent_ids()
            .map(|pid| blame_repo.find_commit(blame_map[&pid]).unwrap())
            .collect::<Vec<_>>();

        let mut file_movement = HashMap::new();
        if commit.parent_count() == 1 {
            let mut movement = HashMap::new();
            let mut diff = git_repo
                .diff_tree_to_tree(
                    Some(&commit.parent(0).unwrap().tree().unwrap()),
                    Some(&commit.tree().unwrap()),
                    None,
                )
                .unwrap();
            diff.find_similar(Some(
                DiffFindOptions::new().copies(true).rename_limit(1000000),
            ))
            .unwrap();
            for delta in diff.deltas() {
                if !delta.old_file().id().is_zero()
                    && !delta.new_file().id().is_zero()
                    && delta.old_file().path() != delta.new_file().path()
                {
                    movement.insert(
                        delta.new_file().id(),
                        delta.old_file().path().unwrap().to_path_buf(),
                    );
                }
            }
            file_movement.insert(commit.parent_id(0).unwrap(), movement);
        }

        let mut builder = blame_repo.treebuilder(None).unwrap();
        build_blame_tree(
            &mut builder,
            &file_movement,
            &git_repo,
            &commit,
            &blame_repo,
            &blame_parents,
            PathBuf::new(),
        )
        .unwrap();
        let tree_oid = builder.write().unwrap();

        let commit_msg = if let Some(hg_rev) = hg_rev {
            format!("git {}\nhg {}\n", git_oid, hg_rev)
        } else {
            format!("git {}\n", git_oid)
        };

        let commit_ref = if refobj.is_some() {
            None
        } else {
            // This should only happen on the first commit, if the blame_ref
            // doesn't exist yet in the destination repo
            Some(blame_ref.as_str())
        };
        let blame_oid = blame_repo
            .commit(
                commit_ref,
                &commit.author(),
                &commit.committer(),
                &commit_msg,
                &blame_repo.find_tree(tree_oid).unwrap(),
                &blame_parents.iter().collect::<Vec<_>>(),
            )
            .unwrap();

        if let Some(ref mut refobj) = refobj {
            *refobj = refobj.set_target(blame_oid, "").unwrap();
        } else {
            refobj = blame_repo
                .find_reference(&blame_ref)
                .ok()
                .and_then(|r| r.resolve().ok());
            assert!(refobj.is_some());
        }

        blame_map.insert(git_oid, blame_oid);
        info!("  -> {}", blame_oid);
    }

    if let Some(mut helper) = hg_helper {
        helper.kill().unwrap();
    }
}
<commit_msg>Bug 1634772 - Use new cinnabar batch API instead of cinnabar-helper<commit_after>extern crate env_logger;
extern crate git2;
#[macro_use]
extern crate log;
extern crate tools;
extern crate unicode_normalization;

use std::borrow::Borrow;
use std::collections::HashMap;
use std::env;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};

use git2::{DiffFindOptions, ObjectType, Oid, Patch, Repository, Sort};
use tools::config::index_blame;
use unicode_normalization::UnicodeNormalization;

fn get_hg_rev(helper: &mut Child, git_oid: &Oid) -> Option<String> {
    write!(helper.stdin.as_mut().unwrap(), "{}\n", git_oid).unwrap();
    let mut reader = BufReader::new(helper.stdout.as_mut().unwrap());
    let mut result = String::new();
    reader.read_line(&mut result).unwrap();
    let hgrev = result.trim();
    if hgrev.chars().all(|c| c == '0') {
        return None;
    }
    return Some(hgrev.to_string());
}

fn start_cinnabar_helper(git_repo: &Repository) -> Child {
    Command::new("git")
        .arg("cinnabar")
        .arg("git2hg")
        .arg("--batch")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(git_repo.path())
        .spawn()
        .unwrap()
}

fn count_lines(blob: &git2::Blob) -> usize {
    let data = blob.content();
    if data.is_empty() {
        return 0;
    }
    let mut linecount = 0;
    for b in data {
        if *b == b'\n' {
            linecount += 1;
        }
    }
    if data[data.len() - 1] != b'\n' {
        linecount += 1;
    }
    linecount
}

fn unmodified_lines(
    blob: &git2::Blob,
    parent_blob: &git2::Blob,
) -> Result<Vec<(usize, usize)>, git2::Error> {
    let mut unchanged = Vec::new();

    let patch = Patch::from_blobs(parent_blob, None, blob, None, None)?;

    if patch.delta().flags().is_binary() {
        return Ok(unchanged);
    }

    fn add_delta(lineno: usize, delta: i32) -> usize {
        ((lineno as i32) + delta) as usize
    }

    let mut latest_line: usize = 0;
    let mut delta: i32 = 0;

    for hunk_index in 0..patch.num_hunks() {
        for line_index in 0..patch.num_lines_in_hunk(hunk_index)? {
            let line = patch.line_in_hunk(hunk_index, line_index)?;

            if let Some(lineno) = line.new_lineno() {
                let lineno = lineno as usize;
                for i in latest_line..lineno - 1 {
                    unchanged.push((i, add_delta(i, delta)));
                }
                latest_line = (lineno - 1) + 1;
            }

            match line.origin() {
                '+' => delta -= 1,
                '-' => delta += 1,
                ' ' => {
                    assert_eq!(
                        line.old_lineno().unwrap() as usize,
                        add_delta(line.new_lineno().unwrap() as usize, delta)
                    );
                    unchanged.push((
                        (line.new_lineno().unwrap() - 1) as usize,
                        (line.old_lineno().unwrap() - 1) as usize,
                    ));
                }
                _ => (),
            };
        }
    }

    let linecount = count_lines(blob);
    for i in latest_line..linecount {
        unchanged.push((i, add_delta(i, delta)));
    }
    Ok(unchanged)
}

fn blame_for_path(
    file_movement: &HashMap<Oid, HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    path: &Path,
) -> Result<String, git2::Error> {
    let blob = commit
        .tree()?
        .get_path(path)?
        .to_object(git_repo)?
        .peel_to_blob()?;
    let linecount = count_lines(&blob);
    // TODO: drop this author field entirely, I don't think it gets consumed by anything
    let commit_id = commit.id();
    let author = commit
        .author()
        .name()
        .map(|n| n.nfkd().filter(|c| c.is_ascii()).collect::<String>())
        .unwrap_or_default();
    let mut blame = Vec::with_capacity(linecount);
    for line in 1..=linecount {
        blame.push(format!("{}:%:{}:{}", commit_id, line, author));
    }

    for (parent, blame_parent) in commit.parents().zip(blame_parents.iter()).rev() {
        let parent_path = file_movement
            .get(&parent.id())
            .and_then(|m| m.get(&blob.id()))
            .map(|p| p.borrow())
            .unwrap_or(path);
        let parent_blob = match parent.tree()?.get_path(parent_path) {
            Ok(t) if t.kind() == Some(ObjectType::Blob) => t.to_object(git_repo)?.peel_to_blob()?,
            _ => {
                continue;
            }
        };
        let parent_blame_blob = match blame_parent.tree()?.get_path(parent_path) {
            Ok(entry) => entry.to_object(blame_repo)?.peel_to_blob()?,
            _ => continue,
        };
        let parent_blame = std::str::from_utf8(parent_blame_blob.content())
            .unwrap() // We only ever put ascii in the blame blob (for now)
            .lines()
            .collect::<Vec<&str>>();

        let path_unchanged = path == parent_path;
        for (lineno, parent_lineno) in unmodified_lines(&blob, &parent_blob)? {
            if path_unchanged {
                blame[lineno] = String::from(parent_blame[parent_lineno]);
                continue;
            }
            let mut pieces = parent_blame[parent_lineno].splitn(4, ':');
            let p_rev = pieces.next().unwrap();
            let mut p_fname = pieces.next().unwrap();
            let p_lineno = pieces.next().unwrap();
            let p_author = pieces.next().unwrap();
            if p_fname == "%" {
                p_fname = parent_path.to_str().unwrap();
            }
            blame[lineno] = format!("{}:{}:{}:{}", p_rev, p_fname, p_lineno, p_author);
        }
    }
    // Extra entry so the `join` call after adds a trailing newline
    blame.push(String::new());
    Ok(blame.join("\n"))
}

fn build_blame_tree(
    builder: &mut git2::TreeBuilder,
    file_movement: &HashMap<Oid, HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    mut path: PathBuf,
) -> Result<(), git2::Error> {
    let tree_at_path = if path == PathBuf::new() {
        commit.tree()?
    } else {
        commit
            .tree()?
            .get_path(&path)?
            .to_object(git_repo)?
            .peel_to_tree()?
    };
    'outer: for entry in tree_at_path.iter() {
        path.push(entry.name().unwrap());
        for (i, parent) in commit.parents().enumerate() {
            if let Ok(parent_entry) = parent.tree()?.get_path(&path) {
                if parent_entry.id() == entry.id() {
                    // Item at `path` is the same in the tree for `commit` as in the tree
                    // for `parent`, so the blame must be the same too
                    let blame_parent_entry = blame_parents[i].tree()?.get_path(&path)?;
                    builder.insert(
                        entry.name().unwrap(),
                        blame_parent_entry.id(),
                        entry.filemode(),
                    )?;
                    path.pop();
                    continue 'outer;
                }
            }
        }

        match entry.kind() {
            Some(ObjectType::Blob) => {
                let blame_text = blame_for_path(
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    &path,
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    blame_repo.blob(&blame_text.as_bytes())?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Commit) => {
                // This is a submodule, just treat it as an empty dir. We could
                // probably also skip over it entirely.
                let entry_builder = blame_repo.treebuilder(None)?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Tree) => {
                let mut entry_builder = blame_repo.treebuilder(None)?;
                build_blame_tree(
                    &mut entry_builder,
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    path.clone(),
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            _ => {
                panic!(
                    "Unexpected entry kind {:?} found in tree for commit {:?} at path {:?}",
                    entry.kind(),
                    commit.id(),
                    path
                );
            }
        };

        path.pop();
    }

    Ok(())
}

fn main() {
    env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init();

    let args: Vec<_> = env::args().collect();
    let git_repo = Repository::open(&args[1]).unwrap();
    let blame_repo = Repository::open(&args[2]).unwrap();
    let use_cinnabar = env::var("CINNABAR").map_or(true, |v| v != "0");
    let mut hg_helper = if use_cinnabar {
        Some(start_cinnabar_helper(&git_repo))
    } else {
        None
    };
    let blame_ref = env::var("BLAME_REF").ok().unwrap_or("HEAD".to_string());

    info!("Reading existing blame map of ref {}...", blame_ref);
    let mut blame_map = if let Ok(oid) = blame_repo.refname_to_id(&blame_ref) {
        let (blame_map, _) = index_blame(&blame_repo, Some(oid));
        blame_map
    } else {
        HashMap::new()
    };

    let mut refobj = blame_repo
        .find_reference(&blame_ref)
        .ok()
        .and_then(|r| r.resolve().ok());

    let mut walk = git_repo.revwalk().unwrap();
    walk.set_sorting(Sort::TOPOLOGICAL | Sort::REVERSE).unwrap();
    walk.push(git_repo.refname_to_id(&blame_ref).unwrap())
        .unwrap();
    let revs_to_process = walk
        .map(|r| r.unwrap()) // walk produces Result<git2::Oid> so we unwrap to just the Oid
        .filter(|git_oid| !blame_map.contains_key(git_oid))
        .collect::<Vec<_>>();
    let rev_count = revs_to_process.len();

    info!("Transforming new commits...");

    let mut rev_done = 0;
    for git_oid in revs_to_process {
        rev_done += 1;

        let hg_rev = match hg_helper {
            Some(ref mut helper) => get_hg_rev(helper, &git_oid),
            None => None, // we don't support mapfiles any more.
        };

        info!(
            "Transforming {} (hg {:?}) progress {}/{}",
            git_oid, hg_rev, rev_done, rev_count
        );
        let commit = git_repo.find_commit(git_oid).unwrap();
        let blame_parents = commit
            .parent_ids()
            .map(|pid| blame_repo.find_commit(blame_map[&pid]).unwrap())
            .collect::<Vec<_>>();

        let mut file_movement = HashMap::new();
        if commit.parent_count() == 1 {
            let mut movement = HashMap::new();
            let mut diff = git_repo
                .diff_tree_to_tree(
                    Some(&commit.parent(0).unwrap().tree().unwrap()),
                    Some(&commit.tree().unwrap()),
                    None,
                )
                .unwrap();
            diff.find_similar(Some(
                DiffFindOptions::new().copies(true).rename_limit(1000000),
            ))
            .unwrap();
            for delta in diff.deltas() {
                if !delta.old_file().id().is_zero()
                    && !delta.new_file().id().is_zero()
                    && delta.old_file().path() != delta.new_file().path()
                {
                    movement.insert(
                        delta.new_file().id(),
                        delta.old_file().path().unwrap().to_path_buf(),
                    );
                }
            }
            file_movement.insert(commit.parent_id(0).unwrap(), movement);
        }

        let mut builder = blame_repo.treebuilder(None).unwrap();
        build_blame_tree(
            &mut builder,
            &file_movement,
            &git_repo,
            &commit,
            &blame_repo,
            &blame_parents,
            PathBuf::new(),
        )
        .unwrap();
        let tree_oid = builder.write().unwrap();

        let commit_msg = if let Some(hg_rev) = hg_rev {
            format!("git {}\nhg {}\n", git_oid, hg_rev)
        } else {
            format!("git {}\n", git_oid)
        };

        let commit_ref = if refobj.is_some() {
            None
        } else {
            // This should only happen on the first commit, if the blame_ref
            // doesn't exist yet in the destination repo
            Some(blame_ref.as_str())
        };
        let blame_oid = blame_repo
            .commit(
                commit_ref,
                &commit.author(),
                &commit.committer(),
                &commit_msg,
                &blame_repo.find_tree(tree_oid).unwrap(),
                &blame_parents.iter().collect::<Vec<_>>(),
            )
            .unwrap();

        if let Some(ref mut refobj) = refobj {
            *refobj = refobj.set_target(blame_oid, "").unwrap();
        } else {
            refobj = blame_repo
                .find_reference(&blame_ref)
                .ok()
                .and_then(|r| r.resolve().ok());
            assert!(refobj.is_some());
        }

        blame_map.insert(git_oid, blame_oid);
        info!("  -> {}", blame_oid);
    }

    if let Some(mut helper) = hg_helper {
        helper.kill().unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Alexander Reece
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::str;
use nom::IResult;
use nom::{be_u8, be_u32};

named!(pub bool_bit<(&[u8], usize), bool>,
    map!(take_bits!(u8, 1), |b: u8| -> bool { b != 0 })
);

named!(pub shortstr<&str>, map_res!(
    length_bytes!(be_u8),
    str::from_utf8
));

named!(pub longstr, length_bytes!(be_u32));<commit_msg>[nom-parser] Remove unused nom::IResult from common.rs<commit_after>// Copyright 2016 Alexander Reece
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::str;
use nom::{be_u8, be_u32};

named!(pub bool_bit<(&[u8], usize), bool>,
    map!(take_bits!(u8, 1), |b: u8| -> bool { b != 0 })
);

named!(pub shortstr<&str>, map_res!(
    length_bytes!(be_u8),
    str::from_utf8
));

named!(pub longstr, length_bytes!(be_u32));<|endoftext|>


<commit_before>use crate::config::Config;
use crate::cpp_checker::cpp_checker_step;
use crate::cpp_explicit_xstructors::add_explicit_xstructors_step;
use crate::cpp_ffi_generator::cpp_ffi_generator_step;
use crate::cpp_parser::cpp_parser_step;
use crate::cpp_template_instantiator::find_template_instantiations_step;
use crate::cpp_template_instantiator::instantiate_templates_step;
use crate::crate_writer::crate_writer_step;
use crate::database::{CppDatabaseItem, Database};
use crate::rust_generator::clear_rust_info_step;
use crate::rust_generator::rust_generator_step;
use crate::type_allocation_places::choose_allocation_places_step;
use crate::workspace::Workspace;
use itertools::Itertools;
use log::{debug, error, info};
use ritual_common::errors::{bail, err_msg, Result, ResultExt};
use ritual_common::utils::MapIfOk;
use std::cmp::Ordering;
use std::fmt;
use std::iter::once;
use std::path::PathBuf;
use std::time::Instant;

/// Creates output and cache directories if they don't exist.
/// Returns `Err` if any path in `config` is invalid or relative.
fn check_all_paths(config: &Config) -> Result<()> {
    let check_dir = |path: &PathBuf| -> Result<()> {
        if !path.is_absolute() {
            bail!(
                "Only absolute paths allowed. Relative path: {}",
                path.display()
            );
        }
        if !path.exists() {
            bail!("Directory doesn't exist: {}", path.display());
        }
        if !path.is_dir() {
            bail!("Path is not a directory: {}", path.display());
        }
        Ok(())
    };

    if let Some(path) = config.crate_template_path() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().include_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().lib_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().framework_paths() {
        check_dir(path)?;
    }
    for path in config.target_include_paths() {
        check_dir(path)?;
    }
    Ok(())
}

pub struct ProcessorData<'a> {
    pub workspace: &'a mut Workspace,
    pub config: &'a Config,
    pub current_database: &'a mut Database,
    pub dep_databases: &'a [Database],
}

impl<'a> ProcessorData<'a> {
    pub fn all_databases(&self) -> Vec<&Database> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .collect()
    }
    pub fn all_items(&self) -> Vec<&CppDatabaseItem> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .flat_map(|d| d.cpp_items.iter())
            .collect()
    }
}

pub struct ProcessingStep {
    pub name: String,
    pub is_const: bool,
    pub function: Box<Fn(&mut ProcessorData) -> Result<()>>,
}

impl fmt::Debug for ProcessingStep {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ProcessingStep")
            .field("name", &self.name)
            .field("is_const", &self.is_const)
            .finish()
    }
}

#[derive(Debug)]
pub struct ProcessingSteps {
    all_steps: Vec<ProcessingStep>,
    main_procedure: Vec<String>,
}

impl Default for ProcessingSteps {
    fn default() -> Self {
        let main_steps = vec![
            cpp_parser_step(),
            add_explicit_xstructors_step(),
            choose_allocation_places_step(),
            find_template_instantiations_step(),
            instantiate_templates_step(),
            cpp_ffi_generator_step(),
            // TODO: generate_slot_wrappers
            cpp_checker_step(),
            rust_generator_step(),
            crate_writer_step(),
            steps::build_crate(),
        ];

        let main_procedure = main_steps.iter().map(|s| s.name.clone()).collect();

        let mut all_steps = main_steps;
        all_steps.extend(vec![
            steps::print_database(),
            steps::clear_ffi(),
            clear_rust_info_step(),
        ]);
        Self {
            all_steps,
            main_procedure,
        }
    }
}

impl ProcessingSteps {
    pub fn add_after(&mut self, after: &[&str], step: ProcessingStep) -> Result<()> {
        let indexes: Vec<usize> = after.iter().map_if_ok(|s| {
            self.main_procedure
                .iter()
                .position(|a| a == s)
                .ok_or_else(|| err_msg(format!("requested step not found: {}", s)))
        })?;

        let max_index = indexes
            .into_iter()
            .max()
            .ok_or_else(|| err_msg("no steps provided"))?;
        self.main_procedure.insert(max_index + 1, step.name.clone());
        self.all_steps.push(step);
        Ok(())
    }
}

impl ProcessingStep {
    pub fn new<S: Into<String>, F: 'static + Fn(&mut ProcessorData) -> Result<()>>(
        name: S,
        function: F,
    ) -> ProcessingStep {
        ProcessingStep {
            name: name.into(),
            is_const: false,
            function: Box::new(function),
        }
    }
    pub fn new_const<S: Into<String>, F: 'static + Fn(&mut ProcessorData) -> Result<()>>(
        name: S,
        function: F,
    ) -> ProcessingStep {
        let name = name.into();
        ProcessingStep {
            name,
            is_const: true,
            function: Box::new(function),
        }
    }
}

mod steps {
    use crate::processor::ProcessingStep;
    use log::trace;
    use ritual_common::utils::run_command;
    use std::process::Command;

    pub fn print_database() -> ProcessingStep {
        ProcessingStep::new_const("print_database", |data| {
            for item in &data.current_database.cpp_items {
                trace!(
                    "[database_item] cpp_data={}; source={:?}",
                    item.cpp_data.to_string(),
                    item.source
                );
                for ffi_item in &item.ffi_items {
                    trace!("[database_item]   ffi_item={:?}", ffi_item);
                }
            }
            Ok(())
        })
    }

    pub fn clear_ffi() -> ProcessingStep {
        ProcessingStep::new("clear_ffi", |data| {
            for item in &mut data.current_database.cpp_items {
                item.ffi_items.clear();
                item.is_cpp_ffi_processed = false;
            }
            Ok(())
        })
    }

    pub fn build_crate() -> ProcessingStep {
        ProcessingStep::new_const("build_crate", |data| {
            let path = data
                .workspace
                .crate_path(&data.current_database.crate_name)?;
            for cargo_cmd in &["update", "build", "test", "doc"] {
                let mut command = Command::new("cargo");
                command.arg(cargo_cmd);
                command.current_dir(&path);
                run_command(&mut command)?;
            }
            Ok(())
        })
    }
}

#[derive(Debug)]
struct MainItemRef<'a> {
    step: &'a ProcessingStep,
    run_after: &'a [String],
}

impl PartialEq for MainItemRef<'_> {
    fn eq(&self, other: &MainItemRef) -> bool {
        self.step.name == other.step.name
    }
}

impl PartialOrd for MainItemRef<'_> {
    fn partial_cmp(&self, other: &MainItemRef) -> Option<Ordering> {
        if self.run_after.contains(&other.step.name) {
            Some(Ordering::Greater)
        } else {
            Some(Ordering::Less)
        }
    }
}
impl Eq for MainItemRef<'_> {}
impl Ord for MainItemRef<'_> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

#[allow(clippy::useless_let_if_seq)]
pub fn process(workspace: &mut Workspace, config: &Config, step_names: &[String]) -> Result<()> {
    info!("Processing crate: {}", config.crate_properties().name());
    check_all_paths(&config)?;

    // TODO: allow to remove any prefix through `Config` (#25)

    info!("Loading current crate data");
    let mut current_database = workspace
        .load_or_create_crate(config.crate_properties().name())
        .with_context(|_| "failed to load current crate data")?;

    if !config.dependent_cpp_crates().is_empty() {
        info!("Loading dependencies");
    }
    let dependent_cpp_crates = config.dependent_cpp_crates().iter().map_if_ok(|name| {
        workspace
            .load_crate(name)
            .with_context(|_| "failed to load dependency")
    })?;

    let mut current_database_saved = true;

    if current_database.crate_version != config.crate_properties().version() {
        current_database.crate_version = config.crate_properties().version().to_string();
        current_database_saved = false;
    }

    let mut steps_result = Ok(());

    for step_name in step_names {
        if steps_result.is_err() {
            break;
        }

        let step_names = if step_name == "main" {
            config.processing_steps().main_procedure.clone()
        } else if step_name.starts_with("from:") {
            let start_step = &step_name["from:".len()..];
            let start_index = config
                .processing_steps()
                .main_procedure
                .iter()
                .position(|s| s == start_step)
                .ok_or_else(|| err_msg(format!("requested step not found: {}", start_step)))?;
            config.processing_steps().main_procedure[start_index..].to_vec()
        } else {
            vec![step_name.to_string()]
        };

        for step_name in step_names {
            let step = if let Some(item) = config
                .processing_steps()
                .all_steps
                .iter()
                .find(|item| item.name == step_name)
            {
                item
            } else {
                bail!(
                    "Unknown operation: {}. Supported operations: main, {}.",
                    step_name,
                    config
                        .processing_steps()
                        .all_steps
                        .iter()
                        .map(|item| &item.name)
                        .join(", ")
                );
            };

            info!("Running processing step: {}", &step.name);

            let mut data = ProcessorData {
                workspace,
                current_database: &mut current_database,
                dep_databases: &dependent_cpp_crates,
                config,
            };

            if !step.is_const {
                current_database_saved = false;
            }

            let started_time = Instant::now();

            if let Err(err) = (step.function)(&mut data) {
                steps_result = Err(err);
                error!("Step failed! Aborting...");
                break;
            }

            debug!(
                "Step '{}' completed in {:?}",
                step.name,
                started_time.elapsed()
            );
        }
    }

    for database in dependent_cpp_crates {
        workspace.put_crate(database, true);
    }

    if !current_database_saved {
        info!("Saving data");
    }

    workspace.put_crate(current_database, current_database_saved);
    workspace.save_data()?;

    steps_result
}
<commit_msg>support until:step specifier<commit_after>use crate::config::Config;
use crate::cpp_checker::cpp_checker_step;
use crate::cpp_explicit_xstructors::add_explicit_xstructors_step;
use crate::cpp_ffi_generator::cpp_ffi_generator_step;
use crate::cpp_parser::cpp_parser_step;
use crate::cpp_template_instantiator::find_template_instantiations_step;
use crate::cpp_template_instantiator::instantiate_templates_step;
use crate::crate_writer::crate_writer_step;
use crate::database::{CppDatabaseItem, Database};
use crate::rust_generator::clear_rust_info_step;
use crate::rust_generator::rust_generator_step;
use crate::type_allocation_places::choose_allocation_places_step;
use crate::workspace::Workspace;
use itertools::Itertools;
use log::{debug, error, info};
use ritual_common::errors::{bail, err_msg, Result, ResultExt};
use ritual_common::utils::MapIfOk;
use std::cmp::Ordering;
use std::fmt;
use std::iter::once;
use std::path::PathBuf;
use std::time::Instant;

/// Creates output and cache directories if they don't exist.
/// Returns `Err` if any path in `config` is invalid or relative.
fn check_all_paths(config: &Config) -> Result<()> {
    let check_dir = |path: &PathBuf| -> Result<()> {
        if !path.is_absolute() {
            bail!(
                "Only absolute paths allowed. Relative path: {}",
                path.display()
            );
        }
        if !path.exists() {
            bail!("Directory doesn't exist: {}", path.display());
        }
        if !path.is_dir() {
            bail!("Path is not a directory: {}", path.display());
        }
        Ok(())
    };

    if let Some(path) = config.crate_template_path() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().include_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().lib_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().framework_paths() {
        check_dir(path)?;
    }
    for path in config.target_include_paths() {
        check_dir(path)?;
    }
    Ok(())
}

pub struct ProcessorData<'a> {
    pub workspace: &'a mut Workspace,
    pub config: &'a Config,
    pub current_database: &'a mut Database,
    pub dep_databases: &'a [Database],
}

impl<'a> ProcessorData<'a> {
    pub fn all_databases(&self) -> Vec<&Database> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .collect()
    }
    pub fn all_items(&self) -> Vec<&CppDatabaseItem> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .flat_map(|d| d.cpp_items.iter())
            .collect()
    }
}

pub struct ProcessingStep {
    pub name: String,
    pub is_const: bool,
    pub function: Box<Fn(&mut ProcessorData) -> Result<()>>,
}

impl fmt::Debug for ProcessingStep {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ProcessingStep")
            .field("name", &self.name)
            .field("is_const", &self.is_const)
            .finish()
    }
}

#[derive(Debug)]
pub struct ProcessingSteps {
    all_steps: Vec<ProcessingStep>,
    main_procedure: Vec<String>,
}

impl Default for ProcessingSteps {
    fn default() -> Self {
        let main_steps = vec![
            cpp_parser_step(),
            add_explicit_xstructors_step(),
            choose_allocation_places_step(),
            find_template_instantiations_step(),
            instantiate_templates_step(),
            cpp_ffi_generator_step(),
            // TODO: generate_slot_wrappers
            cpp_checker_step(),
            rust_generator_step(),
            crate_writer_step(),
            steps::build_crate(),
        ];

        let main_procedure = main_steps.iter().map(|s| s.name.clone()).collect();

        let mut all_steps = main_steps;
        all_steps.extend(vec![
            steps::print_database(),
            steps::clear_ffi(),
            clear_rust_info_step(),
        ]);
        Self {
            all_steps,
            main_procedure,
        }
    }
}

impl ProcessingSteps {
    pub fn add_after(&mut self, after: &[&str], step: ProcessingStep) -> Result<()> {
        let indexes: Vec<usize> = after.iter().map_if_ok(|s| {
            self.main_procedure
                .iter()
                .position(|a| a == s)
                .ok_or_else(|| err_msg(format!("requested step not found: {}", s)))
        })?;

        let max_index = indexes
            .into_iter()
            .max()
            .ok_or_else(|| err_msg("no steps provided"))?;
        self.main_procedure.insert(max_index + 1, step.name.clone());
        self.all_steps.push(step);
        Ok(())
    }
}

impl ProcessingStep {
    pub fn new<S: Into<String>, F: 'static + Fn(&mut ProcessorData) -> Result<()>>(
        name: S,
        function: F,
    ) -> ProcessingStep {
        ProcessingStep {
            name: name.into(),
            is_const: false,
            function: Box::new(function),
        }
    }
    pub fn new_const<S: Into<String>, F: 'static + Fn(&mut ProcessorData) -> Result<()>>(
        name: S,
        function: F,
    ) -> ProcessingStep {
        let name = name.into();
        ProcessingStep {
            name,
            is_const: true,
            function: Box::new(function),
        }
    }
}

mod steps {
    use crate::processor::ProcessingStep;
    use log::trace;
    use ritual_common::utils::run_command;
    use std::process::Command;

    pub fn print_database() -> ProcessingStep {
        ProcessingStep::new_const("print_database", |data| {
            for item in &data.current_database.cpp_items {
                trace!(
                    "[database_item] cpp_data={}; source={:?}",
                    item.cpp_data.to_string(),
                    item.source
                );
                for ffi_item in &item.ffi_items {
                    trace!("[database_item]   ffi_item={:?}", ffi_item);
                }
            }
            Ok(())
        })
    }

    pub fn clear_ffi() -> ProcessingStep {
        ProcessingStep::new("clear_ffi", |data| {
            for item in &mut data.current_database.cpp_items {
                item.ffi_items.clear();
                item.is_cpp_ffi_processed = false;
            }
            Ok(())
        })
    }

    pub fn build_crate() -> ProcessingStep {
        ProcessingStep::new_const("build_crate", |data| {
            let path = data
                .workspace
                .crate_path(&data.current_database.crate_name)?;
            for cargo_cmd in &["update", "build", "test", "doc"] {
                let mut command = Command::new("cargo");
                command.arg(cargo_cmd);
                command.current_dir(&path);
                run_command(&mut command)?;
            }
            Ok(())
        })
    }
}

#[derive(Debug)]
struct MainItemRef<'a> {
    step: &'a ProcessingStep,
    run_after: &'a [String],
}

impl PartialEq for MainItemRef<'_> {
    fn eq(&self, other: &MainItemRef) -> bool {
        self.step.name == other.step.name
    }
}

impl PartialOrd for MainItemRef<'_> {
    fn partial_cmp(&self, other: &MainItemRef) -> Option<Ordering> {
        if self.run_after.contains(&other.step.name) {
            Some(Ordering::Greater)
        } else {
            Some(Ordering::Less)
        }
    }
}
impl Eq for MainItemRef<'_> {}
impl Ord for MainItemRef<'_> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

#[allow(clippy::useless_let_if_seq)]
pub fn process(workspace: &mut Workspace, config: &Config, step_names: &[String]) -> Result<()> {
    info!("Processing crate: {}", config.crate_properties().name());
    check_all_paths(&config)?;

    // TODO: allow to remove any prefix through `Config` (#25)

    info!("Loading current crate data");
    let mut current_database = workspace
        .load_or_create_crate(config.crate_properties().name())
        .with_context(|_| "failed to load current crate data")?;

    if !config.dependent_cpp_crates().is_empty() {
        info!("Loading dependencies");
    }
    let dependent_cpp_crates = config.dependent_cpp_crates().iter().map_if_ok(|name| {
        workspace
            .load_crate(name)
            .with_context(|_| "failed to load dependency")
    })?;

    let mut current_database_saved = true;

    if current_database.crate_version != config.crate_properties().version() {
        current_database.crate_version = config.crate_properties().version().to_string();
        current_database_saved = false;
    }

    let mut steps_result = Ok(());

    for step_name in step_names {
        if steps_result.is_err() {
            break;
        }

        let step_names = if step_name == "main" {
            config.processing_steps().main_procedure.clone()
        } else if step_name.starts_with("from:") {
            let start_step = &step_name["from:".len()..];
            let start_index = config
                .processing_steps()
                .main_procedure
                .iter()
                .position(|s| s == start_step)
                .ok_or_else(|| err_msg(format!("requested step not found: {}", start_step)))?;
            config.processing_steps().main_procedure[start_index..].to_vec()
        } else if step_name.starts_with("until:") {
            let end_step = &step_name["until:".len()..];
            let end_index = config
                .processing_steps()
                .main_procedure
                .iter()
                .position(|s| s == end_step)
                .ok_or_else(|| err_msg(format!("requested step not found: {}", end_step)))?;
            config.processing_steps().main_procedure[..end_index].to_vec()
        } else {
            vec![step_name.to_string()]
        };

        for step_name in step_names {
            let step = if let Some(item) = config
                .processing_steps()
                .all_steps
                .iter()
                .find(|item| item.name == step_name)
            {
                item
            } else {
                bail!(
                    "Unknown operation: {}. Supported operations: main, {}.",
                    step_name,
                    config
                        .processing_steps()
                        .all_steps
                        .iter()
                        .map(|item| &item.name)
                        .join(", ")
                );
            };

            info!("Running processing step: {}", &step.name);

            let mut data = ProcessorData {
                workspace,
                current_database: &mut current_database,
                dep_databases: &dependent_cpp_crates,
                config,
            };

            if !step.is_const {
                current_database_saved = false;
            }

            let started_time = Instant::now();

            if let Err(err) = (step.function)(&mut data) {
                steps_result = Err(err);
                error!("Step failed! Aborting...");
                break;
            }

            debug!(
                "Step '{}' completed in {:?}",
                step.name,
                started_time.elapsed()
            );
        }
    }

    for database in dependent_cpp_crates {
        workspace.put_crate(database, true);
    }

    if !current_database_saved {
        info!("Saving data");
    }

    workspace.put_crate(current_database, current_database_saved);
    workspace.save_data()?;

    steps_result
}
<|endoftext|>


<commit_before>
/// Information about the operator being constructed
pub struct OperatorInfo {
    /// Scope-local index assigned to the operator being constructed.
    pub local_id: usize,
    /// Worker-unique identifier.
    pub global_id: usize,
    /// Operator address.
    pub address: Vec<usize>,
}

impl OperatorInfo {
    /// Construct a new `OperatorInfo`.
    pub fn new(local_id: usize, global_id: usize, address: &[usize]) -> OperatorInfo {
        OperatorInfo {
            local_id,
            global_id,
            address: address.to_vec(),
        }
    }
}
<commit_msg>Derive impl of Clone for OperatorInfo<commit_after>
/// Information about the operator being constructed
#[derive(Clone)]
pub struct OperatorInfo {
    /// Scope-local index assigned to the operator being constructed.
    pub local_id: usize,
    /// Worker-unique identifier.
    pub global_id: usize,
    /// Operator address.
    pub address: Vec<usize>,
}

impl OperatorInfo {
    /// Construct a new `OperatorInfo`.
    pub fn new(local_id: usize, global_id: usize, address: &[usize]) -> OperatorInfo {
        OperatorInfo {
            local_id,
            global_id,
            address: address.to_vec(),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::ptr;
use std::sync::Arc;
use smallvec::SmallVec;

use command_buffer::RawCommandBufferPrototype;
use command_buffer::CommandsList;
use command_buffer::CommandsListSink;
use descriptor::descriptor_set::DescriptorSetsCollection;
use descriptor::pipeline_layout::PipelineLayoutRef;
use descriptor::pipeline_layout::PipelineLayoutSetsCompatible;
use device::Device;
use pipeline::ComputePipeline;
use pipeline::GraphicsPipeline;
use VulkanObject;
use VulkanPointers;
use vk;

/// Wraps around a commands list and adds at the end of it a command that binds a pipeline layout and descriptor
/// sets at the end of it.
pub struct CmdBindDescriptorSets<L, S, P> where L: CommandsList {
    // Parent commands list.
    previous: L,
    // The raw Vulkan enum representing the kind of pipeline.
    pipeline_ty: vk::PipelineBindPoint,
    // The raw pipeline object to bind.
    raw_pipeline_layout: vk::PipelineLayout,
    // The raw sets to bind.
    raw_sets: SmallVec<[(u32, SmallVec<[vk::DescriptorSet; 8]>); 4]>,
    // The device of the pipeline object, so that we can compare it with the command buffer's
    // device.
    device: Arc<Device>,
    // The sets to bind. Unused, but we need to keep it alive.
    sets: S,
    // The pipeline layout. Unused, but we need to keep it alive.
    pipeline_layout: P,
}

impl<L, S, P> CmdBindDescriptorSets<L, S, P>
    where L: CommandsList, S: DescriptorSetsCollection, P: PipelineLayoutRef
{
    #[inline]
    pub fn new(previous: L, graphics: bool, pipeline_layout: P, sets: S)
               -> Result<CmdBindDescriptorSets<L, S, P>, CmdBindDescriptorSetsError>
    {
        if !PipelineLayoutSetsCompatible::is_compatible(pipeline_layout.desc(), &sets) {
            return Err(CmdBindDescriptorSetsError::IncompatibleSets);
        }

        let raw_pipeline_layout = pipeline_layout.sys().internal_object();
        let device = pipeline_layout.device().clone();

        let raw_sets = {
            let mut raw_sets: SmallVec<[(u32, SmallVec<[_; 8]>); 4]> = SmallVec::new();
            let mut add_new = true;
            for set_num in 0 .. sets.num_sets() {
                let set = match sets.descriptor_set(set_num) {
                    Some(set) => set.internal_object(),
                    None => { add_new = true; continue; },
                };

                if add_new {
                    let mut v = SmallVec::new(); v.push(set);
                    raw_sets.push((set_num as u32, v));
                    add_new = false;
                } else {
                    raw_sets.last_mut().unwrap().1.push(set);
                }
            }
            raw_sets
        };

        Ok(CmdBindDescriptorSets {
            previous: previous,
            raw_pipeline_layout: raw_pipeline_layout,
            raw_sets: raw_sets,
            pipeline_ty: if graphics { vk::PIPELINE_BIND_POINT_GRAPHICS }
                         else { vk::PIPELINE_BIND_POINT_COMPUTE },
            device: device,
            sets: sets,
            pipeline_layout: pipeline_layout,
        })
    }
}

impl<L, S, P> CmdBindDescriptorSets<L, S, P> where L: CommandsList {
    #[inline]
    fn append<'a>(&'a self, builder: &mut CommandsListSink<'a>) {
        self.previous.append(builder);

        assert_eq!(self.device.internal_object(), builder.device().internal_object());

        builder.add_command(Box::new(move |raw: &mut RawCommandBufferPrototype| {
            unsafe {
                let vk = raw.device.pointers();
                let cmd = raw.command_buffer.clone().take().unwrap();

                for &(first_set, ref sets) in self.raw_sets.iter() {
                    vk.CmdBindDescriptorSets(cmd, self.pipeline_ty, self.raw_pipeline_layout,
                                            first_set, sets.len() as u32, sets.as_ptr(),
                                            0, ptr::null());        // TODO: not supported
                }
            }
        }));
    }
}

/// Error that can happen when creating a `CmdBindDescriptorSets`.
#[derive(Debug, Copy, Clone)]
pub enum CmdBindDescriptorSetsError {
    /// The sets are not compatible with the pipeline layout.
    // TODO: inner error
    IncompatibleSets,
}

impl error::Error for CmdBindDescriptorSetsError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            CmdBindDescriptorSetsError::IncompatibleSets => {
                "the sets are not compatible with the pipeline layout"
            },
        }
    }
}

impl fmt::Display for CmdBindDescriptorSetsError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}
<commit_msg>Some tweaks to CmdBindDescriptorSets docs<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::ptr;
use std::sync::Arc;
use smallvec::SmallVec;

use command_buffer::RawCommandBufferPrototype;
use command_buffer::CommandsList;
use command_buffer::CommandsListSink;
use descriptor::descriptor_set::DescriptorSetsCollection;
use descriptor::pipeline_layout::PipelineLayoutRef;
use descriptor::pipeline_layout::PipelineLayoutSetsCompatible;
use device::Device;
use pipeline::ComputePipeline;
use pipeline::GraphicsPipeline;
use VulkanObject;
use VulkanPointers;
use vk;

/// Wraps around a commands list and adds at the end of it a command that binds descriptor sets.
pub struct CmdBindDescriptorSets<L, S, P> where L: CommandsList {
    // Parent commands list.
    previous: L,
    // The raw Vulkan enum representing the kind of pipeline.
    pipeline_ty: vk::PipelineBindPoint,
    // The raw pipeline object to bind.
    raw_pipeline_layout: vk::PipelineLayout,
    // The raw sets to bind. Array where each element is a tuple of the first set to bind and the
    // sets to bind.
    raw_sets: SmallVec<[(u32, SmallVec<[vk::DescriptorSet; 8]>); 4]>,
    // The device of the pipeline object, so that we can compare it with the command buffer's
    // device.
    device: Arc<Device>,
    // The sets to bind. Unused, but we need to keep them alive.
    sets: S,
    // The pipeline layout. Unused, but we need to keep it alive.
    pipeline_layout: P,
}

impl<L, S, P> CmdBindDescriptorSets<L, S, P>
    where L: CommandsList, S: DescriptorSetsCollection, P: PipelineLayoutRef
{
    /// Builds the command.
    ///
    /// If `graphics` is true, the sets will be bound to the graphics slot. If false, they will be
    /// bound to the compute slot.
    ///
    /// Returns an error if the sets are not compatible with the pipeline layout.
    #[inline]
    pub fn new(previous: L, graphics: bool, pipeline_layout: P, sets: S)
               -> Result<CmdBindDescriptorSets<L, S, P>, CmdBindDescriptorSetsError>
    {
        if !PipelineLayoutSetsCompatible::is_compatible(pipeline_layout.desc(), &sets) {
            return Err(CmdBindDescriptorSetsError::IncompatibleSets);
        }

        let raw_pipeline_layout = pipeline_layout.sys().internal_object();
        let device = pipeline_layout.device().clone();

        let raw_sets = {
            let mut raw_sets: SmallVec<[(u32, SmallVec<[_; 8]>); 4]> = SmallVec::new();
            let mut add_new = true;
            for set_num in 0 .. sets.num_sets() {
                let set = match sets.descriptor_set(set_num) {
                    Some(set) => set.internal_object(),
                    None => { add_new = true; continue; },
                };

                if add_new {
                    let mut v = SmallVec::new(); v.push(set);
                    raw_sets.push((set_num as u32, v));
                    add_new = false;
                } else {
                    raw_sets.last_mut().unwrap().1.push(set);
                }
            }
            raw_sets
        };

        Ok(CmdBindDescriptorSets {
            previous: previous,
            raw_pipeline_layout: raw_pipeline_layout,
            raw_sets: raw_sets,
            pipeline_ty: if graphics { vk::PIPELINE_BIND_POINT_GRAPHICS }
                         else { vk::PIPELINE_BIND_POINT_COMPUTE },
            device: device,
            sets: sets,
            pipeline_layout: pipeline_layout,
        })
    }
}

impl<L, S, P> CmdBindDescriptorSets<L, S, P> where L: CommandsList {
    #[inline]
    fn append<'a>(&'a self, builder: &mut CommandsListSink<'a>) {
        self.previous.append(builder);

        assert_eq!(self.device.internal_object(), builder.device().internal_object());

        builder.add_command(Box::new(move |raw: &mut RawCommandBufferPrototype| {
            unsafe {
                let vk = raw.device.pointers();
                let cmd = raw.command_buffer.clone().take().unwrap();

                for &(first_set, ref sets) in self.raw_sets.iter() {
                    vk.CmdBindDescriptorSets(cmd, self.pipeline_ty, self.raw_pipeline_layout,
                                            first_set, sets.len() as u32, sets.as_ptr(),
                                            0, ptr::null());        // TODO: dynamic offset not supported
                }
            }
        }));
    }
}

/// Error that can happen when creating a `CmdBindDescriptorSets`.
#[derive(Debug, Copy, Clone)]
pub enum CmdBindDescriptorSetsError {
    /// The sets are not compatible with the pipeline layout.
    // TODO: inner error
    IncompatibleSets,
}

impl error::Error for CmdBindDescriptorSetsError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            CmdBindDescriptorSetsError::IncompatibleSets => {
                "the sets are not compatible with the pipeline layout"
            },
        }
    }
}

impl fmt::Display for CmdBindDescriptorSetsError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::DOMParserBinding;
use dom::bindings::codegen::DOMParserBinding::SupportedTypeValues::{Text_html, Text_xml};
use dom::bindings::js::JS;
use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
use dom::bindings::error::{Fallible, FailureUnknown};
use dom::document::{Document, HTMLDocument};
use dom::window::Window;
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct DOMParser {
    owner: JS<Window>, //XXXjdm Document instead?
    reflector_: Reflector
}

impl DOMParser {
    pub fn new_inherited(owner: JS<Window>) -> DOMParser {
        DOMParser {
            owner: owner,
            reflector_: Reflector::new()
        }
    }

    pub fn new(owner: &JS<Window>) -> JS<DOMParser> {
        reflect_dom_object(~DOMParser::new_inherited(owner.clone()), owner.get(),
                           DOMParserBinding::Wrap)
    }

    pub fn Constructor(owner: &JS<Window>) -> Fallible<JS<DOMParser>> {
        Ok(DOMParser::new(owner))
    }

    pub fn ParseFromString(&self,
                           _s: DOMString,
                           ty: DOMParserBinding::SupportedType)
                           -> Fallible<JS<Document>> {
        match ty {
            Text_html => {
                Ok(Document::new(&self.owner, None, HTMLDocument, None))
            }
            Text_xml => {
                Document::Constructor(&self.owner)
            }
            _ => {
                Err(FailureUnknown)
            }
        }
    }
}

impl Reflectable for DOMParser {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        &mut self.reflector_
    }
}
<commit_msg>DOMParser should pass the correct contentType (fixes #1741)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::DOMParserBinding;
use dom::bindings::codegen::DOMParserBinding::SupportedTypeValues::{Text_html, Text_xml};
use dom::bindings::js::JS;
use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
use dom::bindings::error::{Fallible, FailureUnknown};
use dom::document::{Document, HTMLDocument, NonHTMLDocument};
use dom::window::Window;
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct DOMParser {
    owner: JS<Window>, //XXXjdm Document instead?
    reflector_: Reflector
}

impl DOMParser {
    pub fn new_inherited(owner: JS<Window>) -> DOMParser {
        DOMParser {
            owner: owner,
            reflector_: Reflector::new()
        }
    }

    pub fn new(owner: &JS<Window>) -> JS<DOMParser> {
        reflect_dom_object(~DOMParser::new_inherited(owner.clone()), owner.get(),
                           DOMParserBinding::Wrap)
    }

    pub fn Constructor(owner: &JS<Window>) -> Fallible<JS<DOMParser>> {
        Ok(DOMParser::new(owner))
    }

    pub fn ParseFromString(&self,
                           _s: DOMString,
                           ty: DOMParserBinding::SupportedType)
                           -> Fallible<JS<Document>> {
        match ty {
            Text_html => {
                Ok(Document::new(&self.owner, None, HTMLDocument, Some(~"text/html")))
            }
            Text_xml => {
                Ok(Document::new(&self.owner, None, NonHTMLDocument, Some(~"text/xml")))
            }
            _ => {
                Err(FailureUnknown)
            }
        }
    }
}

impl Reflectable for DOMParser {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        &mut self.reflector_
    }
}
<|endoftext|>


<commit_before>//! Enviroment data

use alloc::boxed::Box;

use fs::File;
use path::{Path, PathBuf};
use io::Result;
use string::{String, ToString};
use vec::Vec;

use system::error::{Error, ENOENT};
use system::syscall::sys_chdir;

static mut _args: *mut Vec<&'static str> = 0 as *mut Vec<&'static str>;

pub struct Args {
    i: usize
}

impl Iterator for Args {
    //Yes, this is supposed to be String, do not change it!
    //Only change it if https://doc.rust-lang.org/std/env/struct.Args.html changes from String
    type Item = String;
    fn next(&mut self) -> Option<String> {
        if let Some(arg) = unsafe { (*_args).get(self.i) } {
            self.i += 1;
            Some(arg.to_string())
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = if self.i <= unsafe { (*_args).len() } {
            unsafe { (*_args).len() - self.i }
        } else {
            0
        };
        (len, Some(len))
    }
}

impl ExactSizeIterator for Args {}

/// Arguments
pub fn args() -> Args {
    Args {
        i: 0
    }
}

/// Initialize arguments
pub unsafe fn args_init(args: Vec<&'static str>) {
    _args = Box::into_raw(box args);
}

/// Destroy arguments
pub unsafe fn args_destroy() {
    if _args as usize > 0 {
        drop(Box::from_raw(_args));
    }
}

/// Private function to get the path from a custom location
/// If the custom directory cannot be found, None will be returned
fn get_path_from(location : &str) -> Result<PathBuf> {
    match File::open(dir) {
        Ok(file) => {
            match file.path() {
                Ok(path) => Ok(path),
                Err(err) => Err(err),
            }
        }
        Err(err) => Err(err),
    }
}

/// Method to return the current directory
pub fn current_dir() -> Result<PathBuf> {
    // Return the current path
    get_path_from("./")
}

/// Method to return the home directory
pub fn home_dir() -> Result<PathBuf> {
    get_path_from("/home/")
}

/// Set the current directory
pub fn set_current_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    let file_result = if path.as_ref().inner.is_empty() || path.as_ref().inner.ends_with('/') {
        File::open(&path.as_ref().inner)
    } else {
        File::open(&(path.as_ref().inner.to_string() + "/"))
    };

    match file_result {
        Ok(file) => {
            match file.path() {
                Ok(path) => {
                    if let Some(path_str) = path.to_str() {
                        let path_c = path_str.to_string() + "\0";
                        match Error::demux(unsafe { sys_chdir(path_c.as_ptr()) }) {
                            Ok(_) => Ok(()),
                            Err(err) => Err(err),
                        }
                    } else {
                        Err(Error::new(ENOENT))
                    }
                }
                Err(err) => Err(err),
            }
        }
        Err(err) => Err(err),
    }
}

// TODO: Fully implement `env::var()`
pub fn var(_key: &str) -> Result<String> {
    Ok("This is code filler".to_string())
}
<commit_msg>Correct file name<commit_after>//! Enviroment data

use alloc::boxed::Box;

use fs::File;
use path::{Path, PathBuf};
use io::Result;
use string::{String, ToString};
use vec::Vec;

use system::error::{Error, ENOENT};
use system::syscall::sys_chdir;

static mut _args: *mut Vec<&'static str> = 0 as *mut Vec<&'static str>;

pub struct Args {
    i: usize
}

impl Iterator for Args {
    //Yes, this is supposed to be String, do not change it!
    //Only change it if https://doc.rust-lang.org/std/env/struct.Args.html changes from String
    type Item = String;
    fn next(&mut self) -> Option<String> {
        if let Some(arg) = unsafe { (*_args).get(self.i) } {
            self.i += 1;
            Some(arg.to_string())
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = if self.i <= unsafe { (*_args).len() } {
            unsafe { (*_args).len() - self.i }
        } else {
            0
        };
        (len, Some(len))
    }
}

impl ExactSizeIterator for Args {}

/// Arguments
pub fn args() -> Args {
    Args {
        i: 0
    }
}

/// Initialize arguments
pub unsafe fn args_init(args: Vec<&'static str>) {
    _args = Box::into_raw(box args);
}

/// Destroy arguments
pub unsafe fn args_destroy() {
    if _args as usize > 0 {
        drop(Box::from_raw(_args));
    }
}

/// Private function to get the path from a custom location
/// If the custom directory cannot be found, None will be returned
fn get_path_from(location : &str) -> Result<PathBuf> {
    match File::open(location) {
        Ok(file) => {
            match file.path() {
                Ok(path) => Ok(path),
                Err(err) => Err(err),
            }
        }
        Err(err) => Err(err),
    }
}

/// Method to return the current directory
pub fn current_dir() -> Result<PathBuf> {
    // Return the current path
    get_path_from("./")
}

/// Method to return the home directory
pub fn home_dir() -> Result<PathBuf> {
    get_path_from("/home/")
}

/// Set the current directory
pub fn set_current_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    let file_result = if path.as_ref().inner.is_empty() || path.as_ref().inner.ends_with('/') {
        File::open(&path.as_ref().inner)
    } else {
        File::open(&(path.as_ref().inner.to_string() + "/"))
    };

    match file_result {
        Ok(file) => {
            match file.path() {
                Ok(path) => {
                    if let Some(path_str) = path.to_str() {
                        let path_c = path_str.to_string() + "\0";
                        match Error::demux(unsafe { sys_chdir(path_c.as_ptr()) }) {
                            Ok(_) => Ok(()),
                            Err(err) => Err(err),
                        }
                    } else {
                        Err(Error::new(ENOENT))
                    }
                }
                Err(err) => Err(err),
            }
        }
        Err(err) => Err(err),
    }
}

// TODO: Fully implement `env::var()`
pub fn var(_key: &str) -> Result<String> {
    Ok("This is code filler".to_string())
}
<|endoftext|>


<commit_before>#![feature(collections, io, os, path)]

extern crate "pkg-config" as pkg_config;
extern crate bindgen;
extern crate gcc;

use bindgen::{Bindings, BindgenOptions, LinkType};
use std::default::Default;
use std::old_io::fs;
use std::os;

fn main() {

    let mdb = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap())
                   .join_many(&["mdb", "libraries", "liblmdb"]);

    if !pkg_config::find_library("liblmdb").is_ok() {
        gcc::compile_library("liblmdb.a",
                             &Default::default(),
                             &[mdb.join("mdb.c").as_str().unwrap(),
                               mdb.join("midl.c").as_str().unwrap()]);
    }

    let mut bindgen_opts: BindgenOptions = Default::default();
    bindgen_opts.clang_args.push(mdb.join("lmdb.h").as_str().unwrap().to_string());
    bindgen_opts.links.push(("lmdb".to_string(), LinkType::Default));
    bindgen_opts.builtins = true;

    let bindings: Bindings = Bindings::generate(&bindgen_opts, None, None).unwrap();
    let mut dst = fs::File::create(&Path::new(os::getenv("OUT_DIR").unwrap())
                                         .join("lmdb.rs")).unwrap();
    bindings.write(&mut dst).unwrap()
}
<commit_msg>update for rustc 1.0.0-nightly (f1398d273 2015-01-31 20:21:02 +0000)<commit_after>#![feature(io, os, path)]

extern crate "pkg-config" as pkg_config;
extern crate bindgen;
extern crate gcc;

use bindgen::{Bindings, BindgenOptions, LinkType};
use std::default::Default;
use std::old_io::fs;
use std::os;

fn main() {

    let mdb = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap())
                   .join_many(&["mdb", "libraries", "liblmdb"]);

    if !pkg_config::find_library("liblmdb").is_ok() {
        gcc::compile_library("liblmdb.a",
                             &Default::default(),
                             &[mdb.join("mdb.c").as_str().unwrap(),
                               mdb.join("midl.c").as_str().unwrap()]);
    }

    let mut bindgen_opts: BindgenOptions = Default::default();
    bindgen_opts.clang_args.push(mdb.join("lmdb.h").as_str().unwrap().to_string());
    bindgen_opts.links.push(("lmdb".to_string(), LinkType::Default));
    bindgen_opts.builtins = true;

    let bindings: Bindings = Bindings::generate(&bindgen_opts, None, None).unwrap();
    let mut dst = fs::File::create(&Path::new(os::getenv("OUT_DIR").unwrap())
                                         .join("lmdb.rs")).unwrap();
    bindings.write(&mut dst).unwrap()
}
<|endoftext|>