<commit_before>#![allow(dead_code)]
/// The XML structure for a cobatura report is roughly as follows:
/// ```xml
/// <coverage lines-valid="5" lines-covered="0" line-rate="0.0" branches-valid="0"
/// branches-covered="0" branch-rate="0.0" version="1.9" timestamp="...">
///   <sources>
///     <source>PATH</source>
///     ...
///   </sources>
///
///   <packages>
///     <package name=".."  line-rate="0.0" branch-rate="0.0" complexity="0.0">
///       <classes>
///         <class name="Main" filename="main.rs" line-rate="0.0" branch-rate="0.0" complexity="0.0">
///           <methods>
///             <method name="main" signature="()" line-rate="0.0" branch-rate="0.0">
///               <lines>
///                 <line number="1" hits="5" branch="false"/>
///                 <line number="3" hits="2" branch="true">
///                   <conditions>
///                     <condition number="0" type="jump" coverage="50%"/>
///                     ...
///                   </conditions>
///                 </line>
///               </lines>
///             </method>
///             ...
///           </methods>
///
///           <lines>
///             <line number="10" hits="4" branch="false"/>
///           </lines>
///         </class>
///         ...
///       </classes>
///     </package>
///     ...
///   </packages>
/// </coverage>
/// ```
use std::error;
use std::fmt;
use std::fs::File;
use std::io::{Cursor, Write};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

use quick_xml::{
    events::{BytesDecl, BytesEnd, BytesStart, Event},
    Writer,
};

use chrono::offset::Utc;

use crate::config::Config;
use crate::traces::{CoverageStat, Trace, TraceMap};

pub fn report(traces: &TraceMap, config: &Config) -> Result<(), Error> {
    let result = Report::render(config, traces)?;
    result.export(config)
}

#[derive(Debug)]
pub enum Error {
    Unknown,
    ExportError(quick_xml::Error),
}

impl error::Error for Error {
    #[inline]
    fn description(&self) -> &str {
        match self {
            Error::ExportError(_) => "Error exporting xml file",
            Error::Unknown => "Unknown Error",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::ExportError(ref e) => write!(f, "Export Error {}", e),
            Error::Unknown => write!(f, "Unknown Error"),
        }
    }
}

#[derive(Debug)]
pub struct Report {
    timestamp: i64,
    lines_covered: usize,
    lines_valid: usize,
    line_rate: f64,
    branches_covered: usize,
    branches_valid: usize,
    branch_rate: f64,
    sources: Vec<PathBuf>,
    packages: Vec<Package>,
}

impl Report {
    pub fn render(config: &Config, traces: &TraceMap) -> Result<Self, Error> {
        let timestamp = Utc::now().timestamp();
        let sources = render_sources(config);
        let packages = render_packages(config, traces);
        let mut line_rate = 0.0;
        let mut branch_rate = 0.0;

        if packages.len() > 0 {
            line_rate = packages.iter().map(|x| x.line_rate).sum::<f64>() / packages.len() as f64;
            branch_rate =
                packages.iter().map(|x| x.branch_rate).sum::<f64>() / packages.len() as f64;
        }

        Ok(Report {
            timestamp: timestamp,
            lines_covered: traces.total_covered(),
            lines_valid: traces.total_coverable(),
            line_rate: line_rate,
            branches_covered: 0,
            branches_valid: 0,
            branch_rate: branch_rate,
            sources: sources,
            packages: packages,
        })
    }

    pub fn export(&self, _config: &Config) -> Result<(), Error> {
        let mut file = File::create("cobertura.xml")
            .map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))?;

        let mut writer = Writer::new(Cursor::new(vec![]));
        writer
            .write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))
            .map_err(|e| Error::ExportError(e))?;

        let cov_tag = b"coverage";
        let mut cov = BytesStart::borrowed(cov_tag, cov_tag.len());
        cov.push_attribute(("lines-covered", self.lines_covered.to_string().as_ref()));
        cov.push_attribute(("lines-valid", self.lines_valid.to_string().as_ref()));
        cov.push_attribute(("line-rate", self.line_rate.to_string().as_ref()));
        cov.push_attribute((
            "branches-covered",
            self.branches_covered.to_string().as_ref(),
        ));
        cov.push_attribute(("branches-valid", self.branches_valid.to_string().as_ref()));
        cov.push_attribute(("branch-rate", self.branch_rate.to_string().as_ref()));
        cov.push_attribute(("complexity", "0"));
        cov.push_attribute(("version", "1.9"));

        let secs = match SystemTime::now().duration_since(UNIX_EPOCH) {
            Ok(s) => s.as_secs().to_string(),
            Err(_) => String::from("0"),
        };
        cov.push_attribute(("timestamp", secs.as_ref()));

        writer
            .write_event(Event::Start(cov))
            .map_err(|e| Error::ExportError(e))?;

        self.export_header(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        self.export_packages(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        writer
            .write_event(Event::End(BytesEnd::borrowed(cov_tag)))
            .map_err(|e| Error::ExportError(e))?;

        let result = writer.into_inner().into_inner();
        file.write_all(&result)
            .map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))
    }

    fn export_header<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let sources_tag = b"sources";
        let source_tag = b"source";
        writer.write_event(Event::Start(BytesStart::borrowed(
            sources_tag,
            sources_tag.len(),
        )))?;
        for source in self.sources.iter() {
            if let Some(ref path) = source.to_str() {
                writer.write_event(Event::Start(BytesStart::borrowed(
                    source_tag,
                    source_tag.len(),
                )))?;
                writer.write(path.as_bytes())?;
                writer.write_event(Event::End(BytesEnd::borrowed(source_tag)))?;
            }
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(sources_tag)))
            .map(|_| ())
    }

    fn export_packages<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let packages_tag = b"packages";
        let pack_tag = b"package";

        writer.write_event(Event::Start(BytesStart::borrowed(
            packages_tag,
            packages_tag.len(),
        )))?;
        // Export the package
        for package in &self.packages {
            let mut pack = BytesStart::borrowed(pack_tag, pack_tag.len());
            pack.push_attribute(("name", package.name.as_str()));
            pack.push_attribute(("line-rate", package.line_rate.to_string().as_ref()));
            pack.push_attribute(("branch-rate", package.branch_rate.to_string().as_ref()));
            pack.push_attribute(("complexity", package.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(pack))?;
            self.export_classes(&package.classes, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(pack_tag)))?;
        }

        writer
            .write_event(Event::End(BytesEnd::borrowed(packages_tag)))
            .map(|_| ())
    }

    fn export_classes<T: Write>(
        &self,
        classes: &[Class],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let classes_tag = b"classes";
        let class_tag = b"class";
        let methods_tag = b"methods";

        writer.write_event(Event::Start(BytesStart::borrowed(
            classes_tag,
            classes_tag.len(),
        )))?;
        for class in classes {
            let mut c = BytesStart::borrowed(class_tag, class_tag.len());
            c.push_attribute(("name", class.name.as_ref()));
            c.push_attribute(("filename", class.file_name.as_ref()));
            c.push_attribute(("line-rate", class.line_rate.to_string().as_ref()));
            c.push_attribute(("branch-rate", class.branch_rate.to_string().as_ref()));
            c.push_attribute(("complexity", class.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(c))?;
            writer.write_event(Event::Empty(BytesStart::borrowed(
                methods_tag,
                methods_tag.len(),
            )))?;
            self.export_lines(&class.lines, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(class_tag)))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(classes_tag)))
            .map(|_| ())
    }

    fn export_lines<T: Write>(
        &self,
        lines: &[Line],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let lines_tag = b"lines";
        let line_tag = b"line";

        writer.write_event(Event::Start(BytesStart::borrowed(
            lines_tag,
            lines_tag.len(),
        )))?;
        for line in lines {
            let mut l = BytesStart::borrowed(line_tag, line_tag.len());
            match line {
                Line::Plain {
                    ref number,
                    ref hits,
                } => {
                    l.push_attribute(("number", number.to_string().as_ref()));
                    l.push_attribute(("hits", hits.to_string().as_ref()));
                }
                Line::Branch { .. } => {}
            }
            writer.write_event(Event::Empty(l))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(lines_tag)))
            .map(|_| ())
    }
}

fn render_sources(config: &Config) -> Vec<PathBuf> {
    let render_source = |&x| Path::to_path_buf(x);

    config.manifest.parent().iter().map(render_source).collect()
}

#[derive(Debug)]
struct Package {
    name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    classes: Vec<Class>,
}

fn render_packages(config: &Config, traces: &TraceMap) -> Vec<Package> {
    let mut dirs: Vec<&Path> = traces
        .files()
        .into_iter()
        .filter_map(|x| x.parent())
        .collect();

    dirs.dedup();

    dirs.into_iter()
        .map(|x| render_package(config, traces, x))
        .collect()
}

fn render_package(config: &Config, traces: &TraceMap, pkg: &Path) -> Package {
    let root = config.manifest.parent().unwrap_or(&config.manifest);
    let name = pkg
        .strip_prefix(root)
        .map(|x| Path::to_str(x))
        .unwrap_or_default()
        .unwrap_or_default();

    let line_cover = traces.covered_in_path(pkg) as f64;
    let line_rate = line_cover / (traces.coverable_in_path(pkg) as f64);

    Package {
        name: name.to_string(),
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        classes: render_classes(config, traces, pkg),
    }
}

#[derive(Debug)]
struct Class {
    name: String,
    file_name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    lines: Vec<Line>,
    methods: Vec<Method>,
}

fn render_classes(config: &Config, traces: &TraceMap, pkg: &Path) -> Vec<Class> {
    traces
        .files()
        .iter()
        .filter(|x| x.parent() == Some(pkg))
        .map(|x| render_class(config, traces, x))
        .collect()
}

// TODO: Cobertura distinguishes between lines outside methods, and methods
// (which also contain lines). As there is currently no way to get traces from
// a particular function only, all traces are put into lines, and the vector
// of methods is empty.
//
// Until this is fixed, the render_method function will panic if called, as it
// cannot be properly implemented.
//
fn render_class(config: &Config, traces: &TraceMap, file: &Path) -> Class {
    let root = config.manifest.parent().unwrap_or(&config.manifest);
    let name = file
        .file_stem()
        .map(|x| x.to_str().unwrap())
        .unwrap_or_default()
        .to_string();

    let file_name = file
        .strip_prefix(root)
        .unwrap_or(file)
        .to_str()
        .unwrap()
        .to_string();

    let covered = traces.covered_in_path(file) as f64;
    let line_rate = covered / traces.coverable_in_path(file) as f64;
    let lines = traces
        .get_child_traces(file)
        .iter()
        .map(|x| render_line(x))
        .collect();

    Class {
        name: name,
        file_name: file_name,
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        lines: lines,
        methods: vec![],
    }
}

#[derive(Debug)]
struct Method {
    name: String,
    signature: String,
    line_rate: f64,
    branch_rate: f64,
    lines: Vec<Line>,
}

fn render_methods() -> Vec<Method> {
    unimplemented!()
}

fn render_method() -> Method {
    unimplemented!()
}

#[derive(Debug)]
enum Line {
    Plain {
        number: usize,
        hits: usize,
    },

    Branch {
        number: usize,
        hits: usize,
        conditions: Vec<Condition>,
    },
}

fn render_line(trace: &Trace) -> Line {
    match &trace.stats {
        CoverageStat::Line(hits) => Line::Plain {
            number: trace.line as usize,
            hits: *hits as usize,
        },

        // TODO: Branches in cobertura are given a fresh number as a label,
        // which would require having some form of context when rendering.
        //
        _ => panic!("Not currently supported"),
    }
}

#[derive(Debug)]
struct Condition {
    number: usize,
    cond_type: ConditionType,
    coverage: f64,
}

// Condition types
#[derive(Debug)]
enum ConditionType {
    Jump,
}
<commit_msg>Switched to dyn trait object to remove warning<commit_after>#![allow(dead_code)]
/// The XML structure for a cobatura report is roughly as follows:
/// ```xml
/// <coverage lines-valid="5" lines-covered="0" line-rate="0.0" branches-valid="0"
/// branches-covered="0" branch-rate="0.0" version="1.9" timestamp="...">
///   <sources>
///     <source>PATH</source>
///     ...
///   </sources>
///
///   <packages>
///     <package name=".."  line-rate="0.0" branch-rate="0.0" complexity="0.0">
///       <classes>
///         <class name="Main" filename="main.rs" line-rate="0.0" branch-rate="0.0" complexity="0.0">
///           <methods>
///             <method name="main" signature="()" line-rate="0.0" branch-rate="0.0">
///               <lines>
///                 <line number="1" hits="5" branch="false"/>
///                 <line number="3" hits="2" branch="true">
///                   <conditions>
///                     <condition number="0" type="jump" coverage="50%"/>
///                     ...
///                   </conditions>
///                 </line>
///               </lines>
///             </method>
///             ...
///           </methods>
///
///           <lines>
///             <line number="10" hits="4" branch="false"/>
///           </lines>
///         </class>
///         ...
///       </classes>
///     </package>
///     ...
///   </packages>
/// </coverage>
/// ```
use std::error;
use std::fmt;
use std::fs::File;
use std::io::{Cursor, Write};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

use quick_xml::{
    events::{BytesDecl, BytesEnd, BytesStart, Event},
    Writer,
};

use chrono::offset::Utc;

use crate::config::Config;
use crate::traces::{CoverageStat, Trace, TraceMap};

pub fn report(traces: &TraceMap, config: &Config) -> Result<(), Error> {
    let result = Report::render(config, traces)?;
    result.export(config)
}

#[derive(Debug)]
pub enum Error {
    Unknown,
    ExportError(quick_xml::Error),
}

impl error::Error for Error {
    #[inline]
    fn description(&self) -> &str {
        match self {
            Error::ExportError(_) => "Error exporting xml file",
            Error::Unknown => "Unknown Error",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&dyn error::Error> {
        None
    }
}

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::ExportError(ref e) => write!(f, "Export Error {}", e),
            Error::Unknown => write!(f, "Unknown Error"),
        }
    }
}

#[derive(Debug)]
pub struct Report {
    timestamp: i64,
    lines_covered: usize,
    lines_valid: usize,
    line_rate: f64,
    branches_covered: usize,
    branches_valid: usize,
    branch_rate: f64,
    sources: Vec<PathBuf>,
    packages: Vec<Package>,
}

impl Report {
    pub fn render(config: &Config, traces: &TraceMap) -> Result<Self, Error> {
        let timestamp = Utc::now().timestamp();
        let sources = render_sources(config);
        let packages = render_packages(config, traces);
        let mut line_rate = 0.0;
        let mut branch_rate = 0.0;

        if packages.len() > 0 {
            line_rate = packages.iter().map(|x| x.line_rate).sum::<f64>() / packages.len() as f64;
            branch_rate =
                packages.iter().map(|x| x.branch_rate).sum::<f64>() / packages.len() as f64;
        }

        Ok(Report {
            timestamp: timestamp,
            lines_covered: traces.total_covered(),
            lines_valid: traces.total_coverable(),
            line_rate: line_rate,
            branches_covered: 0,
            branches_valid: 0,
            branch_rate: branch_rate,
            sources: sources,
            packages: packages,
        })
    }

    pub fn export(&self, _config: &Config) -> Result<(), Error> {
        let mut file = File::create("cobertura.xml")
            .map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))?;

        let mut writer = Writer::new(Cursor::new(vec![]));
        writer
            .write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))
            .map_err(|e| Error::ExportError(e))?;

        let cov_tag = b"coverage";
        let mut cov = BytesStart::borrowed(cov_tag, cov_tag.len());
        cov.push_attribute(("lines-covered", self.lines_covered.to_string().as_ref()));
        cov.push_attribute(("lines-valid", self.lines_valid.to_string().as_ref()));
        cov.push_attribute(("line-rate", self.line_rate.to_string().as_ref()));
        cov.push_attribute((
            "branches-covered",
            self.branches_covered.to_string().as_ref(),
        ));
        cov.push_attribute(("branches-valid", self.branches_valid.to_string().as_ref()));
        cov.push_attribute(("branch-rate", self.branch_rate.to_string().as_ref()));
        cov.push_attribute(("complexity", "0"));
        cov.push_attribute(("version", "1.9"));

        let secs = match SystemTime::now().duration_since(UNIX_EPOCH) {
            Ok(s) => s.as_secs().to_string(),
            Err(_) => String::from("0"),
        };
        cov.push_attribute(("timestamp", secs.as_ref()));

        writer
            .write_event(Event::Start(cov))
            .map_err(|e| Error::ExportError(e))?;

        self.export_header(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        self.export_packages(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        writer
            .write_event(Event::End(BytesEnd::borrowed(cov_tag)))
            .map_err(|e| Error::ExportError(e))?;

        let result = writer.into_inner().into_inner();
        file.write_all(&result)
            .map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))
    }

    fn export_header<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let sources_tag = b"sources";
        let source_tag = b"source";
        writer.write_event(Event::Start(BytesStart::borrowed(
            sources_tag,
            sources_tag.len(),
        )))?;
        for source in self.sources.iter() {
            if let Some(ref path) = source.to_str() {
                writer.write_event(Event::Start(BytesStart::borrowed(
                    source_tag,
                    source_tag.len(),
                )))?;
                writer.write(path.as_bytes())?;
                writer.write_event(Event::End(BytesEnd::borrowed(source_tag)))?;
            }
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(sources_tag)))
            .map(|_| ())
    }

    fn export_packages<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let packages_tag = b"packages";
        let pack_tag = b"package";

        writer.write_event(Event::Start(BytesStart::borrowed(
            packages_tag,
            packages_tag.len(),
        )))?;
        // Export the package
        for package in &self.packages {
            let mut pack = BytesStart::borrowed(pack_tag, pack_tag.len());
            pack.push_attribute(("name", package.name.as_str()));
            pack.push_attribute(("line-rate", package.line_rate.to_string().as_ref()));
            pack.push_attribute(("branch-rate", package.branch_rate.to_string().as_ref()));
            pack.push_attribute(("complexity", package.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(pack))?;
            self.export_classes(&package.classes, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(pack_tag)))?;
        }

        writer
            .write_event(Event::End(BytesEnd::borrowed(packages_tag)))
            .map(|_| ())
    }

    fn export_classes<T: Write>(
        &self,
        classes: &[Class],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let classes_tag = b"classes";
        let class_tag = b"class";
        let methods_tag = b"methods";

        writer.write_event(Event::Start(BytesStart::borrowed(
            classes_tag,
            classes_tag.len(),
        )))?;
        for class in classes {
            let mut c = BytesStart::borrowed(class_tag, class_tag.len());
            c.push_attribute(("name", class.name.as_ref()));
            c.push_attribute(("filename", class.file_name.as_ref()));
            c.push_attribute(("line-rate", class.line_rate.to_string().as_ref()));
            c.push_attribute(("branch-rate", class.branch_rate.to_string().as_ref()));
            c.push_attribute(("complexity", class.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(c))?;
            writer.write_event(Event::Empty(BytesStart::borrowed(
                methods_tag,
                methods_tag.len(),
            )))?;
            self.export_lines(&class.lines, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(class_tag)))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(classes_tag)))
            .map(|_| ())
    }

    fn export_lines<T: Write>(
        &self,
        lines: &[Line],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let lines_tag = b"lines";
        let line_tag = b"line";

        writer.write_event(Event::Start(BytesStart::borrowed(
            lines_tag,
            lines_tag.len(),
        )))?;
        for line in lines {
            let mut l = BytesStart::borrowed(line_tag, line_tag.len());
            match line {
                Line::Plain {
                    ref number,
                    ref hits,
                } => {
                    l.push_attribute(("number", number.to_string().as_ref()));
                    l.push_attribute(("hits", hits.to_string().as_ref()));
                }
                Line::Branch { .. } => {}
            }
            writer.write_event(Event::Empty(l))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(lines_tag)))
            .map(|_| ())
    }
}

fn render_sources(config: &Config) -> Vec<PathBuf> {
    let render_source = |&x| Path::to_path_buf(x);

    config.manifest.parent().iter().map(render_source).collect()
}

#[derive(Debug)]
struct Package {
    name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    classes: Vec<Class>,
}

fn render_packages(config: &Config, traces: &TraceMap) -> Vec<Package> {
    let mut dirs: Vec<&Path> = traces
        .files()
        .into_iter()
        .filter_map(|x| x.parent())
        .collect();

    dirs.dedup();

    dirs.into_iter()
        .map(|x| render_package(config, traces, x))
        .collect()
}

fn render_package(config: &Config, traces: &TraceMap, pkg: &Path) -> Package {
    let root = config.manifest.parent().unwrap_or(&config.manifest);
    let name = pkg
        .strip_prefix(root)
        .map(|x| Path::to_str(x))
        .unwrap_or_default()
        .unwrap_or_default();

    let line_cover = traces.covered_in_path(pkg) as f64;
    let line_rate = line_cover / (traces.coverable_in_path(pkg) as f64);

    Package {
        name: name.to_string(),
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        classes: render_classes(config, traces, pkg),
    }
}

#[derive(Debug)]
struct Class {
    name: String,
    file_name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    lines: Vec<Line>,
    methods: Vec<Method>,
}

fn render_classes(config: &Config, traces: &TraceMap, pkg: &Path) -> Vec<Class> {
    traces
        .files()
        .iter()
        .filter(|x| x.parent() == Some(pkg))
        .map(|x| render_class(config, traces, x))
        .collect()
}

// TODO: Cobertura distinguishes between lines outside methods, and methods
// (which also contain lines). As there is currently no way to get traces from
// a particular function only, all traces are put into lines, and the vector
// of methods is empty.
//
// Until this is fixed, the render_method function will panic if called, as it
// cannot be properly implemented.
//
fn render_class(config: &Config, traces: &TraceMap, file: &Path) -> Class {
    let root = config.manifest.parent().unwrap_or(&config.manifest);
    let name = file
        .file_stem()
        .map(|x| x.to_str().unwrap())
        .unwrap_or_default()
        .to_string();

    let file_name = file
        .strip_prefix(root)
        .unwrap_or(file)
        .to_str()
        .unwrap()
        .to_string();

    let covered = traces.covered_in_path(file) as f64;
    let line_rate = covered / traces.coverable_in_path(file) as f64;
    let lines = traces
        .get_child_traces(file)
        .iter()
        .map(|x| render_line(x))
        .collect();

    Class {
        name: name,
        file_name: file_name,
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        lines: lines,
        methods: vec![],
    }
}

#[derive(Debug)]
struct Method {
    name: String,
    signature: String,
    line_rate: f64,
    branch_rate: f64,
    lines: Vec<Line>,
}

fn render_methods() -> Vec<Method> {
    unimplemented!()
}

fn render_method() -> Method {
    unimplemented!()
}

#[derive(Debug)]
enum Line {
    Plain {
        number: usize,
        hits: usize,
    },

    Branch {
        number: usize,
        hits: usize,
        conditions: Vec<Condition>,
    },
}

fn render_line(trace: &Trace) -> Line {
    match &trace.stats {
        CoverageStat::Line(hits) => Line::Plain {
            number: trace.line as usize,
            hits: *hits as usize,
        },

        // TODO: Branches in cobertura are given a fresh number as a label,
        // which would require having some form of context when rendering.
        //
        _ => panic!("Not currently supported"),
    }
}

#[derive(Debug)]
struct Condition {
    number: usize,
    cond_type: ConditionType,
    coverage: f64,
}

// Condition types
#[derive(Debug)]
enum ConditionType {
    Jump,
}
<|endoftext|>


<commit_before>//   Copyright 2014 Colin Sherratt
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

#![crate_name = "snowmew"]

extern crate core = "snowmew-core";
extern crate graphics = "snowmew-graphics";
extern crate loader = "snowmew-loader";
extern crate physics = "snowmew-physics";
extern crate position = "snowmew-position";
extern crate render = "snowmew-render-mux";

pub mod config  {
    pub use core::SnowmewConfig as SnowmewConfig;
}

<commit_msg>exported most of the modules in snowmew<commit_after>//   Copyright 2014 Colin Sherratt
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

#![crate_name = "snowmew"]

extern crate _core = "snowmew-core";
extern crate _graphics = "snowmew-graphics";
extern crate _loader = "snowmew-loader";
extern crate _physics = "snowmew-physics";
extern crate _position = "snowmew-position";
extern crate _render = "snowmew-render-mux";
extern crate _render_data = "snowmew-render-data";

pub mod render {
    pub use _render::RenderFactory as DefaultRender;
    pub use _render_data::RenderData as RenderData;
    pub use _core::{
        RenderFactory,
        Render
    };
}

pub mod graphics {
    pub use _graphics::{
        default,
        Drawable,
        Geometry,
        geometry,
        Graphics,
        GraphicsData,
        light,
        Light,
        material,
        Material,
        texture,
        Texture,
        texture_atlas,
        VertexBuffer,
        VertexBufferIter,
    };
}

pub mod position {
    pub use _position::{
        MatrixManager,
        Deltas,
        ComputedPositionGL,
        ComputedPosition,
        CalcPositionsCl,
        PositionData,
        Positions
    };
}

pub mod core {
    pub use _core::{
        DisplayConfig,
        Render,
        IOManager,
    };

    pub use _core::io::{
        InputHandle,
        Window,
    };
}

pub mod common {
    pub use _core::common::{
        Common,
        CommonData
    };
}

pub mod config  {
    pub use _core::SnowmewConfig as SnowmewConfig;
}

<|endoftext|>


<commit_before>extern crate zmq_sys;

use libc::{c_int, size_t, int64_t, uint64_t};
use std::os::raw::c_void;
use std::{mem, ptr, str};
use std::result;

use super::Result;

pub trait Getter where Self: Sized {
    fn get(sock: *mut c_void, opt: c_int) -> Result<Self>;
}

pub trait Setter where Self: Sized {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()>;
}

macro_rules! getsockopt_num(
    ($c_ty:ty, $ty:ty) => (
        impl Getter for $ty {
            #[allow(trivial_casts)]
            fn get(sock: *mut c_void, opt: c_int) -> Result<$ty> {
                let mut value: $c_ty = 0;
                let value_ptr = &mut value as *mut $c_ty;
                let mut size = mem::size_of::<$c_ty>() as size_t;

                zmq_try!(unsafe {
                    zmq_sys::zmq_getsockopt(
                        sock,
                        opt,
                        value_ptr as *mut c_void,
                        &mut size)
                });
                Ok(value as $ty)
            }
        }
    )
);

getsockopt_num!(c_int, i32);
getsockopt_num!(int64_t, i64);
getsockopt_num!(uint64_t, u64);

pub fn get_string(sock: *mut c_void, opt: c_int, size: size_t,
                  remove_nulbyte: bool) -> Result<result::Result<String, Vec<u8>>> {
    let mut size = size;
    let mut value = vec![0u8; size];

    zmq_try!(unsafe {
        zmq_sys::zmq_getsockopt(
            sock,
            opt,
            value.as_mut_ptr() as *mut c_void,
            &mut size)
    });
    if remove_nulbyte {
        size -= 1;
    }
    value.truncate(size);
    Ok(String::from_utf8(value).map_err(|e| e.into_bytes()))
}

macro_rules! setsockopt_num(
    ($ty:ty) => (
        impl Setter for $ty {
            #[allow(trivial_casts)]
            fn set(sock: *mut c_void, opt: c_int, value: $ty) -> Result<()> {
                let size = mem::size_of::<$ty>() as size_t;

                zmq_try!(unsafe {
                    zmq_sys::zmq_setsockopt(
                        sock,
                        opt,
                        (&value as *const $ty) as *const c_void,
                        size)
                });
                Ok(())
            }
        }
    )
);

setsockopt_num!(i32);
setsockopt_num!(i64);
setsockopt_num!(u64);

fn setsockopt_null(sock: *mut c_void, opt: c_int) -> Result<()> {
    zmq_try!(unsafe { zmq_sys::zmq_setsockopt(sock, opt, ptr::null(), 0) });
    Ok(())
}

impl<'a> Setter for &'a str {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()> {
        set(sock, opt, value.as_bytes())
    }
}

impl<'a> Setter for Option<&'a str> {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()> {
        if let Some(s) = value {
            set(sock, opt, s.as_bytes())
        } else {
            setsockopt_null(sock, opt)
        }
    }
}

impl Getter for bool {
    fn get(sock: *mut c_void, opt: c_int) -> Result<Self> {
        let result: i32 = try!(get(sock, opt));
        Ok(result == 1)
    }
}

impl Setter for bool {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()> {
        set(sock, opt, if value { 1i32 } else { 0i32 })
    }
}

impl<'a> Setter for &'a [u8] {
    fn set(sock: *mut c_void, opt: c_int, value: &'a [u8]) -> Result<()> {
        zmq_try!(unsafe {
            zmq_sys::zmq_setsockopt(
                sock,
                opt,
                value.as_ptr() as *const c_void,
                value.len() as size_t
            )
        });
        Ok(())
    }
}

pub fn get<T: Getter>(sock: *mut c_void, opt: c_int) -> Result<T> {
    T::get(sock, opt)
}

pub fn set<T: Setter>(sock: *mut c_void, opt: c_int, value: T) -> Result<()> {
    T::set(sock, opt, value)
}
<commit_msg>Add sockopt impl for u32 to support RawFd definition on stable-i686-pc-windows-msvc<commit_after>extern crate zmq_sys;

use libc::{c_int, c_uint, size_t, int64_t, uint64_t};
use std::os::raw::c_void;
use std::{mem, ptr, str};
use std::result;

use super::Result;

pub trait Getter where Self: Sized {
    fn get(sock: *mut c_void, opt: c_int) -> Result<Self>;
}

pub trait Setter where Self: Sized {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()>;
}

macro_rules! getsockopt_num(
    ($c_ty:ty, $ty:ty) => (
        impl Getter for $ty {
            #[allow(trivial_casts)]
            fn get(sock: *mut c_void, opt: c_int) -> Result<$ty> {
                let mut value: $c_ty = 0;
                let value_ptr = &mut value as *mut $c_ty;
                let mut size = mem::size_of::<$c_ty>() as size_t;

                zmq_try!(unsafe {
                    zmq_sys::zmq_getsockopt(
                        sock,
                        opt,
                        value_ptr as *mut c_void,
                        &mut size)
                });
                Ok(value as $ty)
            }
        }
    )
);

getsockopt_num!(c_int, i32);
getsockopt_num!(c_uint, u32);
getsockopt_num!(int64_t, i64);
getsockopt_num!(uint64_t, u64);

pub fn get_string(sock: *mut c_void, opt: c_int, size: size_t,
                  remove_nulbyte: bool) -> Result<result::Result<String, Vec<u8>>> {
    let mut size = size;
    let mut value = vec![0u8; size];

    zmq_try!(unsafe {
        zmq_sys::zmq_getsockopt(
            sock,
            opt,
            value.as_mut_ptr() as *mut c_void,
            &mut size)
    });
    if remove_nulbyte {
        size -= 1;
    }
    value.truncate(size);
    Ok(String::from_utf8(value).map_err(|e| e.into_bytes()))
}

macro_rules! setsockopt_num(
    ($ty:ty) => (
        impl Setter for $ty {
            #[allow(trivial_casts)]
            fn set(sock: *mut c_void, opt: c_int, value: $ty) -> Result<()> {
                let size = mem::size_of::<$ty>() as size_t;

                zmq_try!(unsafe {
                    zmq_sys::zmq_setsockopt(
                        sock,
                        opt,
                        (&value as *const $ty) as *const c_void,
                        size)
                });
                Ok(())
            }
        }
    )
);

setsockopt_num!(i32);
setsockopt_num!(i64);
setsockopt_num!(u64);

fn setsockopt_null(sock: *mut c_void, opt: c_int) -> Result<()> {
    zmq_try!(unsafe { zmq_sys::zmq_setsockopt(sock, opt, ptr::null(), 0) });
    Ok(())
}

impl<'a> Setter for &'a str {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()> {
        set(sock, opt, value.as_bytes())
    }
}

impl<'a> Setter for Option<&'a str> {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()> {
        if let Some(s) = value {
            set(sock, opt, s.as_bytes())
        } else {
            setsockopt_null(sock, opt)
        }
    }
}

impl Getter for bool {
    fn get(sock: *mut c_void, opt: c_int) -> Result<Self> {
        let result: i32 = try!(get(sock, opt));
        Ok(result == 1)
    }
}

impl Setter for bool {
    fn set(sock: *mut c_void, opt: c_int, value: Self) -> Result<()> {
        set(sock, opt, if value { 1i32 } else { 0i32 })
    }
}

impl<'a> Setter for &'a [u8] {
    fn set(sock: *mut c_void, opt: c_int, value: &'a [u8]) -> Result<()> {
        zmq_try!(unsafe {
            zmq_sys::zmq_setsockopt(
                sock,
                opt,
                value.as_ptr() as *const c_void,
                value.len() as size_t
            )
        });
        Ok(())
    }
}

pub fn get<T: Getter>(sock: *mut c_void, opt: c_int) -> Result<T> {
    T::get(sock, opt)
}

pub fn set<T: Setter>(sock: *mut c_void, opt: c_int, value: T) -> Result<()> {
    T::set(sock, opt, value)
}
<|endoftext|>


<commit_before>use hyper;
use serde_json;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    #[serde(default = "default_team")]
    pub team: Vec<TeamConfig>,
    #[serde(default = "default_repo")]
    pub repo: Vec<RepoConfig>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    pub proxy_url: Option<String>,
}

fn default_team() -> Vec<TeamConfig> { Vec::new() }
fn default_repo() -> Vec<RepoConfig> { Vec::new() }
fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/sectora.toml") }

impl Config {
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Repo {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team, }
    }
}

impl From<Repo> for Sector {
    fn from(repo: Repo) -> Self {
        Self { id: repo.id,
               name: repo.name,
               sector_type: SectorType::Repo, }
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Debug)]
pub enum CliError {
    Serde(serde_json::Error),
    Io(std::io::Error),
    Toml(toml::de::Error),
    Hyper(hyper::Error),
}

impl From<serde_json::Error> for CliError {
    fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }
}
impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError { CliError::Io(err) }
}
impl From<toml::de::Error> for CliError {
    fn from(err: toml::de::Error) -> CliError { CliError::Toml(err) }
}
impl From<hyper::Error> for CliError {
    fn from(err: hyper::Error) -> CliError { CliError::Hyper(err) }
}
<commit_msg>Add fmt/from_str to some structs<commit_after>use hyper;
use serde_json;
use std;
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    #[serde(default = "default_team")]
    pub team: Vec<TeamConfig>,
    #[serde(default = "default_repo")]
    pub repo: Vec<RepoConfig>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    pub proxy_url: Option<String>,
}

fn default_team() -> Vec<TeamConfig> { Vec::new() }
fn default_repo() -> Vec<RepoConfig> { Vec::new() }
fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/sectora.toml") }

impl Config {
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Repo {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

impl fmt::Display for SectorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &SectorType::Team => write!(f, "T"),
            &SectorType::Repo => write!(f, "R"),
        }
    }
}

impl FromStr for SectorType {
    type Err = std::io::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "T" => Ok(SectorType::Team),
            "R" => Ok(SectorType::Repo),
            _ => Err(std::io::Error::new(std::io::ErrorKind::Other, "unknown sector type")),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team, }
    }
}

impl From<Repo> for Sector {
    fn from(repo: Repo) -> Self {
        Self { id: repo.id,
               name: repo.name,
               sector_type: SectorType::Repo, }
    }
}

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

pub enum ParseSectorError {
    Id(std::num::ParseIntError),
    Type(std::io::Error),
}

impl FromStr for Sector {
    type Err = ParseSectorError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(":").collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse().map_err(|e| ParseSectorError::Id(e))?,
                  name: String::from(parts[1]),
                  sector_type: parts[2].parse().map_err(|e| ParseSectorError::Type(e))?, })
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

impl fmt::Display for SectorGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let members_str = self.members.values()
                              .map(|v| v.to_string())
                              .collect::<Vec<_>>()
                              .join(" ");
        write!(f,
               "{}\t{}\t{}\t{}",
               self.sector,
               self.gid.and_then(|i| Some(i.to_string())).unwrap_or(String::new()),
               self.group.clone().unwrap_or(String::new()),
               members_str)
    }
}

pub enum ParseSectorGroupError {
    Sector(ParseSectorError),
    Gid(std::num::ParseIntError),
    Member(std::num::ParseIntError),
}

impl FromStr for SectorGroup {
    type Err = ParseSectorGroupError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split("\t").collect::<Vec<&str>>();
        let sector = parts[0].parse().map_err(|e| ParseSectorGroupError::Sector(e))?;
        let gid: Option<u64> = match parts[1] {
            "" => None,
            s => Some(s.parse::<u64>().map_err(|e| ParseSectorGroupError::Gid(e))?),
        };
        let group: Option<String> = match parts[2] {
            "" => None,
            s => Some(String::from(s)),
        };
        let members = parts[3].split(" ")
                              .map(|s| s.parse::<Member>().map_err(|e| ParseSectorGroupError::Member(e)))
                              .collect::<Result<Vec<Member>, _>>()?
                              .into_iter()
                              .map(|m| (m.login.clone(), m))
                              .collect::<HashMap<_, _>>();
        Ok(Self { sector,
                  gid,
                  group,
                  members, })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

impl fmt::Display for Member {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.id, self.login) }
}

impl FromStr for Member {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(":").collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse()?,
                  login: String::from(parts[1]), })
    }
}

#[allow(dead_code)]
pub struct MemberGid {
    pub member: Member,
    pub gid: u64,
}

impl fmt::Display for MemberGid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}|{}", self.member, self.gid) }
}

impl FromStr for MemberGid {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split("|").collect::<Vec<&str>>();
        Ok(Self { member: parts[0].parse()?,
                  gid: parts[1].parse()?, })
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Debug)]
pub enum CliError {
    Serde(serde_json::Error),
    Io(std::io::Error),
    Toml(toml::de::Error),
    Hyper(hyper::Error),
}

impl From<serde_json::Error> for CliError {
    fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }
}
impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError { CliError::Io(err) }
}
impl From<toml::de::Error> for CliError {
    fn from(err: toml::de::Error) -> CliError { CliError::Toml(err) }
}
impl From<hyper::Error> for CliError {
    fn from(err: hyper::Error) -> CliError { CliError::Hyper(err) }
}
<|endoftext|>


<commit_before>use time;
use std::old_io::File;
use std::old_io::fs::PathExtensions;
use std::collections::HashMap;
use util::{color, V2};
use backend::{Canvas, CanvasUtil, Event, Key, Fonter};
use world;
use world::action;
use world::action::Input::{Step, Melee};
use world::action::ControlState::*;
use world::{Msg, FovStatus};
use world::Dir6;
use world::Dir6::*;
use world::{Entity};
use world::item::{Slot};
use worldview;
use sprite::{WorldSprites, GibSprite, BeamSprite};
use tilecache;
use tilecache::icon;
use msg_queue::MsgQueue;

pub struct GameState {
    /// Transient effect sprites drawn in game world view.
    world_spr: WorldSprites,
    /// Counters for entities with flashing damage animation.
    damage_timers: HashMap<Entity, u32>,

    /// Flag for autoexploration.
    // TODO: Probably going to need a general "ongoing activity" system at
    // some point.
    exploring: bool,

    msg: MsgQueue,
    ui_state: UiState,

    // Hacky thing to wait for next time Canvas reference is available.
    screenshot_requested: bool,
}

enum UiState {
    Gameplay,
    Inventory
}

impl GameState {
    pub fn new(seed: Option<u32>) -> GameState {
        world::init_world(seed);
        GameState {
            world_spr: WorldSprites::new(),
            damage_timers: HashMap::new(),
            exploring: false,
            msg: MsgQueue::new(),
            ui_state: UiState::Gameplay,
            screenshot_requested: false,
        }
    }

    fn draw_player_ui(&mut self, ctx: &mut Canvas, player: Entity) {
        let hp = player.hp();
        let max_hp = player.max_hp();

        // Draw heart containers.
        for i in 0..((max_hp + 1) / 2) {
            let pos = V2(i as f32 * 8.0, 8.0);
            let idx = if hp >= (i + 1) * 2 { icon::HEART }
                else if hp == i * 2 + 1 { icon::HALF_HEART }
                else { icon::NO_HEART };
            ctx.draw_image(tilecache::get(idx), pos, 0.0, &color::FIREBRICK, &color::BLACK);
        }
    }

    fn base_paint(&mut self, ctx: &mut Canvas) {
        let camera = world::camera();
        worldview::draw_world(&camera, ctx, &self.damage_timers);

        self.world_spr.draw(|x| (camera + x).fov_status() == Some(FovStatus::Seen), &camera, ctx);
        self.world_spr.update();

        let location_name = camera.name();
        write!(&mut ctx.text_writer(V2(640 - location_name.len() as i32 * 8, 8), 0.1, color::LIGHTGRAY)
                       .set_border(color::BLACK),
                       "{}", location_name).unwrap();

        self.msg.draw(ctx);
        if let Some(player) = action::player() {
            self.draw_player_ui(ctx, player);
        }

        let fps = 1.0 / ctx.render_duration;
        let _ = write!(&mut ctx.text_writer(V2(0, 16), 0.1, color::LIGHTGREEN)
                       .set_border(color::BLACK),
                       "FPS {:.0}", fps);
        let _ = write!(&mut ctx.text_writer(V2(0, 360), 0.1, color::LIGHTGRAY)
                      .set_border(color::BLACK),
                      "MAGOG v{}{}",
                      ::version(),
                      if !cfg!(ndebug) { " ***DEBUG build***" } else { "" });
    }

    fn base_update(&mut self, ctx: &mut Canvas) {
        // Process events
        loop {
            match world::pop_msg() {
                Some(Msg::Gib(loc)) => {
                    self.world_spr.add(Box::new(GibSprite::new(loc)));
                }
                Some(Msg::Damage(entity)) => {
                    self.damage_timers.insert(entity, 2);
                }
                Some(Msg::Text(txt)) => {
                    self.msg.msg(txt)
                }
                Some(Msg::Caption(txt)) => {
                    self.msg.caption(txt)
                }
                Some(Msg::Beam(loc1, loc2)) => {
                    self.world_spr.add(Box::new(BeamSprite::new(loc1, loc2, 10)));
                }
                Some(Msg::Sparks(_loc)) => {
                    // TODO
                }
                Some(x) => {
                    println!("Unhandled Msg type {:?}", x);
                }
                None => break
            }
        }

        self.base_paint(ctx);

        if action::control_state() == ReadyToUpdate {
            action::update();
        }

        if self.exploring {
            if action::control_state() == AwaitingInput {
                self.exploring = self.autoexplore();
            }
        }

        self.damage_timers = self.damage_timers.clone().into_iter()
            .filter(|&(_, t)| t > 0)
            .map(|(e, t)| (e, t - 1))
            .collect();

        self.msg.update();
    }

    fn inventory_update(&mut self, ctx: &mut Canvas) {
        let player = action::player().unwrap();
        {
            let mut cursor = ctx.text_writer(V2(0, 8), 0.1, color::GAINSBORO);
            for slot_data in SLOT_DATA.iter() {
                let name = match player.equipped(slot_data.slot) {
                    Some(item) => item.name(),
                    None => "".to_string()
                };
                write!(&mut cursor, "{}] {}: {}\n",
                    slot_data.key, slot_data.name, name).unwrap();
            }
        }

        {
            let mut cursor = ctx.text_writer(V2(0, 360), 0.1, color::GAINSBORO);
            write!(&mut cursor, "Press letter to equip/unequip item. Press shift+letter to drop item.").unwrap();
        }
    }

    pub fn inventory_process(&mut self, event: Event) -> bool {
        let player = action::player().unwrap();
        match event {
            Event::Render(ctx) => { self.update(ctx); }
            Event::KeyPressed(Key::Escape) | Event::KeyPressed(Key::Tab) => {
                self.ui_state = UiState::Gameplay
            }
            Event::KeyPressed(Key::F12) => { self.screenshot_requested = true; }
            Event::KeyPressed(_) => {}

            Event::Char(ch) => {
                for slot_data in SLOT_DATA.iter() {
                    if ch == slot_data.key {
                        if slot_data.slot.is_gear_slot() {
                            // Unequip gear
                            match player.free_bag_slot() {
                                None => {
                                    // No room in bag, can't unequip until
                                    // drop something.
                                    // TODO: Message about full bag.
                                }
                                Some(swap_slot) => {
                                    player.swap_equipped(slot_data.slot, swap_slot);
                                }
                            }
                        }
                        if slot_data.slot.is_bag_slot() {
                            // Bag items get equipped if they have are gear
                            // with a preferred slot.
                            if let Some(item) = player.equipped(slot_data.slot) {
                                let equip_slots = item.equip_slots();
                                for &swap_slot in equip_slots.iter() {
                                    if player.equipped(swap_slot).is_none() {
                                        player.swap_equipped(slot_data.slot, swap_slot);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if ch == slot_data.key.to_uppercase() {
                        // Drop item in slot.
                        if let Some(item) = player.equipped(slot_data.slot) {
                            item.place(player.location().unwrap());
                        }
                        break;
                    }
                }
            }

            _ => ()
        }
        true
    }

    /// Repaint view, update game world if needed.
    pub fn update(&mut self, ctx: &mut Canvas) {
        if self.screenshot_requested {
            use image;
            let shot = ctx.screenshot();
            let mut file = File::create(&Path::new(
                    format!("/tmp/shot-{}.png", time::precise_time_s() as u64)))
                    .unwrap();
            let _ = image::ImageRgb8(shot).save(&mut file, image::PNG);

            self.screenshot_requested = false;
        }

        ctx.clear();

        match self.ui_state {
            UiState::Gameplay => self.base_update(ctx),
            UiState::Inventory => self.inventory_update(ctx),
        }
    }

    pub fn save_game(&self) {
        let save_data = world::save();
        let mut file = File::create(&Path::new("/tmp/magog_save.json"));
        file.write_str(&save_data[]).unwrap();
    }

    pub fn load_game(&mut self) {
        let path = Path::new("/tmp/magog_save.json");
        if !path.exists() { return; }
        let save_data = File::open(&path).read_to_string().unwrap();
        // TODO: Handle failed load nicely.
        world::load(&save_data[]).unwrap();
    }

    fn smart_move(&mut self, dir: Dir6) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();

        let shoot_range = 8;
        let shoot_damage = 10;

        if let Some(e) = action::find_target(player, dir, shoot_range) {
            // Shoot instead of moving if you'd hit an enemy.
            action::shoot(loc, dir, shoot_range, shoot_damage);
            return;
        }

        for &d in vec![dir, dir + 1, dir - 1].iter() {
            let target_loc = loc + d.to_v2();
            if target_loc.has_mobs() {
                action::input(Melee(d));
                return;
            } else if player.can_step(d) {
                action::input(Step(d));
                return;
            }
        }
    }

    fn autoexplore(&mut self) -> bool {
        let player = action::player().unwrap();
        if player.is_threatened() {
            return false;
        }
        if let Some(pathing) = action::autoexplore_map(32) {
            let loc = player.location().unwrap();
            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() == 0 {
                return false;
            }

            action::input(Step(loc.dir6_towards(steps[0]).unwrap()));
            return true;
        }

        false
    }

    /// Context-specific interaction with the current cell.
    fn interact(&mut self) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        if let Some(item) = loc.top_item() {
            player.pick_up(item);
            return;
        }
    }

    /// Process a player control keypress.
    pub fn gameplay_process_key(&mut self, key: Key) -> bool {
        if action::control_state() != AwaitingInput {
            return false;
        }

        if self.exploring {
            self.exploring = false;
        }

        match key {
            Key::Q | Key::Pad7 => { self.smart_move(NorthWest); }
            Key::W | Key::Pad8 | Key::Up => { self.smart_move(North); }
            Key::E | Key::Pad9 => { self.smart_move(NorthEast); }
            Key::A | Key::Pad1 => { self.smart_move(SouthWest); }
            Key::S | Key::Pad2 | Key::Down => { self.smart_move(South); }
            Key::D | Key::Pad3 => { self.smart_move(SouthEast); }

            Key::Enter => { self.interact(); }
            Key::X => { self.exploring = true; }

            // Open inventory
            Key::Tab => { self.ui_state = UiState::Inventory; }

            Key::F5 => { self.save_game(); }
            Key::F9 => { self.load_game(); }
            Key::F12 => { self.screenshot_requested = true; }
            _ => { return false; }
        }
        return true;
    }

    pub fn gameplay_process(&mut self, event: Event) -> bool {
        match event {
            Event::Render(ctx) => {
                self.update(ctx);
            }
            // TODO: Better quit confirmation than just pressing esc.
            Event::KeyPressed(Key::Escape) => {
                return false;
            }
            Event::KeyPressed(k) => {
                self.gameplay_process_key(k);
            }

            Event::Char(ch) => {
                // TODO: Chars and keypresses in same lookup (use variants?)
                match ch {
                    // Debug
                    '>' => { action::next_level(); }
                    _ => ()
                }
            }

            _ => ()
        }
        true
    }

    pub fn process(&mut self, event: Event) -> bool {
        match self.ui_state {
            UiState::Gameplay => self.gameplay_process(event),
            UiState::Inventory => self.inventory_process(event),
        }
    }
}

struct SlotData {
    key: char,
    slot: Slot,
    name: &'static str,
}

static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', slot: Slot::Melee,      name: "Weapon " },
    SlotData { key: 'b', slot: Slot::Ranged,     name: "Ranged " },
    SlotData { key: 'c', slot: Slot::Head,       name: "Head   " },
    SlotData { key: 'd', slot: Slot::Body,       name: "Body   " },
    SlotData { key: 'e', slot: Slot::Feet,       name: "Feet   " },
    SlotData { key: 'f', slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', slot: Slot::InventoryJ, name: "       " },
    SlotData { key: 'k', slot: Slot::InventoryK, name: "       " },
    SlotData { key: 'l', slot: Slot::InventoryL, name: "       " },
    SlotData { key: 'm', slot: Slot::InventoryM, name: "       " },
    SlotData { key: 'n', slot: Slot::InventoryN, name: "       " },
    SlotData { key: 'o', slot: Slot::InventoryO, name: "       " },
    SlotData { key: 'p', slot: Slot::InventoryP, name: "       " },
    SlotData { key: 'q', slot: Slot::InventoryQ, name: "       " },
    SlotData { key: 'r', slot: Slot::InventoryR, name: "       " },
    SlotData { key: 's', slot: Slot::InventoryS, name: "       " },
    SlotData { key: 't', slot: Slot::InventoryT, name: "       " },
    SlotData { key: 'u', slot: Slot::InventoryU, name: "       " },
    SlotData { key: 'v', slot: Slot::InventoryV, name: "       " },
    SlotData { key: 'w', slot: Slot::InventoryW, name: "       " },
    SlotData { key: 'x', slot: Slot::InventoryX, name: "       " },
    SlotData { key: 'y', slot: Slot::InventoryY, name: "       " },
    SlotData { key: 'z', slot: Slot::InventoryZ, name: "       " },
];
<commit_msg>Tweak autofiring logic<commit_after>use time;
use std::old_io::File;
use std::old_io::fs::PathExtensions;
use std::collections::HashMap;
use util::{color, V2};
use backend::{Canvas, CanvasUtil, Event, Key, Fonter};
use world;
use world::action;
use world::action::Input::{Step, Melee};
use world::action::ControlState::*;
use world::{Msg, FovStatus};
use world::Dir6;
use world::Dir6::*;
use world::{Entity};
use world::item::{Slot};
use worldview;
use sprite::{WorldSprites, GibSprite, BeamSprite};
use tilecache;
use tilecache::icon;
use msg_queue::MsgQueue;

pub struct GameState {
    /// Transient effect sprites drawn in game world view.
    world_spr: WorldSprites,
    /// Counters for entities with flashing damage animation.
    damage_timers: HashMap<Entity, u32>,

    /// Flag for autoexploration.
    // TODO: Probably going to need a general "ongoing activity" system at
    // some point.
    exploring: bool,

    msg: MsgQueue,
    ui_state: UiState,

    // Hacky thing to wait for next time Canvas reference is available.
    screenshot_requested: bool,
}

enum UiState {
    Gameplay,
    Inventory
}

impl GameState {
    pub fn new(seed: Option<u32>) -> GameState {
        world::init_world(seed);
        GameState {
            world_spr: WorldSprites::new(),
            damage_timers: HashMap::new(),
            exploring: false,
            msg: MsgQueue::new(),
            ui_state: UiState::Gameplay,
            screenshot_requested: false,
        }
    }

    fn draw_player_ui(&mut self, ctx: &mut Canvas, player: Entity) {
        let hp = player.hp();
        let max_hp = player.max_hp();

        // Draw heart containers.
        for i in 0..((max_hp + 1) / 2) {
            let pos = V2(i as f32 * 8.0, 8.0);
            let idx = if hp >= (i + 1) * 2 { icon::HEART }
                else if hp == i * 2 + 1 { icon::HALF_HEART }
                else { icon::NO_HEART };
            ctx.draw_image(tilecache::get(idx), pos, 0.0, &color::FIREBRICK, &color::BLACK);
        }
    }

    fn base_paint(&mut self, ctx: &mut Canvas) {
        let camera = world::camera();
        worldview::draw_world(&camera, ctx, &self.damage_timers);

        self.world_spr.draw(|x| (camera + x).fov_status() == Some(FovStatus::Seen), &camera, ctx);
        self.world_spr.update();

        let location_name = camera.name();
        write!(&mut ctx.text_writer(V2(640 - location_name.len() as i32 * 8, 8), 0.1, color::LIGHTGRAY)
                       .set_border(color::BLACK),
                       "{}", location_name).unwrap();

        self.msg.draw(ctx);
        if let Some(player) = action::player() {
            self.draw_player_ui(ctx, player);
        }

        let fps = 1.0 / ctx.render_duration;
        let _ = write!(&mut ctx.text_writer(V2(0, 16), 0.1, color::LIGHTGREEN)
                       .set_border(color::BLACK),
                       "FPS {:.0}", fps);
        let _ = write!(&mut ctx.text_writer(V2(0, 360), 0.1, color::LIGHTGRAY)
                      .set_border(color::BLACK),
                      "MAGOG v{}{}",
                      ::version(),
                      if !cfg!(ndebug) { " ***DEBUG build***" } else { "" });
    }

    fn base_update(&mut self, ctx: &mut Canvas) {
        // Process events
        loop {
            match world::pop_msg() {
                Some(Msg::Gib(loc)) => {
                    self.world_spr.add(Box::new(GibSprite::new(loc)));
                }
                Some(Msg::Damage(entity)) => {
                    self.damage_timers.insert(entity, 2);
                }
                Some(Msg::Text(txt)) => {
                    self.msg.msg(txt)
                }
                Some(Msg::Caption(txt)) => {
                    self.msg.caption(txt)
                }
                Some(Msg::Beam(loc1, loc2)) => {
                    self.world_spr.add(Box::new(BeamSprite::new(loc1, loc2, 10)));
                }
                Some(Msg::Sparks(_loc)) => {
                    // TODO
                }
                Some(x) => {
                    println!("Unhandled Msg type {:?}", x);
                }
                None => break
            }
        }

        self.base_paint(ctx);

        if action::control_state() == ReadyToUpdate {
            action::update();
        }

        if self.exploring {
            if action::control_state() == AwaitingInput {
                self.exploring = self.autoexplore();
            }
        }

        self.damage_timers = self.damage_timers.clone().into_iter()
            .filter(|&(_, t)| t > 0)
            .map(|(e, t)| (e, t - 1))
            .collect();

        self.msg.update();
    }

    fn inventory_update(&mut self, ctx: &mut Canvas) {
        let player = action::player().unwrap();
        {
            let mut cursor = ctx.text_writer(V2(0, 8), 0.1, color::GAINSBORO);
            for slot_data in SLOT_DATA.iter() {
                let name = match player.equipped(slot_data.slot) {
                    Some(item) => item.name(),
                    None => "".to_string()
                };
                write!(&mut cursor, "{}] {}: {}\n",
                    slot_data.key, slot_data.name, name).unwrap();
            }
        }

        {
            let mut cursor = ctx.text_writer(V2(0, 360), 0.1, color::GAINSBORO);
            write!(&mut cursor, "Press letter to equip/unequip item. Press shift+letter to drop item.").unwrap();
        }
    }

    pub fn inventory_process(&mut self, event: Event) -> bool {
        let player = action::player().unwrap();
        match event {
            Event::Render(ctx) => { self.update(ctx); }
            Event::KeyPressed(Key::Escape) | Event::KeyPressed(Key::Tab) => {
                self.ui_state = UiState::Gameplay
            }
            Event::KeyPressed(Key::F12) => { self.screenshot_requested = true; }
            Event::KeyPressed(_) => {}

            Event::Char(ch) => {
                for slot_data in SLOT_DATA.iter() {
                    if ch == slot_data.key {
                        if slot_data.slot.is_gear_slot() {
                            // Unequip gear
                            match player.free_bag_slot() {
                                None => {
                                    // No room in bag, can't unequip until
                                    // drop something.
                                    // TODO: Message about full bag.
                                }
                                Some(swap_slot) => {
                                    player.swap_equipped(slot_data.slot, swap_slot);
                                }
                            }
                        }
                        if slot_data.slot.is_bag_slot() {
                            // Bag items get equipped if they have are gear
                            // with a preferred slot.
                            if let Some(item) = player.equipped(slot_data.slot) {
                                let equip_slots = item.equip_slots();
                                for &swap_slot in equip_slots.iter() {
                                    if player.equipped(swap_slot).is_none() {
                                        player.swap_equipped(slot_data.slot, swap_slot);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if ch == slot_data.key.to_uppercase() {
                        // Drop item in slot.
                        if let Some(item) = player.equipped(slot_data.slot) {
                            item.place(player.location().unwrap());
                        }
                        break;
                    }
                }
            }

            _ => ()
        }
        true
    }

    /// Repaint view, update game world if needed.
    pub fn update(&mut self, ctx: &mut Canvas) {
        if self.screenshot_requested {
            use image;
            let shot = ctx.screenshot();
            let mut file = File::create(&Path::new(
                    format!("/tmp/shot-{}.png", time::precise_time_s() as u64)))
                    .unwrap();
            let _ = image::ImageRgb8(shot).save(&mut file, image::PNG);

            self.screenshot_requested = false;
        }

        ctx.clear();

        match self.ui_state {
            UiState::Gameplay => self.base_update(ctx),
            UiState::Inventory => self.inventory_update(ctx),
        }
    }

    pub fn save_game(&self) {
        let save_data = world::save();
        let mut file = File::create(&Path::new("/tmp/magog_save.json"));
        file.write_str(&save_data[]).unwrap();
    }

    pub fn load_game(&mut self) {
        let path = Path::new("/tmp/magog_save.json");
        if !path.exists() { return; }
        let save_data = File::open(&path).read_to_string().unwrap();
        // TODO: Handle failed load nicely.
        world::load(&save_data[]).unwrap();
    }

    fn smart_move(&mut self, dir: Dir6) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();

        let shoot_range = 8;
        let shoot_damage = 10;

        if !(loc + dir.to_v2()).has_mobs() {
            // Shoot instead of moving if you'd hit an enemy and there's no
            // melee target.
            if let Some(e) = action::find_target(player, dir, shoot_range) {
                if player.is_hostile_to(e) {
                    action::shoot(loc, dir, shoot_range, shoot_damage);
                    return;
                }
            }
        }

        for &d in vec![dir, dir + 1, dir - 1].iter() {
            let target_loc = loc + d.to_v2();
            if target_loc.has_mobs() {
                action::input(Melee(d));
                return;
            } else if player.can_step(d) {
                action::input(Step(d));
                return;
            }
        }
    }

    fn autoexplore(&mut self) -> bool {
        let player = action::player().unwrap();
        if player.is_threatened() {
            return false;
        }
        if let Some(pathing) = action::autoexplore_map(32) {
            let loc = player.location().unwrap();
            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() == 0 {
                return false;
            }

            action::input(Step(loc.dir6_towards(steps[0]).unwrap()));
            return true;
        }

        false
    }

    /// Context-specific interaction with the current cell.
    fn interact(&mut self) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        if let Some(item) = loc.top_item() {
            player.pick_up(item);
            return;
        }
    }

    /// Process a player control keypress.
    pub fn gameplay_process_key(&mut self, key: Key) -> bool {
        if action::control_state() != AwaitingInput {
            return false;
        }

        if self.exploring {
            self.exploring = false;
        }

        match key {
            Key::Q | Key::Pad7 => { self.smart_move(NorthWest); }
            Key::W | Key::Pad8 | Key::Up => { self.smart_move(North); }
            Key::E | Key::Pad9 => { self.smart_move(NorthEast); }
            Key::A | Key::Pad1 => { self.smart_move(SouthWest); }
            Key::S | Key::Pad2 | Key::Down => { self.smart_move(South); }
            Key::D | Key::Pad3 => { self.smart_move(SouthEast); }

            Key::Enter => { self.interact(); }
            Key::X => { self.exploring = true; }

            // Open inventory
            Key::Tab => { self.ui_state = UiState::Inventory; }

            Key::F5 => { self.save_game(); }
            Key::F9 => { self.load_game(); }
            Key::F12 => { self.screenshot_requested = true; }
            _ => { return false; }
        }
        return true;
    }

    pub fn gameplay_process(&mut self, event: Event) -> bool {
        match event {
            Event::Render(ctx) => {
                self.update(ctx);
            }
            // TODO: Better quit confirmation than just pressing esc.
            Event::KeyPressed(Key::Escape) => {
                return false;
            }
            Event::KeyPressed(k) => {
                self.gameplay_process_key(k);
            }

            Event::Char(ch) => {
                // TODO: Chars and keypresses in same lookup (use variants?)
                match ch {
                    // Debug
                    '>' => { action::next_level(); }
                    _ => ()
                }
            }

            _ => ()
        }
        true
    }

    pub fn process(&mut self, event: Event) -> bool {
        match self.ui_state {
            UiState::Gameplay => self.gameplay_process(event),
            UiState::Inventory => self.inventory_process(event),
        }
    }
}

struct SlotData {
    key: char,
    slot: Slot,
    name: &'static str,
}

static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', slot: Slot::Melee,      name: "Weapon " },
    SlotData { key: 'b', slot: Slot::Ranged,     name: "Ranged " },
    SlotData { key: 'c', slot: Slot::Head,       name: "Head   " },
    SlotData { key: 'd', slot: Slot::Body,       name: "Body   " },
    SlotData { key: 'e', slot: Slot::Feet,       name: "Feet   " },
    SlotData { key: 'f', slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', slot: Slot::InventoryJ, name: "       " },
    SlotData { key: 'k', slot: Slot::InventoryK, name: "       " },
    SlotData { key: 'l', slot: Slot::InventoryL, name: "       " },
    SlotData { key: 'm', slot: Slot::InventoryM, name: "       " },
    SlotData { key: 'n', slot: Slot::InventoryN, name: "       " },
    SlotData { key: 'o', slot: Slot::InventoryO, name: "       " },
    SlotData { key: 'p', slot: Slot::InventoryP, name: "       " },
    SlotData { key: 'q', slot: Slot::InventoryQ, name: "       " },
    SlotData { key: 'r', slot: Slot::InventoryR, name: "       " },
    SlotData { key: 's', slot: Slot::InventoryS, name: "       " },
    SlotData { key: 't', slot: Slot::InventoryT, name: "       " },
    SlotData { key: 'u', slot: Slot::InventoryU, name: "       " },
    SlotData { key: 'v', slot: Slot::InventoryV, name: "       " },
    SlotData { key: 'w', slot: Slot::InventoryW, name: "       " },
    SlotData { key: 'x', slot: Slot::InventoryX, name: "       " },
    SlotData { key: 'y', slot: Slot::InventoryY, name: "       " },
    SlotData { key: 'z', slot: Slot::InventoryZ, name: "       " },
];
<|endoftext|>


<commit_before>use geom::{tri, vertex, Tri, Vertex};
use math::EuclideanSpace;
use std::ops::{Deref, Index};

/// The number of vertices in a quad.
pub const NUM_VERTICES: u8 = 4;

/// The number of triangles that make up a quad.
pub const NUM_TRIANGLES: u8 = 2;

/// The same as `triangles`, but instead returns the vertex indices for each triangle.
pub const TRIANGLE_INDEX_TRIS: TrianglesIndexTris = [[0, 1, 2], [0, 2, 3]];
pub type TrianglesIndexTris = [[usize; tri::NUM_VERTICES as usize]; NUM_TRIANGLES as usize];

/// The number of indices used to describe each triangle in the quad.
pub const NUM_TRIANGLE_INDICES: u8 = 6;

/// The same as `triangles`, but instead returns the vertex indices for each triangle.
pub const TRIANGLE_INDICES: [usize; NUM_TRIANGLE_INDICES as usize] = [0, 1, 2, 0, 2, 3];

/// A quad represented by its four vertices.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct Quad<V = vertex::Default>(pub [V; NUM_VERTICES as usize]);

/// An `Iterator` yielding the two triangles that make up a quad.
#[derive(Clone, Debug)]
pub struct Triangles<V> {
    a: Option<Tri<V>>,
    b: Option<Tri<V>>,
}

/// A simple iterator yielding each vertex in a `Quad`.
#[derive(Clone, Debug)]
pub struct Vertices<V> {
    quad: Quad<V>,
    index: u8,
}

impl<V> Quad<V>
where
    V: Vertex,
{
    /// Produce an iterator yielding each vertex in the `Quad`.
    pub fn vertices(self) -> Vertices<V> {
        vertices(self)
    }

    /// Produce the centroid of the quad, aka the "mean"/"average" vertex.
    pub fn centroid(&self) -> V
    where
        V: EuclideanSpace,
    {
        centroid(self)
    }

    /// Triangulates the given quad, represented by four points that describe its edges in either
    /// clockwise or anti-clockwise order.
    ///
    /// # Example
    ///
    /// The following rectangle
    ///
    /// ```ignore
    ///  a        b
    ///   --------
    ///   |      |
    ///   |      |
    ///   |      |
    ///   --------
    ///  d        c
    ///
    /// ```
    ///
    /// given as
    ///
    /// ```ignore
    /// triangles([a, b, c, d])
    /// ```
    ///
    /// returns
    ///
    /// ```ignore
    /// (Tri([a, b, c]), Tri([a, c, d]))
    /// ```
    ///
    /// Here's a basic code example:
    ///
    /// ```
    /// extern crate nannou;
    ///
    /// use nannou::geom::{self, Quad, Tri};
    /// use nannou::math::pt2;
    ///
    /// fn main() {
    ///     let a = pt2(0.0, 1.0);
    ///     let b = pt2(1.0, 1.0);
    ///     let c = pt2(1.0, 0.0);
    ///     let d = pt2(0.0, 0.0);
    ///     let quad = Quad([a, b, c, d]);
    ///     let triangles = geom::quad::triangles(&quad);
    ///     assert_eq!(triangles, (Tri([a, b, c]), Tri([a, c, d])));
    /// }
    /// ```
    #[inline]
    pub fn triangles(&self) -> (Tri<V>, Tri<V>) {
        triangles(self)
    }

    /// The same as `triangles` but provided as an **Iterator**.
    pub fn triangles_iter(&self) -> Triangles<V> {
        triangles_iter(self)
    }

    /// Map the **Quad**'s vertices to a new type.
    pub fn map_vertices<F, V2>(self, mut map: F) -> Quad<V2>
    where
        F: FnMut(V) -> V2,
    {
        let (a, b, c, d) = self.into();
        Quad([map(a), map(b), map(c), map(d)])
    }
}

/// Produce an iterator yielding each vertex in the given **Quad**.
pub fn vertices<V>(quad: Quad<V>) -> Vertices<V> {
    let index = 0;
    Vertices { quad, index }
}

/// Produce the centroid of the quad, aka the "mean"/"average" vertex.
pub fn centroid<V>(quad: &Quad<V>) -> V
where
    V: EuclideanSpace,
{
    EuclideanSpace::centroid(&quad[..])
}

/// Triangulates the given quad, represented by four points that describe its edges in either
/// clockwise or anti-clockwise order.
///
/// # Example
///
/// The following rectangle
///
/// ```ignore
///
///  a        b
///   --------
///   |      |
///   |      |
///   |      |
///   --------
///  d        c
///
/// ```
///
/// given as
///
/// ```ignore
/// triangles([a, b, c, d])
/// ```
///
/// returns
///
/// ```ignore
/// (Tri([a, b, c]), Tri([a, c, d]))
/// ```
///
/// Here's a basic code example:
///
/// ```
/// extern crate nannou;
///
/// use nannou::geom::{self, Quad, Tri};
/// use nannou::math::pt2;
///
/// fn main() {
///     let a = pt2(0.0, 1.0);
///     let b = pt2(1.0, 1.0);
///     let c = pt2(1.0, 0.0);
///     let d = pt2(0.0, 0.0);
///     let quad = Quad([a, b, c, d]);
///     let triangles = geom::quad::triangles(&quad);
///     assert_eq!(triangles, (Tri([a, b, c]), Tri([a, c, d])));
/// }
/// ```
#[inline]
pub fn triangles<V>(q: &Quad<V>) -> (Tri<V>, Tri<V>)
where
    V: Vertex,
{
    let a = Tri::from_index_tri(&q.0, &TRIANGLE_INDEX_TRIS[0]);
    let b = Tri::from_index_tri(&q.0, &TRIANGLE_INDEX_TRIS[1]);
    (a, b)
}

/// The same as `triangles` but provided as an `Iterator`.
pub fn triangles_iter<V>(points: &Quad<V>) -> Triangles<V>
where
    V: Vertex,
{
    let (a, b) = triangles(points);
    Triangles {
        a: Some(a),
        b: Some(b),
    }
}

impl<V> Iterator for Triangles<V> {
    type Item = Tri<V>;
    fn next(&mut self) -> Option<Self::Item> {
        self.a.take().or_else(|| self.b.take())
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }
}

impl<V> DoubleEndedIterator for Triangles<V> {
    fn next_back(&mut self) -> Option<Self::Item> {
        self.b.take().or_else(|| self.a.take())
    }
}

impl<V> ExactSizeIterator for Triangles<V> {
    fn len(&self) -> usize {
        match (&self.a, &self.b) {
            (&Some(_), &Some(_)) => 2,
            (&None, &Some(_)) => 0,
            _ => 1,
        }
    }
}

impl<V> Iterator for Vertices<V>
where
    V: Clone,
{
    type Item = V;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index < NUM_VERTICES {
            let v = self.quad[self.index as usize].clone();
            self.index += 1;
            Some(v)
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }
}

impl<V> ExactSizeIterator for Vertices<V>
where
    V: Clone,
{
    fn len(&self) -> usize {
        NUM_VERTICES as usize - self.index as usize
    }
}

impl<V> Deref for Quad<V> {
    type Target = [V; NUM_VERTICES as usize];
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<V> From<[V; NUM_VERTICES as usize]> for Quad<V>
where
    V: Vertex,
{
    fn from(points: [V; NUM_VERTICES as usize]) -> Self {
        Quad(points)
    }
}

impl<V> From<(V, V, V, V)> for Quad<V>
where
    V: Vertex,
{
    fn from((a, b, c, d): (V, V, V, V)) -> Self {
        Quad([a, b, c, d])
    }
}

impl<V> Into<[V; NUM_VERTICES as usize]> for Quad<V>
where
    V: Vertex,
{
    fn into(self) -> [V; NUM_VERTICES as usize] {
        self.0
    }
}

impl<V> Into<(V, V, V, V)> for Quad<V>
where
    V: Vertex,
{
    fn into(self) -> (V, V, V, V) {
        (self[0], self[1], self[2], self[3])
    }
}

impl<V> AsRef<Quad<V>> for Quad<V>
where
    V: Vertex,
{
    fn as_ref(&self) -> &Quad<V> {
        self
    }
}

impl<V> AsRef<[V; NUM_VERTICES as usize]> for Quad<V>
where
    V: Vertex,
{
    fn as_ref(&self) -> &[V; NUM_VERTICES as usize] {
        &self.0
    }
}

impl<V> Index<usize> for Quad<V>
where
    V: Vertex,
{
    type Output = V;
    fn index(&self, index: usize) -> &Self::Output {
        &self.0[index]
    }
}
<commit_msg>Add bounding_rect and bounding_cuboid methods to quad<commit_after>use geom::{tri, vertex, Cuboid, Range, Rect, Tri, Vertex, Vertex2d, Vertex3d};
use math::EuclideanSpace;
use std::ops::{Deref, Index};

/// The number of vertices in a quad.
pub const NUM_VERTICES: u8 = 4;

/// The number of triangles that make up a quad.
pub const NUM_TRIANGLES: u8 = 2;

/// The same as `triangles`, but instead returns the vertex indices for each triangle.
pub const TRIANGLE_INDEX_TRIS: TrianglesIndexTris = [[0, 1, 2], [0, 2, 3]];
pub type TrianglesIndexTris = [[usize; tri::NUM_VERTICES as usize]; NUM_TRIANGLES as usize];

/// The number of indices used to describe each triangle in the quad.
pub const NUM_TRIANGLE_INDICES: u8 = 6;

/// The same as `triangles`, but instead returns the vertex indices for each triangle.
pub const TRIANGLE_INDICES: [usize; NUM_TRIANGLE_INDICES as usize] = [0, 1, 2, 0, 2, 3];

/// A quad represented by its four vertices.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct Quad<V = vertex::Default>(pub [V; NUM_VERTICES as usize]);

/// An `Iterator` yielding the two triangles that make up a quad.
#[derive(Clone, Debug)]
pub struct Triangles<V> {
    a: Option<Tri<V>>,
    b: Option<Tri<V>>,
}

/// A simple iterator yielding each vertex in a `Quad`.
#[derive(Clone, Debug)]
pub struct Vertices<V> {
    quad: Quad<V>,
    index: u8,
}

impl<V> Quad<V>
where
    V: Vertex,
{
    /// Produce an iterator yielding each vertex in the `Quad`.
    pub fn vertices(self) -> Vertices<V> {
        vertices(self)
    }

    /// Produce the centroid of the quad, aka the "mean"/"average" vertex.
    pub fn centroid(&self) -> V
    where
        V: EuclideanSpace,
    {
        centroid(self)
    }

    /// Triangulates the given quad, represented by four points that describe its edges in either
    /// clockwise or anti-clockwise order.
    ///
    /// # Example
    ///
    /// The following rectangle
    ///
    /// ```ignore
    ///  a        b
    ///   --------
    ///   |      |
    ///   |      |
    ///   |      |
    ///   --------
    ///  d        c
    ///
    /// ```
    ///
    /// given as
    ///
    /// ```ignore
    /// triangles([a, b, c, d])
    /// ```
    ///
    /// returns
    ///
    /// ```ignore
    /// (Tri([a, b, c]), Tri([a, c, d]))
    /// ```
    ///
    /// Here's a basic code example:
    ///
    /// ```
    /// extern crate nannou;
    ///
    /// use nannou::geom::{self, Quad, Tri};
    /// use nannou::math::pt2;
    ///
    /// fn main() {
    ///     let a = pt2(0.0, 1.0);
    ///     let b = pt2(1.0, 1.0);
    ///     let c = pt2(1.0, 0.0);
    ///     let d = pt2(0.0, 0.0);
    ///     let quad = Quad([a, b, c, d]);
    ///     let triangles = geom::quad::triangles(&quad);
    ///     assert_eq!(triangles, (Tri([a, b, c]), Tri([a, c, d])));
    /// }
    /// ```
    #[inline]
    pub fn triangles(&self) -> (Tri<V>, Tri<V>) {
        triangles(self)
    }

    /// The same as `triangles` but provided as an **Iterator**.
    pub fn triangles_iter(&self) -> Triangles<V> {
        triangles_iter(self)
    }

    /// The bounding `Rect` of the triangle.
    pub fn bounding_rect(self) -> Rect<V::Scalar>
    where
        V: Vertex2d,
    {
        let (a, b, c, d) = self.into();
        let (a, b, c, d) = (a.point2(), b.point2(), c.point2(), d.point2());
        let rect = Rect { x: Range::new(a.x, a.x), y: Range::new(a.y, a.y) };
        rect.stretch_to_point(b).stretch_to_point(c).stretch_to_point(d)
    }

    /// The bounding `Rect` of the triangle.
    pub fn bounding_cuboid(self) -> Cuboid<V::Scalar>
    where
        V: Vertex3d,
    {
        let (a, b, c, d) = self.into();
        let (a, b, c, d) = (a.point3(), b.point3(), c.point3(), d.point3());
        let cuboid = Cuboid {
            x: Range::new(a.x, a.x),
            y: Range::new(a.y, a.y),
            z: Range::new(a.z, a.z),
        };
        cuboid.stretch_to_point(b).stretch_to_point(c).stretch_to_point(d)
    }

    /// Map the **Quad**'s vertices to a new type.
    pub fn map_vertices<F, V2>(self, mut map: F) -> Quad<V2>
    where
        F: FnMut(V) -> V2,
    {
        let (a, b, c, d) = self.into();
        Quad([map(a), map(b), map(c), map(d)])
    }
}

/// Produce an iterator yielding each vertex in the given **Quad**.
pub fn vertices<V>(quad: Quad<V>) -> Vertices<V> {
    let index = 0;
    Vertices { quad, index }
}

/// Produce the centroid of the quad, aka the "mean"/"average" vertex.
pub fn centroid<V>(quad: &Quad<V>) -> V
where
    V: EuclideanSpace,
{
    EuclideanSpace::centroid(&quad[..])
}

/// Triangulates the given quad, represented by four points that describe its edges in either
/// clockwise or anti-clockwise order.
///
/// # Example
///
/// The following rectangle
///
/// ```ignore
///
///  a        b
///   --------
///   |      |
///   |      |
///   |      |
///   --------
///  d        c
///
/// ```
///
/// given as
///
/// ```ignore
/// triangles([a, b, c, d])
/// ```
///
/// returns
///
/// ```ignore
/// (Tri([a, b, c]), Tri([a, c, d]))
/// ```
///
/// Here's a basic code example:
///
/// ```
/// extern crate nannou;
///
/// use nannou::geom::{self, Quad, Tri};
/// use nannou::math::pt2;
///
/// fn main() {
///     let a = pt2(0.0, 1.0);
///     let b = pt2(1.0, 1.0);
///     let c = pt2(1.0, 0.0);
///     let d = pt2(0.0, 0.0);
///     let quad = Quad([a, b, c, d]);
///     let triangles = geom::quad::triangles(&quad);
///     assert_eq!(triangles, (Tri([a, b, c]), Tri([a, c, d])));
/// }
/// ```
#[inline]
pub fn triangles<V>(q: &Quad<V>) -> (Tri<V>, Tri<V>)
where
    V: Vertex,
{
    let a = Tri::from_index_tri(&q.0, &TRIANGLE_INDEX_TRIS[0]);
    let b = Tri::from_index_tri(&q.0, &TRIANGLE_INDEX_TRIS[1]);
    (a, b)
}

/// The same as `triangles` but provided as an `Iterator`.
pub fn triangles_iter<V>(points: &Quad<V>) -> Triangles<V>
where
    V: Vertex,
{
    let (a, b) = triangles(points);
    Triangles {
        a: Some(a),
        b: Some(b),
    }
}

impl<V> Iterator for Triangles<V> {
    type Item = Tri<V>;
    fn next(&mut self) -> Option<Self::Item> {
        self.a.take().or_else(|| self.b.take())
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }
}

impl<V> DoubleEndedIterator for Triangles<V> {
    fn next_back(&mut self) -> Option<Self::Item> {
        self.b.take().or_else(|| self.a.take())
    }
}

impl<V> ExactSizeIterator for Triangles<V> {
    fn len(&self) -> usize {
        match (&self.a, &self.b) {
            (&Some(_), &Some(_)) => 2,
            (&None, &Some(_)) => 0,
            _ => 1,
        }
    }
}

impl<V> Iterator for Vertices<V>
where
    V: Clone,
{
    type Item = V;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index < NUM_VERTICES {
            let v = self.quad[self.index as usize].clone();
            self.index += 1;
            Some(v)
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }
}

impl<V> ExactSizeIterator for Vertices<V>
where
    V: Clone,
{
    fn len(&self) -> usize {
        NUM_VERTICES as usize - self.index as usize
    }
}

impl<V> Deref for Quad<V> {
    type Target = [V; NUM_VERTICES as usize];
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<V> From<[V; NUM_VERTICES as usize]> for Quad<V>
where
    V: Vertex,
{
    fn from(points: [V; NUM_VERTICES as usize]) -> Self {
        Quad(points)
    }
}

impl<V> From<(V, V, V, V)> for Quad<V>
where
    V: Vertex,
{
    fn from((a, b, c, d): (V, V, V, V)) -> Self {
        Quad([a, b, c, d])
    }
}

impl<V> Into<[V; NUM_VERTICES as usize]> for Quad<V>
where
    V: Vertex,
{
    fn into(self) -> [V; NUM_VERTICES as usize] {
        self.0
    }
}

impl<V> Into<(V, V, V, V)> for Quad<V>
where
    V: Vertex,
{
    fn into(self) -> (V, V, V, V) {
        (self[0], self[1], self[2], self[3])
    }
}

impl<V> AsRef<Quad<V>> for Quad<V>
where
    V: Vertex,
{
    fn as_ref(&self) -> &Quad<V> {
        self
    }
}

impl<V> AsRef<[V; NUM_VERTICES as usize]> for Quad<V>
where
    V: Vertex,
{
    fn as_ref(&self) -> &[V; NUM_VERTICES as usize] {
        &self.0
    }
}

impl<V> Index<usize> for Quad<V>
where
    V: Vertex,
{
    type Output = V;
    fn index(&self, index: usize) -> &Self::Output {
        &self.0[index]
    }
}
<|endoftext|>