<commit_before>use cargo::core::{Workspace, Source};
use cargo::sources::PathSource;
use syntex_syntax::visit::Visitor;
use syntex_syntax::codemap::{CodeMap, Span, BytePos, FilePathMapping};
use syntex_syntax::ast::{NodeId, Mac, Attribute, MetaItemKind, Stmt};
use syntex_syntax::parse::{self, ParseSess};
use syntex_syntax::errors::Handler;
use syntex_syntax::errors::emitter::ColorConfig;
use std::path::{PathBuf, Path};
use std::rc::Rc;
use config::Config;

pub struct IgnoredLines<'a> {
    pub lines: Vec<(PathBuf, usize)>,
    codemap: &'a CodeMap,
    parse_session: &'a ParseSess,
}
/*
 *  MetaItem contains #[test] etc use it to filter those lines and test functions!
 *
 * Need to use walk to traverse DEEPER. is fn under attr?
 */

pub fn get_lines_to_ignore(project: &Workspace) -> Vec<(PathBuf, usize)> {
    let pkg = project.current().unwrap();
    let mut src = PathSource::new(pkg.root(), pkg.package_id().source_id(), project.config());
    src.update();

    let codemap = Rc::new(CodeMap::new(FilePathMapping::empty()));
    let handler = Handler::with_tty_emitter(ColorConfig::Auto, false, false, Some(codemap.clone()));
    let mut parse_session = ParseSess::with_span_handler(handler, codemap.clone());

    let lines = IgnoredLines::from_session(&parse_session);
    for target in pkg.targets() {
        println!("Analysing {} at {}", target.name(), target.src_path().display());
        let mut parser = parse::new_parser_from_file(&parse_session, target.src_path());
    }

    // Add files to codemap
    // Create AST
    // Visit nodes
    // lines.visit_mod()
    lines.lines
}

impl<'a> IgnoredLines<'a> {
    /// Construct a new ignored lines object for the given project
    fn from_session(session: &'a ParseSess) -> IgnoredLines<'a> {
        IgnoredLines {
            lines: vec![],
            codemap: session.codemap(),
            parse_session: &session
        }
    }

    fn parse_project(&self) {
        unimplemented!();
    }
}

impl<'v, 'a> Visitor<'v> for IgnoredLines<'a> {

    fn visit_stmt(&mut self, s: &Stmt) {
        unimplemented!();
    }

    fn visit_mac(&mut self, mac: &Mac) {
        // Use this to ignore unreachable lines
        unimplemented!();
    }

    fn visit_attribute(&mut self, attr: &Attribute) {
        if attr.is_word() {
            // Ignore the line. If test and ignoring tests ignore
        }
    }

}

<commit_msg>Moved to parsing each source file in isolation.<commit_after>use cargo::core::{Workspace, Source};
use cargo::sources::PathSource;
use syntex_syntax::visit::{Visitor, FnKind};
use syntex_syntax::codemap::{CodeMap, Span, BytePos, FilePathMapping};
use syntex_syntax::ast::{NodeId, Mac, Attribute, MetaItemKind, Stmt, FnDecl};
use syntex_syntax::parse::{self, ParseSess};
use syntex_syntax::errors::Handler;
use syntex_syntax::errors::emitter::ColorConfig;
use std::path::{PathBuf, Path};
use std::ffi::OsStr;
use std::rc::Rc;
use config::Config;

struct IgnoredLines<'a> {
    lines: Vec<(PathBuf, usize)>,
    codemap: &'a CodeMap,
    parse_session: &'a ParseSess,
}
/*
 *  MetaItem contains #[test] etc use it to filter those lines and test functions!
 *
 * Need to use walk to traverse DEEPER. is fn under attr?
 */

pub fn get_lines_to_ignore(project: &Workspace) -> Vec<(PathBuf, usize)> {
    let mut result: Vec<(PathBuf, usize)> = Vec::new();

    let pkg = project.current().unwrap();
    let mut src = PathSource::new(pkg.root(), pkg.package_id().source_id(), project.config());
    src.update();

    let codemap = Rc::new(CodeMap::new(FilePathMapping::empty()));
    let handler = Handler::with_tty_emitter(ColorConfig::Auto, false, false, Some(codemap.clone()));
    let mut parse_session = ParseSess::with_span_handler(handler, codemap.clone());

    for file in src.list_files(&pkg).unwrap().iter() {
        // Do I need to do this from the root?
        if file.extension() == Some(OsStr::new("rs")) {
            // Rust source file
            let mut parser = parse::new_parser_from_file(&parse_session, &file);
            if let Ok(mut krate) = parser.parse_crate_mod() {
                let mut visitor = IgnoredLines::from_session(&parse_session);
                visitor.visit_mod(&krate.module, krate.span, &krate.attrs, NodeId::new(0));
                result.append(&mut visitor.lines);
            }
        }
    }
    result
}

impl<'a> IgnoredLines<'a> {
    /// Construct a new ignored lines object for the given project
    fn from_session(session: &'a ParseSess) -> IgnoredLines<'a> {
        IgnoredLines {
            lines: vec![],
            codemap: session.codemap(),
            parse_session: &session
        }
    }
}

impl<'v, 'a> Visitor<'v> for IgnoredLines<'a> {

    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, s: Span, _: NodeId) {

    }

    fn visit_stmt(&mut self, s: &Stmt) {
        unimplemented!();
    }

    fn visit_mac(&mut self, mac: &Mac) {
        // Use this to ignore unreachable lines
        unimplemented!();
    }

    fn visit_attribute(&mut self, attr: &Attribute) {
        if attr.is_word() {
            // Ignore the line. If test and ignoring tests ignore
        }
    }

}

<|endoftext|>


<commit_before>use crate::clean;
use crate::docfs::PathError;
use crate::error::Error;
use crate::fold::DocFolder;
use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::layout;
use crate::html::render::{Context, BASIC_KEYWORDS};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use std::ffi::OsStr;
use std::fs;
use std::path::{Component, Path, PathBuf};

crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> {
    info!("emitting source files");
    let dst = cx.dst.join("src").join(&*krate.name(cx.tcx()).as_str());
    cx.shared.ensure_dir(&dst)?;
    let mut folder = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
    Ok(folder.fold_crate(krate))
}

crate fn collect_local_sources<'tcx>(
    tcx: TyCtxt<'tcx>,
    src_root: &Path,
    krate: clean::Crate,
) -> (clean::Crate, FxHashMap<PathBuf, String>) {
    let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root };

    let krate = lsc.fold_crate(krate);
    (krate, lsc.local_sources)
}

struct LocalSourcesCollector<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    local_sources: FxHashMap<PathBuf, String>,
    src_root: &'a Path,
}

fn is_real_and_local(span: clean::Span, sess: &Session) -> bool {
    span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE
}

impl LocalSourcesCollector<'_, '_> {
    fn add_local_source(&mut self, item: &clean::Item) {
        let sess = self.tcx.sess;
        let span = item.span(self.tcx);
        // skip all synthetic "files"
        if !is_real_and_local(span, sess) {
            return;
        }
        let filename = span.filename(sess);
        let p = match filename {
            FileName::Real(ref file) => match file.local_path() {
                Some(p) => p.to_path_buf(),
                _ => return,
            },
            _ => return,
        };
        if self.local_sources.contains_key(&*p) {
            // We've already emitted this source
            return;
        }

        let mut href = String::new();
        clean_path(self.src_root, &p, false, |component| {
            href.push_str(&component.to_string_lossy());
            href.push('/');
        });

        let mut src_fname = p.file_name().expect("source has no filename").to_os_string();
        src_fname.push(".html");
        href.push_str(&src_fname.to_string_lossy());
        self.local_sources.insert(p, href);
    }
}

impl DocFolder for LocalSourcesCollector<'_, '_> {
    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
        self.add_local_source(&item);

        // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
        // we could return None here without having to walk the rest of the crate.
        Some(self.fold_item_recur(item))
    }
}

/// Helper struct to render all source code to HTML pages
struct SourceCollector<'a, 'tcx> {
    cx: &'a mut Context<'tcx>,

    /// Root destination to place all HTML output into
    dst: PathBuf,
    emitted_local_sources: FxHashSet<PathBuf>,
}

impl DocFolder for SourceCollector<'_, '_> {
    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
        let tcx = self.cx.tcx();
        let span = item.span(tcx);
        let sess = tcx.sess;

        // If we're not rendering sources, there's nothing to do.
        // If we're including source files, and we haven't seen this file yet,
        // then we need to render it out to the filesystem.
        if self.cx.include_sources && is_real_and_local(span, sess) {
            let filename = span.filename(sess);
            let span = span.inner();
            let pos = sess.source_map().lookup_source_file(span.lo());
            let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_pos);
            // If it turns out that we couldn't read this file, then we probably
            // can't read any of the files (generating html output from json or
            // something like that), so just don't include sources for the
            // entire crate. The other option is maintaining this mapping on a
            // per-file basis, but that's probably not worth it...
            self.cx.include_sources = match self.emit_source(&filename, file_span) {
                Ok(()) => true,
                Err(e) => {
                    self.cx.shared.tcx.sess.span_err(
                        span,
                        &format!(
                            "failed to render source code for `{}`: {}",
                            filename.prefer_local(),
                            e,
                        ),
                    );
                    false
                }
            };
        }
        // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
        // we could return None here without having to walk the rest of the crate.
        Some(self.fold_item_recur(item))
    }
}

impl SourceCollector<'_, 'tcx> {
    /// Renders the given filename into its corresponding HTML source file.
    fn emit_source(
        &mut self,
        filename: &FileName,
        file_span: rustc_span::Span,
    ) -> Result<(), Error> {
        let p = match *filename {
            FileName::Real(ref file) => {
                if let Some(local_path) = file.local_path() {
                    local_path.to_path_buf()
                } else {
                    unreachable!("only the current crate should have sources emitted");
                }
            }
            _ => return Ok(()),
        };
        if self.emitted_local_sources.contains(&*p) {
            // We've already emitted this source
            return Ok(());
        }

        let contents = match fs::read_to_string(&p) {
            Ok(contents) => contents,
            Err(e) => {
                return Err(Error::new(e, &p));
            }
        };

        // Remove the utf-8 BOM if any
        let contents = contents.strip_prefix('\u{feff}').unwrap_or(&contents);

        // Create the intermediate directories
        let mut cur = self.dst.clone();
        let mut root_path = String::from("../../");
        clean_path(&self.cx.shared.src_root, &p, false, |component| {
            cur.push(component);
            root_path.push_str("../");
        });

        self.cx.shared.ensure_dir(&cur)?;

        let src_fname = p.file_name().expect("source has no filename").to_os_string();
        let mut fname = src_fname.clone();
        fname.push(".html");
        cur.push(&fname);

        let title = format!("{} - source", src_fname.to_string_lossy());
        let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
        let page = layout::Page {
            title: &title,
            css_class: "source",
            root_path: &root_path,
            static_root_path: self.cx.shared.static_root_path.as_deref(),
            description: &desc,
            keywords: BASIC_KEYWORDS,
            resource_suffix: &self.cx.shared.resource_suffix,
            extra_scripts: &[&format!("source-files{}", self.cx.shared.resource_suffix)],
            static_extra_scripts: &[&format!("source-script{}", self.cx.shared.resource_suffix)],
        };
        let v = layout::render(
            &self.cx.shared.templates,
            &self.cx.shared.layout,
            &page,
            "",
            |buf: &mut _| {
                print_src(
                    buf,
                    contents,
                    self.cx.shared.edition(),
                    file_span,
                    self.cx,
                    &root_path,
                    None,
                    SourceContext::Standalone,
                )
            },
            &self.cx.shared.style_files,
        );
        self.cx.shared.fs.write(cur, v)?;
        self.emitted_local_sources.insert(p);
        Ok(())
    }
}

/// Takes a path to a source file and cleans the path to it. This canonicalizes
/// things like ".." to components which preserve the "top down" hierarchy of a
/// static HTML tree. Each component in the cleaned path will be passed as an
/// argument to `f`. The very last component of the path (ie the file name) will
/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
crate fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
where
    F: FnMut(&OsStr),
{
    // make it relative, if possible
    let p = p.strip_prefix(src_root).unwrap_or(p);

    let mut iter = p.components().peekable();

    while let Some(c) = iter.next() {
        if !keep_filename && iter.peek().is_none() {
            break;
        }

        match c {
            Component::ParentDir => f("up".as_ref()),
            Component::Normal(c) => f(c),
            _ => continue,
        }
    }
}

crate enum SourceContext {
    Standalone,
    Embedded { offset: usize },
}

/// Wrapper struct to render the source code of a file. This will do things like
/// adding line numbers to the left-hand side.
crate fn print_src(
    buf: &mut Buffer,
    s: &str,
    edition: Edition,
    file_span: rustc_span::Span,
    context: &Context<'_>,
    root_path: &str,
    decoration_info: Option<highlight::DecorationInfo>,
    source_context: SourceContext,
) {
    let lines = s.lines().count();
    let mut line_numbers = Buffer::empty_from(buf);
    let mut cols = 0;
    let mut tmp = lines;
    while tmp > 0 {
        cols += 1;
        tmp /= 10;
    }
    line_numbers.write_str("
");
    for i in 1..=lines {
        match source_context {
            SourceContext::Standalone => {
                writeln!(line_numbers, "{0:1$}", i, cols)
            }
            SourceContext::Embedded { offset } => {
                writeln!(line_numbers, "{0:1$}", i + offset, cols)
            }
        }
    }
    line_numbers.write_str("
");
    highlight::render_with_highlighting(
        s,
        buf,
        None,
        None,
        None,
        edition,
        Some(line_numbers),
        Some(highlight::ContextInfo { context, file_span, root_path }),
        decoration_info,
    );
}
<commit_msg>rustdoc: Use `DocVisitor` for sources collection<commit_after>use crate::clean;
use crate::docfs::PathError;
use crate::error::Error;
use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::layout;
use crate::html::render::{Context, BASIC_KEYWORDS};
use crate::visit::DocVisitor;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use std::ffi::OsStr;
use std::fs;
use std::path::{Component, Path, PathBuf};

crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> {
    info!("emitting source files");

    let dst = cx.dst.join("src").join(&*krate.name(cx.tcx()).as_str());
    cx.shared.ensure_dir(&dst)?;

    let mut collector = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
    collector.visit_crate(&krate);
    Ok(krate)
}

crate fn collect_local_sources<'tcx>(
    tcx: TyCtxt<'tcx>,
    src_root: &Path,
    krate: clean::Crate,
) -> (clean::Crate, FxHashMap<PathBuf, String>) {
    let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root };
    lsc.visit_crate(&krate);
    (krate, lsc.local_sources)
}

struct LocalSourcesCollector<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    local_sources: FxHashMap<PathBuf, String>,
    src_root: &'a Path,
}

fn is_real_and_local(span: clean::Span, sess: &Session) -> bool {
    span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE
}

impl LocalSourcesCollector<'_, '_> {
    fn add_local_source(&mut self, item: &clean::Item) {
        let sess = self.tcx.sess;
        let span = item.span(self.tcx);
        // skip all synthetic "files"
        if !is_real_and_local(span, sess) {
            return;
        }
        let filename = span.filename(sess);
        let p = match filename {
            FileName::Real(ref file) => match file.local_path() {
                Some(p) => p.to_path_buf(),
                _ => return,
            },
            _ => return,
        };
        if self.local_sources.contains_key(&*p) {
            // We've already emitted this source
            return;
        }

        let mut href = String::new();
        clean_path(self.src_root, &p, false, |component| {
            href.push_str(&component.to_string_lossy());
            href.push('/');
        });

        let mut src_fname = p.file_name().expect("source has no filename").to_os_string();
        src_fname.push(".html");
        href.push_str(&src_fname.to_string_lossy());
        self.local_sources.insert(p, href);
    }
}

impl DocVisitor for LocalSourcesCollector<'_, '_> {
    fn visit_item(&mut self, item: &clean::Item) {
        self.add_local_source(item);

        // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
        // we could return None here without having to walk the rest of the crate.
        self.visit_item_recur(item)
    }
}

/// Helper struct to render all source code to HTML pages
struct SourceCollector<'a, 'tcx> {
    cx: &'a mut Context<'tcx>,

    /// Root destination to place all HTML output into
    dst: PathBuf,
    emitted_local_sources: FxHashSet<PathBuf>,
}

impl DocVisitor for SourceCollector<'_, '_> {
    fn visit_item(&mut self, item: &clean::Item) {
        let tcx = self.cx.tcx();
        let span = item.span(tcx);
        let sess = tcx.sess;

        // If we're not rendering sources, there's nothing to do.
        // If we're including source files, and we haven't seen this file yet,
        // then we need to render it out to the filesystem.
        if self.cx.include_sources && is_real_and_local(span, sess) {
            let filename = span.filename(sess);
            let span = span.inner();
            let pos = sess.source_map().lookup_source_file(span.lo());
            let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_pos);
            // If it turns out that we couldn't read this file, then we probably
            // can't read any of the files (generating html output from json or
            // something like that), so just don't include sources for the
            // entire crate. The other option is maintaining this mapping on a
            // per-file basis, but that's probably not worth it...
            self.cx.include_sources = match self.emit_source(&filename, file_span) {
                Ok(()) => true,
                Err(e) => {
                    self.cx.shared.tcx.sess.span_err(
                        span,
                        &format!(
                            "failed to render source code for `{}`: {}",
                            filename.prefer_local(),
                            e,
                        ),
                    );
                    false
                }
            };
        }
        // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
        // we could return None here without having to walk the rest of the crate.
        self.visit_item_recur(item)
    }
}

impl SourceCollector<'_, 'tcx> {
    /// Renders the given filename into its corresponding HTML source file.
    fn emit_source(
        &mut self,
        filename: &FileName,
        file_span: rustc_span::Span,
    ) -> Result<(), Error> {
        let p = match *filename {
            FileName::Real(ref file) => {
                if let Some(local_path) = file.local_path() {
                    local_path.to_path_buf()
                } else {
                    unreachable!("only the current crate should have sources emitted");
                }
            }
            _ => return Ok(()),
        };
        if self.emitted_local_sources.contains(&*p) {
            // We've already emitted this source
            return Ok(());
        }

        let contents = match fs::read_to_string(&p) {
            Ok(contents) => contents,
            Err(e) => {
                return Err(Error::new(e, &p));
            }
        };

        // Remove the utf-8 BOM if any
        let contents = contents.strip_prefix('\u{feff}').unwrap_or(&contents);

        // Create the intermediate directories
        let mut cur = self.dst.clone();
        let mut root_path = String::from("../../");
        clean_path(&self.cx.shared.src_root, &p, false, |component| {
            cur.push(component);
            root_path.push_str("../");
        });

        self.cx.shared.ensure_dir(&cur)?;

        let src_fname = p.file_name().expect("source has no filename").to_os_string();
        let mut fname = src_fname.clone();
        fname.push(".html");
        cur.push(&fname);

        let title = format!("{} - source", src_fname.to_string_lossy());
        let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
        let page = layout::Page {
            title: &title,
            css_class: "source",
            root_path: &root_path,
            static_root_path: self.cx.shared.static_root_path.as_deref(),
            description: &desc,
            keywords: BASIC_KEYWORDS,
            resource_suffix: &self.cx.shared.resource_suffix,
            extra_scripts: &[&format!("source-files{}", self.cx.shared.resource_suffix)],
            static_extra_scripts: &[&format!("source-script{}", self.cx.shared.resource_suffix)],
        };
        let v = layout::render(
            &self.cx.shared.templates,
            &self.cx.shared.layout,
            &page,
            "",
            |buf: &mut _| {
                print_src(
                    buf,
                    contents,
                    self.cx.shared.edition(),
                    file_span,
                    self.cx,
                    &root_path,
                    None,
                    SourceContext::Standalone,
                )
            },
            &self.cx.shared.style_files,
        );
        self.cx.shared.fs.write(cur, v)?;
        self.emitted_local_sources.insert(p);
        Ok(())
    }
}

/// Takes a path to a source file and cleans the path to it. This canonicalizes
/// things like ".." to components which preserve the "top down" hierarchy of a
/// static HTML tree. Each component in the cleaned path will be passed as an
/// argument to `f`. The very last component of the path (ie the file name) will
/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
crate fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
where
    F: FnMut(&OsStr),
{
    // make it relative, if possible
    let p = p.strip_prefix(src_root).unwrap_or(p);

    let mut iter = p.components().peekable();

    while let Some(c) = iter.next() {
        if !keep_filename && iter.peek().is_none() {
            break;
        }

        match c {
            Component::ParentDir => f("up".as_ref()),
            Component::Normal(c) => f(c),
            _ => continue,
        }
    }
}

crate enum SourceContext {
    Standalone,
    Embedded { offset: usize },
}

/// Wrapper struct to render the source code of a file. This will do things like
/// adding line numbers to the left-hand side.
crate fn print_src(
    buf: &mut Buffer,
    s: &str,
    edition: Edition,
    file_span: rustc_span::Span,
    context: &Context<'_>,
    root_path: &str,
    decoration_info: Option<highlight::DecorationInfo>,
    source_context: SourceContext,
) {
    let lines = s.lines().count();
    let mut line_numbers = Buffer::empty_from(buf);
    let mut cols = 0;
    let mut tmp = lines;
    while tmp > 0 {
        cols += 1;
        tmp /= 10;
    }
    line_numbers.write_str("
");
    for i in 1..=lines {
        match source_context {
            SourceContext::Standalone => {
                writeln!(line_numbers, "{0:1$}", i, cols)
            }
            SourceContext::Embedded { offset } => {
                writeln!(line_numbers, "{0:1$}", i + offset, cols)
            }
        }
    }
    line_numbers.write_str("
");
    highlight::render_with_highlighting(
        s,
        buf,
        None,
        None,
        None,
        edition,
        Some(line_numbers),
        Some(highlight::ContextInfo { context, file_span, root_path }),
        decoration_info,
    );
}
<|endoftext|>


<commit_before>//! SGX-specific access to architectural features.
//!
//! The functionality in this module is further documented in the Intel
//! Software Developer's Manual, Volume 3, Chapter 40.
#![unstable(feature = "sgx_platform", issue = "56975")]

use mem::MaybeUninit;

/// Wrapper struct to force 16-byte alignment.
#[repr(align(16))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align16<T>(pub T);

/// Wrapper struct to force 128-byte alignment.
#[repr(align(128))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align128<T>(pub T);

/// Wrapper struct to force 512-byte alignment.
#[repr(align(512))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align512<T>(pub T);

const ENCLU_EREPORT: u32 = 0;
const ENCLU_EGETKEY: u32 = 1;

/// Call the `EGETKEY` instruction to obtain a 128-bit secret key.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32> {
    unsafe {
        let mut out = MaybeUninit::uninitialized();
        let error;

        asm!(
            "enclu"
            : "={eax}"(error)
            : "{eax}"(ENCLU_EGETKEY),
              "{rbx}"(request),
              "{rcx}"(out.get_mut())
            : "flags"
        );

        match error {
            0 => Ok(out.into_inner()),
            err => Err(err),
        }
    }
}

/// Call the `EREPORT` instruction.
///
/// This creates a cryptographic report describing the contents of the current
/// enclave. The report may be verified by the enclave described in
/// `targetinfo`.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn ereport(
    targetinfo: &Align512<[u8; 512]>,
    reportdata: &Align128<[u8; 64]>,
) -> Align512<[u8; 432]> {
    unsafe {
        let mut report = MaybeUninit::uninitialized();

        asm!(
            "enclu"
            : /* no output registers */
            : "{eax}"(ENCLU_EREPORT),
              "{rbx}"(targetinfo),
              "{rcx}"(reportdata),
              "{rdx}"(report.get_mut())
        );

        report.into_inner()
    }
}
<commit_msg>Fix undefined behavior<commit_after>//! SGX-specific access to architectural features.
//!
//! The functionality in this module is further documented in the Intel
//! Software Developer's Manual, Volume 3, Chapter 40.
#![unstable(feature = "sgx_platform", issue = "56975")]

use mem::MaybeUninit;

/// Wrapper struct to force 16-byte alignment.
#[repr(align(16))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align16<T>(pub T);

/// Wrapper struct to force 128-byte alignment.
#[repr(align(128))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align128<T>(pub T);

/// Wrapper struct to force 512-byte alignment.
#[repr(align(512))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align512<T>(pub T);

const ENCLU_EREPORT: u32 = 0;
const ENCLU_EGETKEY: u32 = 1;

/// Call the `EGETKEY` instruction to obtain a 128-bit secret key.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32> {
    unsafe {
        let mut out = MaybeUninit::uninitialized();
        let error;

        asm!(
            "enclu"
            : "={eax}"(error)
            : "{eax}"(ENCLU_EGETKEY),
              "{rbx}"(request),
              "{rcx}"(out.as_mut_ptr())
            : "flags"
        );

        match error {
            0 => Ok(out.into_inner()),
            err => Err(err),
        }
    }
}

/// Call the `EREPORT` instruction.
///
/// This creates a cryptographic report describing the contents of the current
/// enclave. The report may be verified by the enclave described in
/// `targetinfo`.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn ereport(
    targetinfo: &Align512<[u8; 512]>,
    reportdata: &Align128<[u8; 64]>,
) -> Align512<[u8; 432]> {
    unsafe {
        let mut report = MaybeUninit::uninitialized();

        asm!(
            "enclu"
            : /* no output registers */
            : "{eax}"(ENCLU_EREPORT),
              "{rbx}"(targetinfo),
              "{rcx}"(reportdata),
              "{rdx}"(report.as_mut_ptr())
        );

        report.into_inner()
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

extern crate collections;
//~^ NOTE previous import of `collections` here

mod foo {
    pub trait collections {
        fn do_something();
    }
}

use foo::collections;
//~^ ERROR E0254
//~| NOTE already imported

fn main() {}
<commit_msg>Fix tidy tests<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

extern crate collections;
//~^ NOTE previous import of `collections` here

mod foo {
    pub trait collections {
        fn do_something();
    }
}

use foo::collections;
//~^ ERROR E0254
//~| NOTE already imported

fn main() {}
<|endoftext|>


<commit_before>// run-pass
// compile-flags: -Z mir-opt-level=2 -C opt-level=0

// example from #68867
pub enum E<'a> {
    Empty,
    Some(&'a E<'a>),
}

fn f(e: &E) -> u32 {
   if let E::Some(E::Some(_)) = e { 1 } else { 2 }
}

fn main() {
   assert_eq!(f(&E::Empty), 2);
}
<commit_msg>fix test case issue ref<commit_after>// run-pass
// compile-flags: -Z mir-opt-level=2 -C opt-level=0

// example from #78496
pub enum E<'a> {
    Empty,
    Some(&'a E<'a>),
}

fn f(e: &E) -> u32 {
   if let E::Some(E::Some(_)) = e { 1 } else { 2 }
}

fn main() {
   assert_eq!(f(&E::Empty), 2);
}
<|endoftext|>


<commit_before>extern crate glib;
use glib::Cast;

extern crate gstreamer as gst;
use gstreamer::{TagSetterExt, Toc, TocEntry, TocEntryType, TocScope, TocSetterExt};

use std::collections::HashMap;

use std::i64;

use super::{Chapter, Exporter};

static EXTENSION: &'static str = "toc.mkv";
static AUDIO_EXTENSION: &'static str = "toc.mka";

pub struct MatroskaTocFormat {
}

impl MatroskaTocFormat {
    pub fn get_extension() -> &'static str {
        &EXTENSION
    }

    pub fn get_audio_extension() -> &'static str {
        &AUDIO_EXTENSION
    }

    pub fn new() -> Self {
        MatroskaTocFormat{}
    }
}

impl Exporter for MatroskaTocFormat {
    fn export(&self,
        metadata: &HashMap<String, String>,
        chapters: &[Chapter],
        destination: &gst::Element,
    ) {
        {
            let tag_setter =
                destination.clone().dynamic_cast::<gst::TagSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TagSetter");

            let mut tag_list = gst::TagList::new();
            {
                let tag_list = tag_list.get_mut().unwrap();
                if let Some(title) = metadata.get(super::METADATA_TITLE) {
                    tag_list.add::<gst::tags::Title>(title, gst::TagMergeMode::Append);
                }
                if let Some(artist) = metadata.get(super::METADATA_ARTIST) {
                    tag_list.add::<gst::tags::Artist>(artist, gst::TagMergeMode::Append);
                }
            }

            println!("**** SETTING Tags");
            tag_setter.merge_tags(&tag_list, gst::TagMergeMode::Append)
        }

        {
            let toc_setter =
                destination.clone().dynamic_cast::<gst::TocSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TocSetter");

            toc_setter.reset();

            let mut toc = Toc::new(TocScope::Global);
            let mut toc_entry = TocEntry::new(TocEntryType::Edition, "00");
            {
                let toc_entry = toc_entry.get_mut().unwrap();

                let mut min_pos = i64::MAX;
                let mut max_pos = 0i64;

                for (index, chapter) in chapters.iter().enumerate() {
                    let mut toc_sub_entry = TocEntry::new(
                        TocEntryType::Chapter,
                        &format!("{:02}", index + 1),
                    );

                    let mut tag_list = gst::TagList::new();
                    tag_list.get_mut().unwrap()
                        .add::<gst::tags::Title>(
                            &chapter.get_title().unwrap_or(super::DEFAULT_TITLE).to_owned(),
                            gst::TagMergeMode::Append,
                        );
                    toc_sub_entry.get_mut().unwrap()
                        .set_tags(tag_list);

                    let start = chapter.start.nano_total as i64;
                    let end = chapter.start.nano_total as i64;
                    toc_entry.set_start_stop_times(start, end);

                    if start < min_pos {
                        min_pos = start;
                    }
                    if end > max_pos {
                        max_pos = end;
                    }

                    toc_entry.append_sub_entry(toc_sub_entry);
                }

                toc_entry.set_start_stop_times(min_pos, max_pos);
            }

            toc.get_mut().unwrap().append_entry(toc_entry);

            println!("**** SETTING TOC");
            toc_setter.set_toc(&toc);
        }
    }
}
<commit_msg>Export to matroska: fix start / stop in toc sub entry (#56)<commit_after>extern crate glib;
use glib::Cast;

extern crate gstreamer as gst;
use gstreamer::{TagSetterExt, Toc, TocEntry, TocEntryType, TocScope, TocSetterExt};

use std::collections::HashMap;

use std::i64;

use super::{Chapter, Exporter};

static EXTENSION: &'static str = "toc.mkv";
static AUDIO_EXTENSION: &'static str = "toc.mka";

pub struct MatroskaTocFormat {
}

impl MatroskaTocFormat {
    pub fn get_extension() -> &'static str {
        &EXTENSION
    }

    pub fn get_audio_extension() -> &'static str {
        &AUDIO_EXTENSION
    }

    pub fn new() -> Self {
        MatroskaTocFormat{}
    }
}

impl Exporter for MatroskaTocFormat {
    fn export(&self,
        metadata: &HashMap<String, String>,
        chapters: &[Chapter],
        destination: &gst::Element,
    ) {
        {
            let tag_setter =
                destination.clone().dynamic_cast::<gst::TagSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TagSetter");

            let mut tag_list = gst::TagList::new();
            {
                let tag_list = tag_list.get_mut().unwrap();
                if let Some(title) = metadata.get(super::METADATA_TITLE) {
                    tag_list.add::<gst::tags::Title>(title, gst::TagMergeMode::Append);
                }
                if let Some(artist) = metadata.get(super::METADATA_ARTIST) {
                    tag_list.add::<gst::tags::Artist>(artist, gst::TagMergeMode::Append);
                }
            }

            println!("**** SETTING Tags");
            tag_setter.merge_tags(&tag_list, gst::TagMergeMode::Append)
        }

        {
            let toc_setter =
                destination.clone().dynamic_cast::<gst::TocSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TocSetter");

            toc_setter.reset();

            let mut toc = Toc::new(TocScope::Global);
            let mut toc_entry = TocEntry::new(TocEntryType::Edition, "00");
            {
                let toc_entry = toc_entry.get_mut().unwrap();

                let mut min_pos = i64::MAX;
                let mut max_pos = 0i64;

                for (index, chapter) in chapters.iter().enumerate() {
                    let mut toc_sub_entry = TocEntry::new(
                        TocEntryType::Chapter,
                        &format!("{:02}", index + 1),
                    );

                    let mut tag_list = gst::TagList::new();
                    tag_list.get_mut().unwrap()
                        .add::<gst::tags::Title>(
                            &chapter.get_title().unwrap_or(super::DEFAULT_TITLE).to_owned(),
                            gst::TagMergeMode::Append,
                        );
                    toc_sub_entry.get_mut().unwrap()
                        .set_tags(tag_list);

                    let start = chapter.start.nano_total as i64;
                    let end = chapter.end.nano_total as i64;
                    toc_sub_entry.get_mut().unwrap().set_start_stop_times(start, end);

                    if start < min_pos {
                        min_pos = start;
                    }
                    if end > max_pos {
                        max_pos = end;
                    }

                    toc_entry.append_sub_entry(toc_sub_entry);
                }

                toc_entry.set_start_stop_times(min_pos, max_pos);
            }

            toc.get_mut().unwrap().append_entry(toc_entry);

            println!("**** SETTING TOC");
            toc_setter.set_toc(&toc);
        }
    }
}
<|endoftext|>


<commit_before>extern crate gtk;
use gtk::prelude::*;

extern crate lazy_static;

use metadata::{Chapter, Timestamp};

const START_COL: i32 = 0;
const END_COL: i32 = 1;
const TITLE_COL: i32 = 2;
const START_STR_COL: i32 = 3;
const END_STR_COL: i32 = 4;

lazy_static! {
    static ref DEFAULT_TITLE: String = "untitled".to_owned();
}

pub struct ChapterEntry<'a> {
    store: &'a gtk::TreeStore,
    iter: &'a gtk::TreeIter,
}

impl<'a> ChapterEntry<'a> {
    pub fn new(store: &'a gtk::TreeStore, iter: &'a gtk::TreeIter) -> ChapterEntry<'a> {
        ChapterEntry {
            store: store,
            iter: iter,
        }
    }

    pub fn title(&self) -> String {
        ChapterEntry::get_title(self.store, self.iter)
    }

    pub fn start(&self) -> u64 {
        ChapterEntry::get_start(self.store, self.iter)
    }

    pub fn start_str(&self) -> String {
        ChapterEntry::get_start_str(self.store, self.iter)
    }

    pub fn start_ts(&self) -> Timestamp {
        Timestamp::from_nano(ChapterEntry::get_start(self.store, self.iter))
    }

    pub fn end(&self) -> u64 {
        ChapterEntry::get_end(self.store, self.iter)
    }

    pub fn end_str(&self) -> String {
        ChapterEntry::get_end_str(self.store, self.iter)
    }

    pub fn end_ts(&self) -> Timestamp {
        Timestamp::from_nano(ChapterEntry::get_end(self.store, self.iter))
    }

    pub fn get_title(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> String {
        store.get_value(iter, TITLE_COL).get::<String>().unwrap()
    }

    pub fn get_start(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> u64 {
        store.get_value(iter, START_COL).get::<u64>().unwrap()
    }

    pub fn get_start_str(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> String {
        store
            .get_value(iter, START_STR_COL)
            .get::<String>()
            .unwrap()
    }

    pub fn get_end(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> u64 {
        store.get_value(iter, END_COL).get::<u64>().unwrap()
    }

    pub fn get_end_str(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> String {
        store.get_value(iter, END_STR_COL).get::<String>().unwrap()
    }
}

pub struct ChapterTreeManager {
    store: gtk::TreeStore,
    iter: Option<gtk::TreeIter>,
    selected_iter: Option<gtk::TreeIter>,
}

impl ChapterTreeManager {
    pub fn new_from(store: gtk::TreeStore) -> Self {
        ChapterTreeManager {
            store: store,
            iter: None,
            selected_iter: None,
        }
    }

    pub fn init_treeview(&self, treeview: &gtk::TreeView) {
        treeview.set_model(Some(&self.store));
        self.add_column(treeview, "Title", TITLE_COL, true, true);
        self.add_column(treeview, "Start", START_STR_COL, false, false);
        self.add_column(treeview, "End", END_STR_COL, false, false);
    }

    fn add_column(
        &self,
        treeview: &gtk::TreeView,
        title: &str,
        col_id: i32,
        can_expand: bool,
        is_editable: bool,
    ) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);

        let renderer = gtk::CellRendererText::new();
        if is_editable {
            renderer.set_property_editable(true);
            let store_clone = self.store.clone();
            renderer.connect_edited(move |_, tree_path, value| {
                if let Some(iter) = store_clone.get_iter(&tree_path) {
                    store_clone.set_value(&iter, TITLE_COL as u32, &gtk::Value::from(value));
                }
            });
        }

        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id);
        if can_expand {
            col.set_min_width(70);
            col.set_expand(can_expand);
        } else {
            // align right
            renderer.set_property_xalign(1f32);
        }
        treeview.append_column(&col);
    }

    pub fn get_selected_iter(&self) -> Option<gtk::TreeIter> {
        self.selected_iter.clone()
    }

    pub fn get_chapter_at_iter<'a: 'b, 'b>(&'a self, iter: &'a gtk::TreeIter) -> ChapterEntry<'b> {
        ChapterEntry::new(&self.store, iter)
    }

    pub fn get_iter(&self, tree_path: &gtk::TreePath) -> Option<gtk::TreeIter> {
        self.store.get_iter(tree_path)
    }

    pub fn unselect(&mut self) {
        self.selected_iter = None;
    }

    pub fn rewind(&mut self) {
        self.iter = self.store.get_iter_first();
    }

    pub fn clear(&mut self) {
        self.store.clear();
    }

    pub fn replace_with(&mut self, chapter_list: &[Chapter]) {
        self.clear();

        for chapter in chapter_list.iter() {
            self.store.insert_with_values(
                None,
                None,
                &[
                    START_COL as u32,
                    END_COL as u32,
                    TITLE_COL as u32,
                    START_STR_COL as u32,
                    END_STR_COL as u32,
                ],
                &[
                    &chapter.start.nano_total,
                    &chapter.end.nano_total,
                    &chapter.get_title().unwrap_or(&DEFAULT_TITLE),
                    &format!("{}", &chapter.start),
                    &format!("{}", chapter.end),
                ],
            );
        }

        self.iter = self.store.get_iter_first();

        self.selected_iter = match self.iter {
            Some(ref iter) => {
                if ChapterEntry::get_start(&self.store, iter) == 0 {
                    Some(iter.clone())
                } else {
                    None
                }
            }
            None => None,
        };
    }

    // Iterate over the chapters and apply func to all elements until the last
    // or until func returns false
    //
    // If first_iter is_some, iterate from first_iter
    pub fn for_each<F>(&self, first_iter: Option<gtk::TreeIter>, mut func: F)
    where
        F: FnMut(ChapterEntry) -> bool, // closure must return true to keep going
    {
        let iter = match first_iter {
            Some(first_iter) => first_iter,
            None => match self.store.get_iter_first() {
                Some(first_iter) => first_iter,
                None => return,
            },
        };

        while func(ChapterEntry::new(&self.store, &iter)) && self.store.iter_next(&iter) {}
    }

    // Update chapter according to the given position
    // Returns (has_changed, prev_selected_iter)
    pub fn update_position(&mut self, position: u64) -> (bool, Option<gtk::TreeIter>) {
        let has_changed = match self.selected_iter {
            Some(ref selected_iter) => {
                if position >= ChapterEntry::get_start(&self.store, selected_iter)
                    && position < ChapterEntry::get_end(&self.store, selected_iter)
                {
                    // regular case: position in current chapter => don't change anything
                    // this check is here to save time in the most frequent case
                    return (false, None);
                }
                // chapter has changed
                true
            }
            None => false,
        };

        let prev_selected_iter = self.selected_iter.take();

        if let Some(iter) = self.iter.take() {
            // not in selected_iter or selected_iter not defined yet => find current chapter
            let mut searching_forward = true;
            while if position >= ChapterEntry::get_start(&self.store, &iter)
                && position < ChapterEntry::get_end(&self.store, &iter)
            {
                // position is in iter
                self.selected_iter = Some(iter.clone());
                self.iter = Some(iter);
                return (true, prev_selected_iter);
            } else if position >= ChapterEntry::get_end(&self.store, &iter) && searching_forward {
                // position is after iter and we were already searching forward
                self.store.iter_next(&iter)
            } else if position < ChapterEntry::get_start(&self.store, &iter) {
                // position before iter
                searching_forward = false;
                if self.store.iter_previous(&iter) {
                    // iter is still valid
                    true
                } else {
                    // before first chapter
                    self.iter = self.store.get_iter_first();
                    return (has_changed, prev_selected_iter);
                }
            } else {
                // in a gap between two chapters
                self.iter = Some(iter);
                return (has_changed, prev_selected_iter);
            } {}

            // passed the end of the last chapter
            // prevent from searching in subsequent calls
            self.iter = None;
        }

        (has_changed, prev_selected_iter)
    }

    pub fn prepare_for_seek(&mut self) {
        if self.iter.is_none() {
            // either there is no chapter or previous position had already passed the end
            // => force scanning of the chapters list in order to set iter back on track
            self.rewind();
        }
    }

    // Returns an iter on the new chapter
    pub fn add_chapter(&mut self, position: u64, duration: u64) -> Option<gtk::TreeIter> {
        let (new_iter, end, end_str) = match self.selected_iter.take() {
            Some(selected_iter) => {
                // a chapter is currently selected
                let (current_start, current_end, current_end_str) = {
                    let selected_chapter = ChapterEntry::new(&self.store, &selected_iter);
                    (
                        selected_chapter.start(),
                        selected_chapter.end(),
                        selected_chapter.end_str(),
                    )
                };

                if current_start != position {
                    // update currently selected chapter end
                    // to match the start of the newly added chapter
                    self.store.set(
                        &selected_iter,
                        &[END_COL as u32, END_STR_COL as u32],
                        &[&position, &Timestamp::format(position, false)],
                    );

                    // insert new chapter after current
                    let new_iter = self.store.insert_after(None, &selected_iter);
                    (new_iter, current_end, current_end_str)
                } else {
                    // attempting to add the new chapter at current position
                    // => restore current state
                    self.selected_iter = Some(selected_iter);
                    return None;
                }
            }
            None => {
                match self.iter.take() {
                    Some(iter) => {
                        // chapters are available, but none is selected:
                        // either position is before the first chapter
                        // or in a gap between two chapters
                        let iter_chapter = ChapterEntry::new(&self.store, &iter);
                        let start = iter_chapter.start();
                        if position > start {
                            panic!(
                                concat!(
                                    "ChapterTreeManager::add_chapter inconsistent position",
                                    " {} with regard to current iter [{}, {}]",
                                ),
                                position,
                                iter_chapter.start(),
                                iter_chapter.end(),
                            );
                        }

                        // iter is next chapter

                        (
                            self.store.insert_before(None, &iter),
                            start,
                            iter_chapter.start_str(),
                        )
                    }
                    None => {
                        // No chapter in iter:
                        // either position is passed the end of last chapter
                        // or there is no chapter
                        let insert_position = match self.store.get_iter_first() {
                                    None => // No chapter yet => inset at the beginning
                                        0i32,
                                    Some(_) => // store contains chapters => insert at the end
                                        -1i32,
                                };
                        (
                            self.store.insert(None, insert_position),
                            duration,
                            Timestamp::format(duration, false),
                        )
                    }
                }
            }
        };

        self.store.set(
            &new_iter,
            &[
                TITLE_COL as u32,
                START_COL as u32,
                START_STR_COL as u32,
                END_COL as u32,
                END_STR_COL as u32,
            ],
            &[
                &*DEFAULT_TITLE,
                &position,
                &Timestamp::format(position, false),
                &end,
                &end_str,
            ],
        );

        self.selected_iter = Some(new_iter.clone());
        self.iter = Some(new_iter.clone());
        Some(new_iter)
    }

    // Returns an iter on the chapter which should be selected, if any
    pub fn remove_selected_chapter(&mut self) -> Option<gtk::TreeIter> {
        match self.selected_iter.take() {
            Some(selected_iter) => {
                let prev_iter = selected_iter.clone();
                let next_selected_iter = if self.store.iter_previous(&prev_iter) {
                    // a previous chapter is available => update its end
                    // with the end of currently selected chapter
                    let selected_chapter = ChapterEntry::new(&self.store, &selected_iter);
                    self.store.set(
                        &prev_iter,
                        &[END_COL as u32, END_STR_COL as u32],
                        &[&selected_chapter.end(), &selected_chapter.end_str()],
                    );

                    Some(prev_iter)
                } else {
                    // no chapter before => nothing to select
                    None
                };

                self.store.remove(&selected_iter);
                self.selected_iter = next_selected_iter.clone();
                match next_selected_iter {
                    None =>
                        // No chapter before => rewind
                        self.rewind(),
                    Some(ref next_selected_iter) => self.iter = Some(next_selected_iter.clone()),
                }
                next_selected_iter
            }
            None => None,
        }
    }
}
<commit_msg>Chapter Tree Manager: fix column type<commit_after>extern crate gtk;
use gtk::prelude::*;

extern crate lazy_static;

use metadata::{Chapter, Timestamp};

const START_COL: u32 = 0;
const END_COL: u32 = 1;
const TITLE_COL: u32 = 2;
const START_STR_COL: u32 = 3;
const END_STR_COL: u32 = 4;

lazy_static! {
    static ref DEFAULT_TITLE: String = "untitled".to_owned();
}

pub struct ChapterEntry<'a> {
    store: &'a gtk::TreeStore,
    iter: &'a gtk::TreeIter,
}

impl<'a> ChapterEntry<'a> {
    pub fn new(store: &'a gtk::TreeStore, iter: &'a gtk::TreeIter) -> ChapterEntry<'a> {
        ChapterEntry {
            store: store,
            iter: iter,
        }
    }

    pub fn title(&self) -> String {
        ChapterEntry::get_title(self.store, self.iter)
    }

    pub fn start(&self) -> u64 {
        ChapterEntry::get_start(self.store, self.iter)
    }

    pub fn start_str(&self) -> String {
        ChapterEntry::get_start_str(self.store, self.iter)
    }

    pub fn start_ts(&self) -> Timestamp {
        Timestamp::from_nano(ChapterEntry::get_start(self.store, self.iter))
    }

    pub fn end(&self) -> u64 {
        ChapterEntry::get_end(self.store, self.iter)
    }

    pub fn end_str(&self) -> String {
        ChapterEntry::get_end_str(self.store, self.iter)
    }

    pub fn end_ts(&self) -> Timestamp {
        Timestamp::from_nano(ChapterEntry::get_end(self.store, self.iter))
    }

    pub fn get_title(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> String {
        store.get_value(iter, TITLE_COL as i32).get::<String>().unwrap()
    }

    pub fn get_start(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> u64 {
        store.get_value(iter, START_COL as i32).get::<u64>().unwrap()
    }

    pub fn get_start_str(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> String {
        store
            .get_value(iter, START_STR_COL as i32)
            .get::<String>()
            .unwrap()
    }

    pub fn get_end(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> u64 {
        store.get_value(iter, END_COL as i32).get::<u64>().unwrap()
    }

    pub fn get_end_str(store: &gtk::TreeStore, iter: &gtk::TreeIter) -> String {
        store.get_value(iter, END_STR_COL as i32).get::<String>().unwrap()
    }
}

pub struct ChapterTreeManager {
    store: gtk::TreeStore,
    iter: Option<gtk::TreeIter>,
    selected_iter: Option<gtk::TreeIter>,
}

impl ChapterTreeManager {
    pub fn new_from(store: gtk::TreeStore) -> Self {
        ChapterTreeManager {
            store: store,
            iter: None,
            selected_iter: None,
        }
    }

    pub fn init_treeview(&self, treeview: &gtk::TreeView) {
        treeview.set_model(Some(&self.store));
        self.add_column(treeview, "Title", TITLE_COL, true, true);
        self.add_column(treeview, "Start", START_STR_COL, false, false);
        self.add_column(treeview, "End", END_STR_COL, false, false);
    }

    fn add_column(
        &self,
        treeview: &gtk::TreeView,
        title: &str,
        col_id: u32,
        can_expand: bool,
        is_editable: bool,
    ) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);

        let renderer = gtk::CellRendererText::new();
        if is_editable {
            renderer.set_property_editable(true);
            let store_clone = self.store.clone();
            renderer.connect_edited(move |_, tree_path, value| {
                if let Some(iter) = store_clone.get_iter(&tree_path) {
                    store_clone.set_value(&iter, TITLE_COL, &gtk::Value::from(value));
                }
            });
        }

        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id as i32);
        if can_expand {
            col.set_min_width(70);
            col.set_expand(can_expand);
        } else {
            // align right
            renderer.set_property_xalign(1f32);
        }
        treeview.append_column(&col);
    }

    pub fn get_selected_iter(&self) -> Option<gtk::TreeIter> {
        self.selected_iter.clone()
    }

    pub fn get_chapter_at_iter<'a: 'b, 'b>(&'a self, iter: &'a gtk::TreeIter) -> ChapterEntry<'b> {
        ChapterEntry::new(&self.store, iter)
    }

    pub fn get_iter(&self, tree_path: &gtk::TreePath) -> Option<gtk::TreeIter> {
        self.store.get_iter(tree_path)
    }

    pub fn unselect(&mut self) {
        self.selected_iter = None;
    }

    pub fn rewind(&mut self) {
        self.iter = self.store.get_iter_first();
    }

    pub fn clear(&mut self) {
        self.store.clear();
    }

    pub fn replace_with(&mut self, chapter_list: &[Chapter]) {
        self.clear();

        for chapter in chapter_list.iter() {
            self.store.insert_with_values(
                None,
                None,
                &[
                    START_COL,
                    END_COL,
                    TITLE_COL,
                    START_STR_COL,
                    END_STR_COL,
                ],
                &[
                    &chapter.start.nano_total,
                    &chapter.end.nano_total,
                    &chapter.get_title().unwrap_or(&DEFAULT_TITLE),
                    &format!("{}", &chapter.start),
                    &format!("{}", chapter.end),
                ],
            );
        }

        self.iter = self.store.get_iter_first();

        self.selected_iter = match self.iter {
            Some(ref iter) => {
                if ChapterEntry::get_start(&self.store, iter) == 0 {
                    Some(iter.clone())
                } else {
                    None
                }
            }
            None => None,
        };
    }

    // Iterate over the chapters and apply func to all elements until the last
    // or until func returns false
    //
    // If first_iter is_some, iterate from first_iter
    pub fn for_each<F>(&self, first_iter: Option<gtk::TreeIter>, mut func: F)
    where
        F: FnMut(ChapterEntry) -> bool, // closure must return true to keep going
    {
        let iter = match first_iter {
            Some(first_iter) => first_iter,
            None => match self.store.get_iter_first() {
                Some(first_iter) => first_iter,
                None => return,
            },
        };

        while func(ChapterEntry::new(&self.store, &iter)) && self.store.iter_next(&iter) {}
    }

    // Update chapter according to the given position
    // Returns (has_changed, prev_selected_iter)
    pub fn update_position(&mut self, position: u64) -> (bool, Option<gtk::TreeIter>) {
        let has_changed = match self.selected_iter {
            Some(ref selected_iter) => {
                if position >= ChapterEntry::get_start(&self.store, selected_iter)
                    && position < ChapterEntry::get_end(&self.store, selected_iter)
                {
                    // regular case: position in current chapter => don't change anything
                    // this check is here to save time in the most frequent case
                    return (false, None);
                }
                // chapter has changed
                true
            }
            None => false,
        };

        let prev_selected_iter = self.selected_iter.take();

        if let Some(iter) = self.iter.take() {
            // not in selected_iter or selected_iter not defined yet => find current chapter
            let mut searching_forward = true;
            while if position >= ChapterEntry::get_start(&self.store, &iter)
                && position < ChapterEntry::get_end(&self.store, &iter)
            {
                // position is in iter
                self.selected_iter = Some(iter.clone());
                self.iter = Some(iter);
                return (true, prev_selected_iter);
            } else if position >= ChapterEntry::get_end(&self.store, &iter) && searching_forward {
                // position is after iter and we were already searching forward
                self.store.iter_next(&iter)
            } else if position < ChapterEntry::get_start(&self.store, &iter) {
                // position before iter
                searching_forward = false;
                if self.store.iter_previous(&iter) {
                    // iter is still valid
                    true
                } else {
                    // before first chapter
                    self.iter = self.store.get_iter_first();
                    return (has_changed, prev_selected_iter);
                }
            } else {
                // in a gap between two chapters
                self.iter = Some(iter);
                return (has_changed, prev_selected_iter);
            } {}

            // passed the end of the last chapter
            // prevent from searching in subsequent calls
            self.iter = None;
        }

        (has_changed, prev_selected_iter)
    }

    pub fn prepare_for_seek(&mut self) {
        if self.iter.is_none() {
            // either there is no chapter or previous position had already passed the end
            // => force scanning of the chapters list in order to set iter back on track
            self.rewind();
        }
    }

    // Returns an iter on the new chapter
    pub fn add_chapter(&mut self, position: u64, duration: u64) -> Option<gtk::TreeIter> {
        let (new_iter, end, end_str) = match self.selected_iter.take() {
            Some(selected_iter) => {
                // a chapter is currently selected
                let (current_start, current_end, current_end_str) = {
                    let selected_chapter = ChapterEntry::new(&self.store, &selected_iter);
                    (
                        selected_chapter.start(),
                        selected_chapter.end(),
                        selected_chapter.end_str(),
                    )
                };

                if current_start != position {
                    // update currently selected chapter end
                    // to match the start of the newly added chapter
                    self.store.set(
                        &selected_iter,
                        &[END_COL, END_STR_COL],
                        &[&position, &Timestamp::format(position, false)],
                    );

                    // insert new chapter after current
                    let new_iter = self.store.insert_after(None, &selected_iter);
                    (new_iter, current_end, current_end_str)
                } else {
                    // attempting to add the new chapter at current position
                    // => restore current state
                    self.selected_iter = Some(selected_iter);
                    return None;
                }
            }
            None => {
                match self.iter.take() {
                    Some(iter) => {
                        // chapters are available, but none is selected:
                        // either position is before the first chapter
                        // or in a gap between two chapters
                        let iter_chapter = ChapterEntry::new(&self.store, &iter);
                        let start = iter_chapter.start();
                        if position > start {
                            panic!(
                                concat!(
                                    "ChapterTreeManager::add_chapter inconsistent position",
                                    " {} with regard to current iter [{}, {}]",
                                ),
                                position,
                                iter_chapter.start(),
                                iter_chapter.end(),
                            );
                        }

                        // iter is next chapter

                        (
                            self.store.insert_before(None, &iter),
                            start,
                            iter_chapter.start_str(),
                        )
                    }
                    None => {
                        // No chapter in iter:
                        // either position is passed the end of last chapter
                        // or there is no chapter
                        let insert_position = match self.store.get_iter_first() {
                                    None => // No chapter yet => inset at the beginning
                                        0i32,
                                    Some(_) => // store contains chapters => insert at the end
                                        -1i32,
                                };
                        (
                            self.store.insert(None, insert_position),
                            duration,
                            Timestamp::format(duration, false),
                        )
                    }
                }
            }
        };

        self.store.set(
            &new_iter,
            &[
                TITLE_COL,
                START_COL,
                START_STR_COL,
                END_COL,
                END_STR_COL,
            ],
            &[
                &*DEFAULT_TITLE,
                &position,
                &Timestamp::format(position, false),
                &end,
                &end_str,
            ],
        );

        self.selected_iter = Some(new_iter.clone());
        self.iter = Some(new_iter.clone());
        Some(new_iter)
    }

    // Returns an iter on the chapter which should be selected, if any
    pub fn remove_selected_chapter(&mut self) -> Option<gtk::TreeIter> {
        match self.selected_iter.take() {
            Some(selected_iter) => {
                let prev_iter = selected_iter.clone();
                let next_selected_iter = if self.store.iter_previous(&prev_iter) {
                    // a previous chapter is available => update its end
                    // with the end of currently selected chapter
                    let selected_chapter = ChapterEntry::new(&self.store, &selected_iter);
                    self.store.set(
                        &prev_iter,
                        &[END_COL, END_STR_COL],
                        &[&selected_chapter.end(), &selected_chapter.end_str()],
                    );

                    Some(prev_iter)
                } else {
                    // no chapter before => nothing to select
                    None
                };

                self.store.remove(&selected_iter);
                self.selected_iter = next_selected_iter.clone();
                match next_selected_iter {
                    None =>
                        // No chapter before => rewind
                        self.rewind(),
                    Some(ref next_selected_iter) => self.iter = Some(next_selected_iter.clone()),
                }
                next_selected_iter
            }
            None => None,
        }
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use regex::Regex;
use reqwest;
use rust_htslib::bcf;
use rust_htslib::bcf::Read;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::str;

#[derive(Serialize, Deserialize, Debug)]
struct Dgidb {
    #[serde(rename = "matchedTerms")]
    matched_terms: Vec<MatchedTerm>,
}

#[derive(Serialize, Deserialize, Debug)]
struct MatchedTerm {
    #[serde(rename = "geneName")]
    gene_name: String,
    interactions: Vec<Interaction>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Interaction {
    #[serde(rename = "drugName")]
    drug_name: String,
    #[serde(rename = "interactionTypes")]
    interaction_types: Vec<String>,
}

pub fn annotate_dgidb(
    vcf_path: &str,
    api_path: String,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let gene_drug_interactions = request_interaction_drugs(vcf_path, api_path)?;
    modify_vcf_entries(vcf_path, gene_drug_interactions, field_name)
}

fn request_interaction_drugs(
    vcf_path: &str,
    mut api_path: String,
) -> Result<HashMap<String, Vec<(String, Vec<String>)>>, Box<dyn Error>> {
    let mut genes = collect_genes(vcf_path)?;
    api_path.push_str(genes.drain().join(",").as_str());
    let res: Dgidb = reqwest::get(&api_path)?.json()?;

    let mut gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>> = HashMap::new();
    for term in res.matched_terms {
        if !term.interactions.is_empty() {
            gene_drug_interactions.insert(
                term.gene_name,
                term.interactions
                    .iter()
                    .map(|interaction| {
                        (
                            interaction.drug_name.clone(),
                            interaction.interaction_types.clone(),
                        )
                    })
                    .collect(),
            );
        }
    }
    Ok(gene_drug_interactions)
}

fn collect_genes(vcf_path: &str) -> Result<HashSet<String>, Box<dyn Error>> {
    let mut total_genes = HashSet::new();
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    for result in reader.records() {
        let mut rec = result?;
        let genes_opt = extract_genes(&mut rec)?;
        match genes_opt {
            Some(genes) => {
                for gene in genes {
                    total_genes.insert(gene);
                }
            }
            None => {}
        }
    }
    Ok(total_genes)
}

//TODO Remove split by ',' when updating to latest htslib release
fn extract_genes<'a>(
    rec: &'a mut bcf::Record,
) -> Result<Option<impl Iterator<Item = String> + 'a>, Box<dyn Error>> {
    let annotation = rec.info("ANN".as_bytes()).string()?;
    match annotation {
        Some(transcripts) => Ok(Some(transcripts[0].split(|c| *c == b',').map(
            |transcript| {
                str::from_utf8(transcript.split(|c| *c == b'|').nth(3).unwrap())
                    .unwrap()
                    .to_owned()
            },
        ))),
        None => Ok(None),
    }
}

fn modify_vcf_entries(
    vcf_path: &str,
    gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>>,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    let mut header = bcf::header::Header::from_template(reader.header());
    header.push_record(format!("##INFO=<ID={},Number=.,Type=String,Description=\"Combination of gene, drug, interaction types extracted from dgiDB. Each combination is pipe-seperated annotated as GENE|DRUG|TYPE\">", field_name).as_bytes());
    let mut writer = bcf::Writer::from_stdout(&header, true, true)?;

    for result in reader.records() {
        let mut rec = result?;
        writer.translate(&mut rec);
        let genes = extract_genes(&mut rec)?.map(|genes| genes.collect_vec());;
        if let Some(mut genes) = genes {
            genes.dedup();
            let field_entries = build_dgidb_field(&gene_drug_interactions, genes)?;
            let field_entries: Vec<&[u8]> = field_entries.iter().map(|v| v.as_slice()).collect();
            rec.push_info_string(field_name.as_bytes(), &field_entries[..])?;
        }
        writer.write(&rec)?;
    }
    Ok(())
}

fn build_dgidb_field(
    gene_drug_interactions: &HashMap<String, Vec<(String, Vec<String>)>>,
    genes: Vec<String>,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
    let mut field_entries: Vec<Vec<u8>> = Vec::new();
    let re = Regex::new(r"\s\(\w+\)").unwrap();
    for gene in genes.iter() {
        match gene_drug_interactions.get(gene) {
            Some(drug_interactions) => {
                drug_interactions
                    .iter()
                    .for_each(|(drug, interaction_types)| {
                        if !interaction_types.is_empty() {
                            interaction_types.iter().for_each(|interaction_type| {
                                field_entries.push(
                                    format!(
                                        "{g}|{d}|{t}",
                                        g = gene,
                                        d = re.replace(drug, ""),
                                        t = interaction_type
                                    )
                                    .as_bytes()
                                    .to_vec(),
                                )
                            })
                        } else {
                            field_entries.push(
                                format!("{g}|{d}|.", g = gene, d = re.replace(drug, ""))
                                    .as_bytes()
                                    .to_vec(),
                            )
                        }
                    });
            }
            None => field_entries.push(format!("{g}|.|.", g = gene).as_bytes().to_vec()),
        }
    }
    Ok(field_entries)
}
<commit_msg>Fixed handling of empty vcf files<commit_after>use itertools::Itertools;
use regex::Regex;
use reqwest;
use rust_htslib::bcf;
use rust_htslib::bcf::Read;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::str;

#[derive(Serialize, Deserialize, Debug)]
struct Dgidb {
    #[serde(rename = "matchedTerms")]
    matched_terms: Vec<MatchedTerm>,
}

#[derive(Serialize, Deserialize, Debug)]
struct MatchedTerm {
    #[serde(rename = "geneName")]
    gene_name: String,
    interactions: Vec<Interaction>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Interaction {
    #[serde(rename = "drugName")]
    drug_name: String,
    #[serde(rename = "interactionTypes")]
    interaction_types: Vec<String>,
}

pub fn annotate_dgidb(
    vcf_path: &str,
    api_path: String,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let gene_drug_interactions = request_interaction_drugs(vcf_path, api_path)?;
    modify_vcf_entries(vcf_path, gene_drug_interactions, field_name)
}

fn request_interaction_drugs(
    vcf_path: &str,
    mut api_path: String,
) -> Result<Option<HashMap<String, Vec<(String, Vec<String>)>>>, Box<dyn Error>> {
    let mut genes = collect_genes(vcf_path)?;
    if genes.is_empty() {
        return Ok(None)
    }
    api_path.push_str(genes.drain().join(",").as_str());
    let res: Dgidb = reqwest::get(&api_path)?.json()?;

    let mut gene_drug_interactions: HashMap<String, Vec<(String, Vec<String>)>> = HashMap::new();
    for term in res.matched_terms {
        if !term.interactions.is_empty() {
            gene_drug_interactions.insert(
                term.gene_name,
                term.interactions
                    .iter()
                    .map(|interaction| {
                        (
                            interaction.drug_name.clone(),
                            interaction.interaction_types.clone(),
                        )
                    })
                    .collect(),
            );
        }
    }
    Ok(Some(gene_drug_interactions))
}

fn collect_genes(vcf_path: &str) -> Result<HashSet<String>, Box<dyn Error>> {
    let mut total_genes = HashSet::new();
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    for result in reader.records() {
        let mut rec = result?;
        let genes_opt = extract_genes(&mut rec)?;
        match genes_opt {
            Some(genes) => {
                for gene in genes {
                    total_genes.insert(gene);
                }
            }
            None => {}
        }
    }
    Ok(total_genes)
}

//TODO Remove split by ',' when updating to latest htslib release
fn extract_genes<'a>(
    rec: &'a mut bcf::Record,
) -> Result<Option<impl Iterator<Item = String> + 'a>, Box<dyn Error>> {
    let annotation = rec.info("ANN".as_bytes()).string()?;
    match annotation {
        Some(transcripts) => Ok(Some(transcripts[0].split(|c| *c == b',').map(
            |transcript| {
                str::from_utf8(transcript.split(|c| *c == b'|').nth(3).unwrap())
                    .unwrap()
                    .to_owned()
            },
        ))),
        None => Ok(None),
    }
}

fn modify_vcf_entries(
    vcf_path: &str,
    gene_drug_interactions_opt: Option<HashMap<String, Vec<(String, Vec<String>)>>>,
    field_name: &str,
) -> Result<(), Box<dyn Error>> {
    let mut reader = bcf::Reader::from_path(vcf_path)?;
    let mut header = bcf::header::Header::from_template(reader.header());
    match gene_drug_interactions_opt {
        None => {
            bcf::Writer::from_stdout(&header, true, true)?;
        },
        Some(gene_drug_interactions) => {
            header.push_record(format!("##INFO=<ID={},Number=.,Type=String,Description=\"Combination of gene, drug, interaction types extracted from dgiDB. Each combination is pipe-seperated annotated as GENE|DRUG|TYPE\">", field_name).as_bytes());
            let mut writer = bcf::Writer::from_stdout(&header, true, true)?;

            for result in reader.records() {
                let mut rec = result?;
                writer.translate(&mut rec);
                let genes = extract_genes(&mut rec)?.map(|genes| genes.collect_vec());
                if let Some(mut genes) = genes {
                    genes.dedup();
                    let field_entries = build_dgidb_field(&gene_drug_interactions, genes)?;
                    let field_entries: Vec<&[u8]> = field_entries.iter().map(|v| v.as_slice()).collect();
                    rec.push_info_string(field_name.as_bytes(), &field_entries[..])?;
                }
                writer.write(&rec)?;
            }
        }
    }

    Ok(())
}

fn build_dgidb_field(
    gene_drug_interactions: &HashMap<String, Vec<(String, Vec<String>)>>,
    genes: Vec<String>,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
    let mut field_entries: Vec<Vec<u8>> = Vec::new();
    let re = Regex::new(r"\s\(\w+\)").unwrap();
    for gene in genes.iter() {
        match gene_drug_interactions.get(gene) {
            Some(drug_interactions) => {
                drug_interactions
                    .iter()
                    .for_each(|(drug, interaction_types)| {
                        if !interaction_types.is_empty() {
                            interaction_types.iter().for_each(|interaction_type| {
                                field_entries.push(
                                    format!(
                                        "{g}|{d}|{t}",
                                        g = gene,
                                        d = re.replace(drug, ""),
                                        t = interaction_type
                                    )
                                    .as_bytes()
                                    .to_vec(),
                                )
                            })
                        } else {
                            field_entries.push(
                                format!("{g}|{d}|.", g = gene, d = re.replace(drug, ""))
                                    .as_bytes()
                                    .to_vec(),
                            )
                        }
                    });
            }
            None => field_entries.push(format!("{g}|.|.", g = gene).as_bytes().to_vec()),
        }
    }
    Ok(field_entries)
}
<|endoftext|>


<commit_before>#![allow(warnings)]

pub const STILL: u8 = 0;
pub const NORTH: u8 = 1;
pub const EAST: u8 = 2;
pub const SOUTH: u8 = 3;
pub const WEST: u8 = 4;

pub const DIRECTIONS: [u8; 5] = [STILL, NORTH, EAST, SOUTH, WEST];
pub const CARDINALS: [u8; 4] = [NORTH, EAST, SOUTH, WEST];

#[derive(Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub struct Location {
    pub x: u16,
    pub y: u16,
}

#[derive(Copy, Clone, Eq, Debug, PartialEq)]
pub struct Site {
    pub owner: u8,
    pub strength: u8,
    pub production: u8,
}

#[derive(Clone, Debug)]
pub struct GameMap {
    pub width: u16, //Number of columns.
    pub height: u16, //Number of rows.
    pub contents: Vec< Vec<Site> >,
}

impl GameMap {
    pub fn in_bounds(&self, l: Location) -> bool {
        l.x < self.width && l.y < self.height
    }
    pub fn get_distance(&self, l1: Location, l2: Location) -> u16 {
        let mut dx = (l1.x as i16 - l2.x as i16).abs();
        let mut dy = (l1.y as i16 - l2.y as i16).abs();
        if dx > self.width as i16 / 2 { dx = self.width as i16 - dx; }
        if dy > self.height as i16 / 2 { dy = self.height as i16 - dy; }
        (dx + dy) as u16
    }
    pub fn get_angle(&self, l1: Location, l2: Location) -> f64 {
        let mut dx = l2.x as i16- l1.x as i16;
        let mut dy = l2.y as i16 - l1.y as i16;
        if dx > self.width as i16 - dx { dx -= self.width as i16; }
        else if-dx > self.width as i16 + dx { dx += self.width as i16; }
        if dy > self.height as i16 - dy { dy -= self.height as i16; }
        else if -dy > self.height as i16 + dy { dy += self.height as i16; }
        (dy as f64).atan2(dx as f64)
    }
    pub fn get_location(&self, l: Location, d: u8) -> Location {
        let mut loc = Location { x: l.x, y: l.y };
        if d == NORTH {
            if loc.y == 0 { loc.y = self.height - 1; }
            else { loc.y -= 1; }
        } else if d == EAST {
            if loc.x == self.width - 1 { loc.x = 0; }
            else { loc.x += 1; }
        } else if d == SOUTH {
            if loc.y == self.height - 1 { loc.y = 0; }
            else { loc.y += 1; }
        } else if d == WEST {
            if loc.x == 0 { loc.x = self.width - 1; }
            else { loc.x -= 1; }
        }
        loc
    }
    pub fn get_site(&mut self, l: Location, d: u8) -> &mut Site {
        let loc = self.get_location(l, d);
        &mut self.contents[loc.y as usize][loc.x as usize]
    }
}
<commit_msg>adding get_site_ref method to types<commit_after>#![allow(warnings)]

pub const STILL: u8 = 0;
pub const NORTH: u8 = 1;
pub const EAST: u8 = 2;
pub const SOUTH: u8 = 3;
pub const WEST: u8 = 4;

pub const DIRECTIONS: [u8; 5] = [STILL, NORTH, EAST, SOUTH, WEST];
pub const CARDINALS: [u8; 4] = [NORTH, EAST, SOUTH, WEST];

#[derive(Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub struct Location {
    pub x: u16,
    pub y: u16,
}

#[derive(Copy, Clone, Eq, Debug, PartialEq)]
pub struct Site {
    pub owner: u8,
    pub strength: u8,
    pub production: u8,
}

#[derive(Clone, Debug)]
pub struct GameMap {
    pub width: u16, //Number of columns.
    pub height: u16, //Number of rows.
    pub contents: Vec< Vec<Site> >,
}

impl GameMap {
    pub fn in_bounds(&self, l: Location) -> bool {
        l.x < self.width && l.y < self.height
    }
    pub fn get_distance(&self, l1: Location, l2: Location) -> u16 {
        let mut dx = (l1.x as i16 - l2.x as i16).abs();
        let mut dy = (l1.y as i16 - l2.y as i16).abs();
        if dx > self.width as i16 / 2 { dx = self.width as i16 - dx; }
        if dy > self.height as i16 / 2 { dy = self.height as i16 - dy; }
        (dx + dy) as u16
    }
    pub fn get_angle(&self, l1: Location, l2: Location) -> f64 {
        let mut dx = l2.x as i16- l1.x as i16;
        let mut dy = l2.y as i16 - l1.y as i16;
        if dx > self.width as i16 - dx { dx -= self.width as i16; }
        else if-dx > self.width as i16 + dx { dx += self.width as i16; }
        if dy > self.height as i16 - dy { dy -= self.height as i16; }
        else if -dy > self.height as i16 + dy { dy += self.height as i16; }
        (dy as f64).atan2(dx as f64)
    }
    pub fn get_location(&self, l: Location, d: u8) -> Location {
        let mut loc = Location { x: l.x, y: l.y };
        if d == NORTH {
            if loc.y == 0 { loc.y = self.height - 1; }
            else { loc.y -= 1; }
        } else if d == EAST {
            if loc.x == self.width - 1 { loc.x = 0; }
            else { loc.x += 1; }
        } else if d == SOUTH {
            if loc.y == self.height - 1 { loc.y = 0; }
            else { loc.y += 1; }
        } else if d == WEST {
            if loc.x == 0 { loc.x = self.width - 1; }
            else { loc.x -= 1; }
        }
        loc
    }
    pub fn get_site(&mut self, l: Location, d: u8) -> &mut Site {
        let loc = self.get_location(l, d);
        &mut self.contents[loc.y as usize][loc.x as usize]
    }
    pub fn get_site_ref(&self, l: Location, d: u8) -> &Site {
        let loc = self.get_location(l, d);
        &self.contents[loc.y as usize][loc.x as usize]
    }
}
<|endoftext|>


<commit_before>extern crate quickcheck;

use std::collections::HashMap;

use Scalar;
use maths::{Approximations, Quat, Transform, UnitQuat, Vec3D};
use algorithms::{Execute, PanicOnIteration};
use collisions::CollisionData;
use collisions::geometry::ConvexPolyhedron;
use collisions::geometry::shapes::Cuboid;
use collisions::detection::gjkepa::{ContactTracker, EPA, GJK, GJKSimplex};

quickcheck! {
    fn it_generates_valid_convex_polyhedron(rot: UnitQuat) -> quickcheck::TestResult {
        let control = CollisionData::new(Box::new(Cuboid::cube(1.0)), Transform::identity());
        let data = CollisionData::new(
            Box::new(Cuboid::cube(1.0)),
            Transform {
                translation: Vec3D::zero(),
                rotation: rot.into(),
            }
        );

        let mut contact_tracker = ContactTracker::new(&control, &data);
        let simplex = find_origin(&mut contact_tracker, &control, &data)
            .expect("Expected simplex to contain origin but it did not");

        let epa_polyhedron = EPA::new(simplex, &control, &data)
            .panic_on_iteration(1000, "EPA failed to converge after 1000 iterations (in test)")
            .execute();
        let polyhedron = epa_polyhedron.polyhedron();

        // assert_vertices_are_subset_of_input(&polyhedron, &vertices)
        quickcheck_expect!(has_enough_faces(&polyhedron));
        quickcheck_expect!(edges_are_only_connected_to_two_faces(&polyhedron));
        quickcheck_expect!(all_points_are_on_the_convex_hull(&polyhedron));
        quickcheck_expect!(all_faces_are_pointing_outwards(&polyhedron));

        quickcheck::TestResult::passed()
    }
}

fn find_origin<'a>(tracker: &'a mut ContactTracker, data_0: &'a CollisionData, data_1: &'a CollisionData) -> Option<&'a GJKSimplex> {
    GJK::using_simplex(tracker.simplex_mut(), data_0, data_1)
        .panic_on_iteration(1000, "looking for origin (in tests)")
        .execute()
}

fn has_enough_faces(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    let face_count = polyhedron.faces_iter().count();
    let vertex_count = polyhedron.vertices_iter().count();

    let expected_face_count = 4 + (vertex_count - 4) * 2;

    quickcheck_assert!(face_count == expected_face_count, format!("expected polyhedron to have {} faces, but got {} instead", expected_face_count, face_count));

    quickcheck::TestResult::passed()
}

fn edges_are_only_connected_to_two_faces(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    let total_edge_counts = polyhedron.triangulation_iter()
        .flat_map(|triangulation| {
            vec!(
                sort_pair(triangulation[0], triangulation[1]),
                sort_pair(triangulation[1], triangulation[2]),
                sort_pair(triangulation[2], triangulation[0]),
            ).into_iter()
        })
        .fold(<HashMap<(usize, usize), usize>>::new(), |mut edge_counts, edge| {
            let count = edge_counts.get(&edge).cloned().unwrap_or(0usize);

            edge_counts.insert(edge, count + 1);

            return edge_counts;
        });

    let outliers: Vec<((usize, usize), usize)> = total_edge_counts.into_iter()
        .filter(|&(_edge, count)| count != 2)
        .collect();

    if outliers.len() == 0 {
        quickcheck::TestResult::passed()
    } else {
        quickcheck::TestResult::error(format!("expected all edges to be connected to two faces, but found the following outliers instead: {:?}", outliers))
    }
}

fn all_points_are_on_the_convex_hull(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    use maths::Approximations;

    let mut outside_points_count = 0;
    for face in polyhedron.faces_iter() {
        for vertex in polyhedron.vertices_iter() {
            if face.normal_projection_of(*vertex).is_strictly_positive() {
                outside_points_count += 1;
            }
        }
    }

    quickcheck_assert!(outside_points_count == 0, format!("expected polyhedron to have 0 points outside the separating planes, but found {} instead", outside_points_count));

    quickcheck::TestResult::passed()
}

fn all_faces_are_pointing_outwards(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    let mid_point = polyhedron.vertices().iter()
        .fold(Vec3D::zero(), |total, vertex| {
            total + vertex
        }) / polyhedron.vertices().len() as Scalar;

    for face in polyhedron.faces_iter() {
        quickcheck_assert!(
            face.normal_projection_of(mid_point).is_strictly_negative(),
            format!("The ConvexPolyhedron has a face ({:?}) facing the wrong way!", face.normal()),
        );
    }

    quickcheck::TestResult::passed()
}

fn sort_pair(a: usize, b: usize) -> (usize, usize) {
    if a < b {
        (a, b)
    } else {
        (b, a)
    }
}
<commit_msg>Add a commented out failing test, to catch edge case<commit_after>extern crate quickcheck;

use std::collections::HashMap;

use Scalar;
use maths::{Approximations, Quat, Transform, UnitQuat, Vec3D};
use algorithms::{Execute, PanicOnIteration};
use collisions::CollisionData;
use collisions::geometry::ConvexPolyhedron;
use collisions::geometry::shapes::Cuboid;
use collisions::detection::gjkepa::{ContactTracker, EPA, GJK, GJKSimplex};

// TODO fix this failing scenario
// #[test]
// fn failing_test_test() {
//     // TODO FIXME FIXME FIXME
//     temp(Quat::new(0.99956876, -0.015006613, -0.00038310335, 0.025238236).normalize());
//     fn temp(rot: UnitQuat) -> quickcheck::TestResult {
//         let control = CollisionData::new(Box::new(Cuboid::cube(1.0)), Transform::identity());
//         let data = CollisionData::new(
//             Box::new(Cuboid::cube(1.0)),
//             Transform {
//                 translation: Vec3D::zero(),
//                 rotation: rot.into(),
//             }
//         );
//
//         let mut contact_tracker = ContactTracker::new(&control, &data);
//         let simplex = find_origin(&mut contact_tracker, &control, &data)
//             .expect("Expected simplex to contain origin but it did not");
//
//         let epa_polyhedron = EPA::new(simplex, &control, &data)
//             .panic_on_iteration(1000, "EPA failed to converge after 1000 iterations (in test)")
//             .execute();
//         let polyhedron = epa_polyhedron.polyhedron();
//
//         // assert_vertices_are_subset_of_input(&polyhedron, &vertices)
//         quickcheck_expect!(has_enough_faces(&polyhedron));
//         quickcheck_expect!(edges_are_only_connected_to_two_faces(&polyhedron));
//         quickcheck_expect!(all_points_are_on_the_convex_hull(&polyhedron));
//         quickcheck_expect!(all_faces_are_pointing_outwards(&polyhedron));
//
//         quickcheck::TestResult::passed()
//     }
// }

quickcheck! {
    fn it_generates_valid_convex_polyhedron(rot: UnitQuat) -> quickcheck::TestResult {
        let control = CollisionData::new(Box::new(Cuboid::cube(1.0)), Transform::identity());
        let data = CollisionData::new(
            Box::new(Cuboid::cube(1.0)),
            Transform {
                translation: Vec3D::zero(),
                rotation: rot.into(),
            }
        );

        let mut contact_tracker = ContactTracker::new(&control, &data);
        let simplex = find_origin(&mut contact_tracker, &control, &data)
            .expect("Expected simplex to contain origin but it did not");

        let epa_polyhedron = EPA::new(simplex, &control, &data)
            .panic_on_iteration(1000, "EPA failed to converge after 1000 iterations (in test)")
            .execute();
        let polyhedron = epa_polyhedron.polyhedron();

        // assert_vertices_are_subset_of_input(&polyhedron, &vertices)
        quickcheck_expect!(has_enough_faces(&polyhedron));
        quickcheck_expect!(edges_are_only_connected_to_two_faces(&polyhedron));
        quickcheck_expect!(all_points_are_on_the_convex_hull(&polyhedron));
        quickcheck_expect!(all_faces_are_pointing_outwards(&polyhedron));

        quickcheck::TestResult::passed()
    }
}

fn find_origin<'a>(tracker: &'a mut ContactTracker, data_0: &'a CollisionData, data_1: &'a CollisionData) -> Option<&'a GJKSimplex> {
    GJK::using_simplex(tracker.simplex_mut(), data_0, data_1)
        .panic_on_iteration(1000, "looking for origin (in tests)")
        .execute()
}

fn has_enough_faces(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    let face_count = polyhedron.faces_iter().count();
    let vertex_count = polyhedron.vertices_iter().count();

    let expected_face_count = 4 + (vertex_count - 4) * 2;

    quickcheck_assert!(face_count == expected_face_count, format!("expected polyhedron to have {} faces, but got {} instead", expected_face_count, face_count));

    quickcheck::TestResult::passed()
}

fn edges_are_only_connected_to_two_faces(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    let total_edge_counts = polyhedron.triangulation_iter()
        .flat_map(|triangulation| {
            vec!(
                sort_pair(triangulation[0], triangulation[1]),
                sort_pair(triangulation[1], triangulation[2]),
                sort_pair(triangulation[2], triangulation[0]),
            ).into_iter()
        })
        .fold(<HashMap<(usize, usize), usize>>::new(), |mut edge_counts, edge| {
            let count = edge_counts.get(&edge).cloned().unwrap_or(0usize);

            edge_counts.insert(edge, count + 1);

            return edge_counts;
        });

    let outliers: Vec<((usize, usize), usize)> = total_edge_counts.into_iter()
        .filter(|&(_edge, count)| count != 2)
        .collect();

    if outliers.len() == 0 {
        quickcheck::TestResult::passed()
    } else {
        quickcheck::TestResult::error(format!("expected all edges to be connected to two faces, but found the following outliers instead: {:?}", outliers))
    }
}

fn all_points_are_on_the_convex_hull(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    use maths::Approximations;

    let mut outside_points_count = 0;
    for face in polyhedron.faces_iter() {
        for vertex in polyhedron.vertices_iter() {
            if face.normal_projection_of(*vertex).is_strictly_positive() {
                outside_points_count += 1;
            }
        }
    }

    quickcheck_assert!(outside_points_count == 0, format!("expected polyhedron to have 0 points outside the separating planes, but found {} instead", outside_points_count));

    quickcheck::TestResult::passed()
}

fn all_faces_are_pointing_outwards(polyhedron: &ConvexPolyhedron) -> quickcheck::TestResult {
    let mid_point = polyhedron.vertices().iter()
        .fold(Vec3D::zero(), |total, vertex| {
            total + vertex
        }) / polyhedron.vertices().len() as Scalar;

    for face in polyhedron.faces_iter() {
        quickcheck_assert!(
            face.normal_projection_of(mid_point).is_strictly_negative(),
            format!("The ConvexPolyhedron has a face ({:?}) facing the wrong way!", face.normal()),
        );
    }

    quickcheck::TestResult::passed()
}

fn sort_pair(a: usize, b: usize) -> (usize, usize) {
    if a < b {
        (a, b)
    } else {
        (b, a)
    }
}
<|endoftext|>