<commit_before>//! Geometric traits.
//!
//! This module provides traits that specify the geometry of `MeshGraph`s and
//! express the spatial operations supported by that geometry. The most basic
//! and only required trait is `Geometry`, which uses associated types to
//! specify the type of the `geometry` field in the payloads for vertices,
//! arcs, edges, and faces in a graph.
//!
//! To support useful spatial operations, types that implement `Geometry` may
//! also implement `AsPosition`. The `AsPosition` trait exposes positional data
//! in vertices. If that positional data also implements spatial traits from
//! the [theon](https://crates.io/crates/theon) crate, then spatial operations
//! will be enabled, such as `smooth`, `split_at_midpoint`, and
//! `poke_with_offset`.
//!
//! This module also defines traits that define the capabilities of geometry in
//! a `MeshGraph`. These traits enable generic code without the need to express
//! complicated relationships between types representing a Euclidean space. For
//! example, the `FaceCentroid` trait is defined for `Geometry` types that
//! expose positional data that implements the necessary traits to compute the
//! centroid of a face.
//!
//! # Examples
//!
//! A function that subdivides faces in a graph by splitting edges at their
//! midpoints:
//!
//! ```rust
//! # extern crate plexus;
//! # extern crate smallvec;
//! #
//! use plexus::geometry::AsPosition;
//! use plexus::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph, VertexPosition};
//! use plexus::prelude::*;
//! use smallvec::SmallVec;
//!
//! # fn main() {
//! // Requires `EdgeMidpoint` for `split_at_midpoint`.
//! pub fn circumscribe<G>(face: FaceView<&mut MeshGraph<G>, G>) -> FaceView<&mut MeshGraph<G>, G>
//! where
//!     G: EdgeMidpoint<Midpoint = VertexPosition<G>> + GraphGeometry,
//!     G::Vertex: AsPosition,
//! {
//!     let arity = face.arity();
//!     let mut arc = face.into_arc();
//!     let mut splits = SmallVec::<[_; 4]>::with_capacity(arity);
//!     for _ in 0..arity {
//!         let vertex = arc.split_at_midpoint();
//!         splits.push(vertex.key());
//!         arc = vertex.into_outgoing_arc().into_next_arc();
//!     }
//!     let mut face = arc.into_face().unwrap();
//!     for (a, b) in splits.into_iter().perimeter() {
//!         face = face.split(ByKey(a), ByKey(b)).unwrap().into_face().unwrap();
//!     }
//!     face
//! }
//! # }
//! ```

// TODO: Integrate this module documentation into the `graph` module.

use theon::ops::{Cross, Interpolate, Project};
use theon::space::{EuclideanSpace, InnerSpace, Vector};

use crate::geometry::AsPosition;
use crate::graph::borrow::Reborrow;
use crate::graph::storage::payload::{ArcPayload, EdgePayload, FacePayload, VertexPayload};
use crate::graph::storage::AsStorage;
use crate::graph::view::edge::{ArcView, EdgeView};
use crate::graph::view::face::FaceView;
use crate::graph::view::vertex::VertexView;
use crate::graph::GraphError;

pub type VertexPosition<G> = <<G as GraphGeometry>::Vertex as AsPosition>::Target;

// TODO: Require `Clone` instead of `Copy` once non-`Copy` types are supported
//       by the slotmap crate. See https://github.com/orlp/slotmap/issues/27
/// Graph geometry.
///
/// Specifies the types used to represent geometry for vertices, arcs, edges,
/// and faces in a graph. Arbitrary types can be used, including `()` for no
/// geometry at all.
///
/// Geometry is vertex-based. Geometric operations depend on understanding the
/// positional data in vertices exposed by the `AsPosition` trait.
///
/// # Examples
///
/// ```rust
/// # extern crate decorum;
/// # extern crate nalgebra;
/// # extern crate num;
/// # extern crate plexus;
/// #
/// use decorum::N64;
/// use nalgebra::{Point3, Vector4};
/// use num::Zero;
/// use plexus::geometry::{AsPosition, IntoGeometry};
/// use plexus::graph::{GraphGeometry, MeshGraph};
/// use plexus::prelude::*;
/// use plexus::primitive::sphere::UvSphere;
///
/// // Vertex-only geometry with position and color data.
/// #[derive(Clone, Copy, Eq, Hash, PartialEq)]
/// pub struct Vertex {
///     pub position: Point3<N64>,
///     pub color: Vector4<N64>,
/// }
///
/// impl GraphGeometry for Vertex {
///     type Vertex = Self;
///     type Arc = ();
///     type Edge = ();
///     type Face = ();
/// }
///
/// impl AsPosition for Vertex {
///     type Target = Point3<N64>;
///
///     fn as_position(&self) -> &Self::Target {
///         &self.position
///     }
///
///     fn as_position_mut(&mut self) -> &mut Self::Target {
///         &mut self.position
///     }
/// }
///
/// # fn main() {
/// // Create a mesh from a sphere primitive and map the geometry data.
/// let mut graph = UvSphere::new(8, 8)
///     .polygons_with_position::<Point3<N64>>()
///     .map_vertices(|position| Vertex {
///         position,
///         color: Zero::zero(),
///     })
///     .collect::<MeshGraph<Vertex>>();
/// # }
/// ```
pub trait GraphGeometry: Sized {
    type Vertex: Copy;
    type Arc: Copy + Default;
    type Edge: Copy + Default;
    type Face: Copy + Default;
}

impl GraphGeometry for () {
    type Vertex = ();
    type Arc = ();
    type Edge = ();
    type Face = ();
}

impl<T> GraphGeometry for (T, T, T)
where
    T: Copy,
{
    type Vertex = Self;
    type Arc = ();
    type Edge = ();
    type Face = ();
}

pub trait FaceNormal: GraphGeometry {
    type Normal;

    fn normal<M>(face: FaceView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

impl<G> FaceNormal for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    Vector<VertexPosition<G>>: Cross<Output = Vector<VertexPosition<G>>>,
    VertexPosition<G>: EuclideanSpace,
{
    type Normal = Vector<VertexPosition<G>>;

    fn normal<M>(face: FaceView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>,
    {
        let mut positions = face
            .reachable_vertices()
            .take(3)
            .map(|vertex| vertex.geometry.as_position().clone());
        let a = positions
            .next()
            .ok_or_else(|| GraphError::TopologyNotFound)?;
        let b = positions
            .next()
            .ok_or_else(|| GraphError::TopologyNotFound)?;
        let c = positions
            .next()
            .ok_or_else(|| GraphError::TopologyNotFound)?;
        let ab = a - b;
        let bc = b - c;
        ab.cross(bc).normalize().ok_or_else(|| GraphError::Geometry)
    }
}

pub trait VertexCentroid: GraphGeometry {
    type Centroid;

    fn centroid<M>(vertex: VertexView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>;
}

impl<G> VertexCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace,
{
    type Centroid = VertexPosition<G>;

    fn centroid<M>(vertex: VertexView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>,
    {
        VertexPosition::<G>::centroid(
            vertex
                .reachable_incoming_arcs()
                .flat_map(|arc| arc.into_reachable_source_vertex())
                .map(|vertex| vertex.geometry.as_position().clone()),
        )
        .ok_or_else(|| GraphError::TopologyNotFound)
    }
}

pub trait FaceCentroid: GraphGeometry {
    type Centroid;

    fn centroid<M>(face: FaceView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

impl<G> FaceCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace,
{
    type Centroid = VertexPosition<G>;

    fn centroid<M>(face: FaceView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>,
    {
        VertexPosition::<G>::centroid(
            face.reachable_vertices()
                .map(|vertex| vertex.geometry.as_position().clone()),
        )
        .ok_or_else(|| GraphError::TopologyNotFound)
    }
}

pub trait FacePlane: GraphGeometry {
    type Plane;

    fn plane<M>(face: FaceView<M, Self>) -> Result<Self::Plane, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

pub trait EdgeMidpoint: GraphGeometry {
    type Midpoint;

    fn midpoint<M>(edge: EdgeView<M, Self>) -> Result<Self::Midpoint, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<EdgePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

impl<G> EdgeMidpoint for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace + Interpolate<Output = VertexPosition<G>>,
{
    type Midpoint = VertexPosition<G>;

    fn midpoint<M>(edge: EdgeView<M, Self>) -> Result<Self::Midpoint, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<EdgePayload<Self>>
            + AsStorage<VertexPayload<Self>>,
    {
        let a = edge
            .reachable_arc()
            .ok_or_else(|| GraphError::TopologyNotFound)
            .and_then(|arc| {
                arc.reachable_source_vertex()
                    .ok_or_else(|| GraphError::TopologyNotFound)
                    .map(|vertex| vertex.geometry.as_position().clone())
            })?;
        let b = edge
            .reachable_arc()
            .ok_or_else(|| GraphError::TopologyNotFound)
            .and_then(|arc| {
                arc.reachable_destination_vertex()
                    .ok_or_else(|| GraphError::TopologyNotFound)
                    .map(|vertex| vertex.geometry.as_position().clone())
            })?;
        Ok(a.midpoint(b))
    }
}

pub trait ArcNormal: GraphGeometry {
    type Normal;

    fn normal<M>(arc: ArcView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>;
}

impl<G> ArcNormal for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    Vector<VertexPosition<G>>: Project<Output = Vector<VertexPosition<G>>>,
    VertexPosition<G>: EuclideanSpace,
{
    type Normal = Vector<VertexPosition<G>>;

    fn normal<M>(arc: ArcView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>,
    {
        let a = arc
            .reachable_source_vertex()
            .ok_or_else(|| GraphError::TopologyNotFound)?
            .geometry
            .as_position()
            .clone();
        let b = arc
            .reachable_destination_vertex()
            .ok_or_else(|| GraphError::TopologyNotFound)?
            .geometry
            .as_position()
            .clone();
        let c = arc
            .reachable_next_arc()
            .ok_or_else(|| GraphError::TopologyNotFound)?
            .reachable_destination_vertex()
            .ok_or_else(|| GraphError::TopologyNotFound)?
            .geometry
            .as_position()
            .clone();
        let ab = a - b;
        let cb = c - b;
        let p = b + ab.project(cb);
        (p - c).normalize().ok_or_else(|| GraphError::Geometry)
    }
}

// TODO: The `array` feature only supports Linux. See
//       https://github.com/olson-sean-k/theon/issues/1
#[cfg(target_os = "linux")]
mod array {
    use super::*;

    use smallvec::SmallVec;
    use theon::array::ArrayScalar;
    use theon::query::Plane;
    use theon::space::{FiniteDimensional, Scalar};
    use theon::{FromItems, IntoItems};
    use typenum::U3; // TODO: Maybe theon should re-export these types?

    impl<G> FacePlane for G
    where
        G: GraphGeometry,
        G::Vertex: AsPosition,
        VertexPosition<G>: EuclideanSpace + FiniteDimensional<N = U3>,
        Scalar<VertexPosition<G>>: ArrayScalar,
        Vector<VertexPosition<G>>: FromItems + IntoItems,
    {
        type Plane = Plane<VertexPosition<G>>;

        fn plane<M>(face: FaceView<M, Self>) -> Result<Self::Plane, GraphError>
        where
            M: Reborrow,
            M::Target: AsStorage<ArcPayload<Self>>
                + AsStorage<FacePayload<Self>>
                + AsStorage<VertexPayload<Self>>,
        {
            let points = face
                .reachable_vertices()
                .map(|vertex| vertex.geometry.as_position().clone())
                .collect::<SmallVec<[_; 4]>>();
            Plane::from_points(points).ok_or_else(|| GraphError::Geometry)
        }
    }
}
<commit_msg>Use `FromItems` to reduce duplication and use centroids to compute normals.<commit_after>//! Geometric traits.
//!
//! This module provides traits that specify the geometry of `MeshGraph`s and
//! express the spatial operations supported by that geometry. The most basic
//! and only required trait is `Geometry`, which uses associated types to
//! specify the type of the `geometry` field in the payloads for vertices,
//! arcs, edges, and faces in a graph.
//!
//! To support useful spatial operations, types that implement `Geometry` may
//! also implement `AsPosition`. The `AsPosition` trait exposes positional data
//! in vertices. If that positional data also implements spatial traits from
//! the [theon](https://crates.io/crates/theon) crate, then spatial operations
//! will be enabled, such as `smooth`, `split_at_midpoint`, and
//! `poke_with_offset`.
//!
//! This module also defines traits that define the capabilities of geometry in
//! a `MeshGraph`. These traits enable generic code without the need to express
//! complicated relationships between types representing a Euclidean space. For
//! example, the `FaceCentroid` trait is defined for `Geometry` types that
//! expose positional data that implements the necessary traits to compute the
//! centroid of a face.
//!
//! # Examples
//!
//! A function that subdivides faces in a graph by splitting edges at their
//! midpoints:
//!
//! ```rust
//! # extern crate plexus;
//! # extern crate smallvec;
//! #
//! use plexus::geometry::AsPosition;
//! use plexus::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph, VertexPosition};
//! use plexus::prelude::*;
//! use smallvec::SmallVec;
//!
//! # fn main() {
//! // Requires `EdgeMidpoint` for `split_at_midpoint`.
//! pub fn circumscribe<G>(face: FaceView<&mut MeshGraph<G>, G>) -> FaceView<&mut MeshGraph<G>, G>
//! where
//!     G: EdgeMidpoint<Midpoint = VertexPosition<G>> + GraphGeometry,
//!     G::Vertex: AsPosition,
//! {
//!     let arity = face.arity();
//!     let mut arc = face.into_arc();
//!     let mut splits = SmallVec::<[_; 4]>::with_capacity(arity);
//!     for _ in 0..arity {
//!         let vertex = arc.split_at_midpoint();
//!         splits.push(vertex.key());
//!         arc = vertex.into_outgoing_arc().into_next_arc();
//!     }
//!     let mut face = arc.into_face().unwrap();
//!     for (a, b) in splits.into_iter().perimeter() {
//!         face = face.split(ByKey(a), ByKey(b)).unwrap().into_face().unwrap();
//!     }
//!     face
//! }
//! # }
//! ```

// TODO: Integrate this module documentation into the `graph` module.

use theon::ops::{Cross, Interpolate, Project};
use theon::space::{EuclideanSpace, InnerSpace, Vector};
use theon::FromItems;

use crate::geometry::AsPosition;
use crate::graph::borrow::Reborrow;
use crate::graph::storage::payload::{ArcPayload, EdgePayload, FacePayload, VertexPayload};
use crate::graph::storage::AsStorage;
use crate::graph::view::edge::{ArcView, EdgeView};
use crate::graph::view::face::FaceView;
use crate::graph::view::vertex::VertexView;
use crate::graph::GraphError;

pub type VertexPosition<G> = <<G as GraphGeometry>::Vertex as AsPosition>::Target;

// TODO: Require `Clone` instead of `Copy` once non-`Copy` types are supported
//       by the slotmap crate. See https://github.com/orlp/slotmap/issues/27
/// Graph geometry.
///
/// Specifies the types used to represent geometry for vertices, arcs, edges,
/// and faces in a graph. Arbitrary types can be used, including `()` for no
/// geometry at all.
///
/// Geometry is vertex-based. Geometric operations depend on understanding the
/// positional data in vertices exposed by the `AsPosition` trait.
///
/// # Examples
///
/// ```rust
/// # extern crate decorum;
/// # extern crate nalgebra;
/// # extern crate num;
/// # extern crate plexus;
/// #
/// use decorum::N64;
/// use nalgebra::{Point3, Vector4};
/// use num::Zero;
/// use plexus::geometry::{AsPosition, IntoGeometry};
/// use plexus::graph::{GraphGeometry, MeshGraph};
/// use plexus::prelude::*;
/// use plexus::primitive::sphere::UvSphere;
///
/// // Vertex-only geometry with position and color data.
/// #[derive(Clone, Copy, Eq, Hash, PartialEq)]
/// pub struct Vertex {
///     pub position: Point3<N64>,
///     pub color: Vector4<N64>,
/// }
///
/// impl GraphGeometry for Vertex {
///     type Vertex = Self;
///     type Arc = ();
///     type Edge = ();
///     type Face = ();
/// }
///
/// impl AsPosition for Vertex {
///     type Target = Point3<N64>;
///
///     fn as_position(&self) -> &Self::Target {
///         &self.position
///     }
///
///     fn as_position_mut(&mut self) -> &mut Self::Target {
///         &mut self.position
///     }
/// }
///
/// # fn main() {
/// // Create a mesh from a sphere primitive and map the geometry data.
/// let mut graph = UvSphere::new(8, 8)
///     .polygons_with_position::<Point3<N64>>()
///     .map_vertices(|position| Vertex {
///         position,
///         color: Zero::zero(),
///     })
///     .collect::<MeshGraph<Vertex>>();
/// # }
/// ```
pub trait GraphGeometry: Sized {
    type Vertex: Copy;
    type Arc: Copy + Default;
    type Edge: Copy + Default;
    type Face: Copy + Default;
}

impl GraphGeometry for () {
    type Vertex = ();
    type Arc = ();
    type Edge = ();
    type Face = ();
}

impl<T> GraphGeometry for (T, T, T)
where
    T: Copy,
{
    type Vertex = Self;
    type Arc = ();
    type Edge = ();
    type Face = ();
}

pub trait VertexCentroid: GraphGeometry {
    type Centroid;

    fn centroid<M>(vertex: VertexView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>;
}

impl<G> VertexCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace,
{
    type Centroid = VertexPosition<G>;

    fn centroid<M>(vertex: VertexView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>,
    {
        VertexPosition::<G>::centroid(
            vertex
                .reachable_incoming_arcs()
                .flat_map(|arc| arc.into_reachable_source_vertex())
                .map(|vertex| vertex.geometry.as_position().clone()),
        )
        .ok_or_else(|| GraphError::TopologyNotFound)
    }
}

pub trait ArcNormal: GraphGeometry {
    type Normal;

    fn normal<M>(arc: ArcView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>;
}

impl<G> ArcNormal for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    Vector<VertexPosition<G>>: Project<Output = Vector<VertexPosition<G>>>,
    VertexPosition<G>: EuclideanSpace,
{
    type Normal = Vector<VertexPosition<G>>;

    fn normal<M>(arc: ArcView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>> + AsStorage<VertexPayload<Self>>,
    {
        let (a, b) = FromItems::from_items(
            arc.reachable_vertices()
                .map(|vertex| vertex.position().clone()),
        )
        .ok_or_else(|| GraphError::TopologyNotFound)?;
        let c = arc
            .reachable_next_arc()
            .ok_or_else(|| GraphError::TopologyNotFound)?
            .reachable_destination_vertex()
            .ok_or_else(|| GraphError::TopologyNotFound)?
            .position()
            .clone();
        let ab = a - b;
        let cb = c - b;
        let p = b + ab.project(cb);
        (p - c).normalize().ok_or_else(|| GraphError::Geometry)
    }
}

pub trait EdgeMidpoint: GraphGeometry {
    type Midpoint;

    fn midpoint<M>(edge: EdgeView<M, Self>) -> Result<Self::Midpoint, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<EdgePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

impl<G> EdgeMidpoint for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace + Interpolate<Output = VertexPosition<G>>,
{
    type Midpoint = VertexPosition<G>;

    fn midpoint<M>(edge: EdgeView<M, Self>) -> Result<Self::Midpoint, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<EdgePayload<Self>>
            + AsStorage<VertexPayload<Self>>,
    {
        let arc = edge
            .reachable_arc()
            .ok_or_else(|| GraphError::TopologyNotFound)?;
        let (a, b) = FromItems::from_items(
            arc.reachable_vertices()
                .map(|vertex| vertex.position().clone()),
        )
        .ok_or_else(|| GraphError::TopologyNotFound)?;
        Ok(a.midpoint(b))
    }
}

pub trait FaceCentroid: GraphGeometry {
    type Centroid;

    fn centroid<M>(face: FaceView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

impl<G> FaceCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace,
{
    type Centroid = VertexPosition<G>;

    fn centroid<M>(face: FaceView<M, Self>) -> Result<Self::Centroid, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>,
    {
        VertexPosition::<G>::centroid(
            face.reachable_vertices()
                .map(|vertex| vertex.position().clone()),
        )
        .ok_or_else(|| GraphError::TopologyNotFound)
    }
}

pub trait FaceNormal: GraphGeometry {
    type Normal;

    fn normal<M>(face: FaceView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

impl<G> FaceNormal for G
where
    G: FaceCentroid<Centroid = VertexPosition<G>> + GraphGeometry,
    G::Vertex: AsPosition,
    Vector<VertexPosition<G>>: Cross<Output = Vector<VertexPosition<G>>>,
    VertexPosition<G>: EuclideanSpace,
{
    type Normal = Vector<VertexPosition<G>>;

    fn normal<M>(face: FaceView<M, Self>) -> Result<Self::Normal, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>,
    {
        let (a, b) = FromItems::from_items(
            face.reachable_vertices()
                .take(2)
                .map(|vertex| vertex.position().clone()),
        )
        .ok_or_else(|| GraphError::TopologyNotFound)?;
        let c = G::centroid(face)?;
        let ab = a - b;
        let bc = b - c;
        ab.cross(bc).normalize().ok_or_else(|| GraphError::Geometry)
    }
}

pub trait FacePlane: GraphGeometry {
    type Plane;

    fn plane<M>(face: FaceView<M, Self>) -> Result<Self::Plane, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<ArcPayload<Self>>
            + AsStorage<FacePayload<Self>>
            + AsStorage<VertexPayload<Self>>;
}

// TODO: The `array` feature only supports Linux. See
//       https://github.com/olson-sean-k/theon/issues/1
#[cfg(target_os = "linux")]
mod array {
    use super::*;

    use smallvec::SmallVec;
    use theon::array::ArrayScalar;
    use theon::query::Plane;
    use theon::space::{FiniteDimensional, Scalar};
    use theon::{FromItems, IntoItems};
    use typenum::U3; // TODO: Maybe theon should re-export these types?

    impl<G> FacePlane for G
    where
        G: GraphGeometry,
        G::Vertex: AsPosition,
        VertexPosition<G>: EuclideanSpace + FiniteDimensional<N = U3>,
        Scalar<VertexPosition<G>>: ArrayScalar,
        Vector<VertexPosition<G>>: FromItems + IntoItems,
    {
        type Plane = Plane<VertexPosition<G>>;

        fn plane<M>(face: FaceView<M, Self>) -> Result<Self::Plane, GraphError>
        where
            M: Reborrow,
            M::Target: AsStorage<ArcPayload<Self>>
                + AsStorage<FacePayload<Self>>
                + AsStorage<VertexPayload<Self>>,
        {
            let points = face
                .reachable_vertices()
                .map(|vertex| vertex.geometry.as_position().clone())
                .collect::<SmallVec<[_; 4]>>();
            Plane::from_points(points).ok_or_else(|| GraphError::Geometry)
        }
    }
}
<|endoftext|>


<commit_before>extern crate digest;
extern crate md5;
extern crate sha1;
extern crate sha2;
extern crate sha3;

use digest::digest::{Input, ExtendableOutput, XofReader};
use hex::ToHex;

pub trait Digest {
    fn new() -> Self where Self: Sized;
    fn input(&mut self, input: &[u8]);
    fn result(&mut self, out: &mut [u8]);
    fn reset(&mut self);
    fn output_bits(&self) -> usize;
    fn output_bytes(&self) -> usize {
        (self.output_bits() + 7) / 8
    }
    fn result_str(&mut self) -> String {
        let mut buf: Vec<u8> = vec![0; self.output_bytes()];
        self.result(&mut buf);
        buf.to_hex()
    }
}

impl Digest for md5::Context {
    fn new() -> Self {
        md5::Context::new()
    }

    fn input(&mut self, input: &[u8]) {
        self.consume(input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(&*self.compute());
    }

    fn reset(&mut self) {
        *self = md5::Context::new();
    }

    fn output_bits(&self) -> usize { 128 }
}

impl Digest for sha1::Sha1 {
    fn new() -> Self {
        sha1::Sha1::new()
    }

    fn input(&mut self, input: &[u8]) {
        self.update(input);
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(&self.digest().bytes());
    }

    fn reset(&mut self) {
        self.reset();
    }

    fn output_bits(&self) -> usize { 160 }
}

impl Digest for sha2::Sha224 {
    fn new() -> Self {
        sha2::Sha224::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input);
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = sha2::Sha224::default();
    }

    fn output_bits(&self) -> usize { 224 }
}

impl Digest for sha2::Sha256 {
    fn new() -> Self {
        sha2::Sha256::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input);
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = sha2::Sha256::default();
    }

    fn output_bits(&self) -> usize { 256 }
}

impl Digest for sha2::Sha384 {
    fn new() -> Self {
        sha2::Sha384::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = sha2::Sha384::default();
    }

    fn output_bits(&self) -> usize { 384 }
}

impl Digest for sha2::Sha512 {
    fn new() -> Self {
        sha2::Sha512::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = sha2::Sha512::default();
    }

    fn output_bits(&self) -> usize { 512 }
}

impl Digest for sha3::Sha3_224 {
    fn new() -> Self {
        Self::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = Self::default();
    }

    fn output_bits(&self) -> usize { 224 }
}

impl Digest for sha3::Sha3_256 {
    fn new() -> Self {
        sha3::Sha3_256::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = sha3::Sha3_256::default();
    }

    fn output_bits(&self) -> usize { 256 }
}

impl Digest for sha3::Sha3_384 {
    fn new() -> Self {
        sha3::Sha3_384::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = sha3::Sha3_384::default();
    }

    fn output_bits(&self) -> usize { 384 }
}

impl Digest for sha3::Sha3_512 {
    fn new() -> Self {
        sha3::Sha3_512::default()
    }

    fn input(&mut self, input: &[u8]) {
        digest::Digest::input(self, input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(digest::Digest::result(*self).as_slice());
    }

    fn reset(&mut self) {
        *self = sha3::Sha3_512::default();
    }

    fn output_bits(&self) -> usize { 512 }
}

impl Digest for sha3::Shake128 {
    fn new() -> Self {
        sha3::Shake128::default()
    }

    fn input(&mut self, input: &[u8]) {
        self.process(input);
    }

    fn result(&mut self, out: &mut [u8]) {
        self.xof_result().read(out);
    }

    fn reset(&mut self) {
        *self = sha3::Shake128::default();
    }

    fn output_bits(&self) -> usize { 0 }
}

impl Digest for sha3::Shake256 {
    fn new() -> Self {
        sha3::Shake256::default()
    }

    fn input(&mut self, input: &[u8]) {
        self.process(input);
    }

    fn result(&mut self, out: &mut [u8]) {
        self.xof_result().read(out);
    }

    fn reset(&mut self) {
        *self = sha3::Shake256::default();
    }

    fn output_bits(&self) -> usize { 0 }
}
<commit_msg>hashsum: use macro for Digest implementation<commit_after>extern crate digest;
extern crate md5;
extern crate sha1;
extern crate sha2;
extern crate sha3;

use digest::digest::{Input, ExtendableOutput, XofReader};
use hex::ToHex;

pub trait Digest {
    fn new() -> Self where Self: Sized;
    fn input(&mut self, input: &[u8]);
    fn result(&mut self, out: &mut [u8]);
    fn reset(&mut self);
    fn output_bits(&self) -> usize;
    fn output_bytes(&self) -> usize {
        (self.output_bits() + 7) / 8
    }
    fn result_str(&mut self) -> String {
        let mut buf: Vec<u8> = vec![0; self.output_bytes()];
        self.result(&mut buf);
        buf.to_hex()
    }
}

impl Digest for md5::Context {
    fn new() -> Self {
        md5::Context::new()
    }

    fn input(&mut self, input: &[u8]) {
        self.consume(input)
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(&*self.compute());
    }

    fn reset(&mut self) {
        *self = md5::Context::new();
    }

    fn output_bits(&self) -> usize { 128 }
}

impl Digest for sha1::Sha1 {
    fn new() -> Self {
        sha1::Sha1::new()
    }

    fn input(&mut self, input: &[u8]) {
        self.update(input);
    }

    fn result(&mut self, out: &mut [u8]) {
        out.copy_from_slice(&self.digest().bytes());
    }

    fn reset(&mut self) {
        self.reset();
    }

    fn output_bits(&self) -> usize { 160 }
}

// Implements the Digest trait for sha2 / sha3 algorithms with fixed ouput
macro_rules! impl_digest_sha {
    ($type: ty, $size: expr) => (
        impl Digest for $type {
            fn new() -> Self {
                Self::default()
            }

            fn input(&mut self, input: &[u8]) {
                digest::Digest::input(self, input);
            }

            fn result(&mut self, out: &mut [u8]) {
                out.copy_from_slice(digest::Digest::result(*self).as_slice());
            }

            fn reset(&mut self) {
                *self = Self::new();
            }

            fn output_bits(&self) -> usize { $size }
        }
    )
}

// Implements the Digest trait for sha2 / sha3 algorithms with variable ouput
macro_rules! impl_digest_shake {
    ($type: ty) => (
        impl Digest for $type {
            fn new() -> Self {
                Self::default()
            }

            fn input(&mut self, input: &[u8]) {
                self.process(input);
            }

            fn result(&mut self, out: &mut [u8]) {
                self.xof_result().read(out);
            }

            fn reset(&mut self) {
                *self = Self::new();
            }

            fn output_bits(&self) -> usize { 0 }
        }
    )
}

impl_digest_sha!(sha2::Sha224, 224);
impl_digest_sha!(sha2::Sha256, 256);
impl_digest_sha!(sha2::Sha384, 384);
impl_digest_sha!(sha2::Sha512, 512);

impl_digest_sha!(sha3::Sha3_224, 224);
impl_digest_sha!(sha3::Sha3_256, 256);
impl_digest_sha!(sha3::Sha3_384, 384);
impl_digest_sha!(sha3::Sha3_512, 512);
impl_digest_shake!(sha3::Shake128);
impl_digest_shake!(sha3::Shake256);
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::time::Instant;
use nix::Error as NixErr;
use nix::sys::ptrace::ptrace::*;
use nix::sys::wait::*;
use nix::sys::signal;
use nix::Errno;
use nix::Result;
use nix::unistd::Pid;
use tracer::*;
use breakpoint::*;
use ptrace_control::*;
use config::Config;
///
/// So we are either:
///     Waiting for an signal
///     Handling a stop
///     Handling an exit
///     Handling being signalled
///     Handling ptrace events
///     Handling an error
///     Continuing execution
///     Handling ptrace error
///
/// So we wait -> other thing -> continuing
///
/// Cannot do wait -> wait (unless non-polling, timeout?)
///
/// Or wait -> other thing -> other thing -> continue
///
/// Or wait -> other thing -> continue -> continue

/// Possible states when executing an inferior process. This is an attempt at
/// a platform agnostic abstracting to provide the potential of future
/// implementations for other operating systems and provides the implementation
/// of the test running state machine
/// T is data used to store the necessary process information to enable tracing
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TestState {
    /// Start state. Wait for test to appear and track time to enable timeout
    Start {
        start_time: Instant,
    },
    /// Initialise: once test process appears instrument
    Initialise ,
    /// Waiting for breakpoint to be hit or test to end
    Waiting {
        start_time: Instant,
    },
    /// Test process stopped, check coverage
    Stopped,
    /// Process timed out
    Timeout,
    /// Unrecoverable error occurred
    Unrecoverable,
    /// Test exited normally
    End,
    /// An error occurred that indicates no future runs will succeed such as
    /// PIE issues in OS.
    Abort,
}

impl TestState {
    /// Convenience function used to check if the test has finished or errored
    pub fn is_finished(self) -> bool {
        match self {
            TestState::End | TestState::Unrecoverable | TestState::Abort => true,
            _ => false,
        }
    }

    /// Convenience function for creating start states
    fn start_state() -> TestState {
        TestState::Start{start_time: Instant::now()}
    }

    /// Convenience function for creating wait states
    fn wait_state() -> TestState {
        TestState::Waiting{start_time: Instant::now()}
    }
}

/// Trait for state machines to implement
pub trait StateMachine<T> where T:StateData {
    /// Update the states
    fn step(self, data: &mut T, config: &Config) -> TestState;
}

/// Tracing a process on an OS will have platform specific code.
/// Structs containing the platform specific datastructures should
/// provide this trait with an implementation of the handling of
/// the given states.
pub trait StateData {
    /// Starts the tracing. Returns None while waiting for
    /// start. Statemachine then checks timeout
    fn start(&mut self) -> Option<TestState>;
    /// Initialises test for tracing returns next state
    fn init(&mut self) -> TestState;
    /// Waits for notification from test executable that there's
    /// something to do. Selects the next appropriate state if there's
    /// something to do otherwise None
    fn wait(&mut self) -> Option<TestState>;
    /// Handle a stop in the test executable. Coverage data will
    /// be collected here as well as other OS specific functions
    fn stop(&mut self) -> TestState;
    /// Cleanup the system state - killing processes etc
    fn cleanup(&mut self);
}

impl <T> StateMachine<T> for TestState where T:StateData {

    fn step(self, data: &mut T, config: &Config) -> TestState {
        match self {
            TestState::Start{start_time} => {
                if let Some(s) = data.start() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out when starting test");
                    TestState::Timeout
                } else {
                    TestState::Start{start_time:start_time}
                }
            },
            TestState::Initialise => {
                data.init()
            },
            TestState::Waiting{start_time} => {
                if let Some(s) =data.wait() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out waiting for test response");
                    TestState::Timeout
                } else {
                    TestState::Waiting{start_time}
                }
            },
            TestState::Stopped => {
                data.stop()
            },
            TestState::Timeout => {
                data.cleanup();
                TestState::End
            },
            TestState::Unrecoverable => {
                data.cleanup();
                TestState::End
            },
            _ => {
                TestState::End
            }
        }
    }
}

pub fn create_state_machine<'a>(test: Pid,
                                traces: &'a mut Vec<TracerData>,
                                config: &'a Config) -> (TestState, LinuxData<'a>) {
    let mut data = LinuxData::new(traces, config);
    data.parent = test;
    (TestState::start_state(), data)
}

/// Handle to linux process state
#[derive(Debug)]
pub struct LinuxData<'a> {
    /// Recent result from waitpid to be handled by statemachine
    wait: WaitStatus,
    /// Current Pid to process
    current: Pid,
    /// Parent PID of test process
    parent: Pid,
    /// Map of addresses to breakpoints
    breakpoints: HashMap<u64, Breakpoint>,
    /// Instrumentation points in code with associated coverage data
    traces: &'a mut Vec<TracerData>,
    /// Program config
    config: &'a Config,
    /// Used to store error for user in the event something goes wrong
    pub error_message: Option<String>,
    /// Thread count. Hopefully getting rid of in future
    thread_count: isize,
    /// Used to show anomalies noticed so hit counts disabled
    force_disable_hit_count: bool
}

impl <'a> StateData for LinuxData<'a> {

    fn start(&mut self) -> Option<TestState> {
        match waitpid(self.current, Some(WNOHANG)) {
            Ok(WaitStatus::StillAlive) => None,
            Ok(sig @ WaitStatus::Stopped(_, signal::SIGTRAP)) => {
                if let WaitStatus::Stopped(child, _) = sig {
                    self.current = child;
                }
                self.wait = sig;
                Some(TestState::Initialise)
            },
            Ok(_) => {
                println!("Unexpected signal when starting test");
                None
            },
            Err(e) => {
                println!("Error when starting test: {}", e);
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn init(&mut self) -> TestState {
        if trace_children(self.current).is_err() {
            println!("Failed to trace child threads");
        }
        let mut instrumented = true;
        for trace in self.traces.iter() {
            if let Some(addr) = trace.address {
                match Breakpoint::new(self.current, addr) {
                    Ok(bp) => {
                        let _ = self.breakpoints.insert(addr, bp);
                    },
                    Err(e) if e==NixErr::Sys(Errno::EIO) => {
                        println!("ERROR: Tarpaulin cannot find code addresses \
                                  check that pie is disabled for your linker. \
                                  If linking with gcc try adding -C link-args=-no-pie \
                                  to your rust flags");
                        instrumented = false;
                        break;
                    }
                    Err(_) => {
                        self.error_message = Some("Failed to instrument test executable".to_string());
                    },
                }
            }
        }
        if !instrumented {
            TestState::Abort
        }
        else if let Ok(_) = continue_exec(self.parent, None) {
            TestState::wait_state()
        } else {
            TestState::Unrecoverable
        }
    }

    fn wait(&mut self) -> Option<TestState> {
        let wait = waitpid(Pid::from_raw(-1), Some(WNOHANG | __WALL));
        match wait {
            Ok(WaitStatus::StillAlive) => {
                self.wait = WaitStatus::StillAlive;
                None
            },
            Ok(s) => {
                self.wait = s;
                Some(TestState::Stopped)
            },
            Err(_) => {
                self.error_message = Some("An error occurred while waiting for response from test".to_string());
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn stop(&mut self) -> TestState {
        match self.wait {
            WaitStatus::PtraceEvent(_,_,_) => {
                match self.handle_ptrace_event() {
                    Ok(s) => s,
                    Err(e) => {
                        let msg = format!("Error occurred when handling ptrace event: {}", e);
                        self.error_message = Some(msg);
                        TestState::Unrecoverable
                    },
                }
            },
            WaitStatus::Stopped(c,signal::SIGTRAP) => {
                self.current = c;
                match self.collect_coverage_data() {
                    Ok(s) => s,
                    Err(e) => {
                        self.error_message = Some(format!("Error when collecting coverage: {}", e));
                        TestState::Unrecoverable
                    }
                }
            },
            WaitStatus::Stopped(child, signal::SIGSTOP) => {
                if continue_exec(child, None).is_ok() {
                    TestState::wait_state()
                } else {
                    self.error_message = Some("Error processing SIGSTOP".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Stopped(_, signal::SIGSEGV) => TestState::Unrecoverable,
            WaitStatus::Stopped(c, s) => {
                let sig = if self.config.forward_signals {
                    Some(s)
                } else {
                    None
                };
                let _ = continue_exec(c, sig);
                TestState::wait_state()
            },
            WaitStatus::Signaled(_,_,_) => {
                if let Ok(s) = self.handle_signaled() {
                    s
                } else {
                    self.error_message = Some("Error attempting to handle tarpaulin being signaled".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Exited(child, _) => {
                for (_, ref mut value) in self.breakpoints.iter_mut() {
                    value.thread_killed(child);
                }
                if child == self.parent {
                    TestState::End
                } else {
                    // Process may have already been destroyed. This is just incase
                    let _ = continue_exec(self.parent, None);
                    TestState::wait_state()
                }
            },
            _ => TestState::Unrecoverable,
        }
    }

    fn cleanup(&mut self)  {
        if let Some(ref e) = self.error_message {
            println!("An error occurred during run. Coverage results may be inaccurate.");
            println!("{}", e);
        }
    }
}

impl <'a>LinuxData<'a> {
    pub fn new(traces: &'a mut Vec<TracerData>, config: &'a Config) -> LinuxData<'a> {
        LinuxData {
            wait: WaitStatus::StillAlive,
            current: Pid::from_raw(0),
            parent: Pid::from_raw(0),
            breakpoints: HashMap::new(),
            traces: traces,
            config: config,
            error_message:None,
            thread_count: 0,
            force_disable_hit_count: !config.no_count
        }
    }

    fn handle_ptrace_event(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_CLONE) => {
                if get_event_data(child).is_ok() {
                    self.thread_count += 1;
                    continue_exec(child, None)?;
                    Ok(TestState::wait_state())
                } else {
                    self.error_message = Some("Error occurred upon test executable thread creation".to_string());
                    Ok(TestState::Unrecoverable)
                }
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_FORK) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_VFORK) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXEC) => {
                detach_child(child)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXIT) => {
                self.thread_count -= 1;
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            _ => Ok(TestState::Unrecoverable),
        }
    }

    fn collect_coverage_data(&mut self) -> Result<TestState> {
        if let Ok(rip) = current_instruction_pointer(self.current) {
            let rip = (rip - 1) as u64;
            if  self.breakpoints.contains_key(&rip) {
                let bp = &mut self.breakpoints.get_mut(&rip).unwrap();
                let enable = (!self.config.no_count) && (self.thread_count < 2);
                if !enable && self.force_disable_hit_count {
                    println!("Code is mulithreaded, disabling hit count");
                    println!("Results may be improved by using the '--no-count' option when running tarpaulin");
                    self.force_disable_hit_count = false;
                }
                // Don't reenable if multithreaded as can't yet sort out segfault issue
                let updated = if let Ok(x) = bp.process(self.current, enable) {
                     x
                } else {
                    false
                };
                if updated {
                    for t in self.traces.iter_mut()
                                        .filter(|x| x.address == Some(rip)) {
                        (*t).hits += 1;
                    }
                }
            } else {
                continue_exec(self.current, None)?;
            }
        } else {
            continue_exec(self.current, None)?;
        }
        Ok(TestState::wait_state())
    }

    fn handle_signaled(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::Signaled(child, signal::SIGTRAP, true) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            _ => {
                self.error_message = Some("Unexpected stop".to_string());
                Ok(TestState::Unrecoverable)
            },
        }
    }
}
<commit_msg>Removed unnecessary trait and design comment.<commit_after>use std::collections::HashMap;
use std::time::Instant;
use nix::Error as NixErr;
use nix::sys::ptrace::ptrace::*;
use nix::sys::wait::*;
use nix::sys::signal;
use nix::Errno;
use nix::Result;
use nix::unistd::Pid;
use tracer::*;
use breakpoint::*;
use ptrace_control::*;
use config::Config;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TestState {
    /// Start state. Wait for test to appear and track time to enable timeout
    Start {
        start_time: Instant,
    },
    /// Initialise: once test process appears instrument
    Initialise ,
    /// Waiting for breakpoint to be hit or test to end
    Waiting {
        start_time: Instant,
    },
    /// Test process stopped, check coverage
    Stopped,
    /// Process timed out
    Timeout,
    /// Unrecoverable error occurred
    Unrecoverable,
    /// Test exited normally
    End,
    /// An error occurred that indicates no future runs will succeed such as
    /// PIE issues in OS.
    Abort,
}

/// Tracing a process on an OS will have platform specific code.
/// Structs containing the platform specific datastructures should
/// provide this trait with an implementation of the handling of
/// the given states.
pub trait StateData {
    /// Starts the tracing. Returns None while waiting for
    /// start. Statemachine then checks timeout
    fn start(&mut self) -> Option<TestState>;
    /// Initialises test for tracing returns next state
    fn init(&mut self) -> TestState;
    /// Waits for notification from test executable that there's
    /// something to do. Selects the next appropriate state if there's
    /// something to do otherwise None
    fn wait(&mut self) -> Option<TestState>;
    /// Handle a stop in the test executable. Coverage data will
    /// be collected here as well as other OS specific functions
    fn stop(&mut self) -> TestState;
    /// Cleanup the system state - killing processes etc
    fn cleanup(&mut self);
}

impl TestState {
    /// Convenience function used to check if the test has finished or errored
    pub fn is_finished(self) -> bool {
        match self {
            TestState::End | TestState::Unrecoverable | TestState::Abort => true,
            _ => false,
        }
    }

    /// Convenience function for creating start states
    fn start_state() -> TestState {
        TestState::Start{start_time: Instant::now()}
    }

    /// Convenience function for creating wait states
    fn wait_state() -> TestState {
        TestState::Waiting{start_time: Instant::now()}
    }

    /// Updates the state machine state
    pub fn step<T:StateData>(self, data: &mut T, config: &Config) -> TestState {
        match self {
            TestState::Start{start_time} => {
                if let Some(s) = data.start() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out when starting test");
                    TestState::Timeout
                } else {
                    TestState::Start{start_time:start_time}
                }
            },
            TestState::Initialise => {
                data.init()
            },
            TestState::Waiting{start_time} => {
                if let Some(s) =data.wait() {
                    s
                } else if start_time.elapsed() >= config.test_timeout {
                    println!("Error: Timed out waiting for test response");
                    TestState::Timeout
                } else {
                    TestState::Waiting{start_time}
                }
            },
            TestState::Stopped => {
                data.stop()
            },
            TestState::Timeout => {
                data.cleanup();
                TestState::End
            },
            TestState::Unrecoverable => {
                data.cleanup();
                TestState::End
            },
            _ => {
                TestState::End
            }
        }
    }
}

pub fn create_state_machine<'a>(test: Pid,
                                traces: &'a mut Vec<TracerData>,
                                config: &'a Config) -> (TestState, LinuxData<'a>) {
    let mut data = LinuxData::new(traces, config);
    data.parent = test;
    (TestState::start_state(), data)
}

/// Handle to linux process state
#[derive(Debug)]
pub struct LinuxData<'a> {
    /// Recent result from waitpid to be handled by statemachine
    wait: WaitStatus,
    /// Current Pid to process
    current: Pid,
    /// Parent PID of test process
    parent: Pid,
    /// Map of addresses to breakpoints
    breakpoints: HashMap<u64, Breakpoint>,
    /// Instrumentation points in code with associated coverage data
    traces: &'a mut Vec<TracerData>,
    /// Program config
    config: &'a Config,
    /// Used to store error for user in the event something goes wrong
    pub error_message: Option<String>,
    /// Thread count. Hopefully getting rid of in future
    thread_count: isize,
    /// Used to show anomalies noticed so hit counts disabled
    force_disable_hit_count: bool
}

impl <'a> StateData for LinuxData<'a> {

    fn start(&mut self) -> Option<TestState> {
        match waitpid(self.current, Some(WNOHANG)) {
            Ok(WaitStatus::StillAlive) => None,
            Ok(sig @ WaitStatus::Stopped(_, signal::SIGTRAP)) => {
                if let WaitStatus::Stopped(child, _) = sig {
                    self.current = child;
                }
                self.wait = sig;
                Some(TestState::Initialise)
            },
            Ok(_) => {
                println!("Unexpected signal when starting test");
                None
            },
            Err(e) => {
                println!("Error when starting test: {}", e);
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn init(&mut self) -> TestState {
        if trace_children(self.current).is_err() {
            println!("Failed to trace child threads");
        }
        let mut instrumented = true;
        for trace in self.traces.iter() {
            if let Some(addr) = trace.address {
                match Breakpoint::new(self.current, addr) {
                    Ok(bp) => {
                        let _ = self.breakpoints.insert(addr, bp);
                    },
                    Err(e) if e==NixErr::Sys(Errno::EIO) => {
                        println!("ERROR: Tarpaulin cannot find code addresses \
                                  check that pie is disabled for your linker. \
                                  If linking with gcc try adding -C link-args=-no-pie \
                                  to your rust flags");
                        instrumented = false;
                        break;
                    }
                    Err(_) => {
                        self.error_message = Some("Failed to instrument test executable".to_string());
                    },
                }
            }
        }
        if !instrumented {
            TestState::Abort
        }
        else if let Ok(_) = continue_exec(self.parent, None) {
            TestState::wait_state()
        } else {
            TestState::Unrecoverable
        }
    }

    fn wait(&mut self) -> Option<TestState> {
        let wait = waitpid(Pid::from_raw(-1), Some(WNOHANG | __WALL));
        match wait {
            Ok(WaitStatus::StillAlive) => {
                self.wait = WaitStatus::StillAlive;
                None
            },
            Ok(s) => {
                self.wait = s;
                Some(TestState::Stopped)
            },
            Err(_) => {
                self.error_message = Some("An error occurred while waiting for response from test".to_string());
                Some(TestState::Unrecoverable)
            },
        }
    }

    fn stop(&mut self) -> TestState {
        match self.wait {
            WaitStatus::PtraceEvent(_,_,_) => {
                match self.handle_ptrace_event() {
                    Ok(s) => s,
                    Err(e) => {
                        let msg = format!("Error occurred when handling ptrace event: {}", e);
                        self.error_message = Some(msg);
                        TestState::Unrecoverable
                    },
                }
            },
            WaitStatus::Stopped(c,signal::SIGTRAP) => {
                self.current = c;
                match self.collect_coverage_data() {
                    Ok(s) => s,
                    Err(e) => {
                        self.error_message = Some(format!("Error when collecting coverage: {}", e));
                        TestState::Unrecoverable
                    }
                }
            },
            WaitStatus::Stopped(child, signal::SIGSTOP) => {
                if continue_exec(child, None).is_ok() {
                    TestState::wait_state()
                } else {
                    self.error_message = Some("Error processing SIGSTOP".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Stopped(_, signal::SIGSEGV) => TestState::Unrecoverable,
            WaitStatus::Stopped(c, s) => {
                let sig = if self.config.forward_signals {
                    Some(s)
                } else {
                    None
                };
                let _ = continue_exec(c, sig);
                TestState::wait_state()
            },
            WaitStatus::Signaled(_,_,_) => {
                if let Ok(s) = self.handle_signaled() {
                    s
                } else {
                    self.error_message = Some("Error attempting to handle tarpaulin being signaled".to_string());
                    TestState::Unrecoverable
                }
            },
            WaitStatus::Exited(child, _) => {
                for (_, ref mut value) in self.breakpoints.iter_mut() {
                    value.thread_killed(child);
                }
                if child == self.parent {
                    TestState::End
                } else {
                    // Process may have already been destroyed. This is just incase
                    let _ = continue_exec(self.parent, None);
                    TestState::wait_state()
                }
            },
            _ => TestState::Unrecoverable,
        }
    }

    fn cleanup(&mut self)  {
        if let Some(ref e) = self.error_message {
            println!("An error occurred during run. Coverage results may be inaccurate.");
            println!("{}", e);
        }
    }
}

impl <'a>LinuxData<'a> {
    pub fn new(traces: &'a mut Vec<TracerData>, config: &'a Config) -> LinuxData<'a> {
        LinuxData {
            wait: WaitStatus::StillAlive,
            current: Pid::from_raw(0),
            parent: Pid::from_raw(0),
            breakpoints: HashMap::new(),
            traces: traces,
            config: config,
            error_message:None,
            thread_count: 0,
            force_disable_hit_count: !config.no_count
        }
    }

    fn handle_ptrace_event(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_CLONE) => {
                if get_event_data(child).is_ok() {
                    self.thread_count += 1;
                    continue_exec(child, None)?;
                    Ok(TestState::wait_state())
                } else {
                    self.error_message = Some("Error occurred upon test executable thread creation".to_string());
                    Ok(TestState::Unrecoverable)
                }
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_FORK) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_VFORK) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXEC) => {
                detach_child(child)?;
                Ok(TestState::wait_state())
            },
            WaitStatus::PtraceEvent(child, signal::SIGTRAP, PTRACE_EVENT_EXIT) => {
                self.thread_count -= 1;
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            _ => Ok(TestState::Unrecoverable),
        }
    }

    fn collect_coverage_data(&mut self) -> Result<TestState> {
        if let Ok(rip) = current_instruction_pointer(self.current) {
            let rip = (rip - 1) as u64;
            if  self.breakpoints.contains_key(&rip) {
                let bp = &mut self.breakpoints.get_mut(&rip).unwrap();
                let enable = (!self.config.no_count) && (self.thread_count < 2);
                if !enable && self.force_disable_hit_count {
                    println!("Code is mulithreaded, disabling hit count");
                    println!("Results may be improved by using the '--no-count' option when running tarpaulin");
                    self.force_disable_hit_count = false;
                }
                // Don't reenable if multithreaded as can't yet sort out segfault issue
                let updated = if let Ok(x) = bp.process(self.current, enable) {
                     x
                } else {
                    false
                };
                if updated {
                    for t in self.traces.iter_mut()
                                        .filter(|x| x.address == Some(rip)) {
                        (*t).hits += 1;
                    }
                }
            } else {
                continue_exec(self.current, None)?;
            }
        } else {
            continue_exec(self.current, None)?;
        }
        Ok(TestState::wait_state())
    }

    fn handle_signaled(&mut self) -> Result<TestState> {
        match self.wait {
            WaitStatus::Signaled(child, signal::SIGTRAP, true) => {
                continue_exec(child, None)?;
                Ok(TestState::wait_state())
            },
            _ => {
                self.error_message = Some("Unexpected stop".to_string());
                Ok(TestState::Unrecoverable)
            },
        }
    }
}
<|endoftext|>


<commit_before>//! I guess these docs will never appear since we re-export the canvas
//! module from graphics...
use std::path;

use gfx::format::{Format, Swizzle};
use gfx::handle::RawRenderTargetView;
use gfx::memory::{Bind, Usage};
use gfx::texture::{AaMode, Kind};
use gfx::Factory;
use glam::Quat;

use crate::context::DebugId;
use crate::error::*;
use crate::graphics::*;
use crate::Context;
use crate::{conf, filesystem};

/// A generic canvas independent of graphics backend. This type should
/// never need to be used directly; use [`graphics::Canvas`](type.Canvas.html)
/// instead.
#[derive(Debug)]
pub struct CanvasGeneric<Spec>
where
    Spec: BackendSpec,
{
    target: RawRenderTargetView<Spec::Resources>,
    image: Image,
    debug_id: DebugId,
}

/// A canvas that can be rendered to instead of the screen (sometimes referred
/// to as "render target" or "render to texture"). Set the canvas with the
/// [`graphics::set_canvas()`](fn.set_canvas.html) function, and then anything you
/// draw will be drawn to the canvas instead of the screen.
///
/// Resume drawing to the screen by calling `graphics::set_canvas(None)`.
///
/// A `Canvas` allows graphics to be rendered to images off-screen
/// in order to do things like saving to an image file or creating cool effects
/// by using shaders that render to an image.
/// If you just want to draw multiple things efficiently, look at
/// [`SpriteBatch`](spritebatch/struct.Spritebatch.html).
///
/// Note that if the canvas is not of the same size as the screen, and you want
/// to render using coordinates relative to the canvas' coordinate system, you
/// need to call [`graphics::set_screen_coordinates`](fn.set_screen_coordinates.html)
/// and pass in a rectangle with position (0, 0) and a size equal to that of the
/// canvas.
///
/// If you draw onto a canvas using BlendMode::Alpha you need to set its blend mode to
/// `BlendMode::Premultiplied` before drawing it to a new surface. This is a side effect
/// of the premultiplication of RGBA values when the canvas was rendered to.
/// This requirement holds for both drawing
/// the `Canvas` directly or converting it to an `Image` first.
/// ```
/// let mut canvas = Canvas::new(width, height, conf::NumSamples::One, get_window_color_format(ctx));
/// graphics::set_canvas(ctx, Some(&canvas));
///
/// // Draw to canvas here...
///
/// graphics::present(ctx);
/// graphics::set_canvas(ctx, None);
/// canvas.set_blend_mode(Some(BlendMode::Premultiplied));
/// ```
pub type Canvas = CanvasGeneric<GlBackendSpec>;

impl<S> CanvasGeneric<S>
where
    S: BackendSpec,
{
    #[allow(clippy::new_ret_no_self)]
    /// Create a new `Canvas` with the given size and number of samples.
    pub fn new(
        ctx: &mut Context,
        width: u16,
        height: u16,
        samples: conf::NumSamples,
        color_format: Format,
    ) -> GameResult<Canvas> {
        let debug_id = DebugId::get(ctx);
        let aa = match samples {
            conf::NumSamples::One => AaMode::Single,
            s => AaMode::Multi(s.into()),
        };
        let kind = Kind::D2(width, height, aa);
        let levels = 1;
        let factory = &mut ctx.gfx_context.factory;
        let texture_create_info = gfx::texture::Info {
            kind,
            levels,
            format: color_format.0,
            bind: Bind::SHADER_RESOURCE | Bind::RENDER_TARGET | Bind::TRANSFER_SRC,
            usage: Usage::Data,
        };
        let tex = factory.create_texture_raw(texture_create_info, Some(color_format.1), None)?;
        let resource_desc = gfx::texture::ResourceDesc {
            channel: color_format.1,
            layer: None,
            min: 0,
            max: levels - 1,
            swizzle: Swizzle::new(),
        };
        let resource = factory.view_texture_as_shader_resource_raw(&tex, resource_desc)?;
        let render_desc = gfx::texture::RenderDesc {
            channel: color_format.1,
            level: 0,
            layer: None,
        };
        let target = factory.view_texture_as_render_target_raw(&tex, render_desc)?;
        Ok(Canvas {
            target,
            image: Image {
                texture: resource,
                texture_handle: tex,
                sampler_info: ctx.gfx_context.default_sampler_info,
                blend_mode: None,
                width,
                height,
                debug_id,
            },
            debug_id,
        })
    }

    /// Create a new `Canvas` with the current window dimensions.
    pub fn with_window_size(ctx: &mut Context) -> GameResult<Canvas> {
        use crate::graphics;
        let (w, h) = graphics::drawable_size(ctx);
        // Default to no multisampling
        Canvas::new(
            ctx,
            w as u16,
            h as u16,
            conf::NumSamples::One,
            get_window_color_format(ctx),
        )
    }

    /// Gets the backend `Image` that is being rendered to. Note that this will be flipped but otherwise the same, use the [`to_image`](#method.to_image) function for the unflipped version.
    pub fn raw_image(&self) -> &Image {
        &self.image
    }

    /// Creates an `Image` with the content of its raw counterpart but transformed to behave like a normal `Image`.
    pub fn to_image(&self, ctx: &mut Context) -> GameResult<Image> {
        let pixel_data = self.to_rgba8(ctx)?;
        Image::from_rgba8(ctx, self.image.width, self.image.height, &pixel_data)
    }

    /// Gets the backend `Target` that is being rendered to.
    pub fn target(&self) -> &RawRenderTargetView<S::Resources> {
        &self.target
    }

    /// Dumps the flipped `Canvas`'s data to a `Vec` of `u8` RBGA8 values.
    pub fn to_rgba8(&self, ctx: &mut Context) -> GameResult<Vec<u8>> {
        let mut pixel_data = self.image.to_rgba8(ctx)?;
        flip_pixel_data(
            &mut pixel_data,
            self.image.width as usize,
            self.image.height as usize,
        );
        Ok(pixel_data)
    }

    /// Encode the `Canvas`'s content to the given file format and
    /// write it out to the given path.
    ///
    /// See the [`filesystem`](../filesystem/index.html) module docs for where exactly
    /// the file will end up.
    pub fn encode<P: AsRef<path::Path>>(
        &self,
        ctx: &mut Context,
        format: ImageFormat,
        path: P,
    ) -> GameResult {
        use std::io;
        let data = self.to_rgba8(ctx)?;
        let f = filesystem::create(ctx, path)?;
        let writer = &mut io::BufWriter::new(f);
        let color_format = ::image::ColorType::Rgba8;
        match format {
            ImageFormat::Png => ::image::png::PngEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width()),
                    u32::from(self.height()),
                    color_format,
                )
                .map_err(Into::into),
        }
    }

    /// Return the width of the canvas.
    pub fn width(&self) -> u16 {
        self.image.width
    }

    /// Return the height of the canvas.
    pub fn height(&self) -> u16 {
        self.image.height
    }

    /// Returns the dimensions of the canvas.
    pub fn dimensions(&self) -> Rect {
        Rect::new(0.0, 0.0, f32::from(self.width()), f32::from(self.height()))
    }

    /// Get the filter mode for the canvas.
    pub fn filter(&self) -> FilterMode {
        self.image.filter()
    }

    /// Set the filter mode for the canvas.
    pub fn set_filter(&mut self, mode: FilterMode) {
        self.image.set_filter(mode)
    }
}

impl Drawable for Canvas {
    fn draw(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
        self.debug_id.assert(ctx);

        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        let scale_x = param.src.w * f32::from(self.width());
        let scale_y = param.src.h * f32::from(self.height());

        let param = param.transform(
            glam::Mat4::from(param.trans.to_bare_matrix())
                * Matrix4::from_scale(glam::vec3(scale_x, scale_y, 1.0)),
        );

        let new_param = flip_draw_param_vertical(param);

        image::draw_image_raw(&self.image, ctx, new_param)
    }
    fn dimensions(&self, _: &mut Context) -> Option<Rect> {
        Some(self.image.dimensions())
    }
    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
        self.image.blend_mode = mode;
    }
    fn blend_mode(&self) -> Option<BlendMode> {
        self.image.blend_mode
    }
}

fn flip_draw_param_vertical(param: DrawParam) -> DrawParam {
    let param = if let Transform::Matrix(mat) = param.trans {
        param.transform(
            glam::Mat4::from(mat)
                * glam::Mat4::from_scale_rotation_translation(
                    glam::vec3(1.0, -1.0, 1.0),
                    Quat::identity(),
                    glam::vec3(0.0, 1.0, 0.0),
                ),
        )
    } else {
        panic!("Can not be called with a non-matrix DrawParam");
    };
    let new_src = Rect {
        x: param.src.x,
        y: (1.0 - param.src.h) - param.src.y,
        w: param.src.w,
        h: param.src.h,
    };
    param.src(new_src)
}

/// Set the `Canvas` to render to. Specifying `Option::None` will cause all
/// rendering to be done directly to the screen.
pub fn set_canvas(ctx: &mut Context, target: Option<&Canvas>) {
    match target {
        Some(surface) => {
            surface.debug_id.assert(ctx);
            ctx.gfx_context.data.out = surface.target.clone();
        }
        None => {
            ctx.gfx_context.data.out = ctx.gfx_context.screen_render_target.clone();
        }
    };
}
<commit_msg>placated the formatting machine-god<commit_after>//! I guess these docs will never appear since we re-export the canvas
//! module from graphics...
use std::path;

use gfx::format::{Format, Swizzle};
use gfx::handle::RawRenderTargetView;
use gfx::memory::{Bind, Usage};
use gfx::texture::{AaMode, Kind};
use gfx::Factory;
use glam::Quat;

use crate::context::DebugId;
use crate::error::*;
use crate::graphics::*;
use crate::Context;
use crate::{conf, filesystem};

/// A generic canvas independent of graphics backend. This type should
/// never need to be used directly; use [`graphics::Canvas`](type.Canvas.html)
/// instead.
#[derive(Debug)]
pub struct CanvasGeneric<Spec>
where
    Spec: BackendSpec,
{
    target: RawRenderTargetView<Spec::Resources>,
    image: Image,
    debug_id: DebugId,
}

/// A canvas that can be rendered to instead of the screen (sometimes referred
/// to as "render target" or "render to texture"). Set the canvas with the
/// [`graphics::set_canvas()`](fn.set_canvas.html) function, and then anything you
/// draw will be drawn to the canvas instead of the screen.
///
/// Resume drawing to the screen by calling `graphics::set_canvas(None)`.
///
/// A `Canvas` allows graphics to be rendered to images off-screen
/// in order to do things like saving to an image file or creating cool effects
/// by using shaders that render to an image.
/// If you just want to draw multiple things efficiently, look at
/// [`SpriteBatch`](spritebatch/struct.Spritebatch.html).
///
/// Note that if the canvas is not of the same size as the screen, and you want
/// to render using coordinates relative to the canvas' coordinate system, you
/// need to call [`graphics::set_screen_coordinates`](fn.set_screen_coordinates.html)
/// and pass in a rectangle with position (0, 0) and a size equal to that of the
/// canvas.
///
/// If you draw onto a canvas using BlendMode::Alpha you need to set its blend mode to
/// `BlendMode::Premultiplied` before drawing it to a new surface. This is a side effect
/// of the premultiplication of RGBA values when the canvas was rendered to.
/// This requirement holds for both drawing
/// the `Canvas` directly or converting it to an `Image` first.
/// ```
/// let mut canvas = Canvas::new(width, height, conf::NumSamples::One, get_window_color_format(ctx));
/// graphics::set_canvas(ctx, Some(&canvas));
///
/// // Draw to canvas here...
///
/// graphics::present(ctx);
/// graphics::set_canvas(ctx, None);
/// canvas.set_blend_mode(Some(BlendMode::Premultiplied));
/// ```
pub type Canvas = CanvasGeneric<GlBackendSpec>;

impl<S> CanvasGeneric<S>
where
    S: BackendSpec,
{
    #[allow(clippy::new_ret_no_self)]
    /// Create a new `Canvas` with the given size and number of samples.
    pub fn new(
        ctx: &mut Context,
        width: u16,
        height: u16,
        samples: conf::NumSamples,
        color_format: Format,
    ) -> GameResult<Canvas> {
        let debug_id = DebugId::get(ctx);
        let aa = match samples {
            conf::NumSamples::One => AaMode::Single,
            s => AaMode::Multi(s.into()),
        };
        let kind = Kind::D2(width, height, aa);
        let levels = 1;
        let factory = &mut ctx.gfx_context.factory;
        let texture_create_info = gfx::texture::Info {
            kind,
            levels,
            format: color_format.0,
            bind: Bind::SHADER_RESOURCE | Bind::RENDER_TARGET | Bind::TRANSFER_SRC,
            usage: Usage::Data,
        };
        let tex = factory.create_texture_raw(texture_create_info, Some(color_format.1), None)?;
        let resource_desc = gfx::texture::ResourceDesc {
            channel: color_format.1,
            layer: None,
            min: 0,
            max: levels - 1,
            swizzle: Swizzle::new(),
        };
        let resource = factory.view_texture_as_shader_resource_raw(&tex, resource_desc)?;
        let render_desc = gfx::texture::RenderDesc {
            channel: color_format.1,
            level: 0,
            layer: None,
        };
        let target = factory.view_texture_as_render_target_raw(&tex, render_desc)?;
        Ok(Canvas {
            target,
            image: Image {
                texture: resource,
                texture_handle: tex,
                sampler_info: ctx.gfx_context.default_sampler_info,
                blend_mode: None,
                width,
                height,
                debug_id,
            },
            debug_id,
        })
    }

    /// Create a new `Canvas` with the current window dimensions.
    pub fn with_window_size(ctx: &mut Context) -> GameResult<Canvas> {
        use crate::graphics;
        let (w, h) = graphics::drawable_size(ctx);
        // Default to no multisampling
        Canvas::new(
            ctx,
            w as u16,
            h as u16,
            conf::NumSamples::One,
            get_window_color_format(ctx),
        )
    }

    /// Gets the backend `Image` that is being rendered to. Note that this will be flipped but otherwise the same, use the [`to_image`](#method.to_image) function for the unflipped version.
    pub fn raw_image(&self) -> &Image {
        &self.image
    }

    /// Creates an `Image` with the content of its raw counterpart but transformed to behave like a normal `Image`.
    pub fn to_image(&self, ctx: &mut Context) -> GameResult<Image> {
        let pixel_data = self.to_rgba8(ctx)?;
        Image::from_rgba8(ctx, self.image.width, self.image.height, &pixel_data)
    }

    /// Gets the backend `Target` that is being rendered to.
    pub fn target(&self) -> &RawRenderTargetView<S::Resources> {
        &self.target
    }

    /// Dumps the flipped `Canvas`'s data to a `Vec` of `u8` RBGA8 values.
    pub fn to_rgba8(&self, ctx: &mut Context) -> GameResult<Vec<u8>> {
        let mut pixel_data = self.image.to_rgba8(ctx)?;
        flip_pixel_data(
            &mut pixel_data,
            self.image.width as usize,
            self.image.height as usize,
        );
        Ok(pixel_data)
    }

    /// Encode the `Canvas`'s content to the given file format and
    /// write it out to the given path.
    ///
    /// See the [`filesystem`](../filesystem/index.html) module docs for where exactly
    /// the file will end up.
    pub fn encode<P: AsRef<path::Path>>(
        &self,
        ctx: &mut Context,
        format: ImageFormat,
        path: P,
    ) -> GameResult {
        use std::io;
        let data = self.to_rgba8(ctx)?;
        let f = filesystem::create(ctx, path)?;
        let writer = &mut io::BufWriter::new(f);
        let color_format = ::image::ColorType::Rgba8;
        match format {
            ImageFormat::Png => ::image::png::PngEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width()),
                    u32::from(self.height()),
                    color_format,
                )
                .map_err(Into::into),
        }
    }

    /// Return the width of the canvas.
    pub fn width(&self) -> u16 {
        self.image.width
    }

    /// Return the height of the canvas.
    pub fn height(&self) -> u16 {
        self.image.height
    }

    /// Returns the dimensions of the canvas.
    pub fn dimensions(&self) -> Rect {
        Rect::new(0.0, 0.0, f32::from(self.width()), f32::from(self.height()))
    }

    /// Get the filter mode for the canvas.
    pub fn filter(&self) -> FilterMode {
        self.image.filter()
    }

    /// Set the filter mode for the canvas.
    pub fn set_filter(&mut self, mode: FilterMode) {
        self.image.set_filter(mode)
    }
}

impl Drawable for Canvas {
    fn draw(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
        self.debug_id.assert(ctx);

        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        let scale_x = param.src.w * f32::from(self.width());
        let scale_y = param.src.h * f32::from(self.height());

        let param = param.transform(
            glam::Mat4::from(param.trans.to_bare_matrix())
                * Matrix4::from_scale(glam::vec3(scale_x, scale_y, 1.0)),
        );

        let new_param = flip_draw_param_vertical(param);

        image::draw_image_raw(&self.image, ctx, new_param)
    }
    fn dimensions(&self, _: &mut Context) -> Option<Rect> {
        Some(self.image.dimensions())
    }
    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
        self.image.blend_mode = mode;
    }
    fn blend_mode(&self) -> Option<BlendMode> {
        self.image.blend_mode
    }
}

fn flip_draw_param_vertical(param: DrawParam) -> DrawParam {
    let param = if let Transform::Matrix(mat) = param.trans {
        param.transform(
            glam::Mat4::from(mat)
                * glam::Mat4::from_scale_rotation_translation(
                    glam::vec3(1.0, -1.0, 1.0),
                    Quat::identity(),
                    glam::vec3(0.0, 1.0, 0.0),
                ),
        )
    } else {
        panic!("Can not be called with a non-matrix DrawParam");
    };
    let new_src = Rect {
        x: param.src.x,
        y: (1.0 - param.src.h) - param.src.y,
        w: param.src.w,
        h: param.src.h,
    };
    param.src(new_src)
}

/// Set the `Canvas` to render to. Specifying `Option::None` will cause all
/// rendering to be done directly to the screen.
pub fn set_canvas(ctx: &mut Context, target: Option<&Canvas>) {
    match target {
        Some(surface) => {
            surface.debug_id.assert(ctx);
            ctx.gfx_context.data.out = surface.target.clone();
        }
        None => {
            ctx.gfx_context.data.out = ctx.gfx_context.screen_render_target.clone();
        }
    };
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate futures;
extern crate linear_map;
extern crate protobuf;

extern crate librespot_core as core;
extern crate librespot_protocol as protocol;

pub mod cover;

use futures::{Future, BoxFuture};
use linear_map::LinearMap;

use core::mercury::MercuryError;
use core::session::Session;
use core::util::{SpotifyId, FileId, StrChunksExt};

pub use protocol::metadata::AudioFile_Format as FileFormat;

fn countrylist_contains(list: &str, country: &str) -> bool {
    list.chunks(2).any(|cc| cc == country)
}

fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
    where I: IntoIterator<Item = &'s protocol::metadata::Restriction>
{
    let mut forbidden = "".to_string();
    let mut has_forbidden = false;

    let mut allowed = "".to_string();
    let mut has_allowed = false;

    let rs = restrictions.into_iter().filter(|r|
        r.get_catalogue_str().contains(&catalogue.to_owned())
    );

    for r in rs {
        if r.has_countries_forbidden() {
            forbidden.push_str(r.get_countries_forbidden());
            has_forbidden = true;
        }

        if r.has_countries_allowed() {
            allowed.push_str(r.get_countries_allowed());
            has_allowed = true;
        }
    }

    (has_forbidden || has_allowed) &&
    (!has_forbidden || !countrylist_contains(forbidden.as_str(), country)) &&
    (!has_allowed || countrylist_contains(allowed.as_str(), country))
}

pub trait Metadata : Send + Sized + 'static {
    type Message: protobuf::MessageStatic;

    fn base_url() -> &'static str;
    fn parse(msg: &Self::Message, session: &Session) -> Self;

    fn get(session: &Session, id: SpotifyId) -> BoxFuture<Self, MercuryError> {
        let uri = format!("{}/{}", Self::base_url(), id.to_base16());
        let request = session.mercury().get(uri);

        let session = session.clone();
        request.and_then(move |response| {
            let data = response.payload.first().expect("Empty payload");
            let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap();

            Ok(Self::parse(&msg, &session))
        }).boxed()
    }
}

#[derive(Debug, Clone)]
pub struct Track {
    pub id: SpotifyId,
    pub name: String,
    pub album: SpotifyId,
    pub artists: Vec<SpotifyId>,
    pub files: LinearMap<FileFormat, FileId>,
    pub alternatives: Vec<SpotifyId>,
    pub available: bool,
}

#[derive(Debug, Clone)]
pub struct Album {
    pub id: SpotifyId,
    pub name: String,
    pub artists: Vec<SpotifyId>,
    pub tracks: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Artist {
    pub id: SpotifyId,
    pub name: String,
    pub top_tracks: Vec<SpotifyId>,
}

impl Metadata for Track {
    type Message = protocol::metadata::Track;

    fn base_url() -> &'static str {
        "hm://metadata/3/track"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let files = msg.get_file()
                       .iter()
                       .filter(|file| file.has_file_id())
                       .map(|file| {
                           let mut dst = [0u8; 20];
                           dst.clone_from_slice(file.get_file_id());
                           (file.get_format(), FileId(dst))
                       })
                       .collect();

        Track {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            album: SpotifyId::from_raw(msg.get_album().get_gid()),
            artists: artists,
            files: files,
            alternatives: msg.get_alternative()
                             .iter()
                             .map(|alt| SpotifyId::from_raw(alt.get_gid()))
                             .collect(),
            available: parse_restrictions(msg.get_restriction(),
                                          &country,
                                          "premium"),
        }
    }
}

impl Metadata for Album {
    type Message = protocol::metadata::Album;

    fn base_url() -> &'static str {
        "hm://metadata/3/album"
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let tracks = msg.get_disc()
                        .iter()
                        .flat_map(|disc| disc.get_track())
                        .filter(|track| track.has_gid())
                        .map(|track| SpotifyId::from_raw(track.get_gid()))
                        .collect::<Vec<_>>();

        let covers = msg.get_cover_group()
                        .get_image()
                        .iter()
                        .filter(|image| image.has_file_id())
                        .map(|image| {
                            let mut dst = [0u8; 20];
                            dst.clone_from_slice(image.get_file_id());
                            FileId(dst)
                        })
                        .collect::<Vec<_>>();

        Album {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            artists: artists,
            tracks: tracks,
            covers: covers,
        }
    }
}

impl Metadata for Artist {
    type Message = protocol::metadata::Artist;

    fn base_url() -> &'static str {
        "hm://metadata/3/artist"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let top_tracks = msg.get_top_track()
                            .iter()
                            .find(|tt| !tt.has_country() || countrylist_contains(tt.get_country(), &country))
                            .unwrap()
                            .get_track()
                            .iter()
                            .filter(|track| track.has_gid())
                            .map(|track| SpotifyId::from_raw(track.get_gid()))
                            .collect::<Vec<_>>();

        Artist {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            top_tracks: top_tracks
        }
    }
}

<commit_msg>fix crash for artists without tracks (closes #103) (#256)<commit_after>extern crate byteorder;
extern crate futures;
extern crate linear_map;
extern crate protobuf;

extern crate librespot_core as core;
extern crate librespot_protocol as protocol;

pub mod cover;

use futures::{Future, BoxFuture};
use linear_map::LinearMap;

use core::mercury::MercuryError;
use core::session::Session;
use core::util::{SpotifyId, FileId, StrChunksExt};

pub use protocol::metadata::AudioFile_Format as FileFormat;

fn countrylist_contains(list: &str, country: &str) -> bool {
    list.chunks(2).any(|cc| cc == country)
}

fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
    where I: IntoIterator<Item = &'s protocol::metadata::Restriction>
{
    let mut forbidden = "".to_string();
    let mut has_forbidden = false;

    let mut allowed = "".to_string();
    let mut has_allowed = false;

    let rs = restrictions.into_iter().filter(|r|
        r.get_catalogue_str().contains(&catalogue.to_owned())
    );

    for r in rs {
        if r.has_countries_forbidden() {
            forbidden.push_str(r.get_countries_forbidden());
            has_forbidden = true;
        }

        if r.has_countries_allowed() {
            allowed.push_str(r.get_countries_allowed());
            has_allowed = true;
        }
    }

    (has_forbidden || has_allowed) &&
    (!has_forbidden || !countrylist_contains(forbidden.as_str(), country)) &&
    (!has_allowed || countrylist_contains(allowed.as_str(), country))
}

pub trait Metadata : Send + Sized + 'static {
    type Message: protobuf::MessageStatic;

    fn base_url() -> &'static str;
    fn parse(msg: &Self::Message, session: &Session) -> Self;

    fn get(session: &Session, id: SpotifyId) -> BoxFuture<Self, MercuryError> {
        let uri = format!("{}/{}", Self::base_url(), id.to_base16());
        let request = session.mercury().get(uri);

        let session = session.clone();
        request.and_then(move |response| {
            let data = response.payload.first().expect("Empty payload");
            let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap();

            Ok(Self::parse(&msg, &session))
        }).boxed()
    }
}

#[derive(Debug, Clone)]
pub struct Track {
    pub id: SpotifyId,
    pub name: String,
    pub album: SpotifyId,
    pub artists: Vec<SpotifyId>,
    pub files: LinearMap<FileFormat, FileId>,
    pub alternatives: Vec<SpotifyId>,
    pub available: bool,
}

#[derive(Debug, Clone)]
pub struct Album {
    pub id: SpotifyId,
    pub name: String,
    pub artists: Vec<SpotifyId>,
    pub tracks: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Artist {
    pub id: SpotifyId,
    pub name: String,
    pub top_tracks: Vec<SpotifyId>,
}

impl Metadata for Track {
    type Message = protocol::metadata::Track;

    fn base_url() -> &'static str {
        "hm://metadata/3/track"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let files = msg.get_file()
                       .iter()
                       .filter(|file| file.has_file_id())
                       .map(|file| {
                           let mut dst = [0u8; 20];
                           dst.clone_from_slice(file.get_file_id());
                           (file.get_format(), FileId(dst))
                       })
                       .collect();

        Track {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            album: SpotifyId::from_raw(msg.get_album().get_gid()),
            artists: artists,
            files: files,
            alternatives: msg.get_alternative()
                             .iter()
                             .map(|alt| SpotifyId::from_raw(alt.get_gid()))
                             .collect(),
            available: parse_restrictions(msg.get_restriction(),
                                          &country,
                                          "premium"),
        }
    }
}

impl Metadata for Album {
    type Message = protocol::metadata::Album;

    fn base_url() -> &'static str {
        "hm://metadata/3/album"
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let tracks = msg.get_disc()
                        .iter()
                        .flat_map(|disc| disc.get_track())
                        .filter(|track| track.has_gid())
                        .map(|track| SpotifyId::from_raw(track.get_gid()))
                        .collect::<Vec<_>>();

        let covers = msg.get_cover_group()
                        .get_image()
                        .iter()
                        .filter(|image| image.has_file_id())
                        .map(|image| {
                            let mut dst = [0u8; 20];
                            dst.clone_from_slice(image.get_file_id());
                            FileId(dst)
                        })
                        .collect::<Vec<_>>();

        Album {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            artists: artists,
            tracks: tracks,
            covers: covers,
        }
    }
}

impl Metadata for Artist {
    type Message = protocol::metadata::Artist;

    fn base_url() -> &'static str {
        "hm://metadata/3/artist"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let top_tracks: Vec<SpotifyId> = match msg.get_top_track()
                            .iter()
                            .find(|tt| !tt.has_country() || countrylist_contains(tt.get_country(), &country)) {
                                Some(tracks) => {
                                    tracks.get_track()
                                    .iter()
                                    .filter(|track| track.has_gid())
                                    .map(|track| SpotifyId::from_raw(track.get_gid()))
                                    .collect::<Vec<_>>()
                                },
                                None => Vec::new()
                            };

        Artist {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            top_tracks: top_tracks
        }
    }
}

<|endoftext|>