<commit_before>//! This module contains all the OpenGEX structures as specified. The module tries to stay as close
//! to the OpenGEX structures as possible.
//!
//! Some of the documentation might be subjected to copyright by Eric Lengyel. For more detailed
//! documentation, please go to http://opengex.org.

use std::collections::HashMap;
use std::sync::Arc;

/// The Material structure contains information about a material. Material structures are
/// referenced by geometry nodes through MaterialRef structures belonging to GeometryNode
/// structures.
pub struct Material {
    /// Whether the material is two-sided.
    pub two_sided: bool,
    /// An optional name.
    pub name: Option<Name>,
    /// Any number of colors.
    pub color: HashMap<String, Color>,
    /// Any number of parameters.
    pub param: ParamMap,
    /// Any number of textures.
    pub texture: HashMap<String, Texture>,
}

/// A Color structure must contain an RGB or RGBA color value.
pub enum Color {
    /// An RGB color value.
    Rgb(f32, f32, f32),
    /// An RGBA color value.
    Rgba(f32, f32, f32, f32)
}

/// The Param structure contains just a float. While the OpenGEX specification defines a Param as a
/// key-value pair, this is better implemented through a HashMap. See ParamMap.
pub type Param = f32;

/// This is a map of different Param structures, for convenience.
pub type ParamMap = HashMap<String, Param>;
/// The Name structure holds the name of a node, morph target, material, or animation clip.
pub type Name = String;

/// The Texture structure holds information about a single texture map, and how it is accessed with
/// texture coordinates.
pub struct Texture {
    /// The index of the texture coordinate set associated with the texture.
    pub texcoord: u32,
    /// A substructure holding the file name of the texture.
    pub file_name: String,
    /// Any number of transformations that are applied to the texture coordinates of a mesh when
    /// they are used to fetch from the texture map.
    pub transformations: Vec<Transformation>,
    /// Any number of animation tracks that are applied to the texture coordinate transformations.
    pub animation: Vec<Animation>
}

/// Helper enum to contain all different kinds of Transformations.
pub enum Transformation {
    /// A Transform structure.
    Transform(Transform),
    /// A Translation structure.
    Translation(Translation),
    /// A Rotation structure.
    Rotation(Rotation),
    /// A Scale structure.
    Scale(Scale)
}

/// The Transform structure holds one or more 4 x 4 transformation matrices. In the cases that a
/// Transform structure is contained inside any type of node structure, a Texture structure, or a
/// Skin structure, it must contain a single matrix. In the case that a Transform structure is
/// contained inside a Skeleton structure, is must contain an array of matrices with one entry for
/// each bone referenced by the skeleton.
///
/// When contained inside a node structure, a Transform structure can be the target of a track
/// stored inside an Animation structure.
pub struct Transform([f32; 16]);

/// The Translation structure holds a translation transformation in one of several possible
/// variants.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Translation structure can be the target of a track
/// stored inside an Animation structure.
pub enum Translation {
    /// The translation occurs along only the X axis.
    X(f32),
    /// The translation occurs along only the Y axis.
    Y(f32),
    /// The translation occurs along only the Z axis.
    Z(f32),
    /// The translation occurs along all three coordinate axes.
    Xyz(f32, f32, f32)
}

/// The Rotation structure represents a rotation along one of several axes.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Rotation structure can be the target of a track
/// stored in an Animation structure.
pub enum Rotation {
    /// The rotation occurs about the X axis.
    X(f32),
    /// The rotation occurs about the Y axis.
    Y(f32),
    /// The rotation occurs about the Z axis.
    Z(f32),
    /// The rotation occurs about an arbitrary axis. The first entry of this structure is the angle
    /// of rotation. The remaining three entries are respectively the X, Y and Z components of the
    /// axis of rotation.
    Axis(f32, f32, f32, f32),
    /// The rotation is given by a quaternion. Please refer to the official OpenGEX documentation
    /// for more information.
    Quaternion(f32, f32, f32, f32)
}

/// The Scale structure represents a scale transformation in one of several possible variants.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Scale structure can be the target of a strack stored
/// inside an Animation structure.
pub enum Scale {
    /// The scaling occurs along only the X axis.
    X(f32),
    /// The scaling occurs along only the Y axis.
    Y(f32),
    /// The scaling occurs along only the Z axis.
    Z(f32),
    /// The scaling occurs along all three coordinate axes.
    Xyz(f32, f32, f32)
}

/// The Animation structure contains animation data for a single node in a scene. Each animation
/// structure is directly contained inside a node structure or Texture structure. Each animation
/// structure contains the data needed to modify its sibling Transformation structures or sibling
/// MorphWeight structures over time.
///
/// More detailed information can be found in the official OpenGEX specification.
pub struct Animation {
    /// Specifies the animation clip index.
    pub clip: u32,
    /// Specifies when the animation begins. If the property is not specifies, the begin time for
    /// the animation is determined by the earliest time values present in the Track structures
    /// belonging to this Animation.
    pub begin: Option<f32>,
    /// Specifies when the animation ends. Like with the begin property, if the property is not
    /// specified, the end time for the animation is determined by the latest time values present
    /// in the Track structures belonging to this Animation.
    pub end: Option<f32>,
    /// One or more tracks that each hold animation keys for a single target.
    pub tracks: Vec<Track>,
}

/// The Track structure contains animation key data for a single Transformation or MorphWeight
/// structure.
pub struct Track {
    /// The target transformation or MorphWeight this track applies to.
    pub target: TrackTarget,
    /// The time curve associated with this track.
    pub time: Time,
    /// The value curve associated with this track.
    pub value: Value
}

/// Enum wrapping over all possible animation track targets.
pub enum TrackTarget {
    /// A Transformation structure. See enum to see possibilities.
    Transformation(Arc<Transformation>),
    /// A MorphWeight structure.
    MorphWeight(Arc<MorphWeight>)
}

/// The Time structure contains key time data in an animation track.
///
/// There are two different kinds of this structure; one for every curve kind.
///
/// The variants in this enum contain vectors. One vector item represents on key time.
pub enum Time {
    /// The times are interpolated linearly.
    Linear(Vec<f32>),
    /// The times are interpolated on a one-dimensional Bézier curve.
    ///
    /// The times in each tuple in the vector are the "value", "-control" and "+control" values of
    /// the Bézier curve respectively.
    Bézier(Vec<(f32, f32, f32)>)
}

/// The Value structure contains key value data in an animation track.
///
/// There are two different kinds of this structure; one for every curve kind.
///
/// The variants in this enum contain vectors. One vector item represents on key value.
pub enum Value {
    /// The values are not interpolated, but remain constant until the next key time.
    Constant(Vec<f32>),
    /// The values are interpolated linearly.
    Linear(Vec<f32>),
    /// The values are interpolated on a one-dimensional Bézier curve.
    ///
    /// The values in each tuple in the vector are the "value", "-control" and "+control" values of
    /// the Bézier curve respectively.
    Bézier(Vec<(f32, f32, f32)>),
    /// The values are interpolated on a tension-continuity-bias (TCB) spline.
    ///
    /// The values in each tuple in the vector are the "value", "tension", "bias" and "continuity"
    /// values of the TCB spline respecitvely. The data contained in these last three values are
    /// always scalar.
    Tcb(Vec<(f32, f32, f32, f32)>)
}

/// A MorphWeight structure holds a single morph weight for a GeometryNode structure, that
/// references a GeometryObject strucure containing vertex data for multiple morph targets.
///
/// A MorphWeight structure can be the target of a track stored inside an Animation structure.
pub struct MorphWeight {
    /// Specifies the morph target index to which this morph weight applies. If the GeometryObject
    /// structure contains no vertex data corresponding to this target index, then this structure
    /// should be ignored. Each MorphWeight structure belonging to any particular GeometryNode
    /// structure must have a unique target index among all morph weights belonging to that
    /// GeometryNode.
    pub target_index: u32,
    /// The weight this MorphWeight structure represents.
    pub weight: f32,
}

/// The LightObject struture contains data for a light object. Multiple LightNode structures may
/// reference a single LightObject. This allows a scene to contain multiple instances of the same
/// light, with different transformations.
pub struct LightObject {
    /// The type of light emitted by this LightObject.
    pub light_type: LightType,
    /// Whether this LightObject casts shadows. This can be overiden by the LightNode referencing
    /// this LightObject.
    pub cast_shadow: bool,
    /// The colors associated with this LightObject.
    ///
    /// The OpenGEX specification only references one type of color: "light". This is the main
    /// color of light emitted by the light souArce. This defaults to an RGB value of
    /// (1.0, 1.0, 1.0).
    ///
    /// There can be any other number of colors in this HashMap, for application-specific kinds.
    pub colors: HashMap<String, Color>,
    /// The parameters associated with this LightObject.
    ///
    /// The OpenGEX specification only references one type of parameter: "intensity". This is the
    /// intensity of the light emitted by the light souArce. This defaults to 0.
    ///
    /// There can be any other number of parameters in this HashMap, for application-specific
    /// kinds.
    pub params: ParamMap,
    /// The textures associated with this LightObject.
    ///
    /// The OpenGEX specification only references one type of texture: "projection". This is an
    /// optional texture projection of this LightObject. The texture map should be oriented so that
    /// the right direction is aligned to the object-space positive x-axis, and the up direction is
    /// aligned to the object-space positive y-axis.
    ///
    /// There can be any other number of textures in this HashMap, for application-specific kinds.
    pub textures: HashMap<String, Texture>,
    /// Any number of attenuation functions to be applied to the LightObject. The values produced
    /// by all of them are multiplied together to determine the intensity of the light reaching
    /// any particular point in space.
    pub attenuations: Atten
}

/// This is an helper-enum representing all different types of lights that a LightObject can emit.
pub enum LightType {
    /// The light souArce is to be treated as if it were infinitely far away so its rays are
    /// parallel. In object space, the rays point in the direction of the negative z-axis.
    Infinite,
    /// The lght souArce is a point light that radiates in all directions.
    Point,
    /// The light source is a spot light that radiates from a single points byt in a limited range
    /// of directions In object space, the primary direction is the negative z-axis.
    Spot
}

/// The Atten structure specifies an attenuation function for a light object.
pub struct Atten {
    /// The kind of attenuation.
    pub kind: AttenuationKind,
    /// The type of curve defining the attenuation.
    pub curve: AttenuationCurve,
    /// Any parameters associated with this Atten structure.
    ///
    /// For the meaning of the parameters, please refer to the official OpenGEX documentation.
    /// There can exist application-defined parameters.
    pub params: ParamMap
}

/// A helper enum representing different kinds of attenuation functions.
pub enum AttenuationKind {
    /// The input to the attenuation function is the radial distance from the LightObject the
    /// parent Atten structure is associated with.
    Distance,
    /// The input to the attenuation function is the angle formed between the negative z-axis and
    /// the direction to the point being illuminated in object space.
    ///
    /// The result of this function should be raised to the power value of a potential "power"
    /// paramter present in the parent Atten structure.
    Angle,
    /// The input ot the attenuation function is the cosine of the angle formed between the
    /// negative z-axis and the direction to the point being illuminated in objet space.
    ///
    /// The result of this function should be raised to the power value of a potential "power"
    /// paramter present in the parent Atten structure.
    CosAngle
}

/// A helper enum representing different kinds of curves for an attenuation function.
///
/// For exact formulas, please refer to the offical OpenGEX documentation.
pub enum AttenuationCurve {
    /// The attenuation is a linear function.
    Linear,
    /// The attenuation is given by a cubic-smooth-step function.
    Cubic,
    /// The attenuation is given by the inverse function.
    Inverse,
    /// The attenuation is given by the inverse square function.
    InverseSquare
}
<commit_msg>Implemented all the different nodes.<commit_after>//! This module contains all the OpenGEX structures as specified. The module tries to stay as close
//! to the OpenGEX structures as possible.
//!
//! Some of the documentation might be subjected to copyright by Eric Lengyel. For more detailed
//! documentation, please go to http://opengex.org.

use std::collections::HashMap;
use std::sync::Arc;

/// The Material structure contains information about a material. Material structures are
/// referenced by geometry nodes through MaterialRef structures belonging to GeometryNode
/// structures.
pub struct Material {
    /// Whether the material is two-sided.
    pub two_sided: bool,
    /// An optional name.
    pub name: Option<Name>,
    /// Any number of colors.
    pub color: HashMap<String, Color>,
    /// Any number of parameters.
    pub param: ParamMap,
    /// Any number of textures.
    pub texture: HashMap<String, Texture>,
}

/// A Color structure must contain an RGB or RGBA color value.
pub enum Color {
    /// An RGB color value.
    Rgb(f32, f32, f32),
    /// An RGBA color value.
    Rgba(f32, f32, f32, f32)
}

/// The Param structure contains just a float. While the OpenGEX specification defines a Param as a
/// key-value pair, this is better implemented through a HashMap. See ParamMap.
pub type Param = f32;

/// This is a map of different Param structures, for convenience.
pub type ParamMap = HashMap<String, Param>;
/// The Name structure holds the name of a node, morph target, material, or animation clip.
pub type Name = String;

/// The Texture structure holds information about a single texture map, and how it is accessed with
/// texture coordinates.
pub struct Texture {
    /// The index of the texture coordinate set associated with the texture.
    pub texcoord: u32,
    /// A substructure holding the file name of the texture.
    pub file_name: String,
    /// Any number of transformations that are applied to the texture coordinates of a mesh when
    /// they are used to fetch from the texture map.
    pub transformations: Vec<Transformation>,
    /// Any number of animation tracks that are applied to the texture coordinate transformations.
    pub animation: Vec<Animation>
}

/// Helper enum to contain all different kinds of Transformations.
pub enum Transformation {
    /// A Transform structure.
    Transform(Transform),
    /// A Translation structure.
    Translation(Translation),
    /// A Rotation structure.
    Rotation(Rotation),
    /// A Scale structure.
    Scale(Scale)
}

/// The Transform structure holds one or more 4 x 4 transformation matrices. In the cases that a
/// Transform structure is contained inside any type of node structure, a Texture structure, or a
/// Skin structure, it must contain a single matrix. In the case that a Transform structure is
/// contained inside a Skeleton structure, is must contain an array of matrices with one entry for
/// each bone referenced by the skeleton.
///
/// When contained inside a node structure, a Transform structure can be the target of a track
/// stored inside an Animation structure.
pub struct Transform([f32; 16]);

/// The Translation structure holds a translation transformation in one of several possible
/// variants.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Translation structure can be the target of a track
/// stored inside an Animation structure.
pub enum Translation {
    /// The translation occurs along only the X axis.
    X(f32),
    /// The translation occurs along only the Y axis.
    Y(f32),
    /// The translation occurs along only the Z axis.
    Z(f32),
    /// The translation occurs along all three coordinate axes.
    Xyz(f32, f32, f32)
}

/// The Rotation structure represents a rotation along one of several axes.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Rotation structure can be the target of a track
/// stored in an Animation structure.
pub enum Rotation {
    /// The rotation occurs about the X axis.
    X(f32),
    /// The rotation occurs about the Y axis.
    Y(f32),
    /// The rotation occurs about the Z axis.
    Z(f32),
    /// The rotation occurs about an arbitrary axis. The first entry of this structure is the angle
    /// of rotation. The remaining three entries are respectively the X, Y and Z components of the
    /// axis of rotation.
    Axis(f32, f32, f32, f32),
    /// The rotation is given by a quaternion. Please refer to the official OpenGEX documentation
    /// for more information.
    Quaternion(f32, f32, f32, f32)
}

/// The Scale structure represents a scale transformation in one of several possible variants.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Scale structure can be the target of a strack stored
/// inside an Animation structure.
pub enum Scale {
    /// The scaling occurs along only the X axis.
    X(f32),
    /// The scaling occurs along only the Y axis.
    Y(f32),
    /// The scaling occurs along only the Z axis.
    Z(f32),
    /// The scaling occurs along all three coordinate axes.
    Xyz(f32, f32, f32)
}

/// The Animation structure contains animation data for a single node in a scene. Each animation
/// structure is directly contained inside a node structure or Texture structure. Each animation
/// structure contains the data needed to modify its sibling Transformation structures or sibling
/// MorphWeight structures over time.
///
/// More detailed information can be found in the official OpenGEX specification.
pub struct Animation {
    /// Specifies the animation clip index.
    pub clip: u32,
    /// Specifies when the animation begins. If the property is not specifies, the begin time for
    /// the animation is determined by the earliest time values present in the Track structures
    /// belonging to this Animation.
    pub begin: Option<f32>,
    /// Specifies when the animation ends. Like with the begin property, if the property is not
    /// specified, the end time for the animation is determined by the latest time values present
    /// in the Track structures belonging to this Animation.
    pub end: Option<f32>,
    /// One or more tracks that each hold animation keys for a single target.
    pub tracks: Vec<Track>,
}

/// The Track structure contains animation key data for a single Transformation or MorphWeight
/// structure.
pub struct Track {
    /// The target transformation or MorphWeight this track applies to.
    pub target: TrackTarget,
    /// The time curve associated with this track.
    pub time: Time,
    /// The value curve associated with this track.
    pub value: Value
}

/// Enum wrapping over all possible animation track targets.
pub enum TrackTarget {
    /// A Transformation structure. See enum to see possibilities.
    Transformation(Arc<Transformation>),
    /// A MorphWeight structure.
    MorphWeight(Arc<MorphWeight>)
}

/// The Time structure contains key time data in an animation track.
///
/// There are two different kinds of this structure; one for every curve kind.
///
/// The variants in this enum contain vectors. One vector item represents on key time.
pub enum Time {
    /// The times are interpolated linearly.
    Linear(Vec<f32>),
    /// The times are interpolated on a one-dimensional Bézier curve.
    ///
    /// The times in each tuple in the vector are the "value", "-control" and "+control" values of
    /// the Bézier curve respectively.
    Bézier(Vec<(f32, f32, f32)>)
}

/// The Value structure contains key value data in an animation track.
///
/// There are two different kinds of this structure; one for every curve kind.
///
/// The variants in this enum contain vectors. One vector item represents on key value.
pub enum Value {
    /// The values are not interpolated, but remain constant until the next key time.
    Constant(Vec<f32>),
    /// The values are interpolated linearly.
    Linear(Vec<f32>),
    /// The values are interpolated on a one-dimensional Bézier curve.
    ///
    /// The values in each tuple in the vector are the "value", "-control" and "+control" values of
    /// the Bézier curve respectively.
    Bézier(Vec<(f32, f32, f32)>),
    /// The values are interpolated on a tension-continuity-bias (TCB) spline.
    ///
    /// The values in each tuple in the vector are the "value", "tension", "bias" and "continuity"
    /// values of the TCB spline respecitvely. The data contained in these last three values are
    /// always scalar.
    Tcb(Vec<(f32, f32, f32, f32)>)
}

/// A MorphWeight structure holds a single morph weight for a GeometryNode structure, that
/// references a GeometryObject strucure containing vertex data for multiple morph targets.
///
/// A MorphWeight structure can be the target of a track stored inside an Animation structure.
pub struct MorphWeight {
    /// Specifies the morph target index to which this morph weight applies. If the GeometryObject
    /// structure contains no vertex data corresponding to this target index, then this structure
    /// should be ignored. Each MorphWeight structure belonging to any particular GeometryNode
    /// structure must have a unique target index among all morph weights belonging to that
    /// GeometryNode.
    pub target_index: u32,
    /// The weight this MorphWeight structure represents.
    pub weight: f32,
}

/// The Atten structure specifies an attenuation function for a light object.
pub struct Atten {
    /// The kind of attenuation.
    pub kind: AttenuationKind,
    /// The type of curve defining the attenuation.
    pub curve: AttenuationCurve,
    /// Any parameters associated with this Atten structure.
    ///
    /// For the meaning of the parameters, please refer to the official OpenGEX documentation.
    /// There can exist application-defined parameters.
    pub params: ParamMap
}

/// A helper enum representing different kinds of attenuation functions.
pub enum AttenuationKind {
    /// The input to the attenuation function is the radial distance from the LightObject the
    /// parent Atten structure is associated with.
    Distance,
    /// The input to the attenuation function is the angle formed between the negative z-axis and
    /// the direction to the point being illuminated in object space.
    ///
    /// The result of this function should be raised to the power value of a potential "power"
    /// paramter present in the parent Atten structure.
    Angle,
    /// The input ot the attenuation function is the cosine of the angle formed between the
    /// negative z-axis and the direction to the point being illuminated in objet space.
    ///
    /// The result of this function should be raised to the power value of a potential "power"
    /// paramter present in the parent Atten structure.
    CosAngle
}

/// A helper enum representing different kinds of curves for an attenuation function.
///
/// For exact formulas, please refer to the offical OpenGEX documentation.
pub enum AttenuationCurve {
    /// The attenuation is a linear function.
    Linear,
    /// The attenuation is given by a cubic-smooth-step function.
    Cubic,
    /// The attenuation is given by the inverse function.
    Inverse,
    /// The attenuation is given by the inverse square function.
    InverseSquare
}

/// Helper enum to represent all different types of Nodes.
pub enum Nodes {
    /// A `Node`.
    Node(Node),
    /// A `BoneNode`.
    BoneNode(BoneNode),
    /// A `GeometryNode`
    GeometryNode(GeometryNode),
    /// A `CameraNode`
    CameraNode(CameraNode),
    /// A `LightNode`
    LightNode(LightNode)
}

/// Macro to do away with the redundancy of the different kinds of Node structures. The common Node
/// properties are placed at the start of the generated structure.
///
/// The common properties are:
/// * `name`: An optional name of the node.
/// * `transformations`: A vector of local transformations to be applied to the node.
/// * `animations`: A vector of animations that can be applied to the node.
/// * `children`: A vector of child nodes part of this node.
macro_rules! node {
    (
        $(#[$attr:meta])*
        pub struct $name:ident {
            $(#[$doc:meta] pub $prop:ident : $type_:ty),*
        }
    ) => (
        $(#[$attr])*
        pub struct $name {
            /// The optional name for this node. This property is generic to all nodes.
            pub name: Option<Name>,
            /// Any local transformations to be applied to this node. This property is generic to
            /// all nodes.
            pub transformations: Vec<Transformation>,
            /// Any animations for this node. This property is generic to all nodes.
            pub animations: Vec<Animation>,
            /// Any sub-nodes of this node. This property is generic to all nodes.
            pub children: Vec<Nodes>,
            $(#[$doc] pub $prop : $type_),*
        }
    )
}

node! {
    #[doc="A Node structure represents a single generic node in a scene, with no associated
           object."]
    pub struct Node {}
}

node! {
    #[doc="A BoneNode structure represents a single bone in a scene. The collection of bone nodes
           forming a complete skeleton for a skinned mesh is referenced by a `BoneRefArray`
           structure contained inside a `Skeleton` structrue. "]
    pub struct BoneNode {}
}

node! {
    #[doc="A GeometryNode structure represents a single geometry node in a scene. "]
    pub struct GeometryNode {
        #[doc="Whether this geometry is visible. Overrides the visibility of the referenced
               `GeometryObject` structure."]
        pub visibile: Option<bool>,
        #[doc="Whether this geometry casts shadows. If unset, this `GeometryNode` inherits the
               visibility of the referenced `GeometryObject` structure."]
        pub casts_shadows: Option<bool>,
        #[doc="Whether the geomery is rendered with motion blur. If unset, this `GeometryNode`
              inherits the visibility of the referenced `GeometryObject` structure. "]
        pub motion_blur: Option<bool>,
        #[doc="A reference to a `GeometryObject` structure containing all of the required mesh data
               and optional skinning data."]
        pub geometry: Arc<GeometryObject>,
        #[doc="A `HashMap` with references to materials associated with this geometry. Each
               material's index in the HashMap specifies to which part of a mesh the material is
               applied, by matching it with the `material` property of each `IndexArray` structure
               in the mesh."]
        pub materials: HashMap<u32, Arc<Material>>,
        #[doc="If the `GeometryObject` referenced by this node contains vertex data for multiple
               morph targets, then the node may contain one or more `MorphWeight` structures that
               specify the blending weight for each target. Each `MorphWeight` structure may be the
               target of a Track structure in the animation belonging to the node."]
        pub morph_weights: Vec<MorphWeight>
    }
}

node! {
    #[doc="A `CameraNode` structure represents a single camera node in a scene. "]
    pub struct CameraNode {
        #[doc="A reference to a `CameraObject` that contains the information neccesary to construct
               the properly configured camera."]
        pub camera: Arc<CameraObject>
    }
}

node! {
    #[doc="A `LightNode` structure represents a single camera node in a scene. "]
    pub struct LightNode {
        #[doc="Whether this light is visible. Overrides the visibility of the referenced
               `LightObject` structure."]
        pub visibile: Option<bool>,
        #[doc="A reference to a `LightObject` that contains the information neccesary to construct
               the proper type of light."]
        pub light: Arc<LightObject>
    }
}

/// The `GeometryObject` structure contains data for a geometry object. Multiple `GeometryNode`
/// structures may reference a single `GeometryObject`. This allows a scene to contain multiple
/// instances of the same geometry with different transforms and materials.
///
/// The `colors` and `textures` properties are for application-specfic use.
pub struct GeometryObject; // TODO: Finish this structure.

/// A `CameraObject` structure contains data for a camera object.
pub struct CameraObject {
    /// A map of parameters associated with this camera.
    ///
    /// The OpenGEX specification defined three parameters: "fov", "near" and "far". Any other
    /// parameters are application-specific.
    pub params: ParamMap,
    /// A map of colors associated with this camera. The OpenGEX specification does not define any
    /// kinds of colors.
    pub colors: HashMap<String, Color>,
    /// A map of textures associated with this camera. The OpenGEX specification does not define
    /// any kinds of textures.
    pub textures: HashMap<String, Texture>
}

/// The LightObject struture contains data for a light object. Multiple LightNode structures may
/// reference a single LightObject. This allows a scene to contain multiple instances of the same
/// light, with different transformations.
pub struct LightObject {
    /// The type of light emitted by this LightObject.
    pub light_type: LightType,
    /// Whether this LightObject casts shadows. This can be overiden by the LightNode referencing
    /// this LightObject.
    pub casts_shadow: bool,
    /// The colors associated with this LightObject.
    ///
    /// The OpenGEX specification only references one type of color: "light". This is the main
    /// color of light emitted by the light souArce. This defaults to an RGB value of
    /// (1.0, 1.0, 1.0).
    ///
    /// There can be any other number of colors in this HashMap, for application-specific kinds.
    pub colors: HashMap<String, Color>,
    /// The parameters associated with this LightObject.
    ///
    /// The OpenGEX specification only references one type of parameter: "intensity". This is the
    /// intensity of the light emitted by the light souArce. This defaults to 0.
    ///
    /// There can be any other number of parameters in this HashMap, for application-specific
    /// kinds.
    pub params: ParamMap,
    /// The textures associated with this LightObject.
    ///
    /// The OpenGEX specification only references one type of texture: "projection". This is an
    /// optional texture projection of this LightObject. The texture map should be oriented so that
    /// the right direction is aligned to the object-space positive x-axis, and the up direction is
    /// aligned to the object-space positive y-axis.
    ///
    /// There can be any other number of textures in this HashMap, for application-specific kinds.
    pub textures: HashMap<String, Texture>,
    /// Any number of attenuation functions to be applied to the LightObject. The values produced
    /// by all of them are multiplied together to determine the intensity of the light reaching
    /// any particular point in space.
    pub attenuations: Atten
}

/// This is an helper-enum representing all different types of lights that a LightObject can emit.
pub enum LightType {
    /// The light souArce is to be treated as if it were infinitely far away so its rays are
    /// parallel. In object space, the rays point in the direction of the negative z-axis.
    Infinite,
    /// The lght souArce is a point light that radiates in all directions.
    Point,
    /// The light source is a spot light that radiates from a single points byt in a limited range
    /// of directions In object space, the primary direction is the negative z-axis.
    Spot
}
<|endoftext|>


<commit_before>use {Errno, Result};
use libc::{self, c_int};
use std::os::unix::io::RawFd;

bitflags!(
    #[repr(C)]
    flags EpollEventKind: u32 {
        const EPOLLIN = 0x001,
        const EPOLLPRI = 0x002,
        const EPOLLOUT = 0x004,
        const EPOLLRDNORM = 0x040,
        const EPOLLRDBAND = 0x080,
        const EPOLLWRNORM = 0x100,
        const EPOLLWRBAND = 0x200,
        const EPOLLMSG = 0x400,
        const EPOLLERR = 0x008,
        const EPOLLHUP = 0x010,
        const EPOLLRDHUP = 0x2000,
        const EPOLLEXCLUSIVE = 1 << 28,
        const EPOLLWAKEUP = 1 << 29,
        const EPOLLONESHOT = 1 << 30,
        const EPOLLET = 1 << 31
    }
);

#[derive(Clone, Copy)]
#[repr(C)]
pub enum EpollOp {
    EpollCtlAdd = 1,
    EpollCtlDel = 2,
    EpollCtlMod = 3
}

libc_bitflags!{
    flags EpollCreateFlags: c_int {
        EPOLL_CLOEXEC,
    }
}

#[derive(Clone, Copy)]
#[repr(C)]
pub struct EpollEvent {
    event: libc::epoll_event,
}

impl EpollEvent {
    pub fn new(events: EpollEventKind, data: u64) -> EpollEvent {
        EpollEvent { event: libc::epoll_event { events: events.bits(), u64: data } }
    }

    pub fn events(&self) -> EpollEventKind {
        EpollEventKind::from_bits(self.event.events).unwrap()
    }

    pub fn data(&self) -> u64 {
        self.event.u64
    }
}

#[inline]
pub fn epoll_create() -> Result<RawFd> {
    let res = unsafe { libc::epoll_create(1024) };

    Errno::result(res)
}

#[inline]
pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
    let res = unsafe { libc::epoll_create1(flags.bits()) };

    Errno::result(res)
}

#[inline]
pub fn epoll_ctl(epfd: RawFd, op: EpollOp, fd: RawFd, event: &mut EpollEvent) -> Result<()> {
    let res = unsafe { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) };

    Errno::result(res).map(drop)
}

#[inline]
pub fn epoll_wait(epfd: RawFd, events: &mut [EpollEvent], timeout_ms: isize) -> Result<usize> {
    let res = unsafe {
        libc::epoll_wait(epfd, events.as_mut_ptr() as *mut libc::epoll_event, events.len() as c_int, timeout_ms as c_int)
    };

    Errno::result(res).map(|r| r as usize)
}
<commit_msg>Rename EpollEventKind to EpollFlags, according to convention.<commit_after>use {Errno, Result};
use libc::{self, c_int};
use std::os::unix::io::RawFd;

bitflags!(
    #[repr(C)]
    flags EpollFlags: u32 {
        const EPOLLIN = 0x001,
        const EPOLLPRI = 0x002,
        const EPOLLOUT = 0x004,
        const EPOLLRDNORM = 0x040,
        const EPOLLRDBAND = 0x080,
        const EPOLLWRNORM = 0x100,
        const EPOLLWRBAND = 0x200,
        const EPOLLMSG = 0x400,
        const EPOLLERR = 0x008,
        const EPOLLHUP = 0x010,
        const EPOLLRDHUP = 0x2000,
        const EPOLLEXCLUSIVE = 1 << 28,
        const EPOLLWAKEUP = 1 << 29,
        const EPOLLONESHOT = 1 << 30,
        const EPOLLET = 1 << 31
    }
);

#[derive(Clone, Copy)]
#[repr(C)]
pub enum EpollOp {
    EpollCtlAdd = 1,
    EpollCtlDel = 2,
    EpollCtlMod = 3
}

libc_bitflags!{
    flags EpollCreateFlags: c_int {
        EPOLL_CLOEXEC,
    }
}

#[derive(Clone, Copy)]
#[repr(C)]
pub struct EpollEvent {
    event: libc::epoll_event,
}

impl EpollEvent {
    pub fn new(events: EpollFlags, data: u64) -> EpollEvent {
        EpollEvent { event: libc::epoll_event { events: events.bits(), u64: data } }
    }

    pub fn events(&self) -> EpollFlags {
        EpollFlags::from_bits(self.event.events).unwrap()
    }

    pub fn data(&self) -> u64 {
        self.event.u64
    }
}

#[inline]
pub fn epoll_create() -> Result<RawFd> {
    let res = unsafe { libc::epoll_create(1024) };

    Errno::result(res)
}

#[inline]
pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
    let res = unsafe { libc::epoll_create1(flags.bits()) };

    Errno::result(res)
}

#[inline]
pub fn epoll_ctl(epfd: RawFd, op: EpollOp, fd: RawFd, event: &mut EpollEvent) -> Result<()> {
    let res = unsafe { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) };

    Errno::result(res).map(drop)
}

#[inline]
pub fn epoll_wait(epfd: RawFd, events: &mut [EpollEvent], timeout_ms: isize) -> Result<usize> {
    let res = unsafe {
        libc::epoll_wait(epfd, events.as_mut_ptr() as *mut libc::epoll_event, events.len() as c_int, timeout_ms as c_int)
    };

    Errno::result(res).map(|r| r as usize)
}
<|endoftext|>


<commit_before>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

enum FilterMode {
    Bytes(u64),
    Lines(u64, u8), // (number of lines, delimiter)
}

struct Settings {
    mode: FilterMode,
    sleep_msec: u32,
    beginning: bool,
    follow: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: FilterMode::Lines(10, '\n' as u8),
            sleep_msec: 1000,
            beginning: false,
            follow: false,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { settings.mode = FilterMode::Lines(n, '\n' as u8); args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("z", "zero-terminated", "Line delimiter is NUL, not newline");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    settings.follow = given_options.opt_present("f");
    if settings.follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { settings.sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                settings.beginning = true;
                slice = &slice[1..];
            }
            match parse_size(slice) {
                Ok(m) => settings.mode = FilterMode::Lines(m, '\n' as u8),
                Err(e) => {
                    show_error!("{}", e.description());
                    return 1;
                }
            }
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    settings.beginning = true;
                    slice = &slice[1..];
                }
                match parse_size(slice) {
                    Ok(m) => settings.mode = FilterMode::Bytes(m),
                    Err(e) => {
                        show_error!("{}", e.description());
                        return 1;
                    }
                }
            }
            None => { }
        }
    };

    if given_options.opt_present("z") {
        if let FilterMode::Lines(count, _) = settings.mode {
            settings.mode = FilterMode::Lines(count, 0);
        }
    }

    let files = given_options.free;

    if files.is_empty() {
        let buffer = BufReader::new(stdin());
        unbounded_tail(buffer, &settings);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in &files {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            bounded_tail(reader, &settings);
        }
    }

    0
}

#[derive(Debug, PartialEq, Eq)]
pub enum ParseSizeErr {
    ParseFailure(String),
    SizeTooBig(String),
}

impl Error for ParseSizeErr {
    fn description(&self) -> &str {
        match *self {
            ParseSizeErr::ParseFailure(ref s) => &*s,
            ParseSizeErr::SizeTooBig(ref s) => &*s,
        }
    }
}

impl fmt::Display for ParseSizeErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", Error::description(self))
    }
}

impl ParseSizeErr {
    fn parse_failure(s: &str) -> ParseSizeErr {
        ParseSizeErr::ParseFailure(format!("invalid size: '{}'", s))
    }

    fn size_too_big(s: &str) -> ParseSizeErr {
        ParseSizeErr::SizeTooBig(
            format!("invalid size: '{}': Value too large to be stored in data type", s))
    }
}

pub type ParseSizeResult = Result<u64, ParseSizeErr>;

pub fn parse_size(mut size_slice: &str) -> Result<u64, ParseSizeErr> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000u64
        } else {
            1024u64
        };

    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' | 'k' => 1u64,
                'M' => 2u64,
                'G' => 3u64,
                'T' => 4u64,
                'P' => 5u64,
                'E' => 6u64,
                'Z' | 'Y' => {
                    return Err(ParseSizeErr::size_too_big(size_slice));
                },
                'b' => {
                    base = 512u64;
                    1u64
                }
                _ => {
                    has_suffix = false;
                    0u64
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0u64
        };

    let mut multiplier = 1u64;
    for _ in 0u64 .. exponent {
        multiplier *= base;
    }
    if base == 1000u64 && exponent == 0u64 {
        // sole B is not a valid suffix
        Err(ParseSizeErr::parse_failure(size_slice))
    } else {
        let value: Option<u64> = size_slice.parse().ok();
        value.map(|v| Ok(multiplier * v))
             .unwrap_or(Err(ParseSizeErr::parse_failure(size_slice)))
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<u64>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<u64> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

/// When reading files in reverse in `bounded_tail`, this is the size of each
/// block read at a time.
const BLOCK_SIZE: u64 = 1 << 16;

fn follow<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
    assert!(settings.follow);
    loop {
        sleep(Duration::new(0, settings.sleep_msec*1000));
        loop {
            let mut datum = String::new();
            match reader.read_line(&mut datum) {
                Ok(0) => break,
                Ok(_) => print!("{}", datum),
                Err(err) => panic!(err)
            }
        }
    }
}

/// Iterate over bytes in the file, in reverse, until `should_stop` returns
/// true. The `file` is left seek'd to the position just after the byte that
/// `should_stop` returned true for.
fn backwards_thru_file<F>(file: &mut File, size: u64, buf: &mut Vec<u8>, delimiter: u8, should_stop: &mut F)
    where F: FnMut(u8) -> bool
{
    assert!(buf.len() >= BLOCK_SIZE as usize);

    let max_blocks_to_read = (size as f64 / BLOCK_SIZE as f64).ceil() as usize;

    for block_idx in 0..max_blocks_to_read {
        let block_size = if block_idx == max_blocks_to_read - 1 {
            size % BLOCK_SIZE
        } else {
            BLOCK_SIZE
        };

        // Seek backwards by the next block, read the full block into
        // `buf`, and then seek back to the start of the block again.
        let pos = file.seek(SeekFrom::Current(-(block_size as i64))).unwrap();
        file.read_exact(&mut buf[0..(block_size as usize)]).unwrap();
        let pos2 = file.seek(SeekFrom::Current(-(block_size as i64))).unwrap();
        assert_eq!(pos, pos2);

        // Iterate backwards through the bytes, calling `should_stop` on each
        // one.
        let slice = &buf[0..(block_size as usize)];
        for (i, ch) in slice.iter().enumerate().rev() {
            // Ignore one trailing newline.
            if block_idx == 0 && i as u64 == block_size - 1 && *ch == delimiter {
                continue;
            }

            if should_stop(*ch) {
                file.seek(SeekFrom::Current((i + 1) as i64)).unwrap();
                return;
            }
        }
    }
}

/// When tail'ing a file, we do not need to read the whole file from start to
/// finish just to find the last n lines or bytes. Instead, we can seek to the
/// end of the file, and then read the file "backwards" in blocks of size
/// `BLOCK_SIZE` until we find the location of the first line/byte. This ends up
/// being a nice performance win for very large files.
fn bounded_tail(mut file: File, settings: &Settings) {
    let size = file.seek(SeekFrom::End(0)).unwrap();
    if size == 0 {
        if settings.follow {
            let reader = BufReader::new(file);
            follow(reader, settings);
        }
        return;
    }

    let mut buf = vec![0; BLOCK_SIZE as usize];

    // Find the position in the file to start printing from.
    match settings.mode {
        FilterMode::Lines(mut count, delimiter) => {
            backwards_thru_file(&mut file, size, &mut buf, delimiter, &mut |byte| {
                if byte == delimiter {
                    count -= 1;
                    count == 0
                } else {
                    false
                }
            });
        },
        FilterMode::Bytes(count) => {
            file.seek(SeekFrom::End(-(count as i64))).unwrap();
        },
    }

    // Print the target section of the file.
    loop {
        let bytes_read = file.read(&mut buf).unwrap();

        let mut stdout = stdout();
        for b in &buf[0..bytes_read] {
            print_byte(&mut stdout, b);
        }

        if bytes_read == 0 {
            break;
        }
    }

    // Continue following changes, if requested.
    if settings.follow {
        let reader = BufReader::new(file);
        follow(reader, settings);
    }
}

fn unbounded_tail<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
    // Read through each line/char and store them in a ringbuffer that always
    // contains count lines/chars. When reaching the end of file, output the
    // data in the ringbuf.
    match settings.mode {
        FilterMode::Lines(mut count, _delimiter) => {
            let mut ringbuf: VecDeque<String> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::u64::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = String::new();
                match reader.read_line(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() as u64 {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_string(&mut stdout, datum);
            }
        },
        FilterMode::Bytes(mut count) => {
            let mut ringbuf: VecDeque<u8> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::u64::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = [0; 1];
                match reader.read(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() as u64 {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum[0]);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_byte(&mut stdout, datum);
            }
        }
    }

    if settings.follow {
        follow(reader, settings);
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &str) {
    print!("{}", s);
}

fn version() {
    println!("{} {}", NAME, VERSION);
}
<commit_msg>tail: don't follow() as part of bounded_tail<commit_after>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

enum FilterMode {
    Bytes(u64),
    Lines(u64, u8), // (number of lines, delimiter)
}

struct Settings {
    mode: FilterMode,
    sleep_msec: u32,
    beginning: bool,
    follow: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: FilterMode::Lines(10, '\n' as u8),
            sleep_msec: 1000,
            beginning: false,
            follow: false,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { settings.mode = FilterMode::Lines(n, '\n' as u8); args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("z", "zero-terminated", "Line delimiter is NUL, not newline");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    settings.follow = given_options.opt_present("f");
    if settings.follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { settings.sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                settings.beginning = true;
                slice = &slice[1..];
            }
            match parse_size(slice) {
                Ok(m) => settings.mode = FilterMode::Lines(m, '\n' as u8),
                Err(e) => {
                    show_error!("{}", e.description());
                    return 1;
                }
            }
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    settings.beginning = true;
                    slice = &slice[1..];
                }
                match parse_size(slice) {
                    Ok(m) => settings.mode = FilterMode::Bytes(m),
                    Err(e) => {
                        show_error!("{}", e.description());
                        return 1;
                    }
                }
            }
            None => { }
        }
    };

    if given_options.opt_present("z") {
        if let FilterMode::Lines(count, _) = settings.mode {
            settings.mode = FilterMode::Lines(count, 0);
        }
    }

    let files = given_options.free;

    if files.is_empty() {
        let buffer = BufReader::new(stdin());
        unbounded_tail(buffer, &settings);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in &files {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            bounded_tail(reader, &settings);
        }
    }

    0
}

#[derive(Debug, PartialEq, Eq)]
pub enum ParseSizeErr {
    ParseFailure(String),
    SizeTooBig(String),
}

impl Error for ParseSizeErr {
    fn description(&self) -> &str {
        match *self {
            ParseSizeErr::ParseFailure(ref s) => &*s,
            ParseSizeErr::SizeTooBig(ref s) => &*s,
        }
    }
}

impl fmt::Display for ParseSizeErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", Error::description(self))
    }
}

impl ParseSizeErr {
    fn parse_failure(s: &str) -> ParseSizeErr {
        ParseSizeErr::ParseFailure(format!("invalid size: '{}'", s))
    }

    fn size_too_big(s: &str) -> ParseSizeErr {
        ParseSizeErr::SizeTooBig(
            format!("invalid size: '{}': Value too large to be stored in data type", s))
    }
}

pub type ParseSizeResult = Result<u64, ParseSizeErr>;

pub fn parse_size(mut size_slice: &str) -> Result<u64, ParseSizeErr> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000u64
        } else {
            1024u64
        };

    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' | 'k' => 1u64,
                'M' => 2u64,
                'G' => 3u64,
                'T' => 4u64,
                'P' => 5u64,
                'E' => 6u64,
                'Z' | 'Y' => {
                    return Err(ParseSizeErr::size_too_big(size_slice));
                },
                'b' => {
                    base = 512u64;
                    1u64
                }
                _ => {
                    has_suffix = false;
                    0u64
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0u64
        };

    let mut multiplier = 1u64;
    for _ in 0u64 .. exponent {
        multiplier *= base;
    }
    if base == 1000u64 && exponent == 0u64 {
        // sole B is not a valid suffix
        Err(ParseSizeErr::parse_failure(size_slice))
    } else {
        let value: Option<u64> = size_slice.parse().ok();
        value.map(|v| Ok(multiplier * v))
             .unwrap_or(Err(ParseSizeErr::parse_failure(size_slice)))
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<u64>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<u64> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

/// When reading files in reverse in `bounded_tail`, this is the size of each
/// block read at a time.
const BLOCK_SIZE: u64 = 1 << 16;

fn follow<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
    assert!(settings.follow);
    loop {
        sleep(Duration::new(0, settings.sleep_msec*1000));
        loop {
            let mut datum = String::new();
            match reader.read_line(&mut datum) {
                Ok(0) => break,
                Ok(_) => print!("{}", datum),
                Err(err) => panic!(err)
            }
        }
    }
}

/// Iterate over bytes in the file, in reverse, until `should_stop` returns
/// true. The `file` is left seek'd to the position just after the byte that
/// `should_stop` returned true for.
fn backwards_thru_file<F>(file: &mut File, size: u64, buf: &mut Vec<u8>, delimiter: u8, should_stop: &mut F)
    where F: FnMut(u8) -> bool
{
    assert!(buf.len() >= BLOCK_SIZE as usize);

    let max_blocks_to_read = (size as f64 / BLOCK_SIZE as f64).ceil() as usize;

    for block_idx in 0..max_blocks_to_read {
        let block_size = if block_idx == max_blocks_to_read - 1 {
            size % BLOCK_SIZE
        } else {
            BLOCK_SIZE
        };

        // Seek backwards by the next block, read the full block into
        // `buf`, and then seek back to the start of the block again.
        let pos = file.seek(SeekFrom::Current(-(block_size as i64))).unwrap();
        file.read_exact(&mut buf[0..(block_size as usize)]).unwrap();
        let pos2 = file.seek(SeekFrom::Current(-(block_size as i64))).unwrap();
        assert_eq!(pos, pos2);

        // Iterate backwards through the bytes, calling `should_stop` on each
        // one.
        let slice = &buf[0..(block_size as usize)];
        for (i, ch) in slice.iter().enumerate().rev() {
            // Ignore one trailing newline.
            if block_idx == 0 && i as u64 == block_size - 1 && *ch == delimiter {
                continue;
            }

            if should_stop(*ch) {
                file.seek(SeekFrom::Current((i + 1) as i64)).unwrap();
                return;
            }
        }
    }
}

/// When tail'ing a file, we do not need to read the whole file from start to
/// finish just to find the last n lines or bytes. Instead, we can seek to the
/// end of the file, and then read the file "backwards" in blocks of size
/// `BLOCK_SIZE` until we find the location of the first line/byte. This ends up
/// being a nice performance win for very large files.
fn bounded_tail(mut file: File, settings: &Settings) {
    let size = file.seek(SeekFrom::End(0)).unwrap();
    let mut buf = vec![0; BLOCK_SIZE as usize];

    // Find the position in the file to start printing from.
    match settings.mode {
        FilterMode::Lines(mut count, delimiter) => {
            backwards_thru_file(&mut file, size, &mut buf, delimiter, &mut |byte| {
                if byte == delimiter {
                    count -= 1;
                    count == 0
                } else {
                    false
                }
            });
        },
        FilterMode::Bytes(count) => {
            file.seek(SeekFrom::End(-(count as i64))).unwrap();
        },
    }

    // Print the target section of the file.
    loop {
        let bytes_read = file.read(&mut buf).unwrap();

        let mut stdout = stdout();
        for b in &buf[0..bytes_read] {
            print_byte(&mut stdout, b);
        }

        if bytes_read == 0 {
            break;
        }
    }
}

fn unbounded_tail<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
    // Read through each line/char and store them in a ringbuffer that always
    // contains count lines/chars. When reaching the end of file, output the
    // data in the ringbuf.
    match settings.mode {
        FilterMode::Lines(mut count, _delimiter) => {
            let mut ringbuf: VecDeque<String> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::u64::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = String::new();
                match reader.read_line(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() as u64 {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_string(&mut stdout, datum);
            }
        },
        FilterMode::Bytes(mut count) => {
            let mut ringbuf: VecDeque<u8> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::u64::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = [0; 1];
                match reader.read(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() as u64 {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum[0]);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_byte(&mut stdout, datum);
            }
        }
    }

    if settings.follow {
        follow(reader, settings);
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &str) {
    print!("{}", s);
}

fn version() {
    println!("{} {}", NAME, VERSION);
}
<|endoftext|>


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

use std::iter::Peekable;
use std::str;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _ => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+' => true,
        _ => false,
    }
}

pub type PeekableTokens<'a> = Peekable<Tokens<'a>>;

pub struct Tokens<'a> {
    chars: Peekable<str::Chars<'a>>,
}

impl<'a> Tokens<'a> {
    pub fn from_str(input: &'a str) -> Self {
        Tokens {
            chars: input.chars().peekable(),
        }
    }
}

impl<'a> Iterator for Tokens<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        // TODO: should this return Result?
        let next_char = self.chars.next()?;

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace().unwrap_or_default();
                match number.trim_left_matches('+').parse::<f64>() {
                    Ok(parsed_num) => Some(Token::Number(parsed_num)),
                    Err(_) => None
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace().unwrap_or_default();
                Some(Token::Word(word))
            }
        }
    }
}

impl<'a> Tokens<'a> {
    fn read_until_whitespace(&mut self) -> Option<String> {
        let next_char = *self.chars.peek()?;

        match next_char {
            '\0' | '(' | ')' | ',' => None,
            c if is_whitespace(c) => {
                let _ = self.chars.next();
                None
            }
            _ => Some(
                self.chars.next().unwrap().to_string()
                    + &self.read_until_whitespace().unwrap_or_default(),
            ),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Word("hello".to_string())]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("hello".to_string()),
            Token::Word("world".to_string()),
        ]
    );
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_invalid_number() {
    let test_str = "4.2p";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(0.4), Token::Number(-2.0)]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("POINT".to_string()),
            Token::ParenOpen,
            Token::Number(10.0),
            Token::Number(-20.0),
            Token::ParenClose,
        ]
    );
}
<commit_msg>Remove recursion to prevent stack overflow<commit_after>// Copyright 2014-2015 The GeoRust Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::Peekable;
use std::str;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _ => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+' => true,
        _ => false,
    }
}

pub type PeekableTokens<'a> = Peekable<Tokens<'a>>;

pub struct Tokens<'a> {
    chars: Peekable<str::Chars<'a>>,
}

impl<'a> Tokens<'a> {
    pub fn from_str(input: &'a str) -> Self {
        Tokens {
            chars: input.chars().peekable(),
        }
    }
}

impl<'a> Iterator for Tokens<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        // TODO: should this return Result?
        let mut next_char = self.chars.next()?;

        // Skip whitespace
        while is_whitespace(next_char) {
            next_char = self.chars.next()?
        }

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace().unwrap_or_default();
                match number.trim_left_matches('+').parse::<f64>() {
                    Ok(parsed_num) => Some(Token::Number(parsed_num)),
                    Err(_) => None
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace().unwrap_or_default();
                Some(Token::Word(word))
            }
        }
    }
}

impl<'a> Tokens<'a> {
    fn read_until_whitespace(&mut self) -> Option<String> {
        let mut result = String::new();

        while let Some(&next_char) = self.chars.peek() {
            let marker = match next_char {
                '\0' | '(' | ')' | ',' => true,
                _ => false,
            };

            // Consume non-markers
            if !marker {
                let _ = self.chars.next();
            }

            let whitespace = is_whitespace(next_char);

            // Append non-whitespace, non-marker characters
            if !marker && !whitespace {
                result.push(next_char);
            }

            // Stop reading when reached marker or whitespace
            if marker || whitespace {
                break;
            }
        }

        if result.is_empty() {
            None
        } else {
            Some(result)
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Word("hello".to_string())]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("hello".to_string()),
            Token::Word("world".to_string()),
        ]
    );
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(4.2)]);
}

#[test]
fn test_tokenizer_invalid_number() {
    let test_str = "4.2p";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![Token::Number(0.4), Token::Number(-2.0)]);
}

#[test]
fn test_no_stack_overflow() {
    fn check(c: &str, count: usize, expected: usize) {
        let test_str = c.repeat(count);
        let tokens : Vec<Token>= Tokens::from_str(&test_str).collect();
        assert_eq!(expected, tokens.len());
    }

    let count = 100_000;
    check("+", count, 0);
    check(" ", count, 0);
    check("A", count, 1);
    check("1", count, 1);
    check("(", count, count);
    check(")", count, count);
    check(",", count, count);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(
        tokens,
        vec![
            Token::Word("POINT".to_string()),
            Token::ParenOpen,
            Token::Number(10.0),
            Token::Number(-20.0),
            Token::ParenClose,
        ]
    );
}
<|endoftext|>


<commit_before>use std::fmt;
use std::iter::Map;
use std::slice::Iter;

use regex::Regex;

type SliceVec = Vec<(usize, usize)>;

#[derive(Debug)]
pub enum Pattern {
    Template
}

impl fmt::Display for Pattern {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let result = match *self {
            Pattern::Template => r"(\{%.*?%\}|\{\{.*?\}\}?|\{\{|\{%)"
        };

        write!(f, "{}", result)
    }
}

impl Pattern {
    pub fn to_regex(&self) -> Regex {
        let pattern = format!("{}", self);
        Regex::new(&pattern).unwrap()
    }
}

pub struct Tokenizer<'t> {
    source: &'t str
}

impl<'t> Tokenizer<'t> {
    pub fn new<'a>(source: &'a str) -> Tokenizer<'a> {
        Tokenizer { source: source }
    }

    pub fn tokenize<'a>(&'a self, pattern: &'a Regex) -> Vec<&'a str> {
        let slices = self.matched_slices(pattern);
        slices.iter().map(|&(start, end)| &self.source[start..end]).collect()
    }

    fn matched_slices(&self, pattern: &Regex) -> SliceVec {
        let mut slices = pattern.find_iter(self.source).collect::<Vec<_>>();
        let missing = self.find_missing_slices(&slices);

        slices.extend(&missing);
        slices.sort();
        slices
    }

    fn find_missing_slices(&self, slices: &SliceVec) -> SliceVec {
        if slices.is_empty() { return vec![(0, self.source.len())]; }

        let mut missing = self.missing_middle_slices(slices);
        let first       = self.missing_first_slice(slices);
        let last        = self.missing_last_slice(slices);

        if first.is_some() { missing.push(first.unwrap()) }
        if last.is_some()  { missing.push(last.unwrap()) }

        missing
    }

    fn missing_first_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let pos = slices[0].0;
        if pos == 0 { return None; }

        Some((0, pos))
    }

    fn missing_last_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let last = slices.last().unwrap();
        let len  = self.source.len();
        if last.1 == len { return None; }

        Some((last.1, len))
    }

    fn missing_middle_slices(&self, slices: &SliceVec) -> SliceVec {
        let mut missing: SliceVec = Vec::new();
        let last_index            = slices.len() - 1;

        for (index, &(_, right)) in slices.iter().enumerate() {
            if index == last_index { continue; }

            let edge = slices[index + 1].0;
            if right != edge {
                missing.push((right, edge));
            }
        }

        missing
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn assert_tokens(tokenizer: &Tokenizer, expected: Vec<&str>) {
        let re     = Pattern::Template.to_regex();
        let actual = tokenizer.tokenize(&re);

        assert_eq!(expected, actual);
    }

    #[test]
    fn tokenize_blank_string() {
        let tokenizer = Tokenizer::new("");
        assert_tokens(&tokenizer, vec![""]);
    }

    #[test]
    fn tokenize_whitespace_only_string() {
        let tokenizer = Tokenizer::new("  ");
        assert_tokens(&tokenizer, vec!["  "]);
    }

    #[test]
    fn tokenize_string_with_no_matches() {
        let tokenizer = Tokenizer::new("hello world");
        assert_tokens(&tokenizer, vec!["hello world"]);
    }

    #[test]
    fn tokenize_single_variable() {
        let tokenizer = Tokenizer::new("{{funk}}");
        assert_tokens(&tokenizer, vec!["{{funk}}"]);
    }

    #[test]
    fn tokenize_single_variable_surrounded_by_whitespace() {
        let tokenizer = Tokenizer::new(" {{funk}} ");
        assert_tokens(&tokenizer, vec![" ", "{{funk}}", " "]);
    }

    #[test]
    fn tokenize_multiple_variables() {
        let tokenizer = Tokenizer::new(" {{funk}} {{so}} {{brutha}} ");
        let expected = vec![
            " ",
            "{{funk}}",
            " ",
            "{{so}}",
            " ",
            "{{brutha}}",
            " "
        ];

        assert_tokens(&tokenizer, expected);
    }

    #[test]
    fn tokenize_single_block() {
        let tokenizer = Tokenizer::new(" {%comment%} ");
        assert_tokens(&tokenizer, vec![" ", "{%comment%}", " "]);
    }

    #[test]
    fn tokenize_empty_block_tag() {
        let tokenizer = Tokenizer::new(" {% thing %} {% comment %} My comment here {% endcomment %} ");

        assert_tokens(&tokenizer, vec![
            " ",
            "{% thing %}",
            " ",
            "{% comment %}",
            " My comment here ",
            "{% endcomment %}",
            " "
        ]);
    }

    #[test]
    fn tokenize_multiline_string() {
        let tokenizer = Tokenizer::new("{%comment%}\nMy Comment\n{%endcomment%}\n");

        assert_tokens(&tokenizer, vec![
            "{%comment%}",
            "\nMy Comment\n",
            "{%endcomment%}",
            "\n"
        ]);
    }

    #[test]
    fn tokenize_html_with_liquid() {
        let content = r#"

    <title>{{ title }}</title>

{% comment %}Content here{% endcomment %}

        "#;

        let tokenizer = Tokenizer::new(&content);
        assert_tokens(&tokenizer, vec![
            "\n\n  \n    <title>",
            "{{ title }}",
            "</title>\n  \n  \n
",
            "{% comment %}",
            "Content here",
            "{% endcomment %}",
            "
\n    \n  \n\n        "
        ]);
    }
}
<commit_msg>style updates<commit_after>use std::fmt;
use std::iter::Map;
use std::slice::Iter;

use regex::Regex;

type SliceVec = Vec<(usize, usize)>;

#[derive(Debug)]
pub enum Pattern {
    Template
}

impl fmt::Display for Pattern {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let result = match *self {
            Pattern::Template => r"(\{%.*?%\}|\{\{.*?\}\}?|\{\{|\{%)"
        };

        write!(f, "{}", result)
    }
}

impl Pattern {
    pub fn to_regex(&self) -> Regex {
        let pattern = format!("{}", self);
        Regex::new(&pattern).unwrap()
    }
}

pub struct Tokenizer<'t> {
    source: &'t str
}

impl<'t> Tokenizer<'t> {
    pub fn new<'a>(source: &'a str) -> Tokenizer<'a> {
        Tokenizer { source: source }
    }

    pub fn tokenize<'a>(&'a self, pattern: &'a Regex) -> Vec<&'a str> {
        let slices = self.matched_slices(pattern);
        slices.iter().map(|&(start, end)| &self.source[start..end]).collect()
    }

    fn matched_slices(&self, pattern: &Regex) -> SliceVec {
        let mut slices = pattern.find_iter(self.source).collect::<Vec<_>>();
        let missing = self.find_missing_slices(&slices);

        slices.extend(&missing);
        slices.sort();
        slices
    }

    fn find_missing_slices(&self, slices: &SliceVec) -> SliceVec {
        if slices.is_empty() { return vec![(0, self.source.len())]; }

        let mut missing = self.missing_middle_slices(slices);

        if let Some(first) = self.missing_first_slice(slices) {
            missing.push(first);
        }

        if let Some(last) = self.missing_last_slice(slices) {
            missing.push(last);
        }

        missing
    }

    fn missing_first_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let pos = slices[0].0;
        if pos == 0 { return None; }

        Some((0, pos))
    }

    fn missing_last_slice(&self, slices: &SliceVec) -> Option<(usize, usize)> {
        let last = slices.last().unwrap();
        let len  = self.source.len();
        if last.1 == len { return None; }

        Some((last.1, len))
    }

    fn missing_middle_slices(&self, slices: &SliceVec) -> SliceVec {
        let mut missing: SliceVec = Vec::new();
        let last_index            = slices.len() - 1;

        for (index, &(_, right)) in slices.iter().enumerate() {
            if index == last_index { continue; }

            let edge = slices[index + 1].0;
            if right != edge {
                missing.push((right, edge));
            }
        }

        missing
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn assert_tokens(tokenizer: &Tokenizer, expected: Vec<&str>) {
        let re     = Pattern::Template.to_regex();
        let actual = tokenizer.tokenize(&re);

        assert_eq!(expected, actual);
    }

    #[test]
    fn tokenize_blank_string() {
        let tokenizer = Tokenizer::new("");
        assert_tokens(&tokenizer, vec![""]);
    }

    #[test]
    fn tokenize_whitespace_only_string() {
        let tokenizer = Tokenizer::new("  ");
        assert_tokens(&tokenizer, vec!["  "]);
    }

    #[test]
    fn tokenize_string_with_no_matches() {
        let tokenizer = Tokenizer::new("hello world");
        assert_tokens(&tokenizer, vec!["hello world"]);
    }

    #[test]
    fn tokenize_single_variable() {
        let tokenizer = Tokenizer::new("{{funk}}");
        assert_tokens(&tokenizer, vec!["{{funk}}"]);
    }

    #[test]
    fn tokenize_single_variable_surrounded_by_whitespace() {
        let tokenizer = Tokenizer::new(" {{funk}} ");
        assert_tokens(&tokenizer, vec![" ", "{{funk}}", " "]);
    }

    #[test]
    fn tokenize_multiple_variables() {
        let tokenizer = Tokenizer::new(" {{funk}} {{so}} {{brutha}} ");
        let expected = vec![
            " ",
            "{{funk}}",
            " ",
            "{{so}}",
            " ",
            "{{brutha}}",
            " "
        ];

        assert_tokens(&tokenizer, expected);
    }

    #[test]
    fn tokenize_single_block() {
        let tokenizer = Tokenizer::new(" {%comment%} ");
        assert_tokens(&tokenizer, vec![" ", "{%comment%}", " "]);
    }

    #[test]
    fn tokenize_empty_block_tag() {
        let tokenizer = Tokenizer::new(" {% thing %} {% comment %} My comment here {% endcomment %} ");

        assert_tokens(&tokenizer, vec![
            " ",
            "{% thing %}",
            " ",
            "{% comment %}",
            " My comment here ",
            "{% endcomment %}",
            " "
        ]);
    }

    #[test]
    fn tokenize_multiline_string() {
        let tokenizer = Tokenizer::new("{%comment%}\nMy Comment\n{%endcomment%}\n");

        assert_tokens(&tokenizer, vec![
            "{%comment%}",
            "\nMy Comment\n",
            "{%endcomment%}",
            "\n"
        ]);
    }

    #[test]
    fn tokenize_html_with_liquid() {
        let content = r#"

    <title>{{ title }}</title>

{% comment %}Content here{% endcomment %}

        "#;

        let tokenizer = Tokenizer::new(&content);
        assert_tokens(&tokenizer, vec![
            "\n\n  \n    <title>",
            "{{ title }}",
            "</title>\n  \n  \n
",
            "{% comment %}",
            "Content here",
            "{% endcomment %}",
            "
\n    \n  \n\n        "
        ]);
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

mod generated;
use self::generated::EFFECTIVENESS_CHART;

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Effectiveness {
    DoubleNotVery,
    NotVery,
    Normal,
    SuperEffective
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum PokeType {
    Normal = 0,
    Fighting = 1,
    Flying = 2,
    Poison = 3,
    Ground = 4,
    Rock = 5,
    Bug = 6,
    Ghost = 7,
    Steel = 8,
    Fire = 9,
    Water = 10,
    Grass = 11,
    Electric = 12,
    Psychic = 13,
    Ice = 14,
    Dragon = 15,
    Dark = 16,
    Fairy = 17
}

impl PokeType {
    pub fn efficacy_against(&self, other: PokeType) -> Effectiveness {
        EFFECTIVENESS_CHART[*self as usize][other as usize]
    }
}

impl FromStr for PokeType {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let lowercase = s.to_lowercase();

        match lowercase.as_ref() {
            "normal" => Ok(PokeType::Normal),
            "fighting" => Ok(PokeType::Fighting),
            "flying" => Ok(PokeType::Flying),
            "poison" => Ok(PokeType::Poison),
            "ground" => Ok(PokeType::Ground),
            "rock" => Ok(PokeType::Rock),
            "bug" => Ok(PokeType::Bug),
            "ghost" => Ok(PokeType::Ghost),
            "steel" => Ok(PokeType::Steel),
            "fire" => Ok(PokeType::Fire),
            "water" => Ok(PokeType::Water),
            "grass" => Ok(PokeType::Grass),
            "electric" => Ok(PokeType::Electric),
            "psychic" => Ok(PokeType::Psychic),
            "ice" => Ok(PokeType::Ice),
            "dragon" => Ok(PokeType::Dragon),
            "dark" => Ok(PokeType::Dark),
            "fairy" => Ok(PokeType::Fairy),
            _ => Err(())
        }
    }
}

<commit_msg>Add tests for efficacy_against<commit_after>use std::str::FromStr;

mod generated;
use self::generated::EFFECTIVENESS_CHART;

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Effectiveness {
    DoubleNotVery,
    NotVery,
    Normal,
    SuperEffective
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum PokeType {
    Normal = 0,
    Fighting = 1,
    Flying = 2,
    Poison = 3,
    Ground = 4,
    Rock = 5,
    Bug = 6,
    Ghost = 7,
    Steel = 8,
    Fire = 9,
    Water = 10,
    Grass = 11,
    Electric = 12,
    Psychic = 13,
    Ice = 14,
    Dragon = 15,
    Dark = 16,
    Fairy = 17
}

impl PokeType {
    pub fn efficacy_against(&self, other: PokeType) -> Effectiveness {
        EFFECTIVENESS_CHART[*self as usize][other as usize]
    }
}

impl FromStr for PokeType {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let lowercase = s.to_lowercase();

        match lowercase.as_ref() {
            "normal" => Ok(PokeType::Normal),
            "fighting" => Ok(PokeType::Fighting),
            "flying" => Ok(PokeType::Flying),
            "poison" => Ok(PokeType::Poison),
            "ground" => Ok(PokeType::Ground),
            "rock" => Ok(PokeType::Rock),
            "bug" => Ok(PokeType::Bug),
            "ghost" => Ok(PokeType::Ghost),
            "steel" => Ok(PokeType::Steel),
            "fire" => Ok(PokeType::Fire),
            "water" => Ok(PokeType::Water),
            "grass" => Ok(PokeType::Grass),
            "electric" => Ok(PokeType::Electric),
            "psychic" => Ok(PokeType::Psychic),
            "ice" => Ok(PokeType::Ice),
            "dragon" => Ok(PokeType::Dragon),
            "dark" => Ok(PokeType::Dark),
            "fairy" => Ok(PokeType::Fairy),
            _ => Err(())
        }
    }
}

#[cfg(test)]
mod tests {
    use ::{PokeType, Effectiveness};

    #[test]
    fn efficacy_against() {
        let electric_v_ground = PokeType::Electric.efficacy_against(PokeType::Ground);
        assert_eq!(Effectiveness::DoubleNotVery, electric_v_ground);

        let grass_v_bug = PokeType::Grass.efficacy_against(PokeType::Bug);
        assert_eq!(Effectiveness::NotVery, grass_v_bug);

        let poison_v_flying = PokeType::Poison.efficacy_against(PokeType::Flying);
        assert_eq!(Effectiveness::Normal, poison_v_flying);

        let fighting_v_normal = PokeType::Fighting.efficacy_against(PokeType::Normal);
        assert_eq!(Effectiveness::SuperEffective, fighting_v_normal);
    }
}
<|endoftext|>