<commit_before>//! Geometric traits and primitives.
//!
//! These traits are used to support high-order operations in generators and
//! graphs. Implementing these traits implicitly implements internal traits,
//! which in turn enable geometric features.
//!
//! To use types as geometry in a `Mesh` only requires implementing the
//! `Geometry` and `Attribute` traits. Implementing operations like `Cross`,
//! `Normalize`, etc., enable features like extrusion and splitting.

use num::{self, Num, NumCast};

pub mod convert;
pub mod ops;

/// Geometric attribute.
pub trait Attribute: Clone {}

/// Graph geometry.
///
/// Specifies the types used to represent geometry for vertices, half-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, and operational traits mostly apply to such
/// positional data.
///
/// # Examples
///
/// ```rust
/// # extern crate nalgebra;
/// # extern crate plexus;
/// use nalgebra::{Point3, Vector4};
/// use plexus::geometry::convert::{AsPosition, IntoGeometry};
/// use plexus::geometry::{Attribute, Geometry};
/// use plexus::graph::Mesh;
/// use plexus::prelude::*;
/// use plexus::primitive::sphere::UvSphere;
/// use plexus::primitive::LruIndexer;
///
/// // Vertex-only geometry with a position and color.
/// #[derive(Clone, Copy, PartialEq, PartialOrd)]
/// pub struct VertexGeometry {
///     pub position: Point3<f32>,
///     pub color: Vector4<f32>,
/// }
///
/// impl Attribute for VertexGeometry {}
///
/// impl Geometry for VertexGeometry {
///     type Vertex = Self;
///     type Edge = ();
///     type Face = ();
/// }
///
/// impl AsPosition for VertexGeometry {
///     type Target = Point3<f32>;
///
///     fn as_position(&self) -> &Self::Target {
///         &self.position
///     }
///
///     fn as_position_mut(&mut self) -> &mut Self::Target {
///         &mut self.position
///     }
/// }
///
/// # fn main() {
/// // Create a mesh from a sphere primitive and map the geometry data.
/// let mut mesh = UvSphere::new(8, 8)
///     .polygons_with_position()
///     .map_vertices(|position| {
///         VertexGeometry {
///             position: position.into_geometry(),
///             color: Vector4::new(1.0, 1.0, 1.0, 1.0),
///         }
///     })
///     .collect_with_indexer::<Mesh<VertexGeometry>, _>(LruIndexer::with_capacity(64));
/// # }
/// ```
pub trait Geometry: Sized {
    type Vertex: Attribute;
    type Edge: Attribute + Default;
    type Face: Attribute + Default;
}

impl Attribute for () {}

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

/// Pair of values.
///
/// Provides basic vertex geometry and a grouping of values emitted by
/// generators. Conversions into commonly used types from commonly used
/// libraries are supported. See feature flags.
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Duplet<T>(pub T, pub T);

/// Triple of values.
///
/// Provides basic vertex geometry and a grouping of values emitted by
/// generators. Conversions into commonly used types from commonly used
/// libraries are supported. See feature flags.
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Triplet<T>(pub T, pub T, pub T);

impl<T> Attribute for Duplet<T> where T: Clone {}

impl<T> Attribute for Triplet<T> where T: Clone {}

impl<T> Geometry for Duplet<T>
where
    T: Clone,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

impl<T> Geometry for Triplet<T>
where
    T: Clone,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

pub fn lerp<T>(a: T, b: T, f: f64) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f64 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f64 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}

#[cfg(feature = "geometry-cgmath")]
mod feature_geometry_cgmath {
    use cgmath::{BaseNum, Point2, Point3, Vector2, Vector3};
    use num::{NumCast, ToPrimitive};

    use geometry::*;

    impl<T, U> From<Point2<U>> for Duplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Point2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> From<Vector2<U>> for Duplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Vector2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> Into<Point2<T>> for Duplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Point2<T> {
            Point2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> Into<Vector2<T>> for Duplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Vector2<T> {
            Vector2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> From<Point3<U>> for Triplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Point3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> From<Vector3<U>> for Triplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Vector3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> Into<Point3<T>> for Triplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Point3<T> {
            Point3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T, U> Into<Vector3<T>> for Triplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Vector3<T> {
            Vector3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T> Attribute for Point2<T> where T: Clone {}

    impl<T> Attribute for Point3<T> where T: Clone {}

    impl<T> Geometry for Point2<T>
    where
        T: Clone,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }

    impl<T> Geometry for Point3<T>
    where
        T: Clone,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }
}

#[cfg(feature = "geometry-nalgebra")]
mod feature_geometry_nalgebra {
    use nalgebra::{Point2, Point3, Scalar, Vector2, Vector3};
    use num::{NumCast, ToPrimitive};

    use geometry::*;

    impl<T, U> From<Point2<U>> for Duplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Point2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> From<Vector2<U>> for Duplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Vector2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> Into<Point2<T>> for Duplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Point2<T> {
            Point2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> Into<Vector2<T>> for Duplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Vector2<T> {
            Vector2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> From<Point3<U>> for Triplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Point3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> From<Vector3<U>> for Triplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Vector3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> Into<Point3<T>> for Triplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Point3<T> {
            Point3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T, U> Into<Vector3<T>> for Triplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Vector3<T> {
            Vector3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T> Attribute for Point2<T> where T: Scalar {}

    impl<T> Attribute for Point3<T> where T: Scalar {}

    impl<T> Geometry for Point2<T>
    where
        T: Scalar,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }

    impl<T> Geometry for Point3<T>
    where
        T: Scalar,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }
}
<commit_msg>Unwrap result in example code.<commit_after>//! Geometric traits and primitives.
//!
//! These traits are used to support high-order operations in generators and
//! graphs. Implementing these traits implicitly implements internal traits,
//! which in turn enable geometric features.
//!
//! To use types as geometry in a `Mesh` only requires implementing the
//! `Geometry` and `Attribute` traits. Implementing operations like `Cross`,
//! `Normalize`, etc., enable features like extrusion and splitting.

use num::{self, Num, NumCast};

pub mod convert;
pub mod ops;

/// Geometric attribute.
pub trait Attribute: Clone {}

/// Graph geometry.
///
/// Specifies the types used to represent geometry for vertices, half-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, and operational traits mostly apply to such
/// positional data.
///
/// # Examples
///
/// ```rust
/// # extern crate nalgebra;
/// # extern crate plexus;
/// use nalgebra::{Point3, Vector4};
/// use plexus::geometry::convert::{AsPosition, IntoGeometry};
/// use plexus::geometry::{Attribute, Geometry};
/// use plexus::graph::Mesh;
/// use plexus::prelude::*;
/// use plexus::primitive::sphere::UvSphere;
/// use plexus::primitive::LruIndexer;
///
/// // Vertex-only geometry with a position and color.
/// #[derive(Clone, Copy, PartialEq, PartialOrd)]
/// pub struct VertexGeometry {
///     pub position: Point3<f32>,
///     pub color: Vector4<f32>,
/// }
///
/// impl Attribute for VertexGeometry {}
///
/// impl Geometry for VertexGeometry {
///     type Vertex = Self;
///     type Edge = ();
///     type Face = ();
/// }
///
/// impl AsPosition for VertexGeometry {
///     type Target = Point3<f32>;
///
///     fn as_position(&self) -> &Self::Target {
///         &self.position
///     }
///
///     fn as_position_mut(&mut self) -> &mut Self::Target {
///         &mut self.position
///     }
/// }
///
/// # fn main() {
/// // Create a mesh from a sphere primitive and map the geometry data.
/// let mut mesh = UvSphere::new(8, 8)
///     .polygons_with_position()
///     .map_vertices(|position| {
///         VertexGeometry {
///             position: position.into_geometry(),
///             color: Vector4::new(1.0, 1.0, 1.0, 1.0),
///         }
///     })
///     .collect_with_indexer::<Mesh<VertexGeometry>, _>(LruIndexer::with_capacity(64))
///     .unwrap();
/// # }
/// ```
pub trait Geometry: Sized {
    type Vertex: Attribute;
    type Edge: Attribute + Default;
    type Face: Attribute + Default;
}

impl Attribute for () {}

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

/// Pair of values.
///
/// Provides basic vertex geometry and a grouping of values emitted by
/// generators. Conversions into commonly used types from commonly used
/// libraries are supported. See feature flags.
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Duplet<T>(pub T, pub T);

/// Triple of values.
///
/// Provides basic vertex geometry and a grouping of values emitted by
/// generators. Conversions into commonly used types from commonly used
/// libraries are supported. See feature flags.
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Triplet<T>(pub T, pub T, pub T);

impl<T> Attribute for Duplet<T> where T: Clone {}

impl<T> Attribute for Triplet<T> where T: Clone {}

impl<T> Geometry for Duplet<T>
where
    T: Clone,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

impl<T> Geometry for Triplet<T>
where
    T: Clone,
{
    type Vertex = Self;
    type Edge = ();
    type Face = ();
}

pub fn lerp<T>(a: T, b: T, f: f64) -> T
where
    T: Num + NumCast,
{
    let f = num::clamp(f, 0.0, 1.0);
    let af = <f64 as NumCast>::from(a).unwrap() * (1.0 - f);
    let bf = <f64 as NumCast>::from(b).unwrap() * f;
    <T as NumCast>::from(af + bf).unwrap()
}

#[cfg(feature = "geometry-cgmath")]
mod feature_geometry_cgmath {
    use cgmath::{BaseNum, Point2, Point3, Vector2, Vector3};
    use num::{NumCast, ToPrimitive};

    use geometry::*;

    impl<T, U> From<Point2<U>> for Duplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Point2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> From<Vector2<U>> for Duplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Vector2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> Into<Point2<T>> for Duplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Point2<T> {
            Point2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> Into<Vector2<T>> for Duplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Vector2<T> {
            Vector2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> From<Point3<U>> for Triplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Point3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> From<Vector3<U>> for Triplet<T>
    where
        T: NumCast,
        U: BaseNum + ToPrimitive,
    {
        fn from(other: Vector3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> Into<Point3<T>> for Triplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Point3<T> {
            Point3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T, U> Into<Vector3<T>> for Triplet<U>
    where
        T: BaseNum + NumCast,
        U: ToPrimitive,
    {
        fn into(self) -> Vector3<T> {
            Vector3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T> Attribute for Point2<T> where T: Clone {}

    impl<T> Attribute for Point3<T> where T: Clone {}

    impl<T> Geometry for Point2<T>
    where
        T: Clone,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }

    impl<T> Geometry for Point3<T>
    where
        T: Clone,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }
}

#[cfg(feature = "geometry-nalgebra")]
mod feature_geometry_nalgebra {
    use nalgebra::{Point2, Point3, Scalar, Vector2, Vector3};
    use num::{NumCast, ToPrimitive};

    use geometry::*;

    impl<T, U> From<Point2<U>> for Duplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Point2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> From<Vector2<U>> for Duplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Vector2<U>) -> Self {
            Duplet(T::from(other.x).unwrap(), T::from(other.y).unwrap())
        }
    }

    impl<T, U> Into<Point2<T>> for Duplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Point2<T> {
            Point2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> Into<Vector2<T>> for Duplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Vector2<T> {
            Vector2::new(T::from(self.0).unwrap(), T::from(self.1).unwrap())
        }
    }

    impl<T, U> From<Point3<U>> for Triplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Point3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> From<Vector3<U>> for Triplet<T>
    where
        T: NumCast,
        U: Scalar + ToPrimitive,
    {
        fn from(other: Vector3<U>) -> Self {
            Triplet(
                T::from(other.x).unwrap(),
                T::from(other.y).unwrap(),
                T::from(other.z).unwrap(),
            )
        }
    }

    impl<T, U> Into<Point3<T>> for Triplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Point3<T> {
            Point3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T, U> Into<Vector3<T>> for Triplet<U>
    where
        T: NumCast + Scalar,
        U: ToPrimitive,
    {
        fn into(self) -> Vector3<T> {
            Vector3::new(
                T::from(self.0).unwrap(),
                T::from(self.1).unwrap(),
                T::from(self.2).unwrap(),
            )
        }
    }

    impl<T> Attribute for Point2<T> where T: Scalar {}

    impl<T> Attribute for Point3<T> where T: Scalar {}

    impl<T> Geometry for Point2<T>
    where
        T: Scalar,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }

    impl<T> Geometry for Point3<T>
    where
        T: Scalar,
    {
        type Vertex = Self;
        type Edge = ();
        type Face = ();
    }
}
<|endoftext|>


<commit_before>use cgmath;
use cgmath::{Angle, Deg, Point2};
use num::{Num, NumCast};

use crate::bullet::BulletDrawable;
use crate::character::CharacterDrawable;
use crate::game::{constants::{RESOLUTION_Y, TERRAIN_OBJECTS, TILE_WIDTH, TILES_PCS_H, TILES_PCS_W, Y_OFFSET}, get_rand_from_range};
use crate::gfx_app::{mouse_controls::MouseInputState};
use crate::graphics::{dimensions::Dimensions, orientation::Orientation};
use crate::shaders::Position;
use crate::terrain_object::TerrainObjectDrawable;
use crate::zombie::ZombieDrawable;

pub mod camera;
pub mod dimensions;
mod graphics_test;
pub mod mesh;
pub mod orientation;
pub mod texture;

#[derive(Default)]
pub struct DeltaTime(pub f64);

#[derive(Default)]
pub struct GameTime(pub u64);

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2::new(point.x, RESOLUTION_Y as f32 - point.y)
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> f32 {
  let theta = Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let Deg(angle) = theta;
  if angle < 0.0 { (360.0 + angle) } else { angle }
}

pub fn direction_movement(direction: f32) -> Point2<f32> {
  let angle = Deg(direction);
  Point2::new(Angle::cos(angle), Angle::sin(angle))
}

pub fn direction_movement_180(movement_direction: Point2<f32>) -> Point2<f32> {
  let angle = Deg(direction(Point2::new(0.0, 0.0), movement_direction) + 180.0);
  Point2::new(Angle::cos(angle), Angle::sin(angle))
}

pub fn orientation_to_direction(angle_in_degrees: f32) -> Orientation {
  match angle_in_degrees as u32 {
    345...360 | 0...22 => Orientation::Right,
    23...68 => Orientation::UpRight,
    69...114 => Orientation::Up,
    115...160 => Orientation::UpLeft,
    161...206 => Orientation::Left,
    207...252 => Orientation::DownLeft,
    253...298 => Orientation::Down,
    299...344 => Orientation::DownRight,
    _ => unreachable!("Invalid orientation")
  }
}

pub fn get_orientation_from_center(mouse_input: &MouseInputState, dim: &Dimensions) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2::new(dim.window_width / 2.0 * dim.hidpi_factor, dim.window_height / 2.0 * dim.hidpi_factor);
    let dir = direction(start_point, flip_y_axel(end_point_gl));
    orientation_to_direction(dir)
  } else {
    Orientation::Right
  }
}

pub fn overlaps(area: Position, el: Position, width: f32, height: f32) -> bool {
  area.x() - width < el.x() &&
    area.x() + width > el.x() &&
    area.y() - height < el.y() &&
    area.y() + height > el.y()
}

pub fn is_within_map_borders(point: Point2<f32>) -> bool {
  point.x >= 0.0 && point.x < (TILES_PCS_W as f32 - 1f32) && point.y >= 0.0 && point.y < (TILES_PCS_H as f32 - 1f32)
}

pub fn can_move(screen_pos: Position) -> bool {
  let point = coords_to_tile(screen_pos);
  is_within_map_borders(Point2::new(point.x as f32, point.y as f32))
}

pub fn is_not_terrain_object<T>(pos: Point2<T>) -> bool
  where T: NumCast + Num, i32: std::cmp::PartialEq<T> {
  !TERRAIN_OBJECTS.iter().any(|e| (e[0] == pos.x) && (e[1] == pos.y))
}

fn is_map_tile(pos: Point2<i32>) -> bool {
  pos.x > 0 && pos.y > 0 && pos.x < (TILES_PCS_W - 1) as i32 && pos.y < (TILES_PCS_H - 1) as i32
}

pub fn can_move_to_tile(screen_pos: Position) -> bool {
  let tile_pos = coords_to_tile(screen_pos);
  is_not_terrain_object(tile_pos) && is_map_tile(tile_pos)
}

pub fn coords_to_tile(position: Position) -> Point2<i32> {
  let pos = Point2::new(-position.x(), position.y() + Y_OFFSET);
  Point2::new(((pos.x  + pos.y) / TILE_WIDTH) as i32, ((pos.y - pos.x) / TILE_WIDTH) as i32)
}

pub fn tile_to_coords(tile: Point2<i32>) -> Position {
  let new_tile = Point2::new(tile.x as f32, tile.y as f32);
  let x = round(new_tile.x * TILE_WIDTH - new_tile.y / TILE_WIDTH, 3);
  let y = round(new_tile.y * TILE_WIDTH - new_tile.x / TILE_WIDTH, 3);
  Position::new(-x, y - Y_OFFSET)
}

fn round(number: f32, precision: usize) -> f32 {
  let ten = 10.0f64;
  let divider = ten.powf(precision as f64) as f32;
  (number * divider).round() / divider
}

pub fn add_random_offset_to_screen_pos(pos: Position) -> Position {
  fn iter(pos: Position) -> Position {
    let offset = Position::new(get_rand_from_range(-2, 2) as f32, get_rand_from_range(-2, 2) as f32);
    let tile_width = TILE_WIDTH;
    let x = round(offset.x() * tile_width - offset.y() / tile_width, 3);
    let y = round(offset.y() * tile_width - offset.y() / tile_width, 3);
    let offset_point = Position::new(x, y);
    pos + offset_point
  }
  loop {
    let res = iter(pos);
    if can_move_to_tile(res) {
      return res;
    }
  }
}

pub fn calc_hypotenuse(a: f32, b: f32) -> f32 {
  (a.powf(2.0) + b.powf(2.0)).sqrt()
}

pub enum Drawables<'b> {
  Bullet(&'b BulletDrawable),
  Character(&'b mut CharacterDrawable),
  TerrainAmmo(&'b TerrainObjectDrawable),
  TerrainHouse(&'b TerrainObjectDrawable),
  TerrainTree(&'b TerrainObjectDrawable),
  Zombie(&'b mut ZombieDrawable),
}

impl<'b> Drawables<'b> {
  pub fn get_vertical_pos(drawable: &Drawables) -> f32 {
    match drawable {
      Drawables::Bullet(e) => e.position.y(),
      Drawables::Zombie(e) => e.position.y(),
      Drawables::TerrainAmmo(e) => e.position.y(),
      Drawables::TerrainHouse(e) => e.position.y(),
      Drawables::TerrainTree(e) => e.position.y(),
      Drawables::Character(e) => e.position.y(),
    }
  }
}
<commit_msg>Refactor graphics utilities<commit_after>use cgmath;
use cgmath::{Angle, Deg, Point2};
use num::{Num, NumCast};

use crate::bullet::BulletDrawable;
use crate::character::CharacterDrawable;
use crate::game::{constants::{RESOLUTION_Y, TERRAIN_OBJECTS, TILE_WIDTH, TILES_PCS_H, TILES_PCS_W, Y_OFFSET}, get_rand_from_range};
use crate::gfx_app::{mouse_controls::MouseInputState};
use crate::graphics::{dimensions::Dimensions, orientation::Orientation};
use crate::shaders::Position;
use crate::terrain_object::TerrainObjectDrawable;
use crate::zombie::ZombieDrawable;

pub mod camera;
pub mod dimensions;
mod graphics_test;
pub mod mesh;
pub mod orientation;
pub mod texture;

#[derive(Default)]
pub struct DeltaTime(pub f64);

#[derive(Default)]
pub struct GameTime(pub u64);

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2::new(point.x, RESOLUTION_Y as f32 - point.y)
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> f32 {
  let theta = Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let Deg(angle) = theta;
  if angle < 0.0 { (360.0 + angle) } else { angle }
}

pub fn direction_movement(direction: f32) -> Point2<f32> {
  let angle = Deg(direction);
  Point2::new(Angle::cos(angle), Angle::sin(angle))
}

pub fn direction_movement_180(movement_direction: Point2<f32>) -> Point2<f32> {
  let angle = Deg(direction(Point2::new(0.0, 0.0), movement_direction) + 180.0);
  Point2::new(Angle::cos(angle), Angle::sin(angle))
}

pub fn orientation_to_direction(angle_in_degrees: f32) -> Orientation {
  match angle_in_degrees as u32 {
    345...360 | 0...22 => Orientation::Right,
    23...68 => Orientation::UpRight,
    69...114 => Orientation::Up,
    115...160 => Orientation::UpLeft,
    161...206 => Orientation::Left,
    207...252 => Orientation::DownLeft,
    253...298 => Orientation::Down,
    299...344 => Orientation::DownRight,
    _ => unreachable!("Invalid orientation")
  }
}

pub fn get_orientation_from_center(mouse_input: &MouseInputState, dim: &Dimensions) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2::new(dim.window_width / 2.0 * dim.hidpi_factor, dim.window_height / 2.0 * dim.hidpi_factor);
    let dir = direction(start_point, flip_y_axel(end_point_gl));
    orientation_to_direction(dir)
  } else {
    Orientation::Right
  }
}

pub fn overlaps(area: Position, el: Position, width: f32, height: f32) -> bool {
  area.x() - width < el.x() &&
    area.x() + width > el.x() &&
    area.y() - height < el.y() &&
    area.y() + height > el.y()
}

pub fn is_within_map_borders(point: Point2<f32>) -> bool {
  point.x >= 0.0 && point.x < (TILES_PCS_W as f32 - 1f32) && point.y >= 0.0 && point.y < (TILES_PCS_H as f32 - 1f32)
}

pub fn can_move(screen_pos: Position) -> bool {
  let point = coords_to_tile(screen_pos);
  is_within_map_borders(Point2::new(point.x as f32, point.y as f32))
}

pub fn is_not_terrain_object<T>(pos: Point2<T>) -> bool
  where T: NumCast + Num, i32: std::cmp::PartialEq<T> {
  !TERRAIN_OBJECTS.iter().any(|e| (e[0] == pos.x) && (e[1] == pos.y))
}

fn is_map_tile(pos: Point2<i32>) -> bool {
  pos.x > 0 && pos.y > 0 && pos.x < (TILES_PCS_W - 1) as i32 && pos.y < (TILES_PCS_H - 1) as i32
}

pub fn can_move_to_tile(screen_pos: Position) -> bool {
  let tile_pos = coords_to_tile(screen_pos);
  is_not_terrain_object(tile_pos) && is_map_tile(tile_pos)
}

pub fn coords_to_tile(position: Position) -> Point2<i32> {
  let pos = Point2::new(-position.x(), position.y() + Y_OFFSET);
  Point2::new(((pos.x + pos.y) / TILE_WIDTH) as i32, ((pos.y - pos.x) / TILE_WIDTH) as i32)
}

pub fn tile_to_coords(tile: Point2<i32>) -> Position {
  let new_tile = Point2::new(tile.x as f32, tile.y as f32);
  let x = round(new_tile.x * TILE_WIDTH - new_tile.y / TILE_WIDTH, 3);
  let y = round(new_tile.y * TILE_WIDTH - new_tile.x / TILE_WIDTH, 3);
  Position::new(-x, y - Y_OFFSET)
}

fn round(number: f32, precision: usize) -> f32 {
  let ten = 10.0f64;
  let divider = ten.powf(precision as f64) as f32;
  (number * divider).round() / divider
}

pub fn add_random_offset_to_screen_pos(pos: Position) -> Position {
  fn iter(pos: Position) -> Position {
    let offset = Position::new(get_rand_from_range(-2, 2) as f32, get_rand_from_range(-2, 2) as f32);
    let offset_point = Position::new(
      round(offset.x() * TILE_WIDTH - offset.y() / TILE_WIDTH, 3),
      round(offset.y() * TILE_WIDTH - offset.y() / TILE_WIDTH, 3),
    );
    pos + offset_point
  }
  loop {
    let res = iter(pos);
    if can_move_to_tile(res) {
      return res;
    }
  }
}

pub fn calc_hypotenuse(a: f32, b: f32) -> f32 {
  (a.powf(2.0) + b.powf(2.0)).sqrt()
}

pub enum Drawables<'b> {
  Bullet(&'b BulletDrawable),
  Character(&'b mut CharacterDrawable),
  TerrainAmmo(&'b TerrainObjectDrawable),
  TerrainHouse(&'b TerrainObjectDrawable),
  TerrainTree(&'b TerrainObjectDrawable),
  Zombie(&'b mut ZombieDrawable),
}

impl<'b> Drawables<'b> {
  pub fn get_vertical_pos(drawable: &Drawables) -> f32 {
    match drawable {
      Drawables::Bullet(e) => e.position.y(),
      Drawables::Zombie(e) => e.position.y(),
      Drawables::TerrainAmmo(e) => e.position.y(),
      Drawables::TerrainHouse(e) => e.position.y(),
      Drawables::TerrainTree(e) => e.position.y(),
      Drawables::Character(e) => e.position.y(),
    }
  }
}
<|endoftext|>


<commit_before>use fragment::matching::AsStr;
use std::string::ToString;
use std::path::PathBuf;

/// This Path + String pair exists so that we can build the path's string
/// representation once when populating the index, rather than on each search.
/// It's also how we support case insensitive indexing, where we need to store
/// a lowercased search value without modifying the case-sensitive PathBuf.
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct IndexedPath {
    pub path: PathBuf,
    pub path_string: String,
}

impl AsStr for IndexedPath {
    fn as_str(&self) -> &str {
        &self.path_string
    }
}

impl IndexedPath {
    pub fn new<T: ToString>(path: T, case_sensitive: bool) -> IndexedPath {
        let mut path_string = path.to_string();
        let path = PathBuf::from(&path_string);

        if !case_sensitive {
            path_string = path_string.to_lowercase();
        }

        IndexedPath{
            path: path,
            path_string: path_string
        }
    }
}

#[cfg(test)]
mod tests {
    use fragment::matching::AsStr;
    use std::path::PathBuf;
    use super::IndexedPath;

    #[test]
    fn new_lowercases_str_representation_when_case_sensitive_is_false() {
        let indexed_path = IndexedPath::new("Cargo.toml", false);
        assert_eq!(indexed_path.as_str(), "cargo.toml");
    }

    #[test]
    fn new_leaves_str_representation_as_is_when_case_sensitive_is_true() {
        let indexed_path = IndexedPath::new("Cargo.toml", true);
        assert_eq!(indexed_path.as_str(), "Cargo.toml");
    }

    #[test]
    fn new_leaves_path_as_is_regardless_of_case_sensitivity() {
        let indexed_path = IndexedPath::new("Cargo.toml", false);
        let case_sensitive_indexed_path = IndexedPath::new("Cargo.toml", true);

        assert_eq!(indexed_path.path, PathBuf::from("Cargo.toml"));
        assert_eq!(case_sensitive_indexed_path.path, PathBuf::from("Cargo.toml"));
    }
}
<commit_msg>Make IndexedPath fields private, exposing path w/ accessor<commit_after>use fragment::matching::AsStr;
use std::string::ToString;
use std::path::{Path, PathBuf};

/// This Path + String pair exists so that we can build the path's string
/// representation once when populating the index, rather than on each search.
/// It's also how we support case insensitive indexing, where we need to store
/// a lowercased search value without modifying the case-sensitive PathBuf.
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct IndexedPath {
    path: PathBuf,
    path_string: String,
}

impl AsStr for IndexedPath {
    fn as_str(&self) -> &str {
        &self.path_string
    }
}

impl IndexedPath {
    pub fn new<T: ToString>(path: T, case_sensitive: bool) -> IndexedPath {
        let mut path_string = path.to_string();
        let path = PathBuf::from(&path_string);

        if !case_sensitive {
            path_string = path_string.to_lowercase();
        }

        IndexedPath{
            path: path,
            path_string: path_string
        }
    }

    pub fn as_path(&self) -> &Path {
        self.path.as_path()
    }
}

#[cfg(test)]
mod tests {
    use fragment::matching::AsStr;
    use std::path::PathBuf;
    use super::IndexedPath;

    #[test]
    fn new_lowercases_str_representation_when_case_sensitive_is_false() {
        let indexed_path = IndexedPath::new("Cargo.toml", false);
        assert_eq!(indexed_path.as_str(), "cargo.toml");
    }

    #[test]
    fn new_leaves_str_representation_as_is_when_case_sensitive_is_true() {
        let indexed_path = IndexedPath::new("Cargo.toml", true);
        assert_eq!(indexed_path.as_str(), "Cargo.toml");
    }

    #[test]
    fn new_leaves_path_as_is_regardless_of_case_sensitivity() {
        let indexed_path = IndexedPath::new("Cargo.toml", false);
        let case_sensitive_indexed_path = IndexedPath::new("Cargo.toml", true);

        assert_eq!(indexed_path.path, PathBuf::from("Cargo.toml"));
        assert_eq!(case_sensitive_indexed_path.path, PathBuf::from("Cargo.toml"));
    }
}
<|endoftext|>


<commit_before>use unicode_width::UnicodeWidthChar;
use rustbox::RustBox;

use uibuf::UIBuffer;
use keyboard::Key;

/// State for the overlay
pub enum OverlayEvent {
    Finished(Option<String>),
    Ok,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum OverlayType {
    Prompt,
    SelectFile,
}

/// An interface for user interaction
///
/// This can be a prompt, autocompletion list, anything thatn requires input
/// from the user.
pub enum Overlay {
    Prompt {
        cursor_x: usize,
        data: String,
        prefix: &'static str,
    },

    SavePrompt {
        cursor_x: usize,
        data: String,
        prefix: &'static str,
    },

    SelectFile {
        cursor_x: usize,
        data: String,
        prefix: &'static str,
    },

    None,
}

impl Overlay {
    pub fn draw(&self, rb: &mut RustBox, uibuf: &mut UIBuffer) {
        match *self {
            Overlay::SelectFile     {prefix, ref data, ..} |
            Overlay::Prompt         {prefix, ref data, ..} |
            Overlay::SavePrompt     {prefix, ref data, ..} => {
                let height = rb.height() - 1;
                let offset = prefix.len();

                // draw the given prefix
                for (index, ch) in prefix.chars().enumerate() {
                    uibuf.update_cell_content(index, height, ch);
                }

                // draw the overlay data
                for (index, ch) in data.chars().enumerate() {
                    uibuf.update_cell_content(index + offset, height, ch);
                }

                uibuf.draw_range(rb, height, height+1);
            }

            _ => {}
        }
    }

    pub fn draw_cursor(&mut self, rb: &mut RustBox) {
        match *self {
            Overlay::SelectFile     {cursor_x, ..} |
            Overlay::Prompt         {cursor_x, ..} |
            Overlay::SavePrompt     {cursor_x, ..} => {
                // Prompt is always on the bottom, so we can use the
                // height given by the frontend here
                let height = rb.height() - 1;
                rb.set_cursor(cursor_x as isize, height as isize)
            },

            _ => {}
        }
    }

    pub fn handle_key_event(&mut self, key: Key) -> OverlayEvent {
        match *self {
            Overlay::SelectFile {ref mut cursor_x, ref mut data, ..} |
            Overlay::Prompt     {ref mut cursor_x, ref mut data, ..} |
            Overlay::SavePrompt {ref mut cursor_x, ref mut data, ..} => {
                match key {
                    Key::Esc => return OverlayEvent::Finished(None),
                    Key::Backspace => {
                        if let Some(c) = data.pop() {
                            if let Some(width) = UnicodeWidthChar::width(c) {
                                *cursor_x -= width;
                            }
                        }
                    }
                    Key::Enter => {
                        // FIXME: dont clone
                        let data = data.clone();
                        return OverlayEvent::Finished(Some(data))
                    }
                    Key::Char(c) => {
                        if let Some(width) = UnicodeWidthChar::width(c) {
                            data.push(c);
                            *cursor_x += width;
                        }
                    }
                    _ => {}
                }
            }

            _ => {}
        }
        OverlayEvent::Ok
    }
}
<commit_msg>Removed UIBuffer references from overlay.rs<commit_after>use unicode_width::UnicodeWidthChar;
use rustbox::{Style, Color, RustBox};

use keyboard::Key;

/// State for the overlay
pub enum OverlayEvent {
    Finished(Option<String>),
    Ok,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum OverlayType {
    Prompt,
    SelectFile,
}

/// An interface for user interaction
///
/// This can be a prompt, autocompletion list, anything thatn requires input
/// from the user.
pub enum Overlay {
    Prompt {
        cursor_x: usize,
        data: String,
        prefix: &'static str,
    },

    SavePrompt {
        cursor_x: usize,
        data: String,
        prefix: &'static str,
    },

    SelectFile {
        cursor_x: usize,
        data: String,
        prefix: &'static str,
    },

    None,
}

impl Overlay {
    pub fn draw(&self, rb: &mut RustBox) {
        match *self {
            Overlay::SelectFile     {prefix, ref data, ..} |
            Overlay::Prompt         {prefix, ref data, ..} |
            Overlay::SavePrompt     {prefix, ref data, ..} => {
                let height = rb.height() - 1;
                let offset = prefix.len();

                // draw the given prefix
                for (index, ch) in prefix.chars().enumerate() {
                    rb.print_char(index, height, Style::empty(), Color::White, Color::Black, ch);
                }

                // draw the overlay data
                for (index, ch) in data.chars().enumerate() {
                    rb.print_char(index + offset, height, Style::empty(), Color::White, Color::Black, ch);
                }
            }

            _ => {}
        }
    }

    pub fn draw_cursor(&mut self, rb: &mut RustBox) {
        match *self {
            Overlay::SelectFile     {cursor_x, ..} |
            Overlay::Prompt         {cursor_x, ..} |
            Overlay::SavePrompt     {cursor_x, ..} => {
                // Prompt is always on the bottom, so we can use the
                // height given by the frontend here
                let height = rb.height() - 1;
                rb.set_cursor(cursor_x as isize, height as isize)
            },

            _ => {}
        }
    }

    pub fn handle_key_event(&mut self, key: Key) -> OverlayEvent {
        match *self {
            Overlay::SelectFile {ref mut cursor_x, ref mut data, ..} |
            Overlay::Prompt     {ref mut cursor_x, ref mut data, ..} |
            Overlay::SavePrompt {ref mut cursor_x, ref mut data, ..} => {
                match key {
                    Key::Esc => return OverlayEvent::Finished(None),
                    Key::Backspace => {
                        if let Some(c) = data.pop() {
                            if let Some(width) = UnicodeWidthChar::width(c) {
                                *cursor_x -= width;
                            }
                        }
                    }
                    Key::Enter => {
                        // FIXME: dont clone
                        let data = data.clone();
                        return OverlayEvent::Finished(Some(data))
                    }
                    Key::Char(c) => {
                        if let Some(width) = UnicodeWidthChar::width(c) {
                            data.push(c);
                            *cursor_x += width;
                        }
                    }
                    _ => {}
                }
            }

            _ => {}
        }
        OverlayEvent::Ok
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use multi_objective::MultiObjective;
use std::f32;

#[derive(Debug)]
pub struct SolutionRankDist {
    pub rank: u32,
    pub dist: f32,
    pub idx: usize,
}

impl PartialEq for SolutionRankDist {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.rank == other.rank && self.dist == other.dist
    }
}

// Implement the crowding-distance comparison operator.
impl PartialOrd for SolutionRankDist {
    #[inline]
    // compare on rank first (ASC), then on dist (DESC)
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.rank.partial_cmp(&other.rank) {
            Some(Ordering::Equal) => {
                // first criterion equal, second criterion decides
                // reverse ordering
                self.dist.partial_cmp(&other.dist).map(|i| i.reverse())
            }
            other => other,
        }
    }
}

pub fn crowding_distance_assignment<P: MultiObjective>(solutions: &[P],
                                                       common_rank: u32,
                                                       individuals_idx: &[usize],
                                                       num_objectives: usize)
                                                       -> Vec<SolutionRankDist> {
    assert!(num_objectives > 0);

    let mut s: Vec<_> = individuals_idx.iter()
                                       .map(|&i| {
                                           SolutionRankDist {
                                               rank: common_rank,
                                               dist: 0.0,
                                               idx: i,
                                           }
                                       })
                                       .collect();
    let l = s.len();

    for m in 0..num_objectives {
        // sort using objective `m`
        s.sort_by(|a, b| solutions[a.idx].cmp_objective(&solutions[b.idx], m));

        // assign infinite crowding distance to edges.
        s[0].dist = f32::INFINITY;
        s[l - 1].dist = f32::INFINITY;

        let min_idx = s[0].idx;
        let max_idx = s[l - 1].idx;

        let dist_max_min = solutions[max_idx].dist_objective(&solutions[min_idx], m);
        if dist_max_min != 0.0 {
            let norm = num_objectives as f32 * dist_max_min;
            debug_assert!(norm != 0.0);
            for i in 1..(l - 1) {
                let next_idx = s[i + 1].idx;
                let prev_idx = s[i - 1].idx;
                let dist = solutions[next_idx].dist_objective(&solutions[prev_idx], m);
                debug_assert!(dist >= 0.0);
                s[i].dist += dist / norm;
            }
        }
    }

    return s;
}
<commit_msg>Fix bug in crowding distance calculation caught by a debug_assert!<commit_after>use std::cmp::Ordering;
use multi_objective::MultiObjective;
use std::f32;

#[derive(Debug)]
pub struct SolutionRankDist {
    pub rank: u32,
    pub dist: f32,
    pub idx: usize,
}

impl PartialEq for SolutionRankDist {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.rank == other.rank && self.dist == other.dist
    }
}

// Implement the crowding-distance comparison operator.
impl PartialOrd for SolutionRankDist {
    #[inline]
    // compare on rank first (ASC), then on dist (DESC)
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.rank.partial_cmp(&other.rank) {
            Some(Ordering::Equal) => {
                // first criterion equal, second criterion decides
                // reverse ordering
                self.dist.partial_cmp(&other.dist).map(|i| i.reverse())
            }
            other => other,
        }
    }
}

pub fn crowding_distance_assignment<P: MultiObjective>(solutions: &[P],
                                                       common_rank: u32,
                                                       individuals_idx: &[usize],
                                                       num_objectives: usize)
                                                       -> Vec<SolutionRankDist> {
    assert!(num_objectives > 0);

    let mut s: Vec<_> = individuals_idx.iter()
                                       .map(|&i| {
                                           SolutionRankDist {
                                               rank: common_rank,
                                               dist: 0.0,
                                               idx: i,
                                           }
                                       })
                                       .collect();
    let l = s.len();

    for m in 0..num_objectives {
        // sort using objective `m`
        s.sort_by(|a, b| solutions[a.idx].cmp_objective(&solutions[b.idx], m));

        // assign infinite crowding distance to edges.
        s[0].dist = f32::INFINITY;
        s[l - 1].dist = f32::INFINITY;

        let min_idx = s[0].idx;
        let max_idx = s[l - 1].idx;

        let dist_max_min = solutions[max_idx].dist_objective(&solutions[min_idx], m);
        if dist_max_min != 0.0 {
            let norm = num_objectives as f32 * dist_max_min;
            debug_assert!(norm != 0.0);
            for i in 1..(l - 1) {
                let next_idx = s[i + 1].idx;
                let prev_idx = s[i - 1].idx;
                let dist = solutions[next_idx].dist_objective(&solutions[prev_idx], m).abs();
                debug_assert!(dist >= 0.0);
                s[i].dist += dist / norm;
            }
        }
    }

    return s;
}
<|endoftext|>


<commit_before>// #get
// #set
// #peek
// #new
// #clear
//has

// Doubly linked list
// struct Node <V> {
//     prev: Option<Box<Node<V>>>,
//     next: Option<Box<Node<V>>>,
//     val: V,
// }

// pub struct LRU <K, V> {
//     cache: HashMap<K, Node<V>>,
//     head: Option<Node<V>>,
//     tail: Option<Node<V>>,
//     len: u16,
// }

// impl <K: Eq + std::hash::Hash + Copy, V> LRU <K, V> {
//     pub fn new() -> Self {
//         LRU {
//             cache: HashMap::new(),
//             head: None,
//             tail: None,
//             len: 0
//         }
//     }
//     pub fn set(&mut self, key: &K, val: V) -> &mut LRU<K, V>{
//         use std::collections::hash_map::Entry;

//         match self.cache.get_mut(K).take() {
//             None => None,
//             Some(node) => {
//                 let node = *node;
//                 self.head =
//             }
//         }
//         self
//     }
// }

struct Node <V> {
    next: Option<Box<Node<V>>>,
    val: V,
}

struct LinkedList <V> {
    size: u16,
    head: Option<Node<V>>,

}

impl <V> LinkedList <V> {
    pub fn new() -> Self {
        LinkedList {
            size: 0,
            head: None,
        }
    }
    pub fn push(&mut self, val: V) -> &mut Self {
        let added_node = Node {
            val: val,
            next: self.head.take().map(|x| Box::new(x))
        };

        self.head = Some(added_node);
        self
    }
    pub fn pop(&mut self) -> Option<V> {
        //we need to pull out the 'next' value from the head node
        self.head.take().map(|node| {
            self.head = node.next.map(|x| *x);
            node.val
        })
    }
    pub fn peek(&self) -> Option<&V> {
        self.head.as_ref().map(|x| &x.val)
    }
    pub fn peek_mut(&mut self) -> Option<&mut V> {
        self.head.as_mut().map(|x| &mut x.val)
    }
}

impl <V> Drop for LinkedList <V> {
    fn drop(&mut self) {
        let mut cur_node = self.head.take();
        while let Some(mut node) = cur_node {
            cur_node = node.next.take().map(|x| *x);
        }
    }
}

pub struct IntoIter<T>(LinkedList<T>);

mod test {
    use super::LinkedList;
    #[test]
    fn basics() {
        let mut list = LinkedList::new();

        // Check empty list behaves right
        assert_eq!(list.pop(), None);

        // Populate list
        list.push(1);
        list.push(2);
        list.push(3);

        // Check normal removal
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push(4);
        list.push(5);

        // Check normal removal
        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), None);
    }

    #[test]
    fn peek() {
        let mut list = LinkedList::new();
        assert_eq!(list.peek(), None);
        assert_eq!(list.peek_mut(), None);
        list.push(1); list.push(2); list.push(3);

        assert_eq!(list.peek(), Some(&3));
        assert_eq!(list.peek_mut(), Some(&mut 3));
    }
}
<commit_msg>iterator implementation<commit_after>
struct Node <V> {
    next: Option<Box<Node<V>>>,
    val: V,
}

struct LinkedList <V> {
    size: u16,
    head: Option<Node<V>>,

}

impl <V> LinkedList <V> {
    pub fn new() -> Self {
        LinkedList {
            size: 0,
            head: None,
        }
    }
    pub fn push(&mut self, val: V) -> &mut Self {
        let added_node = Node {
            val: val,
            next: self.head.take().map(|x| Box::new(x))
        };

        self.head = Some(added_node);
        self
    }
    pub fn pop(&mut self) -> Option<V> {
        //we need to pull out the 'next' value from the head node
        self.head.take().map(|node| {
            self.head = node.next.map(|x| *x);
            node.val
        })
    }
    pub fn peek(&self) -> Option<&V> {
        self.head.as_ref().map(|x| &x.val)
    }
    pub fn peek_mut(&mut self) -> Option<&mut V> {
        self.head.as_mut().map(|x| &mut x.val)
    }

    // Iterator
    pub fn into_iter(self) -> IntoIter<V> {
        IntoIter(self)
    }
}

impl <V> Drop for LinkedList <V> {
    fn drop(&mut self) {
        let mut cur_node = self.head.take();
        while let Some(mut node) = cur_node {
            cur_node = node.next.take().map(|x| *x);
        }
    }
}

pub struct IntoIter<T>(LinkedList<T>);

impl<T> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.pop()
    }
}

// Break

pub struct Iter<'a, T: 'a> {
    next: Option<&'a Node<T>>,
}

impl<T> LinkedList<T> {
    pub fn iter<'a>(&'a self) -> Iter<'a, T> {
        Iter {
            next: self.head.as_ref().map(|node| node)
        }
    }
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|node| {
            self.next = node.next.as_ref().map(|node| &**node);
            &node.val
        })
    }
}

mod test {
    use super::LinkedList;
    #[test]
    fn basics() {
        let mut list = LinkedList::new();

        // Check empty list behaves right
        assert_eq!(list.pop(), None);

        // Populate list
        list.push(1);
        list.push(2);
        list.push(3);

        // Check normal removal
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push(4);
        list.push(5);

        // Check normal removal
        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), None);
    }

    #[test]
    fn peek() {
        let mut list = LinkedList::new();
        assert_eq!(list.peek(), None);
        assert_eq!(list.peek_mut(), None);
        list.push(1); list.push(2); list.push(3);

        assert_eq!(list.peek(), Some(&3));
        assert_eq!(list.peek_mut(), Some(&mut 3));
    }

    #[test]
    fn into_iter() {
        let mut list = LinkedList::new();
        list.push(1); list.push(2); list.push(3);

        let mut iter = list.into_iter();
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(1));
    }

    #[test]
    fn iter() {
        let mut list = LinkedList::new();
        list.push(1); list.push(2); list.push(3);

        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(&3));
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next(), Some(&1));
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::de::from_dynamic;
use rhai::ser::to_dynamic;
use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::Value as Json;

pub struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &Vec<PathAndJson<'reg, 'rc>>,
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
    let params: Dynamic = to_dynamic(params.iter().map(|p| p.value()).collect::<Vec<&Json>>())?;

    let hash: Dynamic = to_dynamic(
        hash.iter()
            .map(|(k, v)| ((*k).to_owned(), v.value()))
            .collect::<HashMap<String, &Json>>(),
    )?;

    let mut scope = Scope::new();
    scope.push_dynamic("params", params);
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    let result_json: Json = from_dynamic(&result)?;

    Ok(Some(ScopedJson::Derived(result_json)))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = to_dynamic(&j0).unwrap();
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = to_dynamic(&j1).unwrap();
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_to_json() {
        let d0 = Dynamic::from("tomcat".to_owned());

        assert_eq!(
            Json::String("tomcat".to_owned()),
            from_dynamic::<Json>(&d0).unwrap()
        );
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<commit_msg>(fix) clippy warnings on scripting feature<commit_after>use std::collections::{BTreeMap, HashMap};

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::de::from_dynamic;
use rhai::ser::to_dynamic;
use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::Value as Json;

pub struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &[PathAndJson<'reg, 'rc>],
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
    let params: Dynamic = to_dynamic(params.iter().map(|p| p.value()).collect::<Vec<&Json>>())?;

    let hash: Dynamic = to_dynamic(
        hash.iter()
            .map(|(k, v)| ((*k).to_owned(), v.value()))
            .collect::<HashMap<String, &Json>>(),
    )?;

    let mut scope = Scope::new();
    scope.push_dynamic("params", params);
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    let result_json: Json = from_dynamic(&result)?;

    Ok(Some(ScopedJson::Derived(result_json)))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = to_dynamic(&j0).unwrap();
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = to_dynamic(&j1).unwrap();
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_to_json() {
        let d0 = Dynamic::from("tomcat".to_owned());

        assert_eq!(
            Json::String("tomcat".to_owned()),
            from_dynamic::<Json>(&d0).unwrap()
        );
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<|endoftext|>


<commit_before>fn main()
{
    println!("Hello, world!");
}
<commit_msg>Remove superfluous main.rs from the sample<commit_after><|endoftext|>


<commit_before>use crate::reflect::EnumDescriptor;
use crate::reflect::EnumValueDescriptor;
use crate::Enum;

/// Trait is implemented for all enum types if lite runtime is not requested.
///
/// This trait provides access to runtime reflection.
pub trait EnumFull: Enum {
    /// Get enum value descriptor.
    fn descriptor(&self) -> EnumValueDescriptor {
        // TODO: multiple enum variants may have the same number.
        self.enum_descriptor()
            .value_by_number(self.value())
            .unwrap()
    }

    /// Get enum descriptor.
    fn enum_descriptor(&self) -> EnumDescriptor {
        Self::enum_descriptor_static()
    }

    /// Get enum descriptor by type.
    fn enum_descriptor_static() -> EnumDescriptor;
}
<commit_msg>Minor EnumFull refactoring<commit_after>use crate::reflect::EnumDescriptor;
use crate::reflect::EnumValueDescriptor;
use crate::Enum;

/// Trait is implemented for all enum types if lite runtime is not requested.
///
/// This trait provides access to runtime reflection.
pub trait EnumFull: Enum {
    /// Get enum value descriptor.
    fn descriptor(&self) -> EnumValueDescriptor {
        // TODO: multiple enum variants may have the same number.
        Self::enum_descriptor_static()
            .value_by_number(self.value())
            .unwrap()
    }

    /// Get enum descriptor.
    fn enum_descriptor(&self) -> EnumDescriptor {
        Self::enum_descriptor_static()
    }

    /// Get enum descriptor by type.
    fn enum_descriptor_static() -> EnumDescriptor;
}
<|endoftext|>


<commit_before>extern crate wm_daemons;
use wm_daemons::config::{load_config, load_config_path};
use wm_daemons::dbus_listen::{CallbackMap, DBusInfo, match_signal};
use wm_daemons::exec::{CommandLine, read_command_line_from_config, run_command_line};

#[macro_use]
extern crate clap;
use clap::{Arg, App};

extern crate dbus;
use self::dbus::{Connection, BusType, Message};

use std::env::var;
use std::error::Error;
use std::path::Path;

struct Context {
    on_lock: Option<CommandLine>,
    on_unlock: Option<CommandLine>,
}

fn run_program(action: &str, cmd_line: &Option<CommandLine>) -> () {
    cmd_line.as_ref().map(|ref cmd| {
        let res = run_command_line(&cmd);
        res.unwrap_or_else(|err| {
            println!("failed to handle '{}' action: {}", action, err);
        });
    });
}

fn handle_signal(ctx: Context, info: &Message) -> Context {
    let (_, _, _, member) = info.headers();

    member.map(|member_name| {
        if member_name == "Lock" {
            run_program("lock", &ctx.on_lock);
        } else if member_name == "Unlock" {
            run_program("unlock", &ctx.on_unlock);
        }
    });

    ctx
}

fn try_main() -> Result<(), Box<Error>> {
    let matches = App::new("wm-session-agent")
        .version(&crate_version!()[..])
        .author("Ben Boeckel <mathstuf@gmail.com>")
        .about("Listens for logind's Lock and Unlock signals")
        .arg(Arg::with_name("CONFIG")
                .short("c")
                .long("config")
                .help("Path to the configuration file")
                .takes_value(true))
        .arg(Arg::with_name("SESSION")
                .short("s")
                .long("session")
                .help("Session ID to listen on")
                .takes_value(true))
        .get_matches();

    let conf = try!(match matches.value_of("CONFIG") {
        Some(path) => load_config_path(Path::new(path)),
        None => load_config("wm-session-agent", "config"),
    });
    let ctx = Context {
        on_lock: read_command_line_from_config(&conf, "actions.lock"),
        on_unlock: read_command_line_from_config(&conf, "actions.unlock"),
    };

    let sid = match matches.value_of("SESSION") {
        Some(sid) => sid.to_string(),
        None => match var("XDG_SESSION_ID") {
            Ok(sid) => sid,
            Err(err) => {
                println!("No session ID given or available: {}", err);
                return Err(Box::new(err));
            },
        },
    };
    let spath = format!("/org/freedesktop/login1/session/_{}", sid);

    let conn = try!(Connection::get_private(BusType::System));

    let cbs: CallbackMap<Context> = vec![
        (DBusInfo {
            path: None,
            object: None,
            member: Some("Lock".to_string()),
        }, handle_signal),
        (DBusInfo {
            path: None,
            object: None,
            member: Some("Unlock".to_string()),
        }, handle_signal),
    ];

    let match_str = format!("type='signal',interface='org.freedesktop.login1.Session',path='{}'", spath);
    try!(conn.add_match(&match_str[..]));

    conn.iter(100).fold(ctx, |inner_ctx, item| {
        match_signal(inner_ctx, &cbs, item)
    });

    Ok(())
}

fn main() -> () {
    if let Err(err) = try_main() {
        panic!("{}", err.description());
    }
}
<commit_msg>wm-session-agent: remove last 'unwrap' usage<commit_after>extern crate wm_daemons;
use wm_daemons::config::{load_config, load_config_path};
use wm_daemons::dbus_listen::{CallbackMap, DBusInfo, match_signal};
use wm_daemons::exec::{CommandLine, read_command_line_from_config, run_command_line};

#[macro_use]
extern crate clap;
use clap::{Arg, App};

extern crate dbus;
use self::dbus::{Connection, BusType, Message};

use std::env::var;
use std::error::Error;
use std::path::Path;

struct Context {
    on_lock: Option<CommandLine>,
    on_unlock: Option<CommandLine>,
}

fn run_program(action: &str, cmd_line: &Option<CommandLine>) -> () {
    cmd_line.as_ref().map(|ref cmd| {
        let _ = run_command_line(&cmd).map_err(|err| {
                println!("failed to handle '{}' action: {}", action, err);
            });
    });
}

fn handle_signal(ctx: Context, info: &Message) -> Context {
    let (_, _, _, member) = info.headers();

    member.map(|member_name| {
        if member_name == "Lock" {
            run_program("lock", &ctx.on_lock);
        } else if member_name == "Unlock" {
            run_program("unlock", &ctx.on_unlock);
        }
    });

    ctx
}

fn try_main() -> Result<(), Box<Error>> {
    let matches = App::new("wm-session-agent")
        .version(&crate_version!()[..])
        .author("Ben Boeckel <mathstuf@gmail.com>")
        .about("Listens for logind's Lock and Unlock signals")
        .arg(Arg::with_name("CONFIG")
                .short("c")
                .long("config")
                .help("Path to the configuration file")
                .takes_value(true))
        .arg(Arg::with_name("SESSION")
                .short("s")
                .long("session")
                .help("Session ID to listen on")
                .takes_value(true))
        .get_matches();

    let conf = try!(match matches.value_of("CONFIG") {
        Some(path) => load_config_path(Path::new(path)),
        None => load_config("wm-session-agent", "config"),
    });
    let ctx = Context {
        on_lock: read_command_line_from_config(&conf, "actions.lock"),
        on_unlock: read_command_line_from_config(&conf, "actions.unlock"),
    };

    let sid = match matches.value_of("SESSION") {
        Some(sid) => sid.to_string(),
        None => match var("XDG_SESSION_ID") {
            Ok(sid) => sid,
            Err(err) => {
                println!("No session ID given or available: {}", err);
                return Err(Box::new(err));
            },
        },
    };
    let spath = format!("/org/freedesktop/login1/session/_{}", sid);

    let conn = try!(Connection::get_private(BusType::System));

    let cbs: CallbackMap<Context> = vec![
        (DBusInfo {
            path: None,
            object: None,
            member: Some("Lock".to_string()),
        }, handle_signal),
        (DBusInfo {
            path: None,
            object: None,
            member: Some("Unlock".to_string()),
        }, handle_signal),
    ];

    let match_str = format!("type='signal',interface='org.freedesktop.login1.Session',path='{}'", spath);
    try!(conn.add_match(&match_str[..]));

    conn.iter(100).fold(ctx, |inner_ctx, item| {
        match_signal(inner_ctx, &cbs, item)
    });

    Ok(())
}

fn main() -> () {
    if let Err(err) = try_main() {
        panic!("{}", err.description());
    }
}
<|endoftext|>


<commit_before>extern crate libedgedns;
extern crate nix;
extern crate regex;
extern crate tempfile;

#[cfg(test)]
mod test {
    extern crate env_logger;
    use libedgedns::{Config, EdgeDNS};

    use nix::sys::signal::{kill, SIGKILL};
    use nix::sys::ioctl::libc::pid_t;
    use nix::unistd::{fork, read, ForkResult, dup2};
    use nix::sys::ioctl::libc::alarm;
    use nix::sys::socket::{socketpair, AddressFamily, SockType, SockFlag};

    use regex::Regex;

    use std::env;
    use std::io::Write;
    use std::process::{exit, Output, Command, ExitStatus};
    use std::os::unix::io::RawFd;
    use std::os::unix::process::CommandExt;
    use std::string::String;
    use std::time::Duration;

    use tempfile::NamedTempFile;

    struct EdgeDNSInstance {
        server: Server,
        udp_ports: Vec<u16>,
        tcp_ports: Vec<u16>,
    }

    struct Server {
        pid: pid_t,
        startup_text: String,
        output: RawFd,
    }
    impl Server {
            pub fn new() -> Server {
                Server{pid: 0, startup_text: String::from(""), output: 0}
            }
    }

    impl Drop for Server {
        fn drop(&mut self) {
            if self.pid != 0 {
                kill(self.pid, SIGKILL).expect("kill failed")
            }
        }
    }

    fn spawn_server<F1, F2>(child_fn: F1, mut is_ready: F2, timeout: Duration) -> Server
        where F1: Fn() -> (),
              F2: FnMut(&str, pid_t) -> bool
    {

        let mut server = Server {
            pid: 0,
            output: 0,
            startup_text: String::new(),
        };
        let (fdc, fdp) = socketpair(AddressFamily::Unix, SockType::Stream, 0, SockFlag::empty())
            .expect("socketpair");
        match fork().expect("fork failed") {
            ForkResult::Parent { child } => {
                server.pid = child;
                server.output = fdp;

                /* FIXME: a non-unsafe way to do this */
                unsafe {
                    alarm(timeout.as_secs() as u32);
                }
                loop {
                    let mut buf = [0; 1];
                    let res = read(fdp, &mut buf);
                    if res.is_err() {
                        break;
                    }
                    let res = res.unwrap();
                    if res > 0 {
                        server.startup_text.push(buf[0] as char);
                    }
                    if is_ready(&server.startup_text, child) {
                        break;
                    }
                }
                unsafe {
                    alarm(0);
                }
            }
            ForkResult::Child => {
                dup2(fdc, 1).expect("dup2 failed");
                dup2(fdc, 2).expect("dup2 failed");
                child_fn();
                println!("The child exited");
                exit(0);
            }
        }
        server
    }

    fn spawn_edgedns(cfg_str: &str) -> EdgeDNSInstance {
        let mut ret = EdgeDNSInstance {
            udp_ports: Vec::new(),
            tcp_ports: Vec::new(),
            server: spawn_server(|| {
                                     env::set_var("RUST_LOG", "info");
                                     env_logger::init().expect("Failed to init logger");
                                     let config = Config::from_string(cfg_str);
                                     assert!(config.is_ok());
                                     EdgeDNS::new(config.unwrap());
                                 },
                                 |out, _| {
                                     out.contains("UDP listener is ready") &&
                                     out.contains("TCP listener is ready")
                                 },
                                 Duration::new(5, 0)),
        };

        for proto in ["TCP", "UDP"].iter() {
            let re = Regex::new(&format!(r"Created a {} socket: (\d+)", proto)).unwrap();
            for cap in re.captures_iter(&ret.server.startup_text) {
                let port = cap[1].parse::<u16>().unwrap();
                if *proto == "UDP" {
                    ret.udp_ports.push(port);
                } else {
                    ret.tcp_ports.push(port);
                }
            }
        }

        ret
    }

    struct CoreDNS {
        server: Server,
        udp_port: u16,
    }

    fn spawn_coredns(domain: &str, zone_str: &str) -> CoreDNS {
        let mut conf_file = NamedTempFile::new().unwrap();
        let mut zone_file = NamedTempFile::new().unwrap();
        zone_file.write_all(zone_str.as_bytes());
        let zfile_path = zone_file.path().to_str().unwrap();
        let conf_str = format!(r#"
{}:0 {{
    file {}
    errors stdout
    log stdout
}}
"#, domain, zfile_path);
        conf_file.write_all(conf_str.as_bytes());
        let cfile_path = conf_file.path().to_str().unwrap();
        let mut ret = CoreDNS {
            udp_port : 0,
            server: Server::new(),
        };
        ret.server = spawn_server(|| {
                let cmd = Command::new("coredns")
                    .args(&["-log", "-dns.port", "0", "-conf", cfile_path])
                    .exec();
                ::std::process::exit(1);
            },
            |out, pid| {
                if !out.contains("CoreDNS-008") {
                    return false;
                }
                let lsof = Command::new("lsof")
                    .arg("-p")
                    .arg(&format!("{}", pid))
                    .output();
                let lsof = String::from_utf8(lsof.unwrap().stdout).unwrap();

                let re = Regex::new(r"0t0\s+UDP \*:(\d+)").unwrap();
                let mut udp_port: u16 = 0;
                for cap in re.captures_iter(&lsof) {
                    udp_port = cap[1].parse::<u16>().unwrap();
                }
                ret.udp_port = udp_port;
                true
            },
            Duration::new(5, 0));
        ret
    }

    enum Qprotocol {
        UDP,
        TCP,
    }
    struct CmdOutput {
        stdout: String,
        stderr: String,
        status: ExitStatus,
    }
    fn dig(query: &str, proto: Qprotocol, server: &str, port: u16) -> CmdOutput {
        let server = format!("@{}", server);
        let port = format!("{}", port);
        let mut args = vec![query, &server, "-p", &port];
        match proto {
            Qprotocol::TCP => args.push("+tcp"),
            _ => (),
        }
        let output = Command::new("dig").args(args).output().unwrap();
        let mut stdout = String::new();
        let mut stderr = String::new();
        match String::from_utf8(output.stdout) {
            Err(_) => (),
            Ok(s) => stdout = s,
        }
        match String::from_utf8(output.stderr) {
            Err(_) => (),
            Ok(s) => stderr = s,
        }
        CmdOutput {
            stdout: stdout,
            stderr: stderr,
            status: output.status,
        }
    }

    /* tests */
    #[test]
    fn coredns_test() {
        let coredns = spawn_coredns("example.com", r#"
$ORIGIN example.com.     ; designates the start of this zone file in the namespace
$TTL 1h                  ; default expiration time of all resource records without their own TTL value
example.com.  IN  SOA   ns.example.com. username.example.com. ( 2007120710 1d 2h 4w 1h )
example.com.  IN  NS    ns                    ; ns.example.com is a nameserver for example.com
example.com.  IN  NS    ns.somewhere.example. ; ns.somewhere.example is a backup nameserver for example.com
example.com.  IN  MX    10 mail.example.com.  ; mail.example.com is the mailserver for example.com
@             IN  MX    20 mail2.example.com. ; equivalent to above line, "@" represents zone origin
@             IN  MX    50 mail3              ; equivalent to above line, but using a relative host name
example.com.  IN  A     192.0.2.1             ; IPv4 address for example.com
IN  AAAA  2001:db8:10::1        ; IPv6 address for example.com
ns            IN  A     192.0.2.2             ; IPv4 address for ns.example.com
IN  AAAA  2001:db8:10::2        ; IPv6 address for ns.example.com
www           IN  CNAME example.com.          ; www.example.com is an alias for example.com
wwwtest       IN  CNAME www                   ; wwwtest.example.com is another alias for www.example.com
mail          IN  A     192.0.2.3             ; IPv4 address for mail.example.com
mail2         IN  A     192.0.2.4             ; IPv4 address for mail2.example.com
mail3         IN  A     192.0.2.5             ; IPv4 address for mail3.example.com
"#);
        let cfg = format!(r#"
[upstream]
servers = ["127.0.0.1:{}"]
[network]
listen = "127.0.0.1:0"
udp_ports = 1
[global]
threads_udp = 1
threads_tcp = 1
"#, coredns.udp_port);
        let server = spawn_edgedns(&cfg);
        let re = Regex::new(r"\n;; ANSWER SECTION:\nmail.example.com.\s+\d+\s+IN\s+A\s+192.0.2.3").unwrap();
        for port in &server.udp_ports {
            let output = dig("mail.example.com", Qprotocol::UDP, "127.0.0.1", *port).stdout;
            assert!(re.is_match(&output));
        }
        for port in &server.tcp_ports {
            let output = dig("127.0.0.1.xip.io", Qprotocol::TCP, "127.0.0.1", *port).stdout;
            println!("{}", output);
            assert!(re.is_match(&output));
        }
    }

    #[test]
    fn empty_config() {
        let cfg = r#"
[upstream]
servers = ["8.8.8.8:53"]
[network]
listen = "127.0.0.1:0"
udp_ports = 1
[global]
threads_udp = 1
threads_tcp = 1
"#;
        let server = spawn_edgedns(&cfg);
    }

    #[test]
    fn simple_dig_query() {
        let cfg = r#"
[upstream]
servers = ["8.8.8.8:53"]
[network]
listen = "127.0.0.1:0"
udp_ports = 1
[global]
threads_udp = 1
threads_tcp = 1
"#;
        let server = spawn_edgedns(&cfg);
        let re = Regex::new(r"\n;; ANSWER SECTION:\n127.0.0.1.xip.io.\s+\d+\s+IN\s+A\s+127.0.0.1")
            .unwrap();
        for port in &server.udp_ports {
            let output = dig("127.0.0.1.xip.io", Qprotocol::UDP, "127.0.0.1", *port).stdout;
            assert!(re.is_match(&output));
        }
        for port in &server.tcp_ports {
            let output = dig("127.0.0.1.xip.io", Qprotocol::TCP, "127.0.0.1", *port).stdout;
            assert!(re.is_match(&output));
        }
    }
}
<commit_msg>Fix warnings, remove test hitting 8.8.8.8<commit_after>extern crate libedgedns;
extern crate nix;
extern crate regex;
extern crate tempfile;

#[cfg(test)]
mod test {
    extern crate env_logger;
    use libedgedns::{Config, EdgeDNS};

    use nix::sys::signal::{kill, SIGKILL};
    use nix::sys::ioctl::libc::pid_t;
    use nix::unistd::{fork, read, ForkResult, dup2};
    use nix::sys::ioctl::libc::alarm;
    use nix::sys::socket::{socketpair, AddressFamily, SockType, SockFlag};

    use regex::Regex;

    use std::env;
    use std::io::Write;
    use std::process::{exit, Command, ExitStatus};
    use std::os::unix::io::RawFd;
    use std::os::unix::process::CommandExt;
    use std::string::String;
    use std::time::Duration;

    use tempfile::NamedTempFile;

    struct EdgeDNSInstance {
        server: Server,
        udp_ports: Vec<u16>,
        tcp_ports: Vec<u16>,
    }

    struct Server {
        pid: pid_t,
        startup_text: String,
        output: RawFd,
    }
    impl Server {
            pub fn new() -> Server {
                Server{pid: 0, startup_text: String::from(""), output: 0}
            }
    }

    impl Drop for Server {
        fn drop(&mut self) {
            if self.pid != 0 {
                kill(self.pid, SIGKILL).expect("kill failed")
            }
        }
    }

    fn spawn_server<F1, F2>(child_fn: F1, mut is_ready: F2, timeout: Duration) -> Server
        where F1: Fn() -> (),
              F2: FnMut(&str, pid_t) -> bool
    {

        let mut server = Server {
            pid: 0,
            output: 0,
            startup_text: String::new(),
        };
        let (fdc, fdp) = socketpair(AddressFamily::Unix, SockType::Stream, 0, SockFlag::empty())
            .expect("socketpair");
        match fork().expect("fork failed") {
            ForkResult::Parent { child } => {
                server.pid = child;
                server.output = fdp;

                /* FIXME: a non-unsafe way to do this */
                unsafe {
                    alarm(timeout.as_secs() as u32);
                }
                loop {
                    let mut buf = [0; 1];
                    let res = read(fdp, &mut buf);
                    if res.is_err() {
                        break;
                    }
                    let res = res.unwrap();
                    if res > 0 {
                        server.startup_text.push(buf[0] as char);
                    }
                    if is_ready(&server.startup_text, child) {
                        break;
                    }
                }
                unsafe {
                    alarm(0);
                }
            }
            ForkResult::Child => {
                dup2(fdc, 1).expect("dup2 failed");
                dup2(fdc, 2).expect("dup2 failed");
                child_fn();
                println!("The child exited");
                exit(0);
            }
        }
        server
    }

    fn spawn_edgedns(cfg_str: &str) -> EdgeDNSInstance {
        let mut ret = EdgeDNSInstance {
            udp_ports: Vec::new(),
            tcp_ports: Vec::new(),
            server: spawn_server(|| {
                                     env::set_var("RUST_LOG", "info");
                                     env_logger::init().expect("Failed to init logger");
                                     let config = Config::from_string(cfg_str);
                                     assert!(config.is_ok());
                                     EdgeDNS::new(config.unwrap());
                                 },
                                 |out, _| {
                                     out.contains("UDP listener is ready") &&
                                     out.contains("TCP listener is ready")
                                 },
                                 Duration::new(5, 0)),
        };

        for proto in ["TCP", "UDP"].iter() {
            let re = Regex::new(&format!(r"Created a {} socket: (\d+)", proto)).unwrap();
            for cap in re.captures_iter(&ret.server.startup_text) {
                let port = cap[1].parse::<u16>().unwrap();
                if *proto == "UDP" {
                    ret.udp_ports.push(port);
                } else {
                    ret.tcp_ports.push(port);
                }
            }
        }

        ret
    }

    struct CoreDNS {
        server: Server,
        udp_port: u16,
    }

    fn spawn_coredns(domain: &str, zone_str: &str) -> CoreDNS {
        let mut conf_file = NamedTempFile::new().unwrap();
        let mut zone_file = NamedTempFile::new().unwrap();
        zone_file.write_all(zone_str.as_bytes()).expect("write_all failed");
        let zfile_path = zone_file.path().to_str().unwrap();
        let conf_str = format!(r#"
{}:0 {{
    file {}
    errors stdout
    log stdout
}}
"#, domain, zfile_path);
        conf_file.write_all(conf_str.as_bytes()).expect("write_all failed");
        let cfile_path = conf_file.path().to_str().unwrap();
        let mut ret = CoreDNS {
            udp_port : 0,
            server: Server::new(),
        };
        ret.server = spawn_server(|| {
                Command::new("coredns")
                    .args(&["-log", "-dns.port", "0", "-conf", cfile_path])
                    .exec();
                ::std::process::exit(1);
            },
            |out, pid| {
                if !out.contains("CoreDNS-008") {
                    return false;
                }
                let lsof = Command::new("lsof")
                    .arg("-p")
                    .arg(&format!("{}", pid))
                    .output();
                let lsof = String::from_utf8(lsof.unwrap().stdout).unwrap();

                let re = Regex::new(r"0t0\s+UDP \*:(\d+)").unwrap();
                let mut udp_port: u16 = 0;
                for cap in re.captures_iter(&lsof) {
                    udp_port = cap[1].parse::<u16>().unwrap();
                }
                ret.udp_port = udp_port;
                true
            },
            Duration::new(5, 0));
        ret
    }

    enum Qprotocol {
        UDP,
        TCP,
    }
    struct CmdOutput {
        stdout: String,
        #[allow(dead_code)]
        stderr: String,
        #[allow(dead_code)]
        status: ExitStatus,
    }
    fn dig(query: &str, proto: Qprotocol, server: &str, port: u16) -> CmdOutput {
        let server = format!("@{}", server);
        let port = format!("{}", port);
        let mut args = vec![query, &server, "-p", &port];
        match proto {
            Qprotocol::TCP => args.push("+tcp"),
            _ => (),
        }
        let output = Command::new("dig").args(args).output().unwrap();
        let mut stdout = String::new();
        let mut stderr = String::new();
        match String::from_utf8(output.stdout) {
            Err(_) => (),
            Ok(s) => stdout = s,
        }
        match String::from_utf8(output.stderr) {
            Err(_) => (),
            Ok(s) => stderr = s,
        }
        CmdOutput {
            stdout: stdout,
            stderr: stderr,
            status: output.status,
        }
    }

    static EXAMPLE_DOT_COM_ZONE : &'static str = r#"
$ORIGIN example.com.     ; designates the start of this zone file in the namespace
$TTL 1h                  ; default expiration time of all resource records without their own TTL value
example.com.  IN  SOA   ns.example.com. username.example.com. ( 2007120710 1d 2h 4w 1h )
example.com.  IN  NS    ns                    ; ns.example.com is a nameserver for example.com
example.com.  IN  NS    ns.somewhere.example. ; ns.somewhere.example is a backup nameserver for example.com
example.com.  IN  MX    10 mail.example.com.  ; mail.example.com is the mailserver for example.com
@             IN  MX    20 mail2.example.com. ; equivalent to above line, "@" represents zone origin
@             IN  MX    50 mail3              ; equivalent to above line, but using a relative host name
example.com.  IN  A     192.0.2.1             ; IPv4 address for example.com
IN  AAAA  2001:db8:10::1        ; IPv6 address for example.com
ns            IN  A     192.0.2.2             ; IPv4 address for ns.example.com
IN  AAAA  2001:db8:10::2        ; IPv6 address for ns.example.com
www           IN  CNAME example.com.          ; www.example.com is an alias for example.com
wwwtest       IN  CNAME www                   ; wwwtest.example.com is another alias for www.example.com
mail          IN  A     192.0.2.3             ; IPv4 address for mail.example.com
mail2         IN  A     192.0.2.4             ; IPv4 address for mail2.example.com
mail3         IN  A     192.0.2.5             ; IPv4 address for mail3.example.com
"#;

    /* tests */
    #[test]
    fn coredns_test() {
        let coredns = spawn_coredns("example.com", EXAMPLE_DOT_COM_ZONE);
        let cfg = format!(r#"
[upstream]
servers = ["127.0.0.1:{}"]
[network]
listen = "127.0.0.1:0"
"#, coredns.udp_port);
        let server = spawn_edgedns(&cfg);
        let re = Regex::new(r"\n;; ANSWER SECTION:\nmail.example.com.\s+\d+\s+IN\s+A\s+192.0.2.3").unwrap();
        for port in &server.udp_ports {
            let output = dig("mail.example.com", Qprotocol::UDP, "127.0.0.1", *port).stdout;
            assert!(re.is_match(&output));
        }
        for port in &server.tcp_ports {
            let output = dig("mail.example.com", Qprotocol::TCP, "127.0.0.1", *port).stdout;
            println!("{}", output);
            assert!(re.is_match(&output));
        }
    }

    #[test]
    fn empty_config() {
        let cfg = r#"
[upstream]
servers = ["127.0.0.1:53"]
[network]
listen = "127.0.0.1:0"
udp_ports = 1
[global]
threads_udp = 1
threads_tcp = 1
"#;
        spawn_edgedns(&cfg);
    }
}
<|endoftext|>