<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 `GraphGeometry`, 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 `GraphGeometry`
//! 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 `GraphGeometry` 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::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph};
//! use plexus::prelude::*;
//! use plexus::AsPosition;
//! use smallvec::SmallVec;
//!
//! // Requires `EdgeMidpoint` for `split_at_midpoint`.
//! pub fn circumscribe<G>(face: FaceView<&mut MeshGraph<G>, G>) -> FaceView<&mut MeshGraph<G>, G>
//! where
//!     G: EdgeMidpoint + 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.

// Geometric traits like `FaceNormal` and `EdgeMidpoint` are defined in such a
// way to reduce the contraints necessary for writing generic user code. With
// few exceptions, these traits only depend on `AsPosition` being implemented
// by the `Vertex` type in their definition. If a more complex implementation
// is necessary, constraints are specified there so that they do not pollute
// user code.

use theon::ops::{Cross, Interpolate, Project};
use theon::query::Plane;
use theon::space::{EuclideanSpace, FiniteDimensional, InnerSpace, Vector, VectorSpace};
use theon::{AsPosition, FromItems, Position};
use typenum::U3;

use crate::graph::borrow::Reborrow;
use crate::graph::mutation::Consistent;
use crate::graph::storage::payload::{Arc, Edge, Face, Vertex};
use crate::graph::storage::AsStorage;
use crate::graph::view::edge::{ArcView, CompositeEdge};
use crate::graph::view::face::Ring;
use crate::graph::view::vertex::VertexView;
use crate::graph::{GraphError, OptionExt as _};

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

// 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::graph::{GraphGeometry, MeshGraph};
/// use plexus::prelude::*;
/// use plexus::primitive::generate::Position;
/// use plexus::primitive::sphere::UvSphere;
/// use plexus::{AsPosition, IntoGeometry};
///
/// // 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 Position = Point3<N64>;
///
///     fn as_position(&self) -> &Self::Position {
///         &self.position
///     }
///
///     fn as_position_mut(&mut self) -> &mut Self::Position {
///         &mut self.position
///     }
/// }
///
/// // Create a mesh from a sphere primitive and map the geometry data.
/// let mut graph = UvSphere::new(8, 8)
///     .polygons::<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)
where
    T: Copy,
{
    type Vertex = Self;
    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 = ();
}

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

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

pub trait VertexCentroid: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn centroid<M>(vertex: VertexView<M, Self>) -> Result<VertexPosition<Self>, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        Ok(VertexPosition::<Self>::centroid(
            vertex
                .neighboring_vertices()
                .map(|vertex| *vertex.geometry.as_position()),
        )
        .expect_consistent())
    }
}

impl<G> VertexCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
{
}

pub trait VertexNormal: FaceNormal
where
    Self::Vertex: AsPosition,
{
    fn normal<M>(vertex: VertexView<M, Self>) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        M: Reborrow,
        M::Target:
            AsStorage<Arc<Self>> + AsStorage<Face<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        Vector::<VertexPosition<Self>>::mean(
            vertex
                .neighboring_faces()
                .map(<Self as FaceNormal>::normal)
                .collect::<Result<Vec<_>, _>>()?,
        )
        .expect_consistent()
        .normalize()
        .ok_or_else(|| GraphError::Geometry)
    }
}

impl<G> VertexNormal for G
where
    G: FaceNormal,
    G::Vertex: AsPosition,
{
}

pub trait ArcNormal: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn normal<M>(arc: ArcView<M, Self>) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> ArcNormal for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace,
    Vector<VertexPosition<G>>: Project<Output = Vector<VertexPosition<G>>>,
{
    fn normal<M>(arc: ArcView<M, Self>) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        let (a, b) = FromItems::from_items(arc.vertices().map(|vertex| *vertex.position()))
            .expect_consistent();
        let c = *arc.next_arc().destination_vertex().position();
        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
where
    Self::Vertex: AsPosition,
{
    fn midpoint<E, M>(edge: E) -> Result<VertexPosition<Self>, GraphError>
    where
        E: CompositeEdge<M, Self>,
        M: Reborrow,
        M::Target:
            AsStorage<Arc<Self>> + AsStorage<Edge<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> EdgeMidpoint for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Interpolate<Output = VertexPosition<G>>,
{
    fn midpoint<E, M>(edge: E) -> Result<VertexPosition<Self>, GraphError>
    where
        E: CompositeEdge<M, Self>,
        M: Reborrow,
        M::Target:
            AsStorage<Arc<Self>> + AsStorage<Edge<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        let arc = edge.into_arc();
        let (a, b) =
            FromItems::from_items(arc.vertices().map(|vertex| *vertex.position())).unwrap();
        Ok(a.midpoint(b))
    }
}

pub trait FaceCentroid: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn centroid<R, M>(ring: R) -> Result<VertexPosition<Self>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        Ok(
            VertexPosition::<Self>::centroid(ring.vertices().map(|vertex| *vertex.position()))
                .expect_consistent(),
        )
    }
}

impl<G> FaceCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
{
}

pub trait FaceNormal: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn normal<R, M>(ring: R) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> FaceNormal for G
where
    G: FaceCentroid + GraphGeometry,
    G::Vertex: AsPosition,
    Vector<VertexPosition<G>>: Cross<Output = Vector<VertexPosition<G>>>,
    VertexPosition<G>: EuclideanSpace,
{
    fn normal<R, M>(ring: R) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        let (a, b) =
            FromItems::from_items(ring.vertices().take(2).map(|vertex| *vertex.position()))
                .expect_consistent();
        let c = G::centroid(ring)?;
        let ab = a - b;
        let bc = b - c;
        ab.cross(bc).normalize().ok_or_else(|| GraphError::Geometry)
    }
}

pub trait FacePlane: GraphGeometry
where
    Self::Vertex: AsPosition,
    VertexPosition<Self>: FiniteDimensional<N = U3>,
{
    fn plane<R, M>(ring: R) -> Result<Plane<VertexPosition<Self>>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

// 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::space::Scalar;
    use theon::{FromItems, IntoItems};

    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,
    {
        fn plane<R, M>(ring: R) -> Result<Plane<VertexPosition<G>>, GraphError>
        where
            R: Ring<M, Self>,
            M: Reborrow,
            M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
        {
            let points = ring
                .vertices()
                .map(|vertex| *vertex.geometry.as_position())
                .collect::<SmallVec<[_; 4]>>();
            Plane::from_points(points).ok_or_else(|| GraphError::Geometry)
        }
    }
}
<commit_msg>Do not provide default implementations for geometric graph traits.<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 `GraphGeometry`, 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 `GraphGeometry`
//! 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 `GraphGeometry` 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::graph::{EdgeMidpoint, FaceView, GraphGeometry, MeshGraph};
//! use plexus::prelude::*;
//! use plexus::AsPosition;
//! use smallvec::SmallVec;
//!
//! // Requires `EdgeMidpoint` for `split_at_midpoint`.
//! pub fn circumscribe<G>(face: FaceView<&mut MeshGraph<G>, G>) -> FaceView<&mut MeshGraph<G>, G>
//! where
//!     G: EdgeMidpoint + 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.

// Geometric traits like `FaceNormal` and `EdgeMidpoint` are defined in such a
// way to reduce the contraints necessary for writing generic user code. With
// few exceptions, these traits only depend on `AsPosition` being implemented
// by the `Vertex` type in their definition. If a more complex implementation
// is necessary, constraints are specified there so that they do not pollute
// user code.

use theon::ops::{Cross, Interpolate, Project};
use theon::query::Plane;
use theon::space::{EuclideanSpace, FiniteDimensional, InnerSpace, Vector, VectorSpace};
use theon::{AsPosition, FromItems, Position};
use typenum::U3;

use crate::graph::borrow::Reborrow;
use crate::graph::mutation::Consistent;
use crate::graph::storage::payload::{Arc, Edge, Face, Vertex};
use crate::graph::storage::AsStorage;
use crate::graph::view::edge::{ArcView, CompositeEdge};
use crate::graph::view::face::Ring;
use crate::graph::view::vertex::VertexView;
use crate::graph::{GraphError, OptionExt as _};

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

// 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::graph::{GraphGeometry, MeshGraph};
/// use plexus::prelude::*;
/// use plexus::primitive::generate::Position;
/// use plexus::primitive::sphere::UvSphere;
/// use plexus::{AsPosition, IntoGeometry};
///
/// // 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 Position = Point3<N64>;
///
///     fn as_position(&self) -> &Self::Position {
///         &self.position
///     }
///
///     fn as_position_mut(&mut self) -> &mut Self::Position {
///         &mut self.position
///     }
/// }
///
/// // Create a mesh from a sphere primitive and map the geometry data.
/// let mut graph = UvSphere::new(8, 8)
///     .polygons::<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)
where
    T: Copy,
{
    type Vertex = Self;
    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 = ();
}

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

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

pub trait VertexCentroid: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn centroid<M>(vertex: VertexView<M, Self>) -> Result<VertexPosition<Self>, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> VertexCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
{
    fn centroid<M>(vertex: VertexView<M, Self>) -> Result<VertexPosition<Self>, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        Ok(VertexPosition::<Self>::centroid(
            vertex
                .neighboring_vertices()
                .map(|vertex| *vertex.geometry.as_position()),
        )
        .expect_consistent())
    }
}

pub trait VertexNormal: FaceNormal
where
    Self::Vertex: AsPosition,
{
    fn normal<M>(vertex: VertexView<M, Self>) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        M: Reborrow,
        M::Target:
            AsStorage<Arc<Self>> + AsStorage<Face<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> VertexNormal for G
where
    G: FaceNormal,
    G::Vertex: AsPosition,
{
    fn normal<M>(vertex: VertexView<M, Self>) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        M: Reborrow,
        M::Target:
            AsStorage<Arc<Self>> + AsStorage<Face<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        Vector::<VertexPosition<Self>>::mean(
            vertex
                .neighboring_faces()
                .map(<Self as FaceNormal>::normal)
                .collect::<Result<Vec<_>, _>>()?,
        )
        .expect_consistent()
        .normalize()
        .ok_or_else(|| GraphError::Geometry)
    }
}

pub trait ArcNormal: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn normal<M>(arc: ArcView<M, Self>) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> ArcNormal for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: EuclideanSpace,
    Vector<VertexPosition<G>>: Project<Output = Vector<VertexPosition<G>>>,
{
    fn normal<M>(arc: ArcView<M, Self>) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        let (a, b) = FromItems::from_items(arc.vertices().map(|vertex| *vertex.position()))
            .expect_consistent();
        let c = *arc.next_arc().destination_vertex().position();
        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
where
    Self::Vertex: AsPosition,
{
    fn midpoint<E, M>(edge: E) -> Result<VertexPosition<Self>, GraphError>
    where
        E: CompositeEdge<M, Self>,
        M: Reborrow,
        M::Target:
            AsStorage<Arc<Self>> + AsStorage<Edge<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> EdgeMidpoint for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Interpolate<Output = VertexPosition<G>>,
{
    fn midpoint<E, M>(edge: E) -> Result<VertexPosition<Self>, GraphError>
    where
        E: CompositeEdge<M, Self>,
        M: Reborrow,
        M::Target:
            AsStorage<Arc<Self>> + AsStorage<Edge<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        let arc = edge.into_arc();
        let (a, b) =
            FromItems::from_items(arc.vertices().map(|vertex| *vertex.position())).unwrap();
        Ok(a.midpoint(b))
    }
}

pub trait FaceCentroid: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn centroid<R, M>(ring: R) -> Result<VertexPosition<Self>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> FaceCentroid for G
where
    G: GraphGeometry,
    G::Vertex: AsPosition,
{
    fn centroid<R, M>(ring: R) -> Result<VertexPosition<Self>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        Ok(
            VertexPosition::<Self>::centroid(ring.vertices().map(|vertex| *vertex.position()))
                .expect_consistent(),
        )
    }
}

pub trait FaceNormal: GraphGeometry
where
    Self::Vertex: AsPosition,
{
    fn normal<R, M>(ring: R) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

impl<G> FaceNormal for G
where
    G: FaceCentroid + GraphGeometry,
    G::Vertex: AsPosition,
    Vector<VertexPosition<G>>: Cross<Output = Vector<VertexPosition<G>>>,
    VertexPosition<G>: EuclideanSpace,
{
    fn normal<R, M>(ring: R) -> Result<Vector<VertexPosition<Self>>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
    {
        let (a, b) =
            FromItems::from_items(ring.vertices().take(2).map(|vertex| *vertex.position()))
                .expect_consistent();
        let c = G::centroid(ring)?;
        let ab = a - b;
        let bc = b - c;
        ab.cross(bc).normalize().ok_or_else(|| GraphError::Geometry)
    }
}

pub trait FacePlane: GraphGeometry
where
    Self::Vertex: AsPosition,
    VertexPosition<Self>: FiniteDimensional<N = U3>,
{
    fn plane<R, M>(ring: R) -> Result<Plane<VertexPosition<Self>>, GraphError>
    where
        R: Ring<M, Self>,
        M: Reborrow,
        M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent;
}

// 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::space::Scalar;
    use theon::{FromItems, IntoItems};

    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,
    {
        fn plane<R, M>(ring: R) -> Result<Plane<VertexPosition<G>>, GraphError>
        where
            R: Ring<M, Self>,
            M: Reborrow,
            M::Target: AsStorage<Arc<Self>> + AsStorage<Vertex<Self>> + Consistent,
        {
            let points = ring
                .vertices()
                .map(|vertex| *vertex.geometry.as_position())
                .collect::<SmallVec<[_; 4]>>();
            Plane::from_points(points).ok_or_else(|| GraphError::Geometry)
        }
    }
}
<|endoftext|>


<commit_before>use walkdir::WalkDir;
use std::path::PathBuf;
use std::{env, fs};

fn main() {
    // The src directory (we are in src/tools/error_index_generator)
    // Note that we could skip one of the .. but this ensures we at least loosely find the right
    // directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let dest = out_dir.join("error_codes.rs");
    let mut idx = 0;
    for entry in WalkDir::new("../../../src") {
        let entry = entry.unwrap();
        if entry.file_name() == "error_codes.rs" {
            println!("cargo:rerun-if-changed={}", entry.path().to_str().unwrap());
            let file = fs::read_to_string(entry.path()).unwrap()
                .replace("use syntax::{register_diagnostics, register_long_diagnostics};", "")
                .replace("use syntax::register_diagnostics;", "")
                .replace("use syntax::register_long_diagnostics;", "");
            let contents = format!("(|| {{\n{}\n}})();", file);

            fs::write(&out_dir.join(&format!("error_{}.rs", idx)), &contents).unwrap();

            idx += 1;
        }
    }

    let mut all = String::new();
    all.push_str("fn register_all() -> Vec<(&'static str, Option<&'static str>)> {\n");
    all.push_str("let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();\n");
    all.push_str(r#"
macro_rules! register_diagnostics {
    ($($code:tt),*) => {{
        long_codes.extend([$(
            stringify!($code),
        )*].iter().cloned().map(|s| (s, None)).collect::<Vec<_>>());
    }};
    ($($code:tt),*,) => {{
        long_codes.extend([$(
            stringify!($code),
        )*].iter().cloned().map(|s| (s, None)));
    }}
}

macro_rules! register_long_diagnostics {
    ($($code:tt: $description:tt),*) => {
        {long_codes.extend([$(
            (stringify!($code), Some(stringify!($description))),
        )*].iter());}
    };
    ($($code:tt: $description:tt),*,) => {
        {long_codes.extend([$(
            (stringify!($code), Some(stringify!($description))),
        )*].iter());}
    }
}"#);
    for idx in 0..idx {
        all.push_str(&format!(r#"include!(concat!(env!("OUT_DIR"), "/error_{}.rs"));"#, idx));
    }
    all.push_str("\nlong_codes\n");
    all.push_str("}\n");

    fs::write(&dest, all).unwrap();
}
<commit_msg>Fix error index generator for new register_diagnostics API<commit_after>use walkdir::WalkDir;
use std::path::PathBuf;
use std::{env, fs};

fn main() {
    // The src directory (we are in src/tools/error_index_generator)
    // Note that we could skip one of the .. but this ensures we at least loosely find the right
    // directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let dest = out_dir.join("error_codes.rs");
    let mut idx = 0;
    for entry in WalkDir::new("../../../src") {
        let entry = entry.unwrap();
        if entry.file_name() == "error_codes.rs" {
            println!("cargo:rerun-if-changed={}", entry.path().to_str().unwrap());
            let file = fs::read_to_string(entry.path()).unwrap()
                .replace("syntax::register_diagnostics!", "register_diagnostics!");
            let contents = format!("(|| {{\n{}\n}})();", file);

            fs::write(&out_dir.join(&format!("error_{}.rs", idx)), &contents).unwrap();

            idx += 1;
        }
    }

    let mut all = String::new();
    all.push_str(r###"
fn register_all() -> Vec<(&'static str, Option<&'static str>)> {
    let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();
    macro_rules! register_diagnostics {
        ($($ecode:ident: $message:expr,)*) => (
            register_diagnostics!{$($ecode:$message,)* ;}
        );

        ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
            $(
                {long_codes.extend([
                    (stringify!($ecode), Some(stringify!($message))),
                ].iter());}
            )*
            $(
                {long_codes.extend([
                    stringify!($code),
                ].iter().cloned().map(|s| (s, None)).collect::<Vec<_>>());}
            )*
        )
    }
"###);
    for idx in 0..idx {
        all.push_str(&format!(r#"include!(concat!(env!("OUT_DIR"), "/error_{}.rs"));"#, idx));
        all.push_str("\n");
    }
    all.push_str("\nlong_codes\n");
    all.push_str("}\n");

    fs::write(&dest, all).unwrap();
}
<|endoftext|>


<commit_before>//! This is a small server which is intended to run inside of an emulator or
//! on a remote test device. This server pairs with the `remote-test-client`
//! program in this repository. The `remote-test-client` connects to this
//! server over a TCP socket and performs work such as:
//!
//! 1. Pushing shared libraries to the server
//! 2. Running tests through the server
//!
//! The server supports running tests concurrently and also supports tests
//! themselves having support libraries. All data over the TCP sockets is in a
//! basically custom format suiting our needs.

#[cfg(not(windows))]
use std::fs::Permissions;
#[cfg(not(windows))]
use std::os::unix::prelude::*;

use std::cmp;
use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::net::{TcpListener, TcpStream};
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus, Stdio};
use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => panic!("{} failed with {}", stringify!($e), e),
        }
    };
}

static TEST: AtomicUsize = AtomicUsize::new(0);

#[derive(Copy, Clone)]
struct Config {
    pub remote: bool,
    pub verbose: bool,
}

impl Config {
    pub fn default() -> Config {
        Config { remote: false, verbose: false }
    }

    pub fn parse_args() -> Config {
        let mut config = Config::default();

        let args = env::args().skip(1);
        for argument in args {
            match &argument[..] {
                "remote" => {
                    config.remote = true;
                }
                "verbose" | "-v" => {
                    config.verbose = true;
                }
                arg => panic!("unknown argument: {}", arg),
            }
        }

        config
    }
}

fn print_verbose(s: &str, conf: Config) {
    if conf.verbose {
        println!("{}", s);
    }
}

fn main() {
    println!("starting test server");

    let config = Config::parse_args();

    let bind_addr = if cfg!(target_os = "android") || cfg!(windows) || config.remote {
        "0.0.0.0:12345"
    } else {
        "10.0.2.15:12345"
    };

    let listener = t!(TcpListener::bind(bind_addr));
    let (work, tmp): (PathBuf, PathBuf) = if cfg!(target_os = "android") {
        ("/data/tmp/work".into(), "/data/tmp/work/tmp".into())
    } else {
        let mut work_dir = env::temp_dir();
        work_dir.push("work");
        let mut tmp_dir = work_dir.clone();
        tmp_dir.push("tmp");
        (work_dir, tmp_dir)
    };
    println!("listening on {}!", bind_addr);

    t!(fs::create_dir_all(&work));
    t!(fs::create_dir_all(&tmp));

    let lock = Arc::new(Mutex::new(()));

    for socket in listener.incoming() {
        let mut socket = t!(socket);
        let mut buf = [0; 4];
        if socket.read_exact(&mut buf).is_err() {
            continue;
        }
        if &buf[..] == b"ping" {
            print_verbose("Received ping", config);
            t!(socket.write_all(b"pong"));
        } else if &buf[..] == b"push" {
            handle_push(socket, &work, config);
        } else if &buf[..] == b"run " {
            let lock = lock.clone();
            let work = work.clone();
            let tmp = tmp.clone();
            thread::spawn(move || handle_run(socket, &work, &tmp, &lock, config));
        } else {
            panic!("unknown command {:?}", buf);
        }
    }
}

fn handle_push(socket: TcpStream, work: &Path, config: Config) {
    let mut reader = BufReader::new(socket);
    let dst = recv(&work, &mut reader);
    print_verbose(&format!("push {:#?}", dst), config);

    let mut socket = reader.into_inner();
    t!(socket.write_all(b"ack "));
}

struct RemoveOnDrop<'a> {
    inner: &'a Path,
}

impl Drop for RemoveOnDrop<'_> {
    fn drop(&mut self) {
        t!(fs::remove_dir_all(self.inner));
    }
}

fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, config: Config) {
    let mut arg = Vec::new();
    let mut reader = BufReader::new(socket);

    // Allocate ourselves a directory that we'll delete when we're done to save
    // space.
    let n = TEST.fetch_add(1, Ordering::SeqCst);
    let path = work.join(format!("test{}", n));
    t!(fs::create_dir(&path));
    let _a = RemoveOnDrop { inner: &path };

    // First up we'll get a list of arguments delimited with 0 bytes. An empty
    // argument means that we're done.
    let mut args = Vec::new();
    while t!(reader.read_until(0, &mut arg)) > 1 {
        args.push(t!(str::from_utf8(&arg[..arg.len() - 1])).to_string());
        arg.truncate(0);
    }

    // Next we'll get a bunch of env vars in pairs delimited by 0s as well
    let mut env = Vec::new();
    arg.truncate(0);
    while t!(reader.read_until(0, &mut arg)) > 1 {
        let key_len = arg.len() - 1;
        let val_len = t!(reader.read_until(0, &mut arg)) - 1;
        {
            let key = &arg[..key_len];
            let val = &arg[key_len + 1..][..val_len];
            let key = t!(str::from_utf8(key)).to_string();
            let val = t!(str::from_utf8(val)).to_string();
            env.push((key, val));
        }
        arg.truncate(0);
    }

    // The section of code from here down to where we drop the lock is going to
    // be a critical section for us. On Linux you can't execute a file which is
    // open somewhere for writing, as you'll receive the error "text file busy".
    // Now here we never have the text file open for writing when we spawn it,
    // so why do we still need a critical section?
    //
    // Process spawning first involves a `fork` on Unix, which clones all file
    // descriptors into the child process. This means that it's possible for us
    // to open the file for writing (as we're downloading it), then some other
    // thread forks, then we close the file and try to exec. At that point the
    // other thread created a child process with the file open for writing, and
    // we attempt to execute it, so we get an error.
    //
    // This race is resolve by ensuring that only one thread can write the file
    // and spawn a child process at once. Kinda an unfortunate solution, but we
    // don't have many other choices with this sort of setup!
    //
    // In any case the lock is acquired here, before we start writing any files.
    // It's then dropped just after we spawn the child. That way we don't lock
    // the execution of the child, just the creation of its files.
    let lock = lock.lock();

    // Next there's a list of dynamic libraries preceded by their filenames.
    while t!(reader.fill_buf())[0] != 0 {
        recv(&path, &mut reader);
    }
    assert_eq!(t!(reader.read(&mut [0])), 1);

    // Finally we'll get the binary. The other end will tell us how big the
    // binary is and then we'll download it all to the exe path we calculated
    // earlier.
    let exe = recv(&path, &mut reader);
    print_verbose(&format!("run {:#?}", exe), config);

    let mut cmd = Command::new(&exe);
    cmd.args(args);
    cmd.envs(env);

    // Support libraries were uploaded to `work` earlier, so make sure that's
    // in `LD_LIBRARY_PATH`. Also include our own current dir which may have
    // had some libs uploaded.
    if cfg!(windows) {
        // On windows, libraries are just searched in the executable directory,
        // system directories, PWD, and PATH, in that order. PATH is the only one
        // we can change for this.
        cmd.env(
            "PATH",
            env::join_paths(
                std::iter::once(work.to_owned())
                    .chain(std::iter::once(path.clone()))
                    .chain(env::split_paths(&env::var_os("PATH").unwrap())),
            )
            .unwrap(),
        );
    } else {
        cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display()));
    }

    // Some tests assume RUST_TEST_TMPDIR exists
    cmd.env("RUST_TEST_TMPDIR", tmp.to_owned());

    // Spawn the child and ferry over stdout/stderr to the socket in a framed
    // fashion (poor man's style)
    let mut child =
        t!(cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());
    drop(lock);
    let mut stdout = child.stdout.take().unwrap();
    let mut stderr = child.stderr.take().unwrap();
    let socket = Arc::new(Mutex::new(reader.into_inner()));
    let socket2 = socket.clone();
    let thread = thread::spawn(move || my_copy(&mut stdout, 0, &*socket2));
    my_copy(&mut stderr, 1, &*socket);
    thread.join().unwrap();

    // Finally send over the exit status.
    let status = t!(child.wait());

    let (which, code) = get_status_code(&status);

    t!(socket.lock().unwrap().write_all(&[
        which,
        (code >> 24) as u8,
        (code >> 16) as u8,
        (code >> 8) as u8,
        (code >> 0) as u8,
    ]));
}

#[cfg(not(windows))]
fn get_status_code(status: &ExitStatus) -> (u8, i32) {
    match status.code() {
        Some(n) => (0, n),
        None => (1, status.signal().unwrap()),
    }
}

#[cfg(windows)]
fn get_status_code(status: &ExitStatus) -> (u8, i32) {
    (0, status.code().unwrap())
}

fn recv<B: BufRead>(dir: &Path, io: &mut B) -> PathBuf {
    let mut filename = Vec::new();
    t!(io.read_until(0, &mut filename));

    // We've got some tests with *really* long names. We try to name the test
    // executable the same on the target as it is on the host to aid with
    // debugging, but the targets we're emulating are often more restrictive
    // than the hosts as well.
    //
    // To ensure we can run a maximum number of tests without modifications we
    // just arbitrarily truncate the filename to 50 bytes. That should
    // hopefully allow us to still identify what's running while staying under
    // the filesystem limits.
    let len = cmp::min(filename.len() - 1, 50);
    let dst = dir.join(t!(str::from_utf8(&filename[..len])));
    let amt = read_u32(io) as u64;
    t!(io::copy(&mut io.take(amt), &mut t!(File::create(&dst))));
    set_permissions(&dst);
    dst
}

#[cfg(not(windows))]
fn set_permissions(path: &Path) {
    t!(fs::set_permissions(&path, Permissions::from_mode(0o755)));
}
#[cfg(windows)]
fn set_permissions(_path: &Path) {}

fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex<dyn Write>) {
    let mut b = [0; 1024];
    loop {
        let n = t!(src.read(&mut b));
        let mut dst = dst.lock().unwrap();
        t!(dst.write_all(&[
            which,
            (n >> 24) as u8,
            (n >> 16) as u8,
            (n >> 8) as u8,
            (n >> 0) as u8,
        ]));
        if n > 0 {
            t!(dst.write_all(&b[..n]));
        } else {
            break;
        }
    }
}

fn read_u32(r: &mut dyn Read) -> u32 {
    let mut len = [0; 4];
    t!(r.read_exact(&mut len));
    ((len[0] as u32) << 24)
        | ((len[1] as u32) << 16)
        | ((len[2] as u32) << 8)
        | ((len[3] as u32) << 0)
}
<commit_msg>Preserve existing LD_LIBRARY_PATH in remote-test-server<commit_after>//! This is a small server which is intended to run inside of an emulator or
//! on a remote test device. This server pairs with the `remote-test-client`
//! program in this repository. The `remote-test-client` connects to this
//! server over a TCP socket and performs work such as:
//!
//! 1. Pushing shared libraries to the server
//! 2. Running tests through the server
//!
//! The server supports running tests concurrently and also supports tests
//! themselves having support libraries. All data over the TCP sockets is in a
//! basically custom format suiting our needs.

#[cfg(not(windows))]
use std::fs::Permissions;
#[cfg(not(windows))]
use std::os::unix::prelude::*;

use std::cmp;
use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::net::{TcpListener, TcpStream};
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus, Stdio};
use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(e) => e,
            Err(e) => panic!("{} failed with {}", stringify!($e), e),
        }
    };
}

static TEST: AtomicUsize = AtomicUsize::new(0);

#[derive(Copy, Clone)]
struct Config {
    pub remote: bool,
    pub verbose: bool,
}

impl Config {
    pub fn default() -> Config {
        Config { remote: false, verbose: false }
    }

    pub fn parse_args() -> Config {
        let mut config = Config::default();

        let args = env::args().skip(1);
        for argument in args {
            match &argument[..] {
                "remote" => {
                    config.remote = true;
                }
                "verbose" | "-v" => {
                    config.verbose = true;
                }
                arg => panic!("unknown argument: {}", arg),
            }
        }

        config
    }
}

fn print_verbose(s: &str, conf: Config) {
    if conf.verbose {
        println!("{}", s);
    }
}

fn main() {
    println!("starting test server");

    let config = Config::parse_args();

    let bind_addr = if cfg!(target_os = "android") || cfg!(windows) || config.remote {
        "0.0.0.0:12345"
    } else {
        "10.0.2.15:12345"
    };

    let listener = t!(TcpListener::bind(bind_addr));
    let (work, tmp): (PathBuf, PathBuf) = if cfg!(target_os = "android") {
        ("/data/tmp/work".into(), "/data/tmp/work/tmp".into())
    } else {
        let mut work_dir = env::temp_dir();
        work_dir.push("work");
        let mut tmp_dir = work_dir.clone();
        tmp_dir.push("tmp");
        (work_dir, tmp_dir)
    };
    println!("listening on {}!", bind_addr);

    t!(fs::create_dir_all(&work));
    t!(fs::create_dir_all(&tmp));

    let lock = Arc::new(Mutex::new(()));

    for socket in listener.incoming() {
        let mut socket = t!(socket);
        let mut buf = [0; 4];
        if socket.read_exact(&mut buf).is_err() {
            continue;
        }
        if &buf[..] == b"ping" {
            print_verbose("Received ping", config);
            t!(socket.write_all(b"pong"));
        } else if &buf[..] == b"push" {
            handle_push(socket, &work, config);
        } else if &buf[..] == b"run " {
            let lock = lock.clone();
            let work = work.clone();
            let tmp = tmp.clone();
            thread::spawn(move || handle_run(socket, &work, &tmp, &lock, config));
        } else {
            panic!("unknown command {:?}", buf);
        }
    }
}

fn handle_push(socket: TcpStream, work: &Path, config: Config) {
    let mut reader = BufReader::new(socket);
    let dst = recv(&work, &mut reader);
    print_verbose(&format!("push {:#?}", dst), config);

    let mut socket = reader.into_inner();
    t!(socket.write_all(b"ack "));
}

struct RemoveOnDrop<'a> {
    inner: &'a Path,
}

impl Drop for RemoveOnDrop<'_> {
    fn drop(&mut self) {
        t!(fs::remove_dir_all(self.inner));
    }
}

fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, config: Config) {
    let mut arg = Vec::new();
    let mut reader = BufReader::new(socket);

    // Allocate ourselves a directory that we'll delete when we're done to save
    // space.
    let n = TEST.fetch_add(1, Ordering::SeqCst);
    let path = work.join(format!("test{}", n));
    t!(fs::create_dir(&path));
    let _a = RemoveOnDrop { inner: &path };

    // First up we'll get a list of arguments delimited with 0 bytes. An empty
    // argument means that we're done.
    let mut args = Vec::new();
    while t!(reader.read_until(0, &mut arg)) > 1 {
        args.push(t!(str::from_utf8(&arg[..arg.len() - 1])).to_string());
        arg.truncate(0);
    }

    // Next we'll get a bunch of env vars in pairs delimited by 0s as well
    let mut env = Vec::new();
    arg.truncate(0);
    while t!(reader.read_until(0, &mut arg)) > 1 {
        let key_len = arg.len() - 1;
        let val_len = t!(reader.read_until(0, &mut arg)) - 1;
        {
            let key = &arg[..key_len];
            let val = &arg[key_len + 1..][..val_len];
            let key = t!(str::from_utf8(key)).to_string();
            let val = t!(str::from_utf8(val)).to_string();
            env.push((key, val));
        }
        arg.truncate(0);
    }

    // The section of code from here down to where we drop the lock is going to
    // be a critical section for us. On Linux you can't execute a file which is
    // open somewhere for writing, as you'll receive the error "text file busy".
    // Now here we never have the text file open for writing when we spawn it,
    // so why do we still need a critical section?
    //
    // Process spawning first involves a `fork` on Unix, which clones all file
    // descriptors into the child process. This means that it's possible for us
    // to open the file for writing (as we're downloading it), then some other
    // thread forks, then we close the file and try to exec. At that point the
    // other thread created a child process with the file open for writing, and
    // we attempt to execute it, so we get an error.
    //
    // This race is resolve by ensuring that only one thread can write the file
    // and spawn a child process at once. Kinda an unfortunate solution, but we
    // don't have many other choices with this sort of setup!
    //
    // In any case the lock is acquired here, before we start writing any files.
    // It's then dropped just after we spawn the child. That way we don't lock
    // the execution of the child, just the creation of its files.
    let lock = lock.lock();

    // Next there's a list of dynamic libraries preceded by their filenames.
    while t!(reader.fill_buf())[0] != 0 {
        recv(&path, &mut reader);
    }
    assert_eq!(t!(reader.read(&mut [0])), 1);

    // Finally we'll get the binary. The other end will tell us how big the
    // binary is and then we'll download it all to the exe path we calculated
    // earlier.
    let exe = recv(&path, &mut reader);
    print_verbose(&format!("run {:#?}", exe), config);

    let mut cmd = Command::new(&exe);
    cmd.args(args);
    cmd.envs(env);

    // On windows, libraries are just searched in the executable directory,
    // system directories, PWD, and PATH, in that order. PATH is the only one
    // we can change for this.
    let library_path = if cfg!(windows) { "PATH" } else { "LD_LIBRARY_PATH" };

    // Support libraries were uploaded to `work` earlier, so make sure that's
    // in `LD_LIBRARY_PATH`. Also include our own current dir which may have
    // had some libs uploaded.
    let mut paths = vec![work.to_owned(), path.clone()];
    if let Some(library_path) = env::var_os(library_path) {
        paths.extend(env::split_paths(&library_path));
    }
    cmd.env(library_path, env::join_paths(paths).unwrap());

    // Some tests assume RUST_TEST_TMPDIR exists
    cmd.env("RUST_TEST_TMPDIR", tmp.to_owned());

    // Spawn the child and ferry over stdout/stderr to the socket in a framed
    // fashion (poor man's style)
    let mut child =
        t!(cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());
    drop(lock);
    let mut stdout = child.stdout.take().unwrap();
    let mut stderr = child.stderr.take().unwrap();
    let socket = Arc::new(Mutex::new(reader.into_inner()));
    let socket2 = socket.clone();
    let thread = thread::spawn(move || my_copy(&mut stdout, 0, &*socket2));
    my_copy(&mut stderr, 1, &*socket);
    thread.join().unwrap();

    // Finally send over the exit status.
    let status = t!(child.wait());

    let (which, code) = get_status_code(&status);

    t!(socket.lock().unwrap().write_all(&[
        which,
        (code >> 24) as u8,
        (code >> 16) as u8,
        (code >> 8) as u8,
        (code >> 0) as u8,
    ]));
}

#[cfg(not(windows))]
fn get_status_code(status: &ExitStatus) -> (u8, i32) {
    match status.code() {
        Some(n) => (0, n),
        None => (1, status.signal().unwrap()),
    }
}

#[cfg(windows)]
fn get_status_code(status: &ExitStatus) -> (u8, i32) {
    (0, status.code().unwrap())
}

fn recv<B: BufRead>(dir: &Path, io: &mut B) -> PathBuf {
    let mut filename = Vec::new();
    t!(io.read_until(0, &mut filename));

    // We've got some tests with *really* long names. We try to name the test
    // executable the same on the target as it is on the host to aid with
    // debugging, but the targets we're emulating are often more restrictive
    // than the hosts as well.
    //
    // To ensure we can run a maximum number of tests without modifications we
    // just arbitrarily truncate the filename to 50 bytes. That should
    // hopefully allow us to still identify what's running while staying under
    // the filesystem limits.
    let len = cmp::min(filename.len() - 1, 50);
    let dst = dir.join(t!(str::from_utf8(&filename[..len])));
    let amt = read_u32(io) as u64;
    t!(io::copy(&mut io.take(amt), &mut t!(File::create(&dst))));
    set_permissions(&dst);
    dst
}

#[cfg(not(windows))]
fn set_permissions(path: &Path) {
    t!(fs::set_permissions(&path, Permissions::from_mode(0o755)));
}
#[cfg(windows)]
fn set_permissions(_path: &Path) {}

fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex<dyn Write>) {
    let mut b = [0; 1024];
    loop {
        let n = t!(src.read(&mut b));
        let mut dst = dst.lock().unwrap();
        t!(dst.write_all(&[
            which,
            (n >> 24) as u8,
            (n >> 16) as u8,
            (n >> 8) as u8,
            (n >> 0) as u8,
        ]));
        if n > 0 {
            t!(dst.write_all(&b[..n]));
        } else {
            break;
        }
    }
}

fn read_u32(r: &mut dyn Read) -> u32 {
    let mut len = [0; 4];
    t!(r.read_exact(&mut len));
    ((len[0] as u32) << 24)
        | ((len[1] as u32) << 16)
        | ((len[2] as u32) << 8)
        | ((len[3] as u32) << 0)
}
<|endoftext|>


<commit_before>use ::MatchResult;

use pact_matching::Mismatch;
use pact_matching::models::{Pact, Interaction, Request, OptionalBody, PactSpecification};
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use pact_matching::models::parse_query_string;

use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
use log::{log, error, warn, info, debug};
use hyper::{Body, Response, Server, Error};
use hyper::http::response::{Builder as ResponseBuilder};
use hyper::http::header::{HeaderMap, HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue};
use hyper::service::service_fn;
use futures::future;
use futures::future::Future;
use futures::stream::Stream;
use itertools::Itertools;

enum MockRequestError {
    InvalidHeaderEncoding,
    RequestBodyError,
    ResponseHeaderEncodingError,
    ResponseBodyError
}

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Result<Option<HashMap<String, String>>, MockRequestError> {
    if headers.len() > 0 {
        let result: Result<HashMap<String, String>, MockRequestError> = headers.keys()
            .map(|name| -> Result<(String, String), MockRequestError> {
                let values = headers.get_all(name);
                let mut iter = values.iter();

                let first_value = iter.next().unwrap();

                if iter.next().is_some() {
                    warn!("Multiple headers associated with '{}', but only the first is used", name);
                }

                Ok((
                    name.as_str().into(),
                    first_value.to_str()
                        .map_err(|err| MockRequestError::InvalidHeaderEncoding)?
                        .into()
                    )
                )
            })
            .collect();

        result.map(|map| Some(map))
    } else {
        Ok(None)
    }
}

pub fn extract_body(chunk: hyper::Chunk) -> OptionalBody {
    let bytes = chunk.into_bytes();
    if bytes.len() > 0 {
        OptionalBody::Present(bytes.to_vec())
    } else {
        OptionalBody::Empty
    }
}

fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> impl Future<Item = Request, Error = MockRequestError> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers());

    future::done(headers)
        .and_then(move |headers| {
            req.into_body()
                .concat2()
                .map_err(|_| MockRequestError::RequestBodyError)
                .map(|body_chunk| (headers, body_chunk))
        })
        .and_then(|(headers, body_chunk)|
            Ok(Request {
                method: method,
                path: path,
                query: query,
                headers: headers,
                body: extract_body(body_chunk),
                matching_rules: MatchingRules::default(),
                generators: Generators::default()
            })
        )
}

fn method_or_path_mismatch(mismatches: &Vec<Mismatch>) -> bool {
    mismatches.iter()
        .map(|mismatch| mismatch.mismatch_type())
        .any(|mismatch_type| mismatch_type == "MethodMismatch" || mismatch_type == "PathMismatch")
}

fn match_request(req: &Request, interactions: &Vec<Interaction>) -> MatchResult {
    let match_results = interactions
        .into_iter()
        .map(|i| (i.clone(), pact_matching::match_request(i.request.clone(), req.clone())))
        .sorted_by(|i1, i2| {
            let list1 = i1.1.clone().into_iter().map(|m| m.mismatch_type()).unique().count();
            let list2 = i2.1.clone().into_iter().map(|m| m.mismatch_type()).unique().count();
            Ord::cmp(&list1, &list2)
        });
    match match_results.first() {
        Some(res) => {
            if res.1.is_empty() {
                MatchResult::RequestMatch(res.0.clone())
            } else if method_or_path_mismatch(&res.1) {
                MatchResult::RequestNotFound(req.clone())
            } else {
                MatchResult::RequestMismatch(res.0.clone(), res.1.clone())
            }
        },
        None => MatchResult::RequestNotFound(req.clone())
    }
}

fn set_hyper_headers(builder: &mut ResponseBuilder, headers: &Option<HashMap<String, String>>) -> Result<(), MockRequestError> {
    let hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                // FIXME?: Headers are not sent in "raw" mode.
                // Names are converted to lower case and values are parsed.
                hyper_headers.insert(
                    HeaderName::from_bytes(k.as_bytes())
                        .map_err(|err| {
                            error!("Invalid header name '{}' ({})", k, err);
                            MockRequestError::ResponseHeaderEncodingError
                        })?,
                    v.parse::<HeaderValue>()
                        .map_err(|err| {
                            error!("Invalid header value '{}': '{}' ({})", k, v, err);
                            MockRequestError::ResponseHeaderEncodingError
                        })?
                );
            }
        },
        _ => {}
    }
    Ok(())
}

fn match_result_to_hyper_response(match_result: MatchResult) -> Result<Response<Body>, MockRequestError> {
    match match_result {
        MatchResult::RequestMatch(ref interaction) => {
            let response = pact_matching::generate_response(&interaction.response);
            info!("Request matched, sending response {:?}", response);
            info!("     body: '{}'\n\n", interaction.response.body.str_value());
            info!("     body: '{}'\n\n", interaction.response.body.str_value());

            let mut builder = Response::builder();
            builder.status(response.status);

            builder.header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");
            set_hyper_headers(&mut builder, &response.headers)?;

            builder.body(match response.body {
                OptionalBody::Present(ref s) => Body::from(s.clone()),
                _ => Body::empty()
            })
                .map_err(|_| MockRequestError::ResponseBodyError)
        },
        _ => {
            Ok(Response::new(Body::from("Hello")))
        }
    }
}

fn handle_request(
    req: hyper::Request<Body>,
    pact: Arc<Pact>,
) -> impl Future<Item = Response<Body>, Error = MockRequestError> {
    debug!("Creating pact request from hyper request");

    hyper_request_to_pact_request(req)
        .and_then(move |req| {
            info!("Received request {:?}", req);
            let match_result = match_request(&req, &pact.interactions);

            // TODO:
            // record_result(&mock_server_id, &match_result);

            match_result_to_hyper_response(match_result)
        })
}

// TODO: Should instead use some form of X-Pact headers
fn handle_mock_request_error(result: Result<Response<Body>, MockRequestError>) -> Result<Response<Body>, Error> {
    match result {
        Ok(response) => Ok(response),
        Err(error) => {
            let response = match error {
                MockRequestError::InvalidHeaderEncoding => Response::builder()
                    .status(400)
                    .body(Body::from("Found an invalid header encoding")),
                MockRequestError::RequestBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process request body")),
                MockRequestError::ResponseBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process response body")),
                MockRequestError::ResponseHeaderEncodingError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not set response header"))
            };
            Ok(response.unwrap())
        }
    }
}

pub fn start(
    id: String,
    pact: Pact,
    port: u16,
    shutdown: impl Future<Item = (), Error = ()>,
) -> (impl Future<Item = (), Error = Error>, u16) {
    let pact = Arc::new(pact);
    let addr = ([0, 0, 0, 0], port).into();

    let server = Server::bind(&addr)
        .serve(move || {
            let pact = pact.clone();
            service_fn(move |req| {
                handle_request(req, pact.clone())
                    .then(handle_mock_request_error)
            })
        });

    let port = server.local_addr().port();

    (server.with_graceful_shutdown(shutdown), port)
}
<commit_msg>InteractionError<commit_after>use ::MatchResult;

use pact_matching::Mismatch;
use pact_matching::models::{Pact, Interaction, Request, OptionalBody, PactSpecification};
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use pact_matching::models::parse_query_string;

use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
use log::{log, error, warn, info, debug};
use hyper::{Body, Response, Server, Error};
use hyper::http::response::{Builder as ResponseBuilder};
use hyper::http::header::{HeaderMap, HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue};
use hyper::service::service_fn;
use futures::future;
use futures::future::Future;
use futures::stream::Stream;
use itertools::Itertools;

enum InteractionError {
    InvalidHeaderEncoding,
    RequestBodyError,
    ResponseHeaderEncodingError,
    ResponseBodyError
}

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Result<Option<HashMap<String, String>>, InteractionError> {
    if headers.len() > 0 {
        let result: Result<HashMap<String, String>, InteractionError> = headers.keys()
            .map(|name| -> Result<(String, String), InteractionError> {
                let values = headers.get_all(name);
                let mut iter = values.iter();

                let first_value = iter.next().unwrap();

                if iter.next().is_some() {
                    warn!("Multiple headers associated with '{}', but only the first is used", name);
                }

                Ok((
                    name.as_str().into(),
                    first_value.to_str()
                        .map_err(|err| InteractionError::InvalidHeaderEncoding)?
                        .into()
                    )
                )
            })
            .collect();

        result.map(|map| Some(map))
    } else {
        Ok(None)
    }
}

pub fn extract_body(chunk: hyper::Chunk) -> OptionalBody {
    let bytes = chunk.into_bytes();
    if bytes.len() > 0 {
        OptionalBody::Present(bytes.to_vec())
    } else {
        OptionalBody::Empty
    }
}

fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> impl Future<Item = Request, Error = InteractionError> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers());

    future::done(headers)
        .and_then(move |headers| {
            req.into_body()
                .concat2()
                .map_err(|_| InteractionError::RequestBodyError)
                .map(|body_chunk| (headers, body_chunk))
        })
        .and_then(|(headers, body_chunk)|
            Ok(Request {
                method: method,
                path: path,
                query: query,
                headers: headers,
                body: extract_body(body_chunk),
                matching_rules: MatchingRules::default(),
                generators: Generators::default()
            })
        )
}

fn method_or_path_mismatch(mismatches: &Vec<Mismatch>) -> bool {
    mismatches.iter()
        .map(|mismatch| mismatch.mismatch_type())
        .any(|mismatch_type| mismatch_type == "MethodMismatch" || mismatch_type == "PathMismatch")
}

fn match_request(req: &Request, interactions: &Vec<Interaction>) -> MatchResult {
    let match_results = interactions
        .into_iter()
        .map(|i| (i.clone(), pact_matching::match_request(i.request.clone(), req.clone())))
        .sorted_by(|i1, i2| {
            let list1 = i1.1.clone().into_iter().map(|m| m.mismatch_type()).unique().count();
            let list2 = i2.1.clone().into_iter().map(|m| m.mismatch_type()).unique().count();
            Ord::cmp(&list1, &list2)
        });
    match match_results.first() {
        Some(res) => {
            if res.1.is_empty() {
                MatchResult::RequestMatch(res.0.clone())
            } else if method_or_path_mismatch(&res.1) {
                MatchResult::RequestNotFound(req.clone())
            } else {
                MatchResult::RequestMismatch(res.0.clone(), res.1.clone())
            }
        },
        None => MatchResult::RequestNotFound(req.clone())
    }
}

fn set_hyper_headers(builder: &mut ResponseBuilder, headers: &Option<HashMap<String, String>>) -> Result<(), InteractionError> {
    let hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                // FIXME?: Headers are not sent in "raw" mode.
                // Names are converted to lower case and values are parsed.
                hyper_headers.insert(
                    HeaderName::from_bytes(k.as_bytes())
                        .map_err(|err| {
                            error!("Invalid header name '{}' ({})", k, err);
                            InteractionError::ResponseHeaderEncodingError
                        })?,
                    v.parse::<HeaderValue>()
                        .map_err(|err| {
                            error!("Invalid header value '{}': '{}' ({})", k, v, err);
                            InteractionError::ResponseHeaderEncodingError
                        })?
                );
            }
        },
        _ => {}
    }
    Ok(())
}

fn match_result_to_hyper_response(match_result: MatchResult) -> Result<Response<Body>, InteractionError> {
    match match_result {
        MatchResult::RequestMatch(ref interaction) => {
            let response = pact_matching::generate_response(&interaction.response);
            info!("Request matched, sending response {:?}", response);
            info!("     body: '{}'\n\n", interaction.response.body.str_value());
            info!("     body: '{}'\n\n", interaction.response.body.str_value());

            let mut builder = Response::builder();
            builder.status(response.status);

            builder.header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");
            set_hyper_headers(&mut builder, &response.headers)?;

            builder.body(match response.body {
                OptionalBody::Present(ref s) => Body::from(s.clone()),
                _ => Body::empty()
            })
                .map_err(|_| InteractionError::ResponseBodyError)
        },
        _ => {
            Ok(Response::new(Body::from("Hello")))
        }
    }
}

fn handle_request(
    req: hyper::Request<Body>,
    pact: Arc<Pact>,
) -> impl Future<Item = Response<Body>, Error = InteractionError> {
    debug!("Creating pact request from hyper request");

    hyper_request_to_pact_request(req)
        .and_then(move |req| {
            info!("Received request {:?}", req);
            let match_result = match_request(&req, &pact.interactions);

            // TODO:
            // record_result(&mock_server_id, &match_result);

            match_result_to_hyper_response(match_result)
        })
}

// TODO: Should instead use some form of X-Pact headers
fn handle_mock_request_error(result: Result<Response<Body>, InteractionError>) -> Result<Response<Body>, Error> {
    match result {
        Ok(response) => Ok(response),
        Err(error) => {
            let response = match error {
                InteractionError::InvalidHeaderEncoding => Response::builder()
                    .status(400)
                    .body(Body::from("Found an invalid header encoding")),
                InteractionError::RequestBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process request body")),
                InteractionError::ResponseBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process response body")),
                InteractionError::ResponseHeaderEncodingError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not set response header"))
            };
            Ok(response.unwrap())
        }
    }
}

pub fn start(
    id: String,
    pact: Pact,
    port: u16,
    shutdown: impl Future<Item = (), Error = ()>,
) -> (impl Future<Item = (), Error = Error>, u16) {
    let pact = Arc::new(pact);
    let addr = ([0, 0, 0, 0], port).into();

    let server = Server::bind(&addr)
        .serve(move || {
            let pact = pact.clone();
            service_fn(move |req| {
                handle_request(req, pact.clone())
                    .then(handle_mock_request_error)
            })
        });

    let port = server.local_addr().port();

    (server.with_graceful_shutdown(shutdown), port)
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use protobuf;
use protobuf::compiler_plugin;
use protobuf::descriptor::*;
use protobuf::descriptorx::*;
use protobuf_codegen::code_writer::CodeWriter;

/// Adjust method name to follow the rust's style.
fn snake_name(name: &str) -> String {
    let mut snake_method_name = String::with_capacity(name.len());
    let mut chars = name.chars();
    // initial char can be any char except '_'.
    let mut last_char = '.';
    'outer: while let Some(c) = chars.next() {
        // Please note that '_' is neither uppercase nor lowercase.
        if !c.is_uppercase() {
            last_char = c;
            snake_method_name.push(c);
            continue;
        }
        let mut can_append_underscore = false;
        // check if it's the first char.
        if !snake_method_name.is_empty() && last_char != '_' {
            snake_method_name.push('_');
        }
        last_char = c;
        // find all continous upper case char and append an underscore before
        // last upper case char if neccessary.
        while let Some(c) = chars.next() {
            if !c.is_uppercase() {
                if can_append_underscore && c != '_' {
                    snake_method_name.push('_');
                }
                snake_method_name.extend(last_char.to_lowercase());
                snake_method_name.push(c);
                last_char = c;
                continue 'outer;
            }
            snake_method_name.extend(last_char.to_lowercase());
            last_char = c;
            can_append_underscore = true;
        }
        snake_method_name.extend(last_char.to_lowercase());
    }
    snake_method_name
}

struct MethodGen<'a> {
    proto: &'a MethodDescriptorProto,
    service_path: String,
    root_scope: &'a RootScope<'a>,
}

impl<'a> MethodGen<'a> {
    fn new(
        proto: &'a MethodDescriptorProto,
        service_path: String,
        root_scope: &'a RootScope<'a>,
    ) -> MethodGen<'a> {
        MethodGen {
            proto: proto,
            service_path: service_path,
            root_scope: root_scope,
        }
    }

    fn snake_name(&self) -> String {
        snake_name(self.proto.get_name())
    }

    fn input_message(&self) -> String {
        format!(
            "super::{}",
            self.root_scope
                .find_message(self.proto.get_input_type())
                .rust_fq_name()
        )
    }

    fn output_message(&self) -> String {
        format!(
            "super::{}",
            self.root_scope
                .find_message(self.proto.get_output_type())
                .rust_fq_name()
        )
    }

    fn client_resp_type(&self) -> String {
        match self.proto.get_server_streaming() {
            false => format!("::grpc::SingleResponse<{}>", self.output_message()),
            true => format!("::grpc::StreamingResponse<{}>", self.output_message()),
        }
    }

    fn client_sig(&self) -> String {
        let req_param = match self.proto.get_client_streaming() {
            false => format!(", req: {}", self.input_message()),
            true => format!(""),
        };
        let resp_type = self.client_resp_type();
        let return_type = match self.proto.get_client_streaming() {
            false => resp_type,
            true => format!(
                "impl ::futures::future::Future<Item=(::grpc::ClientRequestSink<{}>, {}), Error=::grpc::Error>",
                self.input_message(),
                resp_type
            ),
        };
        format!(
            "{}(&self, o: ::grpc::RequestOptions{}) -> {}",
            self.snake_name(),
            req_param,
            return_type,
        )
    }

    fn server_req_type(&self) -> String {
        match self.proto.get_client_streaming() {
            false => format!("::grpc::ServerRequestSingle<{}>", self.input_message()),
            true => format!("::grpc::ServerRequest<{}>", self.input_message()),
        }
    }

    fn server_resp_type(&self) -> String {
        match self.proto.get_server_streaming() {
            false => format!("::grpc::ServerResponseUnarySink<{}>", self.output_message()),
            true => format!("::grpc::ServerResponseSink<{}>", self.output_message()),
        }
    }

    fn server_sig(&self) -> String {
        format!(
            "{}(&self, o: ::grpc::ServerHandlerContext, req: {}, resp: {}) -> ::grpc::Result<()>",
            self.snake_name(),
            self.server_req_type(),
            self.server_resp_type(),
        )
    }

    fn write_server_intf(&self, w: &mut CodeWriter) {
        w.fn_def(&self.server_sig())
    }

    fn streaming_upper(&self) -> &'static str {
        match (
            self.proto.get_client_streaming(),
            self.proto.get_server_streaming(),
        ) {
            (false, false) => "Unary",
            (false, true) => "ServerStreaming",
            (true, false) => "ClientStreaming",
            (true, true) => "Bidi",
        }
    }

    fn streaming_lower(&self) -> &'static str {
        match (
            self.proto.get_client_streaming(),
            self.proto.get_server_streaming(),
        ) {
            (false, false) => "unary",
            (false, true) => "server_streaming",
            (true, false) => "client_streaming",
            (true, true) => "bidi",
        }
    }

    fn write_client(&self, w: &mut CodeWriter) {
        w.pub_fn(&self.client_sig(), |w| {
            self.write_descriptor(
                w,
                "let descriptor = ::grpc::rt::ArcOrStatic::Static(&",
                ");",
            );

            let req = match self.proto.get_client_streaming() {
                false => ", req",
                true => "",
            };
            w.write_line(&format!(
                "self.grpc_client.call_{}(o{}, descriptor)",
                self.streaming_lower(),
                req,
            ))
        });
    }

    fn write_descriptor(&self, w: &mut CodeWriter, before: &str, after: &str) {
        w.block(
            &format!("{}{}", before, "::grpc::rt::MethodDescriptor {"),
            &format!("{}{}", "}", after),
            |w| {
                w.field_entry(
                    "name",
                    &format!(
                        "::grpc::rt::StringOrStatic::Static(\"{}/{}\")",
                        self.service_path,
                        self.proto.get_name()
                    ),
                );
                w.field_entry(
                    "streaming",
                    &format!("::grpc::rt::GrpcStreaming::{}", self.streaming_upper()),
                );
                w.field_entry(
                    "req_marshaller",
                    "::grpc::rt::ArcOrStatic::Static(&::grpc_protobuf::MarshallerProtobuf)",
                );
                w.field_entry(
                    "resp_marshaller",
                    "::grpc::rt::ArcOrStatic::Static(&::grpc_protobuf::MarshallerProtobuf)",
                );
            },
        );
    }
}

struct ServiceGen<'a> {
    proto: &'a ServiceDescriptorProto,
    _root_scope: &'a RootScope<'a>,
    methods: Vec<MethodGen<'a>>,
    service_path: String,
    _package: String,
}

impl<'a> ServiceGen<'a> {
    fn new(
        proto: &'a ServiceDescriptorProto,
        file: &FileDescriptorProto,
        root_scope: &'a RootScope,
    ) -> ServiceGen<'a> {
        let service_path = if file.get_package().is_empty() {
            format!("/{}", proto.get_name())
        } else {
            format!("/{}.{}", file.get_package(), proto.get_name())
        };
        let methods = proto
            .get_method()
            .into_iter()
            .map(|m| MethodGen::new(m, service_path.clone(), root_scope))
            .collect();

        ServiceGen {
            proto,
            _root_scope: root_scope,
            methods,
            service_path,
            _package: file.get_package().to_string(),
        }
    }

    // trait name
    fn server_intf_name(&self) -> &str {
        self.proto.get_name()
    }

    // client struct name
    fn client_name(&self) -> String {
        format!("{}Client", self.proto.get_name())
    }

    // server struct name
    fn server_name(&self) -> String {
        format!("{}Server", self.proto.get_name())
    }

    fn write_server_intf(&self, w: &mut CodeWriter) {
        w.pub_trait(&self.server_intf_name(), |w| {
            for (i, method) in self.methods.iter().enumerate() {
                if i != 0 {
                    w.write_line("");
                }

                method.write_server_intf(w);
            }
        });
    }

    fn write_client_object(&self, grpc_client: &str, w: &mut CodeWriter) {
        w.expr_block(&self.client_name(), |w| {
            w.field_entry("grpc_client", grpc_client);
        });
    }

    fn write_client(&self, w: &mut CodeWriter) {
        w.pub_struct(&self.client_name(), |w| {
            w.field_decl("grpc_client", "::std::sync::Arc<::grpc::Client>");
        });

        w.write_line("");

        w.impl_for_block("::grpc::ClientStub", &self.client_name(), |w| {
            let sig = "with_client(grpc_client: ::std::sync::Arc<::grpc::Client>) -> Self";
            w.def_fn(sig, |w| {
                self.write_client_object("grpc_client", w);
            });
        });

        w.write_line("");

        w.impl_self_block(&self.client_name(), |w| {
            for (i, method) in self.methods.iter().enumerate() {
                if i != 0 {
                    w.write_line("");
                }

                method.write_client(w);
            }
        });
    }

    fn write_service_definition(
        &self,
        before: &str,
        after: &str,
        handler: &str,
        w: &mut CodeWriter,
    ) {
        w.block(
            &format!("{}::grpc::rt::ServerServiceDefinition::new(\"{}\",",
                before, self.service_path),
            &format!("){}", after),
            |w| {
                w.block("vec![", "],", |w| {
                    for method in &self.methods {
                        w.block("::grpc::rt::ServerMethod::new(", "),", |w| {
                            method.write_descriptor(w, "::grpc::rt::ArcOrStatic::Static(&", "),");
                            w.block("{", "},", |w| {
                                w.write_line(&format!("let handler_copy = {}.clone();", handler));
                                w.write_line(&format!("::grpc::rt::MethodHandler{}::new(move |ctx, req, resp| (*handler_copy).{}(ctx, req, resp))",
                                    method.streaming_upper(),
                                    method.snake_name()));
                            });
                        });
                    }
                });
            });
    }

    fn write_server(&self, w: &mut CodeWriter) {
        w.write_line(&format!("pub struct {};", self.server_name()));

        w.write_line("");

        w.write_line("");

        w.impl_self_block(&self.server_name(), |w| {
            w.pub_fn(&format!("new_service_def<H : {} + 'static + Sync + Send + 'static>(handler: H) -> ::grpc::rt::ServerServiceDefinition", self.server_intf_name()), |w| {
                w.write_line("let handler_arc = ::std::sync::Arc::new(handler);");

                self.write_service_definition("", "", "handler_arc", w);
            });
        });
    }

    fn write(&self, w: &mut CodeWriter) {
        w.comment("server interface");
        w.write_line("");
        self.write_server_intf(w);
        w.write_line("");
        w.comment("client");
        w.write_line("");
        self.write_client(w);
        w.write_line("");
        w.comment("server");
        w.write_line("");
        self.write_server(w);
    }
}

fn gen_file(
    file: &FileDescriptorProto,
    root_scope: &RootScope,
) -> Option<compiler_plugin::GenResult> {
    if file.get_service().is_empty() {
        return None;
    }

    let base = protobuf::descriptorx::proto_path_to_rust_mod(file.get_name());

    let mut v = Vec::new();
    {
        let mut w = CodeWriter::new(&mut v);
        w.write_generated();
        w.write_line("");

        for service in file.get_service() {
            w.write_line("");
            ServiceGen::new(service, file, root_scope).write(&mut w);
        }
    }

    Some(compiler_plugin::GenResult {
        name: base + "_grpc.rs",
        content: v,
    })
}

pub fn gen(
    file_descriptors: &[FileDescriptorProto],
    files_to_generate: &[String],
) -> Vec<compiler_plugin::GenResult> {
    let files_map: HashMap<&str, &FileDescriptorProto> =
        file_descriptors.iter().map(|f| (f.get_name(), f)).collect();

    let root_scope = RootScope {
        file_descriptors: file_descriptors,
    };

    let mut results = Vec::new();

    for file_name in files_to_generate {
        let file = files_map[&file_name[..]];

        if file.get_service().is_empty() {
            continue;
        }

        results.extend(gen_file(file, &root_scope).into_iter());
    }

    results
}

pub fn protoc_gen_grpc_rust_main() {
    compiler_plugin::plugin_main(gen);
}

#[cfg(test)]
mod test {
    #[test]
    fn test_snake_name() {
        let cases = vec![
            ("AsyncRequest", "async_request"),
            ("asyncRequest", "async_request"),
            ("async_request", "async_request"),
            ("createID", "create_id"),
            ("CreateIDForReq", "create_id_for_req"),
            ("Create_ID_For_Req", "create_id_for_req"),
            ("ID", "id"),
            ("id", "id"),
        ];

        for (origin, exp) in cases {
            let res = super::snake_name(&origin);
            assert_eq!(res, exp);
        }
    }
}
<commit_msg>grpc-rust: drop generation of client struct<commit_after>use std::collections::HashMap;

use protobuf;
use protobuf::compiler_plugin;
use protobuf::descriptor::*;
use protobuf::descriptorx::*;
use protobuf_codegen::code_writer::CodeWriter;

/// Adjust method name to follow the rust's style.
fn snake_name(name: &str) -> String {
    let mut snake_method_name = String::with_capacity(name.len());
    let mut chars = name.chars();
    // initial char can be any char except '_'.
    let mut last_char = '.';
    'outer: while let Some(c) = chars.next() {
        // Please note that '_' is neither uppercase nor lowercase.
        if !c.is_uppercase() {
            last_char = c;
            snake_method_name.push(c);
            continue;
        }
        let mut can_append_underscore = false;
        // check if it's the first char.
        if !snake_method_name.is_empty() && last_char != '_' {
            snake_method_name.push('_');
        }
        last_char = c;
        // find all continous upper case char and append an underscore before
        // last upper case char if neccessary.
        while let Some(c) = chars.next() {
            if !c.is_uppercase() {
                if can_append_underscore && c != '_' {
                    snake_method_name.push('_');
                }
                snake_method_name.extend(last_char.to_lowercase());
                snake_method_name.push(c);
                last_char = c;
                continue 'outer;
            }
            snake_method_name.extend(last_char.to_lowercase());
            last_char = c;
            can_append_underscore = true;
        }
        snake_method_name.extend(last_char.to_lowercase());
    }
    snake_method_name
}

struct MethodGen<'a> {
    proto: &'a MethodDescriptorProto,
    service_path: String,
    root_scope: &'a RootScope<'a>,
}

impl<'a> MethodGen<'a> {
    fn new(
        proto: &'a MethodDescriptorProto,
        service_path: String,
        root_scope: &'a RootScope<'a>,
    ) -> MethodGen<'a> {
        MethodGen {
            proto: proto,
            service_path: service_path,
            root_scope: root_scope,
        }
    }

    fn snake_name(&self) -> String {
        snake_name(self.proto.get_name())
    }

    fn input_message(&self) -> String {
        format!(
            "super::{}",
            self.root_scope
                .find_message(self.proto.get_input_type())
                .rust_fq_name()
        )
    }

    fn output_message(&self) -> String {
        format!(
            "super::{}",
            self.root_scope
                .find_message(self.proto.get_output_type())
                .rust_fq_name()
        )
    }

    fn server_req_type(&self) -> String {
        match self.proto.get_client_streaming() {
            false => format!("::grpc::ServerRequestSingle<{}>", self.input_message()),
            true => format!("::grpc::ServerRequest<{}>", self.input_message()),
        }
    }

    fn server_resp_type(&self) -> String {
        match self.proto.get_server_streaming() {
            false => format!("::grpc::ServerResponseUnarySink<{}>", self.output_message()),
            true => format!("::grpc::ServerResponseSink<{}>", self.output_message()),
        }
    }

    fn server_sig(&self) -> String {
        format!(
            "{}(&self, o: ::grpc::ServerHandlerContext, req: {}, resp: {}) -> ::grpc::Result<()>",
            self.snake_name(),
            self.server_req_type(),
            self.server_resp_type(),
        )
    }

    fn write_server_intf(&self, w: &mut CodeWriter) {
        w.fn_def(&self.server_sig())
    }

    fn streaming_upper(&self) -> &'static str {
        match (
            self.proto.get_client_streaming(),
            self.proto.get_server_streaming(),
        ) {
            (false, false) => "Unary",
            (false, true) => "ServerStreaming",
            (true, false) => "ClientStreaming",
            (true, true) => "Bidi",
        }
    }

    fn write_descriptor(&self, w: &mut CodeWriter, before: &str, after: &str) {
        w.block(
            &format!("{}{}", before, "::grpc::rt::MethodDescriptor {"),
            &format!("{}{}", "}", after),
            |w| {
                w.field_entry(
                    "name",
                    &format!(
                        "::grpc::rt::StringOrStatic::Static(\"{}/{}\")",
                        self.service_path,
                        self.proto.get_name()
                    ),
                );
                w.field_entry(
                    "streaming",
                    &format!("::grpc::rt::GrpcStreaming::{}", self.streaming_upper()),
                );
                w.field_entry(
                    "req_marshaller",
                    "::grpc::rt::ArcOrStatic::Static(&::grpc_protobuf::MarshallerProtobuf)",
                );
                w.field_entry(
                    "resp_marshaller",
                    "::grpc::rt::ArcOrStatic::Static(&::grpc_protobuf::MarshallerProtobuf)",
                );
            },
        );
    }
}

struct ServiceGen<'a> {
    proto: &'a ServiceDescriptorProto,
    _root_scope: &'a RootScope<'a>,
    methods: Vec<MethodGen<'a>>,
    service_path: String,
    _package: String,
}

impl<'a> ServiceGen<'a> {
    fn new(
        proto: &'a ServiceDescriptorProto,
        file: &FileDescriptorProto,
        root_scope: &'a RootScope,
    ) -> ServiceGen<'a> {
        let service_path = if file.get_package().is_empty() {
            format!("/{}", proto.get_name())
        } else {
            format!("/{}.{}", file.get_package(), proto.get_name())
        };
        let methods = proto
            .get_method()
            .into_iter()
            .map(|m| MethodGen::new(m, service_path.clone(), root_scope))
            .collect();

        ServiceGen {
            proto,
            _root_scope: root_scope,
            methods,
            service_path,
            _package: file.get_package().to_string(),
        }
    }

    // trait name
    fn server_intf_name(&self) -> &str {
        self.proto.get_name()
    }

    // server struct name
    fn server_name(&self) -> String {
        format!("{}Server", self.proto.get_name())
    }

    fn write_server_intf(&self, w: &mut CodeWriter) {
        w.pub_trait(&self.server_intf_name(), |w| {
            for (i, method) in self.methods.iter().enumerate() {
                if i != 0 {
                    w.write_line("");
                }

                method.write_server_intf(w);
            }
        });
    }

    fn write_service_definition(
        &self,
        before: &str,
        after: &str,
        handler: &str,
        w: &mut CodeWriter,
    ) {
        w.block(
            &format!("{}::grpc::rt::ServerServiceDefinition::new(\"{}\",",
                before, self.service_path),
            &format!("){}", after),
            |w| {
                w.block("vec![", "],", |w| {
                    for method in &self.methods {
                        w.block("::grpc::rt::ServerMethod::new(", "),", |w| {
                            method.write_descriptor(w, "::grpc::rt::ArcOrStatic::Static(&", "),");
                            w.block("{", "},", |w| {
                                w.write_line(&format!("let handler_copy = {}.clone();", handler));
                                w.write_line(&format!("::grpc::rt::MethodHandler{}::new(move |ctx, req, resp| (*handler_copy).{}(ctx, req, resp))",
                                    method.streaming_upper(),
                                    method.snake_name()));
                            });
                        });
                    }
                });
            });
    }

    fn write_server(&self, w: &mut CodeWriter) {
        w.write_line(&format!("pub struct {};", self.server_name()));

        w.write_line("");

        w.write_line("");

        w.impl_self_block(&self.server_name(), |w| {
            w.pub_fn(&format!("new_service_def<H : {} + 'static + Sync + Send + 'static>(handler: H) -> ::grpc::rt::ServerServiceDefinition", self.server_intf_name()), |w| {
                w.write_line("let handler_arc = ::std::sync::Arc::new(handler);");

                self.write_service_definition("", "", "handler_arc", w);
            });
        });
    }

    fn write(&self, w: &mut CodeWriter) {
        w.comment("server interface");
        w.write_line("");
        self.write_server_intf(w);
        w.write_line("");
        w.comment("server");
        w.write_line("");
        self.write_server(w);
    }
}

fn gen_file(
    file: &FileDescriptorProto,
    root_scope: &RootScope,
) -> Option<compiler_plugin::GenResult> {
    if file.get_service().is_empty() {
        return None;
    }

    let base = protobuf::descriptorx::proto_path_to_rust_mod(file.get_name());

    let mut v = Vec::new();
    {
        let mut w = CodeWriter::new(&mut v);
        w.write_generated();
        w.write_line("");

        for service in file.get_service() {
            w.write_line("");
            ServiceGen::new(service, file, root_scope).write(&mut w);
        }
    }

    Some(compiler_plugin::GenResult {
        name: base + "_grpc.rs",
        content: v,
    })
}

pub fn gen(
    file_descriptors: &[FileDescriptorProto],
    files_to_generate: &[String],
) -> Vec<compiler_plugin::GenResult> {
    let files_map: HashMap<&str, &FileDescriptorProto> =
        file_descriptors.iter().map(|f| (f.get_name(), f)).collect();

    let root_scope = RootScope {
        file_descriptors: file_descriptors,
    };

    let mut results = Vec::new();

    for file_name in files_to_generate {
        let file = files_map[&file_name[..]];

        if file.get_service().is_empty() {
            continue;
        }

        results.extend(gen_file(file, &root_scope).into_iter());
    }

    results
}

pub fn protoc_gen_grpc_rust_main() {
    compiler_plugin::plugin_main(gen);
}

#[cfg(test)]
mod test {
    #[test]
    fn test_snake_name() {
        let cases = vec![
            ("AsyncRequest", "async_request"),
            ("asyncRequest", "async_request"),
            ("async_request", "async_request"),
            ("createID", "create_id"),
            ("CreateIDForReq", "create_id_for_req"),
            ("Create_ID_For_Req", "create_id_for_req"),
            ("ID", "id"),
            ("id", "id"),
        ];

        for (origin, exp) in cases {
            let res = super::snake_name(&origin);
            assert_eq!(res, exp);
        }
    }
}
<|endoftext|>


<commit_before>pub struct Brackets {
    ok: bool,
    seen: Vec<char>,
}

impl<'a> From<&'a str> for Brackets {
    fn from(input: &str) -> Self {
        let mut b = Brackets {
            ok: true,
            seen: Vec::new(),
        };
        for c in input.chars() {
            match c {
                '[' | '{' | '(' => b.seen.push(c),
                ']' | '}' | ')' => {
                    b.ok = b.seen.pop().map_or(false, |x| match c {
                        ']' => x == '[',
                        '}' => x == '{',
                        ')' => x == '(',
                        _ => false, // impossible but need to exhaust possibilities
                    });
                }
                _ => (),
            }
            if !b.ok {
                break;
            }
        }
        b.ok &= b.seen.is_empty();
        b
    }
}

impl Brackets {
    pub fn are_balanced(&self) -> bool {
        self.ok
    }
}
<commit_msg>use unreachable!()<commit_after>pub struct Brackets {
    ok: bool,
    seen: Vec<char>,
}

impl<'a> From<&'a str> for Brackets {
    fn from(input: &str) -> Self {
        let mut b = Brackets {
            ok: true,
            seen: Vec::new(),
        };
        for c in input.chars() {
            match c {
                '[' | '{' | '(' => b.seen.push(c),
                ']' | '}' | ')' => {
                    b.ok = b.seen.pop().map_or(false, |x| match c {
                        ']' => x == '[',
                        '}' => x == '{',
                        ')' => x == '(',
                        _ => unreachable!("Not a bracket char"),
                    });
                }
                _ => (),
            }
            if !b.ok {
                break;
            }
        }
        b.ok &= b.seen.is_empty();
        b
    }
}

impl Brackets {
    pub fn are_balanced(&self) -> bool {
        self.ok
    }
}
<|endoftext|>