<commit_before>use gltf_derive::Validate;
use serde_derive::{Serialize, Deserialize};
use serde::{de, ser};
use std::fmt;
use crate::validation::{Checked, Error, Validate};
use crate::{accessor, extensions, scene, Extras, Index, Path, Root};

/// All valid animation interpolation algorithms.
pub const VALID_INTERPOLATIONS: &'static [&'static str] = &[
    "LINEAR",
    "STEP",
    "CATMULLROMSPLINE",
    "CUBICSPLINE",
];

/// All valid animation property names.
pub const VALID_PROPERTIES: &'static [&'static str] = &[
    "translation",
    "rotation",
    "scale",
    "weights",
];

/// Specifies an interpolation algorithm.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Interpolation {
    /// Linear interpolation.
    ///
    /// The animated values are linearly interpolated between keyframes.
    /// When targeting a rotation, spherical linear interpolation (slerp) should be
    /// used to interpolate quaternions. The number output of elements must equal
    /// the number of input elements.
    Linear = 1,

    /// Step interpolation.
    ///
    /// The animated values remain constant to the output of the first keyframe,
    /// until the next keyframe. The number of output elements must equal the number
    /// of input elements.
    Step,

    /// Uniform Catmull-Rom spline interpolation.
    ///
    /// The animation's interpolation is computed using a uniform Catmull-Rom spline.
    /// The number of output elements must equal two more than the number of input
    /// elements. The first and last output elements represent the start and end
    /// tangents of the spline. There must be at least four keyframes when using this
    /// interpolation.
    CatmullRomSpline,

    /// Cubic spline interpolation.
    ///
    /// The animation's interpolation is computed using a cubic spline with specified
    /// tangents. The number of output elements must equal three times the number of
    /// input elements. For each input element, the output stores three elements, an
    /// in-tangent, a spline vertex, and an out-tangent. There must be at least two
    /// keyframes when using this interpolation
    CubicSpline,
}

/// Specifies a property to animate.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Property {
    /// XYZ translation vector.
    Translation = 1,

    /// XYZW rotation quaternion.
    Rotation,

    /// XYZ scale vector.
    Scale,

    /// Weights of morph targets.
    MorphTargetWeights,
}

/// A keyframe animation.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Animation {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Animation>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// An array of channels, each of which targets an animation's sampler at a
    /// node's property.
    ///
    /// Different channels of the same animation must not have equal targets.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub channels: Vec<Channel>,

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
    pub name: Option<String>,

    /// An array of samplers that combine input and output accessors with an
    /// interpolation algorithm to define a keyframe graph (but not its target).
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub samplers: Vec<Sampler>,
}

/// Targets an animation's sampler at a node's property.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Channel {
    /// The index of a sampler in this animation used to compute the value for the
    /// target.
    pub sampler: Index<Sampler>,

    /// The index of the node and TRS property to target.
    pub target: Target,

    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Channel>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,
}

/// The index of the node and TRS property that an animation channel targets.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Target {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Target>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of the node to target.
    pub node: Index<scene::Node>,

    /// The name of the node's property to modify or the 'weights' of the
    /// morph targets it instantiates.
    pub path: Checked<Property>,
}

/// Defines a keyframe graph but not its target.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Sampler {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Sampler>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of an accessor containing keyframe input values, e.g., time.
    pub input: Index<accessor::Accessor>,

    /// The interpolation algorithm.
    #[serde(default)]
    pub interpolation: Checked<Interpolation>,

    /// The index of an accessor containing keyframe output values.
    pub output: Index<accessor::Accessor>,
}

impl Validate for Animation {
    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&dyn Fn() -> Path, Error),
    {
        self.samplers.validate(root, || path().field("samplers"), report);
        for (index, channel) in self.channels.iter().enumerate() {
            if channel.sampler.value() as usize >= self.samplers.len() {
                let path = || path().field("channels").index(index).field("sampler");
                report(&path, Error::IndexOutOfBounds);
            }
        }
    }
}

impl Default for Interpolation {
    fn default() -> Self {
        Interpolation::Linear
    }
}

impl<'de> de::Deserialize<'de> for Checked<Interpolation> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Interpolation>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_INTERPOLATIONS)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Interpolation::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "LINEAR" => Valid(Linear),
                    "STEP" => Valid(Step),
                    "CATMULLROMSPLINE" => Valid(CatmullRomSpline),
                    "CUBICSPLINE" => Valid(CubicSpline),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Interpolation {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Interpolation::Linear => "LINEAR",
            Interpolation::Step => "STEP",
            Interpolation::CatmullRomSpline => "CATMULLROMSPLINE",
            Interpolation::CubicSpline => "CUBICSPLINE",
        })
    }
}

impl<'de> de::Deserialize<'de> for Checked<Property> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Property>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_PROPERTIES)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Property::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "translation" => Valid(Translation),
                    "rotation" => Valid(Rotation),
                    "scale" => Valid(Scale),
                    "weights" => Valid(MorphTargetWeights),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Property {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Property::Translation => "translation",
            Property::Rotation => "rotation",
            Property::Scale => "scale",
            Property::MorphTargetWeights => "weights",
        })
    }
}
<commit_msg>Remove CatmullRomSpline<commit_after>use gltf_derive::Validate;
use serde_derive::{Serialize, Deserialize};
use serde::{de, ser};
use std::fmt;
use crate::validation::{Checked, Error, Validate};
use crate::{accessor, extensions, scene, Extras, Index, Path, Root};

/// All valid animation interpolation algorithms.
pub const VALID_INTERPOLATIONS: &'static [&'static str] = &[
    "LINEAR",
    "STEP",
    "CUBICSPLINE",
];

/// All valid animation property names.
pub const VALID_PROPERTIES: &'static [&'static str] = &[
    "translation",
    "rotation",
    "scale",
    "weights",
];

/// Specifies an interpolation algorithm.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Interpolation {
    /// Linear interpolation.
    ///
    /// The animated values are linearly interpolated between keyframes.
    /// When targeting a rotation, spherical linear interpolation (slerp) should be
    /// used to interpolate quaternions. The number output of elements must equal
    /// the number of input elements.
    Linear = 1,

    /// Step interpolation.
    ///
    /// The animated values remain constant to the output of the first keyframe,
    /// until the next keyframe. The number of output elements must equal the number
    /// of input elements.
    Step,

    /// Cubic spline interpolation.
    ///
    /// The animation's interpolation is computed using a cubic spline with specified
    /// tangents. The number of output elements must equal three times the number of
    /// input elements. For each input element, the output stores three elements, an
    /// in-tangent, a spline vertex, and an out-tangent. There must be at least two
    /// keyframes when using this interpolation
    CubicSpline,
}

/// Specifies a property to animate.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Property {
    /// XYZ translation vector.
    Translation = 1,

    /// XYZW rotation quaternion.
    Rotation,

    /// XYZ scale vector.
    Scale,

    /// Weights of morph targets.
    MorphTargetWeights,
}

/// A keyframe animation.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Animation {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Animation>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// An array of channels, each of which targets an animation's sampler at a
    /// node's property.
    ///
    /// Different channels of the same animation must not have equal targets.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub channels: Vec<Channel>,

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
    pub name: Option<String>,

    /// An array of samplers that combine input and output accessors with an
    /// interpolation algorithm to define a keyframe graph (but not its target).
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub samplers: Vec<Sampler>,
}

/// Targets an animation's sampler at a node's property.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Channel {
    /// The index of a sampler in this animation used to compute the value for the
    /// target.
    pub sampler: Index<Sampler>,

    /// The index of the node and TRS property to target.
    pub target: Target,

    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Channel>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,
}

/// The index of the node and TRS property that an animation channel targets.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Target {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Target>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of the node to target.
    pub node: Index<scene::Node>,

    /// The name of the node's property to modify or the 'weights' of the
    /// morph targets it instantiates.
    pub path: Checked<Property>,
}

/// Defines a keyframe graph but not its target.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Sampler {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Sampler>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of an accessor containing keyframe input values, e.g., time.
    pub input: Index<accessor::Accessor>,

    /// The interpolation algorithm.
    #[serde(default)]
    pub interpolation: Checked<Interpolation>,

    /// The index of an accessor containing keyframe output values.
    pub output: Index<accessor::Accessor>,
}

impl Validate for Animation {
    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&dyn Fn() -> Path, Error),
    {
        self.samplers.validate(root, || path().field("samplers"), report);
        for (index, channel) in self.channels.iter().enumerate() {
            if channel.sampler.value() as usize >= self.samplers.len() {
                let path = || path().field("channels").index(index).field("sampler");
                report(&path, Error::IndexOutOfBounds);
            }
        }
    }
}

impl Default for Interpolation {
    fn default() -> Self {
        Interpolation::Linear
    }
}

impl<'de> de::Deserialize<'de> for Checked<Interpolation> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Interpolation>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_INTERPOLATIONS)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Interpolation::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "LINEAR" => Valid(Linear),
                    "STEP" => Valid(Step),
                    "CUBICSPLINE" => Valid(CubicSpline),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Interpolation {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Interpolation::Linear => "LINEAR",
            Interpolation::Step => "STEP",
            Interpolation::CubicSpline => "CUBICSPLINE",
        })
    }
}

impl<'de> de::Deserialize<'de> for Checked<Property> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Property>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_PROPERTIES)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Property::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "translation" => Valid(Translation),
                    "rotation" => Valid(Rotation),
                    "scale" => Valid(Scale),
                    "weights" => Valid(MorphTargetWeights),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Property {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Property::Translation => "translation",
            Property::Rotation => "rotation",
            Property::Scale => "scale",
            Property::MorphTargetWeights => "weights",
        })
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nicholas Bishop
//
// Closest-point method adapted from "Real-Time Collision Detection"
// by Christer Ericson, published by Morgan Kaufmann Publishers,
// Copyright 2005 Elsevier Inc
//
// 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 vector::{distance3, dot3, lerp3, Vec3f};
use range::InclusiveRange;

pub type Rangef = InclusiveRange<f32>;

/// Line segment between two points
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Segment3f {
    pub start: Vec3f,
    pub end: Vec3f
}

impl Segment3f {
    /// Create a segment between two points
    pub fn new(start: &Vec3f, end: &Vec3f) -> Segment3f {
        Segment3f {
            start: *start,
            end: *end
        }
    }

    /// Get vector from start to end (not normalized)
    pub fn to_vec3f(&self) -> Vec3f {
        self.end - self.start
    }

    /// Length of the line segment
    pub fn length(&self) -> f32 {
        distance3(self.start, self.end)
    }

    /// Convert a distance in coordinate space to a distance in the
    /// line segment's parametric space. The sign of the input is
    /// kept.
    pub fn distance_to_parametric_delta(&self, distance: f32) -> f32 {
        distance / self.length()
    }

    /// Convert a parametric delta to coordinate space. The sign of
    /// the input is kept.
    pub fn distance_from_parametric_delta(&self, delta: f32) -> f32 {
        delta * self.length()
    }

    /// Linearly interpolate between the segment's endpoints by the
    /// factor `t`. When `t` is zero the result is `self.start`, and
    /// when `t` is one the result is `self.end`. The range of `t` is
    /// not clamped.
    pub fn point_from_parametric(&self, t: f32) -> Vec3f {
        lerp3(&self.start, &self.end, t)
    }

    /// Treat the range's start and end as parametric coords. Use
    /// `point_from_parametric` to interpolate the range into a new
    /// segment. The output segment is not clamped.
    pub fn segment_from_parametric_range(&self, r: Rangef) -> Segment3f {
        Segment3f::new(&self.point_from_parametric(r.min),
                       &self.point_from_parametric(r.max))
    }

    /// Find the point on the segment closest to the input point. The
    /// return value contains both the parametric and actual location
    /// of the closest point.
    ///
    /// Adapted from "Real-Time Collision Detection" by Christer
    /// Ericson, published by Morgan Kaufmann Publishers, Copyright
    /// 2005 Elsevier Inc
    pub fn closest_point_to_point(&self, point: &Vec3f) -> (f32, Vec3f) {
        let a = self.start;
        let b = self.end;

        let ab = b - a;
        // Project point onto ab, but deferring divide by dot3(ab, ab)
        let t = dot3(*point - a, ab);
        if t <= 0.0f32 {
            // point projects outside the [a,b] interval, on the a
            // side; clamp to a
            (0.0f32, a)
        } else {
            // Always nonnegative since denom = ||ab|| ∧ 2
            let denom = dot3(ab, ab);
            if t >= denom {
                // point projects outside the [a,b] interval, on the b
                // side; clamp to b
                (1.0f32, b)
            } else {
                // point projects inside the [a,b] interval; must do
                // deferred divide now
                (t / denom, a + ab * t)
            }
        }
    }
}

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

    #[test]
    fn test_to_vec3f() {
        use vector::vec3f;
        assert_eq!(Segment3f::new(&vec3f(0, 0, 0),
                                  &vec3f(2, 3, 4)).to_vec3f(),
                   vec3f(2, 3, 4));

    }

    #[test]
    fn test_segment_length() {
        use vector::vec3f;
        let s = Segment3f::new(&vec3f(0, 0, 0),
                               &vec3f(0, 0, 9));
        assert_eq!(s.length(), 9.0);
    }

    #[test]
    fn test_segment_distance_conversion() {
        use vector::vec3f;
        let s = Segment3f::new(&vec3f(0, 1, 0),
                               &vec3f(0, 7, 0));

        let inputs = [
            (0.0f32, 0.0f32),
            (6.0, 1.0),
            (12.0, 2.0),
            (-3.0, -0.5)];

        for &(distance, delta) in inputs.iter() {
            assert_eq!(s.distance_to_parametric_delta(distance), delta);
            assert_eq!(s.distance_from_parametric_delta(delta), distance);
        }
    }

    #[test]
    fn test_point_from_parametric() {
        use vector::vec3f;
        let s = Segment3f::new(&vec3f(0, 0, -1),
                               &vec3f(0, 0, 3));
        assert_eq!(s.point_from_parametric(0.0), vec3f(0, 0, -1));
        assert_eq!(s.point_from_parametric(1.0), vec3f(0, 0, 3));
        assert_eq!(s.point_from_parametric(0.5), vec3f(0, 0, 1));
    }

    #[test]
    fn test_segment_from_parametric_range() {
        use vector::vec3f;
        fn make_seg(a: u32, b: u32) -> Segment3f {
            Segment3f::new(&vec3f(a, 0, 0), &vec3f(b, 0, 0))
        }
        let s = make_seg(0, 4);
        assert_eq!(s.segment_from_parametric_range(Rangef::new(0.0, 1.0)),
                   make_seg(0, 4));
        assert_eq!(s.segment_from_parametric_range(Rangef::new(0.25, 0.75)),
                   make_seg(1, 3));
    }

    #[test]
    fn test_segment_closest_point_to_point() {
        use vector::vec3f;
        let s = Segment3f::new(&vec3f(2, 0, 0),
                               &vec3f(3, 0, 0));
        assert_eq!(s.closest_point_to_point(&vec3f(1, 0, 0)),
                   (0.0, vec3f(2, 0, 0)));
        assert_eq!(s.closest_point_to_point(&vec3f(4, 0, 0)),
                   (1.0, vec3f(3, 0, 0)));
        assert_eq!(s.closest_point_to_point(&vec3f(2.5, 1, 0)),
                   (0.5, vec3f(2.5, 0, 0)));
    }
}
<commit_msg>Minor code cleanup<commit_after>// Copyright 2015 Nicholas Bishop
//
// Closest-point method adapted from "Real-Time Collision Detection"
// by Christer Ericson, published by Morgan Kaufmann Publishers,
// Copyright 2005 Elsevier Inc
//
// 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 vector::{distance3, dot3, lerp3, Vec3f};
use range::InclusiveRange;

pub type Rangef = InclusiveRange<f32>;

/// Line segment between two points
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Segment3f {
    pub start: Vec3f,
    pub end: Vec3f
}

impl Segment3f {
    /// Create a segment between two points
    pub fn new(start: &Vec3f, end: &Vec3f) -> Segment3f {
        Segment3f {
            start: *start,
            end: *end
        }
    }

    /// Get vector from start to end (not normalized)
    pub fn to_vec3f(&self) -> Vec3f {
        self.end - self.start
    }

    /// Length of the line segment
    pub fn length(&self) -> f32 {
        distance3(self.start, self.end)
    }

    /// Convert a distance in coordinate space to a distance in the
    /// line segment's parametric space. The sign of the input is
    /// kept.
    pub fn distance_to_parametric_delta(&self, distance: f32) -> f32 {
        distance / self.length()
    }

    /// Convert a parametric delta to coordinate space. The sign of
    /// the input is kept.
    pub fn distance_from_parametric_delta(&self, delta: f32) -> f32 {
        delta * self.length()
    }

    /// Linearly interpolate between the segment's endpoints by the
    /// factor `t`. When `t` is zero the result is `self.start`, and
    /// when `t` is one the result is `self.end`. The range of `t` is
    /// not clamped.
    pub fn point_from_parametric(&self, t: f32) -> Vec3f {
        lerp3(&self.start, &self.end, t)
    }

    /// Treat the range's start and end as parametric coords. Use
    /// `point_from_parametric` to interpolate the range into a new
    /// segment. The output segment is not clamped.
    pub fn segment_from_parametric_range(&self, r: Rangef) -> Segment3f {
        Segment3f::new(&self.point_from_parametric(r.min),
                       &self.point_from_parametric(r.max))
    }

    /// Find the point on the segment closest to the input point. The
    /// return value contains both the parametric and actual location
    /// of the closest point.
    ///
    /// Adapted from "Real-Time Collision Detection" by Christer
    /// Ericson, published by Morgan Kaufmann Publishers, Copyright
    /// 2005 Elsevier Inc
    pub fn closest_point_to_point(&self, point: &Vec3f) -> (f32, Vec3f) {
        let a = self.start;
        let b = self.end;

        let ab = b - a;
        // Project point onto ab, but deferring divide by dot3(ab, ab)
        let t = dot3(*point - a, ab);
        if t <= 0.0f32 {
            // point projects outside the [a,b] interval, on the a
            // side; clamp to a
            (0.0f32, a)
        } else {
            // Always nonnegative since denom = ||ab|| ∧ 2
            let denom = dot3(ab, ab);
            if t >= denom {
                // point projects outside the [a,b] interval, on the b
                // side; clamp to b
                (1.0f32, b)
            } else {
                // point projects inside the [a,b] interval; must do
                // deferred divide now
                (t / denom, a + ab * t)
            }
        }
    }
}

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

    #[test]
    fn test_to_vec3f() {
        assert_eq!(Segment3f::new(&vec3f(0, 0, 0),
                                  &vec3f(2, 3, 4)).to_vec3f(),
                   vec3f(2, 3, 4));

    }

    #[test]
    fn test_segment_length() {
        let s = Segment3f::new(&vec3f(0, 0, 0),
                               &vec3f(0, 0, 9));
        assert_eq!(s.length(), 9.0);
    }

    #[test]
    fn test_segment_distance_conversion() {
        let s = Segment3f::new(&vec3f(0, 1, 0),
                               &vec3f(0, 7, 0));

        let inputs = [
            (0.0f32, 0.0f32),
            (6.0, 1.0),
            (12.0, 2.0),
            (-3.0, -0.5)];

        for &(distance, delta) in inputs.iter() {
            assert_eq!(s.distance_to_parametric_delta(distance), delta);
            assert_eq!(s.distance_from_parametric_delta(delta), distance);
        }
    }

    #[test]
    fn test_point_from_parametric() {
        let s = Segment3f::new(&vec3f(0, 0, -1),
                               &vec3f(0, 0, 3));
        assert_eq!(s.point_from_parametric(0.0), vec3f(0, 0, -1));
        assert_eq!(s.point_from_parametric(1.0), vec3f(0, 0, 3));
        assert_eq!(s.point_from_parametric(0.5), vec3f(0, 0, 1));
    }

    #[test]
    fn test_segment_from_parametric_range() {
        fn make_seg(a: u32, b: u32) -> Segment3f {
            Segment3f::new(&vec3f(a, 0, 0), &vec3f(b, 0, 0))
        }
        let s = make_seg(0, 4);
        assert_eq!(s.segment_from_parametric_range(Rangef::new(0.0, 1.0)),
                   make_seg(0, 4));
        assert_eq!(s.segment_from_parametric_range(Rangef::new(0.25, 0.75)),
                   make_seg(1, 3));
    }

    #[test]
    fn test_segment_closest_point_to_point() {
        let s = Segment3f::new(&vec3f(2, 0, 0),
                               &vec3f(3, 0, 0));
        assert_eq!(s.closest_point_to_point(&vec3f(1, 0, 0)),
                   (0.0, vec3f(2, 0, 0)));
        assert_eq!(s.closest_point_to_point(&vec3f(4, 0, 0)),
                   (1.0, vec3f(3, 0, 0)));
        assert_eq!(s.closest_point_to_point(&vec3f(2.5, 1, 0)),
                   (0.5, vec3f(2.5, 0, 0)));
    }
}
<|endoftext|>


<commit_before>//! This LintPass catches both string addition and string addition + assignment
//!
//! Note that since we have two lints where one subsumes the other, we try to
//! disable the subsumed lint unless it has a higher level

use rustc::lint::*;
use rustc::middle::ty::TypeVariants::TyStruct;
use syntax::ast::*;
use syntax::codemap::{Span, Spanned};
use eq_op::is_exp_equal;
use types::match_ty_unwrap;
use utils::{match_def_path, span_lint, walk_ptrs_ty, get_parent_expr};

declare_lint! {
    pub STRING_ADD_ASSIGN,
    Warn,
    "Warn on `x = x + ..` where x is a `String`"
}

declare_lint! {
    pub STRING_ADD,
    Allow,
    "Warn on `x + ..` where x is a `String`"
}

#[derive(Copy, Clone)]
pub struct StringAdd;

impl LintPass for StringAdd {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD, STRING_ADD_ASSIGN)
    }

    fn check_expr(&mut self, cx: &Context, e: &Expr) {
        if let &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) = &e.node {
            if is_string(cx, left) {
                if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
                    // the string_add_assign is allow, so no duplicates
                } else {
                    let parent = get_parent_expr(cx, e);
                    if let Some(ref p) = parent {
                        if let &ExprAssign(ref target, _) = &p.node {
                            // avoid duplicate matches
                            if is_exp_equal(target, left) { return; }
                        }
                    }
                }
                //TODO check for duplicates
                 span_lint(cx, STRING_ADD, e.span,
                        "you add something to a string. \
                        Consider using `String::push_str()` instead.")
            }
        } else if let &ExprAssign(ref target, ref  src) = &e.node {
            if is_string(cx, target) && is_add(src, target) {
                span_lint(cx, STRING_ADD_ASSIGN, e.span,
                    "you assign the result of adding something to this string. \
                    Consider using `String::push_str()` instead.")
            }
        }
    }
}

fn is_string(cx: &Context, e: &Expr) -> bool {
    let ty = walk_ptrs_ty(cx.tcx.expr_ty(e));
    if let TyStruct(did, _) = ty.sty {
        match_def_path(cx, did.did, &["collections", "string", "String"])
    } else { false }
}

fn is_add(src: &Expr, target: &Expr) -> bool {
    match &src.node {
        &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) =>
            is_exp_equal(target, left),
        &ExprBlock(ref block) => block.stmts.is_empty() &&
            block.expr.as_ref().map_or(false, |expr| is_add(&*expr, target)),
        &ExprParen(ref expr) => is_add(&*expr, target),
        _ => false
    }
}
<commit_msg>grammar<commit_after>//! This LintPass catches both string addition and string addition + assignment
//!
//! Note that since we have two lints where one subsumes the other, we try to
//! disable the subsumed lint unless it has a higher level

use rustc::lint::*;
use rustc::middle::ty::TypeVariants::TyStruct;
use syntax::ast::*;
use syntax::codemap::{Span, Spanned};
use eq_op::is_exp_equal;
use types::match_ty_unwrap;
use utils::{match_def_path, span_lint, walk_ptrs_ty, get_parent_expr};

declare_lint! {
    pub STRING_ADD_ASSIGN,
    Warn,
    "Warn on `x = x + ..` where x is a `String`"
}

declare_lint! {
    pub STRING_ADD,
    Allow,
    "Warn on `x + ..` where x is a `String`"
}

#[derive(Copy, Clone)]
pub struct StringAdd;

impl LintPass for StringAdd {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD, STRING_ADD_ASSIGN)
    }

    fn check_expr(&mut self, cx: &Context, e: &Expr) {
        if let &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) = &e.node {
            if is_string(cx, left) {
                if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
                    // the string_add_assign is allow, so no duplicates
                } else {
                    let parent = get_parent_expr(cx, e);
                    if let Some(ref p) = parent {
                        if let &ExprAssign(ref target, _) = &p.node {
                            // avoid duplicate matches
                            if is_exp_equal(target, left) { return; }
                        }
                    }
                }
                //TODO check for duplicates
                 span_lint(cx, STRING_ADD, e.span,
                        "you added something to a string. \
                        Consider using `String::push_str()` instead.")
            }
        } else if let &ExprAssign(ref target, ref  src) = &e.node {
            if is_string(cx, target) && is_add(src, target) {
                span_lint(cx, STRING_ADD_ASSIGN, e.span,
                    "you assigned the result of adding something to this string. \
                    Consider using `String::push_str()` instead.")
            }
        }
    }
}

fn is_string(cx: &Context, e: &Expr) -> bool {
    let ty = walk_ptrs_ty(cx.tcx.expr_ty(e));
    if let TyStruct(did, _) = ty.sty {
        match_def_path(cx, did.did, &["collections", "string", "String"])
    } else { false }
}

fn is_add(src: &Expr, target: &Expr) -> bool {
    match &src.node {
        &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) =>
            is_exp_equal(target, left),
        &ExprBlock(ref block) => block.stmts.is_empty() &&
            block.expr.as_ref().map_or(false, |expr| is_add(&*expr, target)),
        &ExprParen(ref expr) => is_add(&*expr, target),
        _ => false
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]

use libc::*;

pub type xmpp_handler = Option<extern "C" fn(*const xmpp_conn_t, *const xmpp_stanza_t, *const c_void)>;
pub type xmpp_conn_handler = Option<extern "C" fn(*const xmpp_conn_t, xmpp_conn_event_t, c_int, *const xmpp_stream_error_t, *const c_void)>;
pub type xmpp_timed_handler = Option<extern "C" fn(*const xmpp_conn_t, *const xmpp_stanza_t, *const c_void)>;
pub type xmpp_log_handler = Option<extern "C" fn(*const c_void, xmpp_log_level_t, *const c_char, *const c_char, *const c_char)>;
pub type xmpp_open_handler = Option<extern "C" fn(*const xmpp_conn_t)>;

pub type hash_free_func = Option<extern "C" fn(*const xmpp_ctx_t, *mut c_void)>;

#[repr(C)]
pub struct xmpp_conn_t {
  _ref: c_uint,
  ctx: *mut xmpp_ctx_t,
  _type: c_int,

  state: c_int,
  timeout_stamp: uint64_t,
  error: c_int,
  stream_error: *mut xmpp_stream_error_t,
  sock: c_int,
  tls: *mut c_void,

  tls_support: c_int,
  tls_disabled: c_int,
  tls_failed: c_int,
  sasl_support: c_int,
  secured: c_int,

  bind_required: c_int,
  session_required: c_int,

  lang: *mut c_char,
  domain: *mut c_char,
  connectdomain: *mut c_char,
  connectport: *mut c_char,
  jid: *mut c_char,
  pass: *mut c_char,
  bound_jid: *mut c_char,
  stream_id: *mut c_char,

  blocking_seed: c_int,
  send_queue_max: c_int,
  send_queue_len: c_int,
  send_queue_head: *mut c_void,
  send_queue_tail: *mut c_void,

  reset_parser: c_int,
  parser: *mut c_void,
  connect_timeout: c_uint,
  open_handler: xmpp_open_handler,
  authenticated: c_int,

  conn_handler: xmpp_conn_handler,
  userdata: *mut c_void,

  timed_handlers: *mut c_void,
  id_handlers: *mut hash_t,
  handlers: *mut c_void
}

#[repr(C)]
pub struct xmpp_stanza_t {
  _ref: c_int,
  ctx: *mut xmpp_ctx_t,
  _type: c_int,
  prev: *mut xmpp_stanza_t,
  next: *mut xmpp_stanza_t,
  children: *mut xmpp_stanza_t,
  parent: *mut xmpp_stanza_t,
  data: *mut c_char,
  attributes: *mut hash_t
}

#[repr(C)]
pub struct xmpp_ctx_t {
  mem: *const xmpp_mem_t,
  log: *const xmpp_log_t,
  loop_status: c_int,
  connlist: *mut xmpp_connlist_t
}

#[repr(C)]
pub struct xmpp_connlist_t {
  conn: *mut xmpp_conn_t,
  next: *mut xmpp_connlist_t
}

#[repr(C)]
pub struct xmpp_mem_t {
  alloc: Option<extern "C" fn(size_t, *mut u8) -> *mut u8>,
  free: Option<extern "C" fn(*mut u8, *const u8)>,
  realloc: Option<extern "C" fn(*mut u8, size_t, *const u8) -> *mut u8>,
  userdata: *mut c_void
}

#[repr(C)]
pub struct xmpp_log_t {
  handler: xmpp_log_handler,
  userdata: *mut c_void
}

#[repr(C)]
pub struct xmpp_stream_error_t {
  _type: c_int, //xmpp_error_type_t
  text: *mut c_char,
  stanza: *mut xmpp_stanza_t
}

#[repr(C)]
pub struct hashentry_t {
  next: *mut hashentry_t,
  key: *mut c_char,
  values: *mut c_void
}

#[repr(C)]
pub struct hash_t {
  _ref: c_uint,
  ctx: *mut xmpp_ctx_t,
  free: hash_free_func,
  length: c_int,
  num_keys: c_int,
  entries: *mut *mut hashentry_t
}

#[repr(C)]
pub enum xmpp_log_level_t {
  XMPP_LEVEL_DEBUG,
  XMPP_LEVEL_INFO,
  XMPP_LEVEL_WARN,
  XMPP_LEVEL_ERROR
}

#[repr(C)]
pub enum xmpp_conn_type_t {
  XMPP_UNKNOWN,
  XMPP_CLIENT,
  XMPP_COMPONENT
}

#[repr(C)]
pub enum xmpp_conn_event_t {
  XMPP_CONN_CONNECT,
  XMPP_CONN_DISCONNECT,
  XMPP_CONN_FAIL
}

#[repr(C)]
pub enum xmpp_error_type_t {
  XMPP_SE_BAD_FORMAT,
  XMPP_SE_BAD_NS_PREFIX,
  XMPP_SE_CONFLICT,
  XMPP_SE_CONN_TIMEOUT,
  XMPP_SE_HOST_GONE,
  XMPP_SE_HOST_UNKNOWN,
  XMPP_SE_IMPROPER_ADDR,
  XMPP_SE_INTERNAL_SERVER_ERROR,
  XMPP_SE_INVALID_FROM,
  XMPP_SE_INVALID_ID,
  XMPP_SE_INVALID_NS,
  XMPP_SE_INVALID_XML,
  XMPP_SE_NOT_AUTHORIZED,
  XMPP_SE_POLICY_VIOLATION,
  XMPP_SE_REMOTE_CONN_FAILED,
  XMPP_SE_RESOURCE_CONSTRAINT,
  XMPP_SE_RESTRICTED_XML,
  XMPP_SE_SEE_OTHER_HOST,
  XMPP_SE_SYSTEM_SHUTDOWN,
  XMPP_SE_UNDEFINED_CONDITION,
  XMPP_SE_UNSUPPORTED_ENCODING,
  XMPP_SE_UNSUPPORTED_STANZA_TYPE,
  XMPP_SE_UNSUPPORTED_VERSION,
  XMPP_SE_XML_NOT_WELL_FORMED
}

pub const XMPP_NS_CLIENT: &'static str = "jabber:client";
pub const XMPP_NS_COMPONENT: &'static str = "jabber:component:accept";
pub const XMPP_NS_STREAMS: &'static str = "http://etherx.jabber.org/streams";
pub const XMPP_NS_STREAMS_IETF: &'static str = "urn:ietf:params:xml:ns:xmpp-streams";
pub const XMPP_NS_TLS: &'static str = "urn:ietf:params:xml:ns:xmpp-tls";
pub const XMPP_NS_SASL: &'static str = "urn:ietf:params:xml:ns:xmpp-sasl";
pub const XMPP_NS_BIND: &'static str = "urn:ietf:params:xml:ns:xmpp-bind";
pub const XMPP_NS_SESSION: &'static str = "urn:ietf:params:xml:ns:xmpp-session";
pub const XMPP_NS_AUTH: &'static str = "jabber:iq:auth";
pub const XMPP_NS_DISCO_INFO: &'static str = "http://jabber.org/protocol/disco#info";
pub const XMPP_NS_DISCO_ITEMS: &'static str = "http://jabber.org/protocol/disco#items";
pub const XMPP_NS_ROSTER: &'static str = "jabber:iq:roster";

pub const XMPP_EOK:    c_int = 0;
pub const XMPP_EMEM:   c_int = -1;
pub const XMPP_EINVOP: c_int = -2;
pub const XMPP_EINT:   c_int = -3;

#[link(name="strophe")]
extern {
  // connection management
  pub fn xmpp_conn_new(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
  pub fn xmpp_conn_close(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
  pub fn xmpp_conn_release(conn: *const xmpp_conn_t) -> c_int;
  pub fn xmpp_conn_get_jid(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_get_bound_jid(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_set_jid(conn: *const xmpp_conn_t, jid: *const c_char);
  pub fn xmpp_conn_get_pass(conn: *const xmpp_conn_t) -> *const c_char;
  pub fn xmpp_conn_set_pass(conn: *const xmpp_conn_t, pass: *const c_char);
  pub fn xmpp_conn_get_context(conn: *const xmpp_conn_t) -> *mut xmpp_ctx_t;
  pub fn xmpp_connect_client(conn: *const xmpp_conn_t, altdomain: *const c_char, altport: c_ushort, callback: xmpp_conn_handler, userdata: *const u8) -> c_int;
  pub fn xmpp_disconnect(conn: *const xmpp_conn_t);
  pub fn xmpp_send(conn: *const xmpp_conn_t, stanza: *const xmpp_stanza_t);

  // context objects
  pub fn xmpp_get_default_logger(level: xmpp_log_level_t) -> *mut xmpp_log_t;
  pub fn xmpp_ctx_new(mem: *const xmpp_mem_t, log: *const xmpp_log_t) -> *mut xmpp_ctx_t;
  pub fn xmpp_ctx_free(ctx: *const xmpp_ctx_t);

  // initialization, shutdown and versioning
  pub fn xmpp_initialize();
  pub fn xmpp_shutdown();
  pub fn xmpp_version_check(major: c_int, minor: c_int) -> c_int;

  // event loop
  pub fn xmpp_run_once(ctx: *mut xmpp_ctx_t, timeout: c_ulong);
  pub fn xmpp_run(ctx: *mut xmpp_ctx_t);
  pub fn xmpp_stop(ctx: *mut xmpp_ctx_t);

  // stanza and timed event handlers
  pub fn xmpp_timed_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_timed_handler);
  pub fn xmpp_id_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler, id: *const c_char);
  pub fn xmpp_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler);
  pub fn xmpp_timed_handler_add(conn: *const xmpp_conn_t, handler: xmpp_timed_handler, period: c_ulong, userdata: *const c_void);
  pub fn xmpp_id_handler_add(conn: *const xmpp_conn_t, handler: xmpp_handler, id: *const c_char, userdata: *const c_void);
  pub fn xmpp_handler_add(conn: *const xmpp_conn_t, handler: xmpp_handler, ns: *const c_char, name: *const c_char, _type: *const c_char, userdata: *const c_void);

  // stanza creation and manipulation
  pub fn xmpp_stanza_new(ctx: *mut xmpp_ctx_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_clone(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_copy(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_release(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_is_text(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_is_tag(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_to_text(stanza: *mut xmpp_stanza_t, buf: *const *const c_char, sizelen: *const size_t) -> c_int;
  pub fn xmpp_stanza_set_name(stanza: *mut xmpp_stanza_t, name: *const c_char) -> c_int;
  pub fn xmpp_stanza_get_name(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_attribute_count(stanza: *const xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_get_attributes(stanza: *const xmpp_stanza_t, attr: *const *const c_char, attrlen: c_int) -> c_int;
  pub fn xmpp_stanza_set_attribute(stanza: *const xmpp_stanza_t, key: *const c_char, value: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> c_int;
  pub fn xmpp_stanza_add_child(stanza: *mut xmpp_stanza_t, child: *mut xmpp_stanza_t) -> c_int;
  pub fn xmpp_stanza_set_text(stanza: *mut xmpp_stanza_t, text: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_text_with_size(stanza: *mut xmpp_stanza_t, text: *const c_char, size: size_t) -> c_int;
  pub fn xmpp_stanza_get_id(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_ns(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_type(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_child_by_name(stanza: *const xmpp_stanza_t, name: *const c_char) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_child_by_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_children(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_next(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
  pub fn xmpp_stanza_get_text(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_get_text_ptr(stanza: *const xmpp_stanza_t) -> *mut c_char;
  pub fn xmpp_stanza_set_id(stanza: *const xmpp_stanza_t, id: *const c_char) -> c_int;
  pub fn xmpp_stanza_set_type(stanza: *const xmpp_stanza_t, _type: *const c_char) -> c_int;
  pub fn xmpp_stanza_get_attribute(stanza: *const xmpp_stanza_t, name: *const c_char) -> *mut c_char;
}

<commit_msg>improve indentation<commit_after>#![allow(non_camel_case_types)]

use libc::*;

pub type xmpp_handler = Option<extern "C" fn(*const xmpp_conn_t,
                                             *const xmpp_stanza_t,
                                             *const c_void)>;
pub type xmpp_conn_handler = Option<extern "C" fn(*const xmpp_conn_t,
                                                  xmpp_conn_event_t,
                                                  c_int,
                                                  *const xmpp_stream_error_t,
                                                  *const c_void)>;
pub type xmpp_timed_handler = Option<extern "C" fn(*const xmpp_conn_t,
                                                   *const xmpp_stanza_t,
                                                   *const c_void)>;
pub type xmpp_log_handler = Option<extern "C" fn(*const c_void,
                                                 xmpp_log_level_t,
                                                 *const c_char,
                                                 *const c_char,
                                                 *const c_char)>;
pub type xmpp_open_handler = Option<extern "C" fn(*const xmpp_conn_t)>;

pub type hash_free_func = Option<extern "C" fn(*const xmpp_ctx_t, *mut c_void)>;

#[repr(C)]
pub struct xmpp_conn_t {
    _ref: c_uint,
    ctx: *mut xmpp_ctx_t,
    _type: c_int,

    state: c_int,
    timeout_stamp: uint64_t,
    error: c_int,
    stream_error: *mut xmpp_stream_error_t,
    sock: c_int,
    tls: *mut c_void,

    tls_support: c_int,
    tls_disabled: c_int,
    tls_failed: c_int,
    sasl_support: c_int,
    secured: c_int,

    bind_required: c_int,
    session_required: c_int,

    lang: *mut c_char,
    domain: *mut c_char,
    connectdomain: *mut c_char,
    connectport: *mut c_char,
    jid: *mut c_char,
    pass: *mut c_char,
    bound_jid: *mut c_char,
    stream_id: *mut c_char,

    blocking_seed: c_int,
    send_queue_max: c_int,
    send_queue_len: c_int,
    send_queue_head: *mut c_void,
    send_queue_tail: *mut c_void,

    reset_parser: c_int,
    parser: *mut c_void,
    connect_timeout: c_uint,
    open_handler: xmpp_open_handler,
    authenticated: c_int,

    conn_handler: xmpp_conn_handler,
    userdata: *mut c_void,

    timed_handlers: *mut c_void,
    id_handlers: *mut hash_t,
    handlers: *mut c_void,
}

#[repr(C)]
pub struct xmpp_stanza_t {
    _ref: c_int,
    ctx: *mut xmpp_ctx_t,
    _type: c_int,
    prev: *mut xmpp_stanza_t,
    next: *mut xmpp_stanza_t,
    children: *mut xmpp_stanza_t,
    parent: *mut xmpp_stanza_t,
    data: *mut c_char,
    attributes: *mut hash_t,
}

#[repr(C)]
pub struct xmpp_ctx_t {
    mem: *const xmpp_mem_t,
    log: *const xmpp_log_t,
    loop_status: c_int,
    connlist: *mut xmpp_connlist_t,
}

#[repr(C)]
pub struct xmpp_connlist_t {
    conn: *mut xmpp_conn_t,
    next: *mut xmpp_connlist_t,
}

#[repr(C)]
pub struct xmpp_mem_t {
    alloc: Option<extern "C" fn(size_t, *mut u8) -> *mut u8>,
    free: Option<extern "C" fn(*mut u8, *const u8)>,
    realloc: Option<extern "C" fn(*mut u8, size_t, *const u8) -> *mut u8>,
    userdata: *mut c_void,
}

#[repr(C)]
pub struct xmpp_log_t {
    handler: xmpp_log_handler,
    userdata: *mut c_void,
}

#[repr(C)]
pub struct xmpp_stream_error_t {
    _type: c_int, // xmpp_error_type_t
    text: *mut c_char,
    stanza: *mut xmpp_stanza_t,
}

#[repr(C)]
pub struct hashentry_t {
    next: *mut hashentry_t,
    key: *mut c_char,
    values: *mut c_void,
}

#[repr(C)]
pub struct hash_t {
    _ref: c_uint,
    ctx: *mut xmpp_ctx_t,
    free: hash_free_func,
    length: c_int,
    num_keys: c_int,
    entries: *mut *mut hashentry_t,
}

#[repr(C)]
pub enum xmpp_log_level_t {
    XMPP_LEVEL_DEBUG,
    XMPP_LEVEL_INFO,
    XMPP_LEVEL_WARN,
    XMPP_LEVEL_ERROR,
}

#[repr(C)]
pub enum xmpp_conn_type_t {
    XMPP_UNKNOWN,
    XMPP_CLIENT,
    XMPP_COMPONENT,
}

#[repr(C)]
pub enum xmpp_conn_event_t {
    XMPP_CONN_CONNECT,
    XMPP_CONN_DISCONNECT,
    XMPP_CONN_FAIL,
}

#[repr(C)]
pub enum xmpp_error_type_t {
    XMPP_SE_BAD_FORMAT,
    XMPP_SE_BAD_NS_PREFIX,
    XMPP_SE_CONFLICT,
    XMPP_SE_CONN_TIMEOUT,
    XMPP_SE_HOST_GONE,
    XMPP_SE_HOST_UNKNOWN,
    XMPP_SE_IMPROPER_ADDR,
    XMPP_SE_INTERNAL_SERVER_ERROR,
    XMPP_SE_INVALID_FROM,
    XMPP_SE_INVALID_ID,
    XMPP_SE_INVALID_NS,
    XMPP_SE_INVALID_XML,
    XMPP_SE_NOT_AUTHORIZED,
    XMPP_SE_POLICY_VIOLATION,
    XMPP_SE_REMOTE_CONN_FAILED,
    XMPP_SE_RESOURCE_CONSTRAINT,
    XMPP_SE_RESTRICTED_XML,
    XMPP_SE_SEE_OTHER_HOST,
    XMPP_SE_SYSTEM_SHUTDOWN,
    XMPP_SE_UNDEFINED_CONDITION,
    XMPP_SE_UNSUPPORTED_ENCODING,
    XMPP_SE_UNSUPPORTED_STANZA_TYPE,
    XMPP_SE_UNSUPPORTED_VERSION,
    XMPP_SE_XML_NOT_WELL_FORMED,
}

pub const XMPP_NS_CLIENT:       &'static str = "jabber:client";
pub const XMPP_NS_COMPONENT:    &'static str = "jabber:component:accept";
pub const XMPP_NS_STREAMS:      &'static str = "http://etherx.jabber.org/streams";
pub const XMPP_NS_STREAMS_IETF: &'static str = "urn:ietf:params:xml:ns:xmpp-streams";
pub const XMPP_NS_TLS:          &'static str = "urn:ietf:params:xml:ns:xmpp-tls";
pub const XMPP_NS_SASL:         &'static str = "urn:ietf:params:xml:ns:xmpp-sasl";
pub const XMPP_NS_BIND:         &'static str = "urn:ietf:params:xml:ns:xmpp-bind";
pub const XMPP_NS_SESSION:      &'static str = "urn:ietf:params:xml:ns:xmpp-session";
pub const XMPP_NS_AUTH:         &'static str = "jabber:iq:auth";
pub const XMPP_NS_DISCO_INFO:   &'static str = "http://jabber.org/protocol/disco#info";
pub const XMPP_NS_DISCO_ITEMS:  &'static str = "http://jabber.org/protocol/disco#items";
pub const XMPP_NS_ROSTER:       &'static str = "jabber:iq:roster";

pub const XMPP_EOK:    c_int = 0;
pub const XMPP_EMEM:   c_int = -1;
pub const XMPP_EINVOP: c_int = -2;
pub const XMPP_EINT:   c_int = -3;

#[link(name="strophe")]
extern "C" {
    // connection management
    pub fn xmpp_conn_new(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
    pub fn xmpp_conn_close(ctx: *const xmpp_ctx_t) -> *mut xmpp_conn_t;
    pub fn xmpp_conn_release(conn: *const xmpp_conn_t) -> c_int;
    pub fn xmpp_conn_get_jid(conn: *const xmpp_conn_t) -> *const c_char;
    pub fn xmpp_conn_get_bound_jid(conn: *const xmpp_conn_t) -> *const c_char;
    pub fn xmpp_conn_set_jid(conn: *const xmpp_conn_t, jid: *const c_char);
    pub fn xmpp_conn_get_pass(conn: *const xmpp_conn_t) -> *const c_char;
    pub fn xmpp_conn_set_pass(conn: *const xmpp_conn_t, pass: *const c_char);
    pub fn xmpp_conn_get_context(conn: *const xmpp_conn_t) -> *mut xmpp_ctx_t;
    pub fn xmpp_connect_client(conn: *const xmpp_conn_t,
                               altdomain: *const c_char,
                               altport: c_ushort,
                               callback: xmpp_conn_handler,
                               userdata: *const c_void) -> c_int;
    pub fn xmpp_disconnect(conn: *const xmpp_conn_t);
    pub fn xmpp_send(conn: *const xmpp_conn_t, stanza: *const xmpp_stanza_t);

    // context objects
    pub fn xmpp_get_default_logger(level: xmpp_log_level_t) -> *mut xmpp_log_t;
    pub fn xmpp_ctx_new(mem: *const xmpp_mem_t, log: *const xmpp_log_t) -> *mut xmpp_ctx_t;
    pub fn xmpp_ctx_free(ctx: *const xmpp_ctx_t);

    // initialization, shutdown and versioning
    pub fn xmpp_initialize();
    pub fn xmpp_shutdown();
    pub fn xmpp_version_check(major: c_int, minor: c_int) -> c_int;

    // event loop
    pub fn xmpp_run_once(ctx: *mut xmpp_ctx_t, timeout: c_ulong);
    pub fn xmpp_run(ctx: *mut xmpp_ctx_t);
    pub fn xmpp_stop(ctx: *mut xmpp_ctx_t);

    // stanza and timed event handlers
    pub fn xmpp_timed_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_timed_handler);
    pub fn xmpp_id_handler_delete(conn: *const xmpp_conn_t,
                                  handler: xmpp_handler,
                                  id: *const c_char);
    pub fn xmpp_handler_delete(conn: *const xmpp_conn_t, handler: xmpp_handler);
    pub fn xmpp_timed_handler_add(conn: *const xmpp_conn_t,
                                  handler: xmpp_timed_handler,
                                  period: c_ulong,
                                  userdata: *const c_void);
    pub fn xmpp_id_handler_add(conn: *const xmpp_conn_t,
                               handler: xmpp_handler,
                               id: *const c_char,
                               userdata: *const c_void);
    pub fn xmpp_handler_add(conn: *const xmpp_conn_t,
                            handler: xmpp_handler,
                            ns: *const c_char,
                            name: *const c_char,
                            _type: *const c_char,
                            userdata: *const c_void);

    // stanza creation and manipulation
    pub fn xmpp_stanza_new(ctx: *mut xmpp_ctx_t) -> *mut xmpp_stanza_t;
    pub fn xmpp_stanza_clone(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
    pub fn xmpp_stanza_copy(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
    pub fn xmpp_stanza_release(stanza: *const xmpp_stanza_t) -> c_int;
    pub fn xmpp_stanza_is_text(stanza: *const xmpp_stanza_t) -> c_int;
    pub fn xmpp_stanza_is_tag(stanza: *const xmpp_stanza_t) -> c_int;
    pub fn xmpp_stanza_to_text(stanza: *mut xmpp_stanza_t,
                               buf: *const *const c_char,
                               sizelen: *const size_t) -> c_int;
    pub fn xmpp_stanza_set_name(stanza: *mut xmpp_stanza_t, name: *const c_char) -> c_int;
    pub fn xmpp_stanza_get_name(stanza: *const xmpp_stanza_t) -> *mut c_char;
    pub fn xmpp_stanza_get_attribute_count(stanza: *const xmpp_stanza_t) -> c_int;
    pub fn xmpp_stanza_get_attributes(stanza: *const xmpp_stanza_t,
                                      attr: *const *const c_char,
                                      attrlen: c_int) -> c_int;
    pub fn xmpp_stanza_set_attribute(stanza: *const xmpp_stanza_t,
                                     key: *const c_char,
                                     value: *const c_char) -> c_int;
    pub fn xmpp_stanza_set_ns(stanza: *const xmpp_stanza_t, ns: *const c_char) -> c_int;
    pub fn xmpp_stanza_add_child(stanza: *mut xmpp_stanza_t, child: *mut xmpp_stanza_t) -> c_int;
    pub fn xmpp_stanza_set_text(stanza: *mut xmpp_stanza_t, text: *const c_char) -> c_int;
    pub fn xmpp_stanza_set_text_with_size(stanza: *mut xmpp_stanza_t,
                                          text: *const c_char,
                                          size: size_t) -> c_int;
    pub fn xmpp_stanza_get_id(stanza: *const xmpp_stanza_t) -> *mut c_char;
    pub fn xmpp_stanza_get_ns(stanza: *const xmpp_stanza_t) -> *mut c_char;
    pub fn xmpp_stanza_get_type(stanza: *const xmpp_stanza_t) -> *mut c_char;
    pub fn xmpp_stanza_get_child_by_name(stanza: *const xmpp_stanza_t,
                                         name: *const c_char) -> *mut xmpp_stanza_t;
    pub fn xmpp_stanza_get_child_by_ns(stanza: *const xmpp_stanza_t,
                                       ns: *const c_char) -> *mut xmpp_stanza_t;
    pub fn xmpp_stanza_get_children(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
    pub fn xmpp_stanza_get_next(stanza: *const xmpp_stanza_t) -> *mut xmpp_stanza_t;
    pub fn xmpp_stanza_get_text(stanza: *const xmpp_stanza_t) -> *mut c_char;
    pub fn xmpp_stanza_get_text_ptr(stanza: *const xmpp_stanza_t) -> *mut c_char;
    pub fn xmpp_stanza_set_id(stanza: *const xmpp_stanza_t, id: *const c_char) -> c_int;
    pub fn xmpp_stanza_set_type(stanza: *const xmpp_stanza_t, _type: *const c_char) -> c_int;
    pub fn xmpp_stanza_get_attribute(stanza: *const xmpp_stanza_t,
                                     name: *const c_char) -> *mut c_char;
}
<|endoftext|>


<commit_before>use ::*;
use errors::*;
use pretty_bytes::converter::convert;
use separator::Separatable;
use std::path::Path;

pub struct Parameters {
    pub compressor: CompressionAlgorithm,
    pub extension: String,
    pub include_filters: Vec<String>,
    pub threads: usize,
}

pub enum CompressionAlgorithm {
    Brotli,
    GZip,
    Zopfli,
}

impl std::str::FromStr for CompressionAlgorithm {
    type Err = errors::Error;
    fn from_str(s: &str) -> Result<Self> {
        let r = match s {
            "gzip" => CompressionAlgorithm::GZip,
            "brotli" => CompressionAlgorithm::Brotli,
            "zopfli" => CompressionAlgorithm::Zopfli,
            _ => bail!("Unsupported compression algorithm option set!"),
        };

        return Ok(r);
    }
}

pub trait DefaultFileCompressor {
    fn compress(&self, source: &Path, destination: &Path) -> Result<()>;
}

pub trait FileCompressor {
    fn compress(&self, source: &Path, destination: &Path, level: u8);
}

pub trait CompressionFormat {
    fn extension(&self) -> &'static str;
}

pub struct Statistics {
    total_compressed: u64,
    total_compressed_now: u64,
    total_file_count: u32,
    total_file_count_now: u32,
    total_uncompressed: u64,
    total_uncompressed_now: u64,
}

impl Statistics {
    pub fn new() -> Statistics {
        Statistics {
            total_compressed: 0,
            total_compressed_now: 0,
            total_file_count: 0,
            total_file_count_now: 0,
            total_uncompressed: 0,
            total_uncompressed_now: 0,
        }
    }

    pub fn update(&mut self, uncompressed_size: u64, compressed_size: u64, newly_compressed: bool) {
        if newly_compressed {
            self.total_compressed_now += compressed_size;
            self.total_file_count_now += 1;
            self.total_uncompressed_now += uncompressed_size;
        }

        self.total_compressed += compressed_size;
        self.total_file_count += 1;
        self.total_uncompressed += uncompressed_size;
    }

    pub fn merge(&mut self, other: &Statistics) {
        self.total_compressed += other.total_compressed;
        self.total_compressed_now += other.total_compressed_now;
        self.total_file_count += other.total_file_count;
        self.total_file_count_now += other.total_file_count_now;
        self.total_uncompressed += other.total_uncompressed;
        self.total_uncompressed_now += other.total_uncompressed_now;
    }

    pub fn savings_ratio(&self) -> f32 {
        return self.total_compressed as f32 / self.total_uncompressed as f32;
    }

    pub fn savings_ratio_now(&self) -> f32 {
        return self.total_compressed_now as f32 / self.total_uncompressed_now as f32;
    }
}

impl std::fmt::Display for Statistics {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        writeln!(f, "");
        let table = table!(["", "This Run", "Total"],
                           ["Count", self.total_file_count_now.separated_string(), self.total_file_count.separated_string()],
                           ["Compressed Size", convert(self.total_compressed_now as f64), convert(self.total_compressed as f64)],
                           ["Uncompressed Size", convert(self.total_uncompressed_now as f64), convert(self.total_uncompressed as f64)],
                           ["Total Savings", format!("{:.2}%", 100f32 - 100f32 * self.savings_ratio_now()), format!("{:.2}%", 100f32 - 100f32 * self.savings_ratio())]);

        writeln!(f, "{}", table);
        Ok(())
    }
}
<commit_msg>Handle return codes from fmt::Display<commit_after>use ::*;
use errors::*;
use pretty_bytes::converter::convert;
use separator::Separatable;
use std::path::Path;

pub struct Parameters {
    pub compressor: CompressionAlgorithm,
    pub extension: String,
    pub include_filters: Vec<String>,
    pub threads: usize,
}

pub enum CompressionAlgorithm {
    Brotli,
    GZip,
    Zopfli,
}

impl std::str::FromStr for CompressionAlgorithm {
    type Err = errors::Error;
    fn from_str(s: &str) -> Result<Self> {
        let r = match s {
            "gzip" => CompressionAlgorithm::GZip,
            "brotli" => CompressionAlgorithm::Brotli,
            "zopfli" => CompressionAlgorithm::Zopfli,
            _ => bail!("Unsupported compression algorithm option set!"),
        };

        return Ok(r);
    }
}

pub trait DefaultFileCompressor {
    fn compress(&self, source: &Path, destination: &Path) -> Result<()>;
}

pub trait FileCompressor {
    fn compress(&self, source: &Path, destination: &Path, level: u8);
}

pub trait CompressionFormat {
    fn extension(&self) -> &'static str;
}

pub struct Statistics {
    total_compressed: u64,
    total_compressed_now: u64,
    total_file_count: u32,
    total_file_count_now: u32,
    total_uncompressed: u64,
    total_uncompressed_now: u64,
}

impl Statistics {
    pub fn new() -> Statistics {
        Statistics {
            total_compressed: 0,
            total_compressed_now: 0,
            total_file_count: 0,
            total_file_count_now: 0,
            total_uncompressed: 0,
            total_uncompressed_now: 0,
        }
    }

    pub fn update(&mut self, uncompressed_size: u64, compressed_size: u64, newly_compressed: bool) {
        if newly_compressed {
            self.total_compressed_now += compressed_size;
            self.total_file_count_now += 1;
            self.total_uncompressed_now += uncompressed_size;
        }

        self.total_compressed += compressed_size;
        self.total_file_count += 1;
        self.total_uncompressed += uncompressed_size;
    }

    pub fn merge(&mut self, other: &Statistics) {
        self.total_compressed += other.total_compressed;
        self.total_compressed_now += other.total_compressed_now;
        self.total_file_count += other.total_file_count;
        self.total_file_count_now += other.total_file_count_now;
        self.total_uncompressed += other.total_uncompressed;
        self.total_uncompressed_now += other.total_uncompressed_now;
    }

    pub fn savings_ratio(&self) -> f32 {
        return self.total_compressed as f32 / self.total_uncompressed as f32;
    }

    pub fn savings_ratio_now(&self) -> f32 {
        return self.total_compressed_now as f32 / self.total_uncompressed_now as f32;
    }
}

impl std::fmt::Display for Statistics {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        writeln!(f, "")?;
        let table = table!(["", "This Run", "Total"],
                           ["Count", self.total_file_count_now.separated_string(), self.total_file_count.separated_string()],
                           ["Compressed Size", convert(self.total_compressed_now as f64), convert(self.total_compressed as f64)],
                           ["Uncompressed Size", convert(self.total_uncompressed_now as f64), convert(self.total_uncompressed as f64)],
                           ["Total Savings", format!("{:.2}%", 100f32 - 100f32 * self.savings_ratio_now()), format!("{:.2}%", 100f32 - 100f32 * self.savings_ratio())]);

        writeln!(f, "{}", table)?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::Command;
use std::{env, fs};

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot, Target};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(
    cmode: &CompilationMode,
    blueprint: Blueprint,
    ctoml: &cargo::Toml,
    home: &Home,
    rustflags: &Rustflags,
    sysroot: &Sysroot,
    hash: u64,
    verbose: bool,
) -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib
        .remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;

    if cmode.triple().contains("pc-windows-gnu") {
        let src = &sysroot
            .path()
            .join("lib")
            .join("rustlib")
            .join(cmode.triple())
            .join("lib");

        // These are required for linking executables/dlls
        for file in ["rsbegin.o", "rsend.o", "crt2.o", "dllcrt2.o"].iter() {
            let file_src = src.join(file);
            let file_dst = dst.join(file);
            fs::copy(&file_src, &file_dst).chain_err(|| {
                format!(
                    "couldn't copy {} to {}",
                    file_src.display(),
                    file_dst.display()
                )
            })?;
        }
    }

    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();
        {
            let mut map = Table::new();

            map.insert("dependencies".to_owned(), Value::Table(stage.dependencies));
            map.insert("patch".to_owned(), Value::Table(stage.patch));

            stoml.push_str(&Value::Table(map).to_string());
        }

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            let mut flags = rustflags.for_xargo(home);
            flags.push_str(" -Z force-unstable-if-unmarked");
            if verbose {
                writeln!(io::stderr(), "+ RUSTFLAGS={:?}", flags).ok();
            }
            cmd.env("RUSTFLAGS", flags);
            cmd.env_remove("CARGO_TARGET_DIR");

            // As of rust-lang/cargo#4788 Cargo invokes rustc with a changed "current directory" so
            // we can't assume that such directory will be the same as the directory from which
            // Xargo was invoked. This is specially true when compiling the sysroot as the std
            // source is provided as a workspace and Cargo will change the current directory to the
            // root of the workspace when building one. To ensure rustc finds a target specification
            // file stored in the current directory we'll set `RUST_TARGET_PATH`  to the current
            // directory.
            if env::var_os("RUST_TARGET_PATH").is_none() {
                if let CompilationMode::Cross(ref target) = *cmode {
                    if let Target::Custom { ref json, .. } = *target {
                        cmd.env("RUST_TARGET_PATH", json.parent().unwrap());
                    }
                }
            }

            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(
            &td.join("target")
                .join(cmode.triple())
                .join(profile())
                .join("deps"),
            &dst,
        )?;
    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(
    cmode: &CompilationMode,
    blueprint: &Blueprint,
    rustflags: &Rustflags,
    ctoml: &cargo::Toml,
    meta: &VersionMeta,
) -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(
    cmode: &CompilationMode,
    home: &Home,
    root: &Root,
    rustflags: &Rustflags,
    meta: &VersionMeta,
    src: &Src,
    sysroot: &Sysroot,
    verbose: bool,
) -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(
            cmode,
            blueprint,
            &ctoml,
            home,
            rustflags,
            sysroot,
            hash,
            verbose,
        )?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("lib"),
        &dst,
    )?;

    let bin_dst = lock.parent().join("bin");
    util::mkdir(&bin_dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("bin"),
        &bin_dst,
    )?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    dependencies: Table,
    patch: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint {
            stages: BTreeMap::new(),
        }
    }

    fn from(toml: Option<&xargo::Toml>, target: &str, root: &Root, src: &Src) -> Result<Self> {
        let deps = match (
            toml.and_then(|t| t.dependencies()),
            toml.and_then(|t| t.target_dependencies(target)),
        ) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value
                    .as_table()
                    .cloned()
                    .ok_or_else(|| format!("Xargo.toml: `dependencies` must be a table"))?;

                let more_deps = tvalue.as_table().ok_or_else(|| {
                    format!(
                        "Xargo.toml: `target.{}.dependencies` must be \
                         a table",
                        target
                    )
                })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!(
                            "found duplicate dependency name {}, \
                             but all dependencies must have a \
                             unique name",
                            k
                        ))?
                    }
                }

                deps
            }
            (Some(value), None) | (None, Some(value)) => if let Some(table) = value.as_table() {
                table.clone()
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies must be \
                     a table",
                    target
                ))?
            },
            (None, None) => {
                // If no dependencies were listed, we assume `core` and `compiler_builtins` as the
                // dependencies
                let mut t = BTreeMap::new();
                let mut core = BTreeMap::new();
                core.insert("stage".to_owned(), Value::Integer(0));
                t.insert("core".to_owned(), Value::Table(core));
                let mut cb = BTreeMap::new();
                cb.insert(
                    "features".to_owned(),
                    Value::Array(vec![Value::String("mem".to_owned())]),
                );
                cb.insert("stage".to_owned(), Value::Integer(1));
                t.insert(
                    "compiler_builtins".to_owned(),
                    Value::Table(cb),
                );
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value
                        .as_integer()
                        .ok_or_else(|| format!("dependencies.{}.stage must be an integer", k))?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| format!("dependencies.{}.path must be a string", k))?);

                    if !p.is_absolute() {
                        *path = Value::String(
                            root.path()
                                .join(&p)
                                .canonicalize()
                                .chain_err(|| format!("couldn't canonicalize {}", p.display()))?
                                .display()
                                .to_string(),
                        );
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path().join(format!("lib{}", k)).display().to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map, src);
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies.{} must be \
                     a table",
                    target, k
                ))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table, src: &Src) {
        let stage = self.stages.entry(stage).or_insert_with(|| Stage {
            crates: vec![],
            dependencies: Table::new(),
            patch: {
                // For a new stage, we also need to compute the patch section of the toml
                fn make_singleton_map(key: &str, val: Value) -> Table {
                    let mut map = Table::new();
                    map.insert(key.to_owned(), val);
                    map
                }
                make_singleton_map("crates-io", Value::Table(
                    make_singleton_map("rustc-std-workspace-core", Value::Table(
                        make_singleton_map("path", Value::String(
                            src.path().join("tools/rustc-std-workspace-core")
                                .display().to_string()
                        ))
                    ))
                ))
            }
        });

        stage.dependencies.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
    where
        H: Hasher,
    {
        for stage in self.stages.values() {
            for (k, v) in stage.dependencies.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<commit_msg>compatibility with old rust-src packages<commit_after>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::Command;
use std::{env, fs};

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot, Target};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(
    cmode: &CompilationMode,
    blueprint: Blueprint,
    ctoml: &cargo::Toml,
    home: &Home,
    rustflags: &Rustflags,
    sysroot: &Sysroot,
    hash: u64,
    verbose: bool,
) -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib
        .remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;

    if cmode.triple().contains("pc-windows-gnu") {
        let src = &sysroot
            .path()
            .join("lib")
            .join("rustlib")
            .join(cmode.triple())
            .join("lib");

        // These are required for linking executables/dlls
        for file in ["rsbegin.o", "rsend.o", "crt2.o", "dllcrt2.o"].iter() {
            let file_src = src.join(file);
            let file_dst = dst.join(file);
            fs::copy(&file_src, &file_dst).chain_err(|| {
                format!(
                    "couldn't copy {} to {}",
                    file_src.display(),
                    file_dst.display()
                )
            })?;
        }
    }

    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();
        {
            let mut map = Table::new();

            map.insert("dependencies".to_owned(), Value::Table(stage.dependencies));
            if let Some(patch) = stage.patch {
                map.insert("patch".to_owned(), Value::Table(patch));
            }

            stoml.push_str(&Value::Table(map).to_string());
        }

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            let mut flags = rustflags.for_xargo(home);
            flags.push_str(" -Z force-unstable-if-unmarked");
            if verbose {
                writeln!(io::stderr(), "+ RUSTFLAGS={:?}", flags).ok();
            }
            cmd.env("RUSTFLAGS", flags);
            cmd.env_remove("CARGO_TARGET_DIR");

            // As of rust-lang/cargo#4788 Cargo invokes rustc with a changed "current directory" so
            // we can't assume that such directory will be the same as the directory from which
            // Xargo was invoked. This is specially true when compiling the sysroot as the std
            // source is provided as a workspace and Cargo will change the current directory to the
            // root of the workspace when building one. To ensure rustc finds a target specification
            // file stored in the current directory we'll set `RUST_TARGET_PATH`  to the current
            // directory.
            if env::var_os("RUST_TARGET_PATH").is_none() {
                if let CompilationMode::Cross(ref target) = *cmode {
                    if let Target::Custom { ref json, .. } = *target {
                        cmd.env("RUST_TARGET_PATH", json.parent().unwrap());
                    }
                }
            }

            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(
            &td.join("target")
                .join(cmode.triple())
                .join(profile())
                .join("deps"),
            &dst,
        )?;
    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(
    cmode: &CompilationMode,
    blueprint: &Blueprint,
    rustflags: &Rustflags,
    ctoml: &cargo::Toml,
    meta: &VersionMeta,
) -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(
    cmode: &CompilationMode,
    home: &Home,
    root: &Root,
    rustflags: &Rustflags,
    meta: &VersionMeta,
    src: &Src,
    sysroot: &Sysroot,
    verbose: bool,
) -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(
            cmode,
            blueprint,
            &ctoml,
            home,
            rustflags,
            sysroot,
            hash,
            verbose,
        )?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("lib"),
        &dst,
    )?;

    let bin_dst = lock.parent().join("bin");
    util::mkdir(&bin_dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("bin"),
        &bin_dst,
    )?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    dependencies: Table,
    patch: Option<Table>,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint {
            stages: BTreeMap::new(),
        }
    }

    fn from(toml: Option<&xargo::Toml>, target: &str, root: &Root, src: &Src) -> Result<Self> {
        let deps = match (
            toml.and_then(|t| t.dependencies()),
            toml.and_then(|t| t.target_dependencies(target)),
        ) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value
                    .as_table()
                    .cloned()
                    .ok_or_else(|| format!("Xargo.toml: `dependencies` must be a table"))?;

                let more_deps = tvalue.as_table().ok_or_else(|| {
                    format!(
                        "Xargo.toml: `target.{}.dependencies` must be \
                         a table",
                        target
                    )
                })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!(
                            "found duplicate dependency name {}, \
                             but all dependencies must have a \
                             unique name",
                            k
                        ))?
                    }
                }

                deps
            }
            (Some(value), None) | (None, Some(value)) => if let Some(table) = value.as_table() {
                table.clone()
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies must be \
                     a table",
                    target
                ))?
            },
            (None, None) => {
                // If no dependencies were listed, we assume `core` and `compiler_builtins` as the
                // dependencies
                let mut t = BTreeMap::new();
                let mut core = BTreeMap::new();
                core.insert("stage".to_owned(), Value::Integer(0));
                t.insert("core".to_owned(), Value::Table(core));
                let mut cb = BTreeMap::new();
                cb.insert(
                    "features".to_owned(),
                    Value::Array(vec![Value::String("mem".to_owned())]),
                );
                cb.insert("stage".to_owned(), Value::Integer(1));
                t.insert(
                    "compiler_builtins".to_owned(),
                    Value::Table(cb),
                );
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value
                        .as_integer()
                        .ok_or_else(|| format!("dependencies.{}.stage must be an integer", k))?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| format!("dependencies.{}.path must be a string", k))?);

                    if !p.is_absolute() {
                        *path = Value::String(
                            root.path()
                                .join(&p)
                                .canonicalize()
                                .chain_err(|| format!("couldn't canonicalize {}", p.display()))?
                                .display()
                                .to_string(),
                        );
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path().join(format!("lib{}", k)).display().to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map, src);
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies.{} must be \
                     a table",
                    target, k
                ))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table, src: &Src) {
        let stage = self.stages.entry(stage).or_insert_with(|| Stage {
            crates: vec![],
            dependencies: Table::new(),
            patch: {
                let rustc_std_workspace_core = src.path().join("tools/rustc-std-workspace-core");
                if rustc_std_workspace_core.exists() {
                    // For a new stage, we also need to compute the patch section of the toml
                    fn make_singleton_map(key: &str, val: Value) -> Table {
                        let mut map = Table::new();
                        map.insert(key.to_owned(), val);
                        map
                    }
                    Some(make_singleton_map("crates-io", Value::Table(
                        make_singleton_map("rustc-std-workspace-core", Value::Table(
                            make_singleton_map("path", Value::String(
                                rustc_std_workspace_core.display().to_string()
                            ))
                        ))
                    )))
                } else {
                    // an old rustc, doesn't need a rustc_std_workspace_core
                    None
                }
            }
        });

        stage.dependencies.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
    where
        H: Hasher,
    {
        for stage in self.stages.values() {
            for (k, v) in stage.dependencies.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<|endoftext|>