<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cssparser::RGBA;
use style::properties::animated_properties::{Animatable, IntermediateRGBA};

fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA {
    let from: IntermediateRGBA = from.into();
    let to: IntermediateRGBA = to.into();
    from.interpolate(&to, progress).unwrap().into()
}

#[test]
fn test_rgba_color_interepolation_preserves_transparent() {
    assert_eq!(interpolate_rgba(RGBA::transparent(),
                                RGBA::transparent(), 0.5),
               RGBA::transparent());
}

#[test]
fn test_rgba_color_interepolation_alpha() {
    assert_eq!(interpolate_rgba(RGBA::new(200, 0, 0, 100),
                                RGBA::new(0, 200, 0, 200), 0.5),
               RGBA::new(67, 133, 0, 150));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_1() {
    // Some cubic-bezier functions produce values that are out of range [0, 1].
    // Unclamped cases.
    assert_eq!(interpolate_rgba(RGBA::from_floats(0.3, 0.0, 0.0, 0.4),
                                RGBA::from_floats(0.0, 1.0, 0.0, 0.6), -0.5),
               RGBA::new(154, 0, 0, 77));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_2() {
    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.6),
                                RGBA::from_floats(0.0, 0.3, 0.0, 0.4), 1.5),
               RGBA::new(0, 154, 0, 77));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_clamped_1() {
    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.8),
                                RGBA::from_floats(0.0, 1.0, 0.0, 0.2), -0.5),
               RGBA::from_floats(1.0, 0.0, 0.0, 1.0));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_clamped_2() {
    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.8),
                                RGBA::from_floats(0.0, 1.0, 0.0, 0.2), 1.5),
               RGBA::from_floats(0.0, 0.0, 0.0, 0.0));
}
<commit_msg>Add some unit tests for basic transform operations for Servo.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use cssparser::RGBA;
use style::properties::animated_properties::{Animatable, IntermediateRGBA};
use style::properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
use style::properties::longhands::transform::computed_value::T as TransformList;

fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA {
    let from: IntermediateRGBA = from.into();
    let to: IntermediateRGBA = to.into();
    from.interpolate(&to, progress).unwrap().into()
}

// Color
#[test]
fn test_rgba_color_interepolation_preserves_transparent() {
    assert_eq!(interpolate_rgba(RGBA::transparent(),
                                RGBA::transparent(), 0.5),
               RGBA::transparent());
}

#[test]
fn test_rgba_color_interepolation_alpha() {
    assert_eq!(interpolate_rgba(RGBA::new(200, 0, 0, 100),
                                RGBA::new(0, 200, 0, 200), 0.5),
               RGBA::new(67, 133, 0, 150));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_1() {
    // Some cubic-bezier functions produce values that are out of range [0, 1].
    // Unclamped cases.
    assert_eq!(interpolate_rgba(RGBA::from_floats(0.3, 0.0, 0.0, 0.4),
                                RGBA::from_floats(0.0, 1.0, 0.0, 0.6), -0.5),
               RGBA::new(154, 0, 0, 77));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_2() {
    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.6),
                                RGBA::from_floats(0.0, 0.3, 0.0, 0.4), 1.5),
               RGBA::new(0, 154, 0, 77));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_clamped_1() {
    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.8),
                                RGBA::from_floats(0.0, 1.0, 0.0, 0.2), -0.5),
               RGBA::from_floats(1.0, 0.0, 0.0, 1.0));
}

#[test]
fn test_rgba_color_interepolation_out_of_range_clamped_2() {
    assert_eq!(interpolate_rgba(RGBA::from_floats(1.0, 0.0, 0.0, 0.8),
                                RGBA::from_floats(0.0, 1.0, 0.0, 0.2), 1.5),
               RGBA::from_floats(0.0, 0.0, 0.0, 0.0));
}

// Transform
#[test]
fn test_transform_interpolation_on_translate() {
    use style::values::computed::{CalcLengthOrPercentage, LengthOrPercentage};

    let from = TransformList(Some(vec![
        TransformOperation::Translate(LengthOrPercentage::Length(Au(0)),
                                      LengthOrPercentage::Length(Au(100)),
                                      Au(25))]));
    let to = TransformList(Some(vec![
        TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
                                      LengthOrPercentage::Length(Au(0)),
                                      Au(75))]));
    assert_eq!(from.interpolate(&to, 0.5).unwrap(),
               TransformList(Some(vec![
                   TransformOperation::Translate(LengthOrPercentage::Length(Au(50)),
                                                 LengthOrPercentage::Length(Au(50)),
                                                 Au(50))])));

    let from = TransformList(Some(vec![
        TransformOperation::Translate(LengthOrPercentage::Percentage(0.5),
                                      LengthOrPercentage::Percentage(1.0),
                                      Au(25))]));
    let to = TransformList(Some(vec![
        TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
                                      LengthOrPercentage::Length(Au(50)),
                                      Au(75))]));
    assert_eq!(from.interpolate(&to, 0.5).unwrap(),
               TransformList(Some(vec![
                   TransformOperation::Translate(LengthOrPercentage::Calc(
                                                     // calc(50px + 25%)
                                                     CalcLengthOrPercentage::new(Au(50),
                                                                                Some(0.25))),
                                                 LengthOrPercentage::Calc(
                                                     // calc(25px + 50%)
                                                     CalcLengthOrPercentage::new(Au(25),
                                                                                Some(0.5))),
                                                 Au(50))])));
}

#[test]
fn test_transform_interpolation_on_scale() {
    let from = TransformList(Some(vec![TransformOperation::Scale(1.0, 2.0, 1.0)]));
    let to = TransformList(Some(vec![TransformOperation::Scale(2.0, 4.0, 2.0)]));
    assert_eq!(from.interpolate(&to, 0.5).unwrap(),
               TransformList(Some(vec![TransformOperation::Scale(1.5, 3.0, 1.5)])));
}

#[test]
fn test_transform_interpolation_on_rotate() {
    use style::values::computed::Angle;

    let from = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
                                                                  Angle::from_radians(0.0))]));
    let to = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
                                                                Angle::from_radians(100.0))]));
    assert_eq!(from.interpolate(&to, 0.5).unwrap(),
               TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
                                                                  Angle::from_radians(50.0))])));
}

#[test]
fn test_transform_interpolation_on_skew() {
    use style::values::computed::Angle;

    let from = TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(0.0),
                                                                Angle::from_radians(100.0))]));
    let to = TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(100.0),
                                                              Angle::from_radians(0.0))]));
    assert_eq!(from.interpolate(&to, 0.5).unwrap(),
               TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(50.0),
                                                                Angle::from_radians(50.0))])));
}

#[test]
fn test_transform_interpolation_on_mismatched_lists() {
    use style::values::computed::{Angle, LengthOrPercentage, Percentage};

    let from = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
                                                                  Angle::from_radians(100.0))]));
    let to = TransformList(Some(vec![
        TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
                                      LengthOrPercentage::Length(Au(0)),
                                      Au(0))]));
    assert_eq!(from.interpolate(&to, 0.5).unwrap(),
               TransformList(Some(vec![TransformOperation::InterpolateMatrix {
                   from_list: from.clone(),
                   to_list: to.clone(),
                   progress: Percentage(0.5)
               }])));
}
<|endoftext|>


<commit_before>//! `codec` module contains the `Package` (frame) decoding and an `tokio_core::io::Codec`
//! implementation.

use std::io::{self, Read, Write};
use uuid::Uuid;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use tokio_io::codec::{Encoder, Decoder};
use bytes::{BytesMut, BufMut};

use errors::ErrorKind;
use package::Package;
use {UsernamePassword};
use raw::RawMessage;

bitflags!{
    /// `TcpFlags` describes if optional fields (authentication) is present.
    pub flags TcpFlags: u8 {
        /// No authentication information present
        const FLAG_NONE = 0x00,
        /// Package contains authentication
        const FLAG_AUTHENTICATED = 0x01,
        //const FLAG_TRUSTED_WRITE = 0x02, // only in core
    }
}

/// Stateless simple PackageCodec
pub struct PackageCodec;

impl PackageCodec {
    fn decode_inner(&mut self, buf: &mut BytesMut) -> io::Result<Option<Package>> {
        if buf.len() < 4 + 1 + 1 + 16 {
            return Ok(None);
        }

        let len = io::Cursor::new(&buf[0..4]).read_u32::<LittleEndian>()? as usize;

        if len < 18 {
            panic!("length is too little: {}", len);
        }

        if buf.len() < len + 4 {
            return Ok(None);
        }

        let decoded_frame = self.decode_body(&buf[4..(4 + len)]);

        match decoded_frame {
            Ok((c, a, m)) => {
                buf.split_to(4 + len);
                Ok(Some(Package {
                    correlation_id: c,
                    authentication: a,
                    message: m.into(),
                }))
            }
            Err(e) => Err(e),
        }
    }

    fn decode_body(&mut self, buf: &[u8]) -> io::Result<(Uuid, Option<UsernamePassword>, RawMessage<'static>)> {
        let (d, c, a, pos) = self.decode_header(buf)?;
        let message = RawMessage::decode(d, &buf[pos..])?.into_owned();
        Ok((c, a, message))
    }

    fn decode_header(&mut self, buf: &[u8]) -> io::Result<(u8, Uuid, Option<UsernamePassword>, usize)> {
        let (d, c, a, pos) = {
            let mut cursor = io::Cursor::new(buf);
            let discriminator = cursor.read_u8()?;
            let flags = cursor.read_u8()?;
            let flags = match TcpFlags::from_bits(flags) {
                Some(flags) => flags,
                None => bail!(ErrorKind::InvalidFlags(flags)),
            };

            let correlation_id = {
                let mut uuid_bytes = [0u8; 16];
                cursor.read_exact(&mut uuid_bytes)?;
                // this should only err if len is not 16
                Uuid::from_bytes(&uuid_bytes).unwrap()
            };

            let authentication = if flags.contains(FLAG_AUTHENTICATED) {
                Some(UsernamePassword::decode(&mut cursor)?)
            } else {
                None
            };

            (discriminator, correlation_id, authentication, cursor.position() as usize)
        };

        Ok((d, c, a, pos))
    }

    #[doc(hidden)]
    pub fn encode_parts<'a>(&self, cursor: &mut io::Cursor<Vec<u8>>, correlation_id: &Uuid, authentication: Option<&UsernamePassword>, raw: &RawMessage<'a>) -> io::Result<()> {
        let mut flags = FLAG_NONE;
        if authentication.is_some() {
            flags.insert(FLAG_AUTHENTICATED);
        }

        cursor.write_u32::<LittleEndian>(0)?; // placeholder for prefix
        cursor.write_u8(raw.discriminator())?;
        cursor.write_u8(flags.bits())?;
        cursor.write_all(correlation_id.as_bytes())?;
        if flags.contains(FLAG_AUTHENTICATED) {
            authentication
                .expect("According to flag authentication token is present")
                .encode(cursor)?;
        } else {
            assert!(authentication.is_none());
        }

        raw.encode(cursor)?;

        let at_end = cursor.position();
        let len = at_end as u32 - 4;

        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(len)?;
        Ok(())
    }
}

impl Decoder for PackageCodec {
    type Item = Package;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Self::Item>> {
        self.decode_inner(buf).map_err(|e| e.into())
    }
}

impl Encoder for PackageCodec {
    type Item = Package;
    type Error = io::Error;

    fn encode(&mut self, msg: Package, buf: &mut BytesMut) -> io::Result<()> {
        let mut cursor = io::Cursor::new(Vec::new());

        self.encode_parts(&mut cursor, &msg.correlation_id, msg.authentication.as_ref(), &msg.message)?;

        let tmp = cursor.into_inner();
        buf.put_slice(&tmp);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use std::fmt::Debug;
    use rustc_serialize::hex::FromHex;
    use tokio_io::codec::{Decoder, Encoder};
    use uuid::Uuid;
    use super::{PackageCodec};
    use package::Package;
    use raw::RawMessage;
    use raw::client_messages::{WriteEventsCompleted, OperationResult};

    #[test]
    fn decode_ping() {
        test_decoding_hex("1200000003007b50a1b034b9224e8f9d708c394fab2d",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
                              message: RawMessage::Ping.into(),
                          });
    }

    #[test]
    fn decode_ping_with_junk() {
        test_decoding_hex("1300000003007b50a1b034b9224e8f9d708c394fab2d00",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
                              message: RawMessage::Ping.into(),
                          });
    }

    #[test]
    fn encode_ping() {
        test_encoding_hex("1200000003007b50a1b034b9224e8f9d708c394fab2d",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
                              message: RawMessage::Ping.into(),
                          });
    }

    #[test]
    fn decode_unknown_discriminator() {
        use std::borrow::Cow;

        let err = PackageCodec.decode(&mut ("12000000ff007b50a1b034b9224e8f9d708c394fab2d"
                .to_string()
                .from_hex()
                .unwrap()
                .into()))
            .unwrap() // Result
            .unwrap();// Option

        assert_eq!(err, Package {
            authentication: None,
            correlation_id: Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
            message: RawMessage::Unsupported(255, Cow::Owned(vec![])).into()
        });
    }

    #[test]
    fn decode_write_events_completed() {
        let input = "2200000083009b59d8734e9fd84eb8a421f2666a3aa40800181e20272884d6bc563084d6bc56";
        test_decoding_hex(input,
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("9b59d873-4e9f-d84e-b8a4-21f2666a3aa4").unwrap(),
                              message: RawMessage::WriteEventsCompleted(WriteEventsCompleted {
                                  result: Some(OperationResult::Success),
                                  message: None,
                                  first_event_number: 30,
                                  last_event_number: 39,
                                  prepare_position: Some(181349124),
                                  commit_position: Some(181349124)
                              }).into()
                          });
    }

    #[test]
    fn encode_write_events_completed() {
        test_encoding_hex("2200000083009b59d8734e9fd84eb8a421f2666a3aa40800181e20272884d6bc563084d6bc56",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("9b59d873-4e9f-d84e-b8a4-21f2666a3aa4").unwrap(),
                              message: RawMessage::WriteEventsCompleted(WriteEventsCompleted {
                                  result: Some(OperationResult::Success),
                                  message: None,
                                  first_event_number: 30,
                                  last_event_number: 39,
                                  prepare_position: Some(181349124),
                                  commit_position: Some(181349124)
                              }).into()
                          });

    }

    #[test]
    fn decode_authenticated_package() {
        use bytes::BytesMut;
        use auth::UsernamePassword;

        let mut buf = BytesMut::with_capacity(1024);
        let id = Uuid::new_v4();

        let msg = Package {
            correlation_id: id,
            authentication: Some(UsernamePassword::new("foobar", "abbacd")),
            message: RawMessage::Ping,
        };

        PackageCodec.encode(msg.clone(), &mut buf).unwrap();

        let decoded = PackageCodec.decode(&mut buf).unwrap().unwrap();

        assert_eq!(msg, decoded);
    }

    fn test_decoding_hex<C: Decoder>(input: &str, codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        test_decoding(input.to_string().from_hex().unwrap(), codec, expected);
    }

    fn test_decoding<C: Decoder>(input: Vec<u8>, mut codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        // decode whole buffer
        {
            let mut buf = input.clone().into();
            let item = codec.decode(&mut buf).unwrap().unwrap();

            assert_eq!(item, expected);
            assert_eq!(buf.len(), 0, "decoding correctly sized buffer left bytes");
        }

        // decoding partial buffer consumes no bytes
        for len in 1..(input.len() - 1) {
            let mut part = input.clone();
            part.truncate(len);

            let mut buf = part.into();
            assert!(codec.decode(&mut buf).unwrap().is_none());
            assert_eq!(buf.len(), len, "decoding partial buffer consumed bytes");
        }

        // decoding a too long buffer consumes no extra bytes
        {
            let mut input = input.clone();
            let len = input.len();
            input.extend(vec![0u8; len]);
            let mut buf = input.into();
            let item = codec.decode(&mut buf).unwrap().unwrap();
            assert_eq!(item, expected);
            assert_eq!(buf.len(), len, "decoding oversized buffer overused bytes");
        }
    }

    fn test_encoding_hex<C: Encoder>(input: &str, codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        test_encoding(input.to_string().from_hex().unwrap(), codec, expected);
    }

    fn test_encoding<C: Encoder>(input: Vec<u8>, mut codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        use bytes::BytesMut;

        let mut buf = BytesMut::with_capacity(input.len());
        codec.encode(expected, &mut buf).unwrap();
        assert_eq!(&buf[..],
                   &input[..],
                   "encoding did not yield same");
    }
}
<commit_msg>error-cleanup: Remove panics in the lib, that's too critical<commit_after>//! `codec` module contains the `Package` (frame) decoding and an `tokio_core::io::Codec`
//! implementation.

use std::io::{self, Read, Write};
use uuid::Uuid;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use tokio_io::codec::{Encoder, Decoder};
use bytes::{BytesMut, BufMut};

use errors::ErrorKind;
use package::Package;
use {UsernamePassword};
use raw::RawMessage;

bitflags!{
    /// `TcpFlags` describes if optional fields (authentication) is present.
    pub flags TcpFlags: u8 {
        /// No authentication information present
        const FLAG_NONE = 0x00,
        /// Package contains authentication
        const FLAG_AUTHENTICATED = 0x01,
        //const FLAG_TRUSTED_WRITE = 0x02, // only in core
    }
}

/// Stateless simple PackageCodec
pub struct PackageCodec;

impl PackageCodec {
    fn decode_inner(&mut self, buf: &mut BytesMut) -> io::Result<Option<Package>> {
        if buf.len() < 4 + 1 + 1 + 16 {
            return Ok(None);
        }

        let len = io::Cursor::new(&buf[0..4]).read_u32::<LittleEndian>()? as usize;

        if len < 18 {
            return Err(io::Error::new(io::ErrorKind::InvalidData, "length is too small"))
        }

        if buf.len() < len + 4 {
            return Ok(None);
        }

        let decoded_frame = self.decode_body(&buf[4..(4 + len)]);

        match decoded_frame {
            Ok((c, a, m)) => {
                buf.split_to(4 + len);
                Ok(Some(Package {
                    correlation_id: c,
                    authentication: a,
                    message: m.into(),
                }))
            }
            Err(e) => Err(e),
        }
    }

    fn decode_body(&mut self, buf: &[u8]) -> io::Result<(Uuid, Option<UsernamePassword>, RawMessage<'static>)> {
        let (d, c, a, pos) = self.decode_header(buf)?;
        let message = RawMessage::decode(d, &buf[pos..])?.into_owned();
        Ok((c, a, message))
    }

    fn decode_header(&mut self, buf: &[u8]) -> io::Result<(u8, Uuid, Option<UsernamePassword>, usize)> {
        let (d, c, a, pos) = {
            let mut cursor = io::Cursor::new(buf);
            let discriminator = cursor.read_u8()?;
            let flags = cursor.read_u8()?;
            let flags = match TcpFlags::from_bits(flags) {
                Some(flags) => flags,
                None => bail!(ErrorKind::InvalidFlags(flags)),
            };

            let correlation_id = {
                let mut uuid_bytes = [0u8; 16];
                cursor.read_exact(&mut uuid_bytes)?;
                // this should only err if len is not 16
                Uuid::from_bytes(&uuid_bytes).unwrap()
            };

            let authentication = if flags.contains(FLAG_AUTHENTICATED) {
                Some(UsernamePassword::decode(&mut cursor)?)
            } else {
                None
            };

            (discriminator, correlation_id, authentication, cursor.position() as usize)
        };

        Ok((d, c, a, pos))
    }

    #[doc(hidden)]
    pub fn encode_parts<'a>(&self, cursor: &mut io::Cursor<Vec<u8>>, correlation_id: &Uuid, authentication: Option<&UsernamePassword>, raw: &RawMessage<'a>) -> io::Result<()> {
        let mut flags = FLAG_NONE;
        if authentication.is_some() {
            flags.insert(FLAG_AUTHENTICATED);
        }

        cursor.write_u32::<LittleEndian>(0)?; // placeholder for prefix
        cursor.write_u8(raw.discriminator())?;
        cursor.write_u8(flags.bits())?;
        cursor.write_all(correlation_id.as_bytes())?;
        if flags.contains(FLAG_AUTHENTICATED) {
            authentication
                .expect("According to flag authentication token is present")
                .encode(cursor)?;
        } else {
            assert!(authentication.is_none());
        }

        raw.encode(cursor)?;

        let at_end = cursor.position();
        let len = at_end as u32 - 4;

        cursor.set_position(0);
        cursor.write_u32::<LittleEndian>(len)?;
        Ok(())
    }
}

impl Decoder for PackageCodec {
    type Item = Package;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Self::Item>> {
        self.decode_inner(buf).map_err(|e| e.into())
    }
}

impl Encoder for PackageCodec {
    type Item = Package;
    type Error = io::Error;

    fn encode(&mut self, msg: Package, buf: &mut BytesMut) -> io::Result<()> {
        let mut cursor = io::Cursor::new(Vec::new());

        self.encode_parts(&mut cursor, &msg.correlation_id, msg.authentication.as_ref(), &msg.message)?;

        let tmp = cursor.into_inner();
        buf.put_slice(&tmp);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use std::fmt::Debug;
    use rustc_serialize::hex::FromHex;
    use tokio_io::codec::{Decoder, Encoder};
    use uuid::Uuid;
    use super::{PackageCodec};
    use package::Package;
    use raw::RawMessage;
    use raw::client_messages::{WriteEventsCompleted, OperationResult};

    #[test]
    fn decode_ping() {
        test_decoding_hex("1200000003007b50a1b034b9224e8f9d708c394fab2d",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
                              message: RawMessage::Ping.into(),
                          });
    }

    #[test]
    fn decode_ping_with_junk() {
        test_decoding_hex("1300000003007b50a1b034b9224e8f9d708c394fab2d00",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
                              message: RawMessage::Ping.into(),
                          });
    }

    #[test]
    fn encode_ping() {
        test_encoding_hex("1200000003007b50a1b034b9224e8f9d708c394fab2d",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
                              message: RawMessage::Ping.into(),
                          });
    }

    #[test]
    fn decode_unknown_discriminator() {
        use std::borrow::Cow;

        let err = PackageCodec.decode(&mut ("12000000ff007b50a1b034b9224e8f9d708c394fab2d"
                .to_string()
                .from_hex()
                .unwrap()
                .into()))
            .unwrap() // Result
            .unwrap();// Option

        assert_eq!(err, Package {
            authentication: None,
            correlation_id: Uuid::parse_str("7b50a1b0-34b9-224e-8f9d-708c394fab2d").unwrap(),
            message: RawMessage::Unsupported(255, Cow::Owned(vec![])).into()
        });
    }

    #[test]
    fn decode_write_events_completed() {
        let input = "2200000083009b59d8734e9fd84eb8a421f2666a3aa40800181e20272884d6bc563084d6bc56";
        test_decoding_hex(input,
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("9b59d873-4e9f-d84e-b8a4-21f2666a3aa4").unwrap(),
                              message: RawMessage::WriteEventsCompleted(WriteEventsCompleted {
                                  result: Some(OperationResult::Success),
                                  message: None,
                                  first_event_number: 30,
                                  last_event_number: 39,
                                  prepare_position: Some(181349124),
                                  commit_position: Some(181349124)
                              }).into()
                          });
    }

    #[test]
    fn encode_write_events_completed() {
        test_encoding_hex("2200000083009b59d8734e9fd84eb8a421f2666a3aa40800181e20272884d6bc563084d6bc56",
                          PackageCodec,
                          Package {
                              authentication: None,
                              correlation_id:
                                  Uuid::parse_str("9b59d873-4e9f-d84e-b8a4-21f2666a3aa4").unwrap(),
                              message: RawMessage::WriteEventsCompleted(WriteEventsCompleted {
                                  result: Some(OperationResult::Success),
                                  message: None,
                                  first_event_number: 30,
                                  last_event_number: 39,
                                  prepare_position: Some(181349124),
                                  commit_position: Some(181349124)
                              }).into()
                          });

    }

    #[test]
    fn decode_authenticated_package() {
        use bytes::BytesMut;
        use auth::UsernamePassword;

        let mut buf = BytesMut::with_capacity(1024);
        let id = Uuid::new_v4();

        let msg = Package {
            correlation_id: id,
            authentication: Some(UsernamePassword::new("foobar", "abbacd")),
            message: RawMessage::Ping,
        };

        PackageCodec.encode(msg.clone(), &mut buf).unwrap();

        let decoded = PackageCodec.decode(&mut buf).unwrap().unwrap();

        assert_eq!(msg, decoded);
    }

    fn test_decoding_hex<C: Decoder>(input: &str, codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        test_decoding(input.to_string().from_hex().unwrap(), codec, expected);
    }

    fn test_decoding<C: Decoder>(input: Vec<u8>, mut codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        // decode whole buffer
        {
            let mut buf = input.clone().into();
            let item = codec.decode(&mut buf).unwrap().unwrap();

            assert_eq!(item, expected);
            assert_eq!(buf.len(), 0, "decoding correctly sized buffer left bytes");
        }

        // decoding partial buffer consumes no bytes
        for len in 1..(input.len() - 1) {
            let mut part = input.clone();
            part.truncate(len);

            let mut buf = part.into();
            assert!(codec.decode(&mut buf).unwrap().is_none());
            assert_eq!(buf.len(), len, "decoding partial buffer consumed bytes");
        }

        // decoding a too long buffer consumes no extra bytes
        {
            let mut input = input.clone();
            let len = input.len();
            input.extend(vec![0u8; len]);
            let mut buf = input.into();
            let item = codec.decode(&mut buf).unwrap().unwrap();
            assert_eq!(item, expected);
            assert_eq!(buf.len(), len, "decoding oversized buffer overused bytes");
        }
    }

    fn test_encoding_hex<C: Encoder>(input: &str, codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        test_encoding(input.to_string().from_hex().unwrap(), codec, expected);
    }

    fn test_encoding<C: Encoder>(input: Vec<u8>, mut codec: C, expected: C::Item)
        where C::Item: Debug + PartialEq, C::Error: Debug
    {
        use bytes::BytesMut;

        let mut buf = BytesMut::with_capacity(input.len());
        codec.encode(expected, &mut buf).unwrap();
        assert_eq!(&buf[..],
                   &input[..],
                   "encoding did not yield same");
    }
}
<|endoftext|>


<commit_before>pub fn reply(message: &str) -> &str {
    if is_silence(message) { "Fine. Be that way." }
    else if is_yelling(message) { "Woah, chill out!" }
    else if is_question(message) { "Sure." }
    else  { "Whatever." }
}

fn is_silence(message: &str) -> bool {
    message == ""
}

fn is_yelling(message: &str) -> bool {
    let upcase_message: &[Ascii] = message.to_ascii().to_upper();
    let ascii_message: &[Ascii] = message.to_ascii();
    upcase_message == ascii_message
}

fn is_question(message: &str) -> bool {
    message.ends_with("?")
}
<commit_msg>Improve implementation of is_yelling<commit_after>pub fn reply(message: &str) -> &str {
    if is_silence(message) { "Fine. Be that way." }
    else if is_yelling(message) { "Woah, chill out!" }
    else if is_question(message) { "Sure." }
    else  { "Whatever." }
}

fn is_silence(message: &str) -> bool {
    message == ""
}

fn is_yelling(message: &str) -> bool {
    message.iter().all(|char| !char.is_lowercase())
}

fn is_question(message: &str) -> bool {
    message.ends_with("?")
}
<|endoftext|>


<commit_before>#![feature(phase)]

#[phase(plugin)] extern crate assert;
extern crate test;

extern crate tgff;

#[test]
fn parse_002_040() {
    let result = tgff::parse(read_fixture("002_040.tgff").as_slice()).unwrap();

    assert_eq!(result.attributes["HYPERPERIOD".to_string()], 8);
    assert_eq!(result.graphs.len(), 1);
    assert_eq!(result.tables.len(), 2);

    let graph = &result.graphs[0];
    assert_eq!(graph.name, "GRAPH".to_string());
    assert_eq!(graph.id, 0);
    assert_eq!(graph.attributes["PERIOD".to_string()], 8);
    assert_eq!(graph.tasks.len(), 40);
    assert_eq!(graph.arcs.len(), 52);
    assert_eq!(graph.deadlines.len(), 18);

    let prices = vec![10.5042, 14.8562];
    for i in range(0u, 2) {
        let table = &result.tables[i];
        assert_eq!(table.name, "CORE".to_string());
        assert_eq!(table.id, i);
        assert_eq!(table.attributes["price".to_string()] as f32, prices[i]);
        assert_eq!(table.columns.len(), 4);
        assert_eq!(table.columns[0].name, "type".to_string());
        assert_eq!(table.columns[1].name, "version".to_string());
        assert_eq!(table.columns[2].name, "dynamic_power".to_string());
        assert_eq!(table.columns[3].name, "execution_time".to_string());
    }

    let dynamic_power = vec![
        14.41, 9.38, 14.19, 15.48, 12.15, 16.57, 16.98, 11.02, 17.6, 5.42,
        13.95, 7.08, 5.66, 18.08, 16.51, 5.86, 9.31, 17.25, 8.48, 7.29,
    ];
    assert_close!(result.tables[0].columns[2].data, dynamic_power);

    let execution_time = vec![
        0.025, 0.019, 0.025, 0.026, 0.022, 0.027, 0.028, 0.021, 0.028, 0.015,
        0.024, 0.017, 0.015, 0.029, 0.027, 0.015, 0.019, 0.028, 0.018, 0.017,
    ];
    assert_close!(result.tables[0].columns[3].data, execution_time);
}

#[test]
fn parse_032_640() {
    let result = tgff::parse(read_fixture("032_640.tgff").as_slice()).unwrap();

    assert_eq!(result.attributes["HYPERPERIOD".to_string()], 18);
    assert_eq!(result.graphs.len(), 1);
    assert_eq!(result.tables.len(), 32);

    let graph = &result.graphs[0];
    assert_eq!(graph.tasks.len(), 640);
    assert_eq!(graph.arcs.len(), 848);
    assert_eq!(graph.deadlines.len(), 259);

    for table in result.tables.iter() {
        assert_eq!(table.attributes.len(), 1);
        assert_eq!(table.columns.len(), 4);
        for column in table.columns.iter() {
            assert_eq!(column.data.len(), 320);
        }
    }
}

fn read_fixture(name: &str) -> String {
    use std::io::fs::PathExtensions;
    let path = Path::new("tests").join_many(["fixtures", name]);
    assert!(path.exists());
    std::io::File::open(&path).read_to_string().unwrap()
}
<commit_msg>Keeping up with the nightly builds<commit_after>#![feature(phase)]

#[phase(plugin)] extern crate assert;
extern crate test;

extern crate tgff;

#[test]
fn parse_002_040() {
    let result = tgff::parse(read_fixture("002_040.tgff").as_slice()).unwrap();

    assert_eq!(result.attributes["HYPERPERIOD".to_string()], 8);
    assert_eq!(result.graphs.len(), 1);
    assert_eq!(result.tables.len(), 2);

    let graph = &result.graphs[0];
    assert_eq!(graph.name, "GRAPH".to_string());
    assert_eq!(graph.id, 0);
    assert_eq!(graph.attributes["PERIOD".to_string()], 8);
    assert_eq!(graph.tasks.len(), 40);
    assert_eq!(graph.arcs.len(), 52);
    assert_eq!(graph.deadlines.len(), 18);

    let prices = vec![10.5042, 14.8562];
    for i in range(0u, 2) {
        let table = &result.tables[i];
        assert_eq!(table.name, "CORE".to_string());
        assert_eq!(table.id, i);
        assert_eq!(table.attributes["price".to_string()] as f32, prices[i]);
        assert_eq!(table.columns.len(), 4);
        assert_eq!(table.columns[0].name, "type".to_string());
        assert_eq!(table.columns[1].name, "version".to_string());
        assert_eq!(table.columns[2].name, "dynamic_power".to_string());
        assert_eq!(table.columns[3].name, "execution_time".to_string());
    }

    let dynamic_power = vec![
        14.41, 9.38, 14.19, 15.48, 12.15, 16.57, 16.98, 11.02, 17.6, 5.42,
        13.95, 7.08, 5.66, 18.08, 16.51, 5.86, 9.31, 17.25, 8.48, 7.29,
    ];
    assert_close!(result.tables[0].columns[2].data, dynamic_power);

    let execution_time = vec![
        0.025, 0.019, 0.025, 0.026, 0.022, 0.027, 0.028, 0.021, 0.028, 0.015,
        0.024, 0.017, 0.015, 0.029, 0.027, 0.015, 0.019, 0.028, 0.018, 0.017,
    ];
    assert_close!(result.tables[0].columns[3].data, execution_time);
}

#[test]
fn parse_032_640() {
    let result = tgff::parse(read_fixture("032_640.tgff").as_slice()).unwrap();

    assert_eq!(result.attributes["HYPERPERIOD".to_string()], 18);
    assert_eq!(result.graphs.len(), 1);
    assert_eq!(result.tables.len(), 32);

    let graph = &result.graphs[0];
    assert_eq!(graph.tasks.len(), 640);
    assert_eq!(graph.arcs.len(), 848);
    assert_eq!(graph.deadlines.len(), 259);

    for table in result.tables.iter() {
        assert_eq!(table.attributes.len(), 1);
        assert_eq!(table.columns.len(), 4);
        for column in table.columns.iter() {
            assert_eq!(column.data.len(), 320);
        }
    }
}

fn read_fixture(name: &str) -> String {
    use std::io::fs::PathExtensions;
    let path = Path::new("tests").join_many(&["fixtures", name]);
    assert!(path.exists());
    std::io::File::open(&path).read_to_string().unwrap()
}
<|endoftext|>


<commit_before>extern crate sorting;

#[test]
fn it_works() {
    // let array: Vec<_> = vec![10, 11, 13, 14, 15, 12];
    let array: Vec<_> = vec![15, 14, 17, 12, 14, 19];
    // let array: Vec<_> = vec![5.2, 4.3, 7.4, 2.3, 4.1, 9.0];
    println!("it_works()...");
    sorting::simplesorts::insertion_sort::sort(&array);
    println!("it_works() done!");
}
<commit_msg>Amplement a proper test.<commit_after>extern crate sorting;

use std::fmt::Debug;

/// Verify order of input vector as sorted by indices.
///
/// # Details
///
/// This function takes as arguments a vector of data and a vector representing the indices of the
/// first vector making it sorted.
///
/// The elements of the sorted vector are compared to the next ones to make sure they are less
/// or equal.
///
/// # Panics
///
/// The sorting of the data vector is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// let sorted_indices: Vec<usize> = vec![3, 1, 2, 0];
/// verify_sorting(&data, &sorted_indices);
/// ```
///
fn verify_sorting<T: Debug + PartialOrd>(array: &Vec<T>, sorted_indices: &Vec<usize>) {
    let n = array.len();
    assert_eq!(n , sorted_indices.len());

    for i in 0..n-1 {
        println!("array[sorted_indices[i={:?}]]={:?}   array[sorted_indices[i+1={:?}]]={:?}",
            i, array[sorted_indices[i]], i+1, array[sorted_indices[i+1]]);
        assert!(array[sorted_indices[i]] <= array[sorted_indices[i+1]]);
    }
}

#[test]
fn simple_insertion_multiple_sizes_f64() {
    let to_sort: Vec<f64> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                 3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];

    let sorted_indices = sorting::simplesorts::insertion_sort::sort(&to_sort);

    verify_sorting(&to_sort, &sorted_indices);
}
<|endoftext|>


<commit_before>use libc::c_int;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};

use ReadsMemory;

pub struct MemReader {
  pid: u32
}

impl MemReader {
  pub fn new(pid: u32) -> Self {
    MemReader {
      pid: pid
    }
  }

  fn get_memory_file(&self) -> Result<File, c_int> {
    File::open(&format!("/proc/{}/maps", self.pid)).map_err(|_| -1)
  }
}

impl ReadsMemory for MemReader {
  fn read_bytes(&self, address: usize, n: usize) -> Result<Vec<u8>, c_int> {
    let mut file = self.get_memory_file()?;
    file.seek(SeekFrom::Start(address as i64)).map_err(|_| -2)?;
    let mut bytes = Vec::with_capacity(n);
    file.read_exact(&mut bytes).map_err(|_| -3)?;
    Ok(bytes)
  }
}
<commit_msg>fix: use u64<commit_after>use libc::c_int;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};

use ReadsMemory;

pub struct MemReader {
  pid: u32
}

impl MemReader {
  pub fn new(pid: u32) -> Self {
    MemReader {
      pid: pid
    }
  }

  fn get_memory_file(&self) -> Result<File, c_int> {
    File::open(&format!("/proc/{}/maps", self.pid)).map_err(|_| -1)
  }
}

impl ReadsMemory for MemReader {
  fn read_bytes(&self, address: usize, n: usize) -> Result<Vec<u8>, c_int> {
    let mut file = self.get_memory_file()?;
    file.seek(SeekFrom::Start(address as u64)).map_err(|_| -2)?;
    let mut bytes = Vec::with_capacity(n);
    file.read_exact(&mut bytes).map_err(|_| -3)?;
    Ok(bytes)
  }
}
<|endoftext|>


<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    url: url::Url,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.url.scheme(),
               self.url.domain().unwrap_or("bad hostname provided"),
               self.url.port().map(|port_u16| port_u16.to_string()).unwrap_or("no port provided".to_string()),
               self.pagination)
    }
}

fn validate_url(scheme: &str, domain: &str, port: u16) -> Result<url::Url> {

    let index = match domain.find(".") {
        None => bail!(format!("invalid domain: '{}' should contain at least one dot", domain)),
        Some(index) => index
    };
    if index == 0 {
        bail!(format!("invalid domain: '{}' cannot start with a dot", domain));
    }
    if domain.ends_with(".") {
        bail!(format!("invalid domain: '{}' cannot end with a dot", domain));
    }

    let url_string = format!("{}://{}/api/v{}/", scheme, domain, API_VERSION);
    let mut url = url::Url::parse(&url_string)
        .chain_err(|| format!("failure to parse URL '{}'", url_string))?;
    url.set_port(Some(port)).expect("bad port provided");

    {
        let url_host = url.host_str();
        if url_host.is_none() {
            bail!("failure to get URL's hostname");
        }
        if url_host.unwrap() != domain {
            bail!(format!("invalid hostname '{}'", domain));
        }
    }

    Ok(url)
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> Result<GitLab> {
        if private_token.len() != 20 {
            bail!(format!("private token should be a 20 characters string (not {})", private_token.len()));
        }

        let url: url::Url = validate_url(scheme, domain, port).chain_err(|| "invalid URL")?;

        Ok(GitLab {
            url: url,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1].parse().chain_err(|| format!("failure to set port to {}", port))?;

                    hyper::Client::with_http_proxy(hostname, port)
                }
                Err(_) => hyper::Client::new(),
            },
        })
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> Result<GitLab> {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> Result<GitLab> {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.url.set_port(Some(port)).unwrap();
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.url.set_scheme(scheme).unwrap();
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXX").unwrap();
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url =
            self.url.clone().join(query).chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            })?;
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query).chain_err(|| format!("failure to build url for query '{}'", query))?;
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        // assert_eq!(res.status, hyper::status::StatusCode::Ok);
        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code ({}) not Ok()", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<commit_msg>Add unit tests on URL build<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    url: url::Url,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.url.scheme(),
               self.url.domain().unwrap_or("bad hostname provided"),
               self.url.port().map(|port_u16| port_u16.to_string()).unwrap_or("no port provided".to_string()),
               self.pagination)
    }
}

fn validate_url(scheme: &str, domain: &str, port: u16) -> Result<url::Url> {

    let index = match domain.find(".") {
        None => bail!(format!("invalid domain: '{}' should contain at least one dot", domain)),
        Some(index) => index
    };
    if index == 0 {
        bail!(format!("invalid domain: '{}' cannot start with a dot", domain));
    }
    if domain.ends_with(".") {
        bail!(format!("invalid domain: '{}' cannot end with a dot", domain));
    }

    let url_string = format!("{}://{}/api/v{}/", scheme, domain, API_VERSION);
    let mut url = url::Url::parse(&url_string)
        .chain_err(|| format!("failure to parse URL '{}'", url_string))?;
    url.set_port(Some(port)).expect("bad port provided");

    {
        let url_host = url.host_str();
        if url_host.is_none() {
            bail!("failure to get URL's hostname");
        }
        if url_host.unwrap() != domain {
            bail!(format!("invalid hostname '{}'", domain));
        }
    }

    Ok(url)
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> Result<GitLab> {
        if private_token.len() != 20 {
            bail!(format!("private token should be a 20 characters string (not {})", private_token.len()));
        }

        let url: url::Url = validate_url(scheme, domain, port).chain_err(|| "invalid URL")?;

        Ok(GitLab {
            url: url,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1].parse().chain_err(|| format!("failure to set port to {}", port))?;

                    hyper::Client::with_http_proxy(hostname, port)
                }
                Err(_) => hyper::Client::new(),
            },
        })
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> Result<GitLab> {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> Result<GitLab> {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.url.set_port(Some(port)).unwrap();
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.url.set_scheme(scheme).unwrap();
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXX").unwrap();
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url =
            self.url.clone().join(query).chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            })?;
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query).chain_err(|| format!("failure to build url for query '{}'", query))?;
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        // assert_eq!(res.status, hyper::status::StatusCode::Ok);
        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code ({}) not Ok()", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use gitlab::*;
    use errors::*;

    fn verify_ok<T>(result: &Result<T>) {
        if let &Err(ref e) = result {
            println!("error: {}", e);

            for e in e.iter().skip(1) {
                println!("caused by: {}", e);
            }

            // The backtrace is not always generated. Try to run this example
            // with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                println!("backtrace: {:?}", backtrace);
            }
        }
        assert!(result.is_ok());
    }

    fn verify_err<T>(result: &Result<T>)
        where T: fmt::Debug
    {
        match result {
            &Err(_) => { /* pass */ },
            &Ok(ref t) => {
                panic!(format!("Expected an Err(), got an Ok(t), with t: {:?}", t));
            }
        }
    }

    #[test]
    fn new_valid() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);
    }

    #[test]
    fn new_invalid_url_0() {
        let gl = GitLab::new("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_1() {
        let gl = GitLab::new("gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_2() {
        let gl = GitLab::new("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_3() {
        let gl = GitLab::new("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_4() {
        let gl = GitLab::new("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_5() {
        let gl = GitLab::new("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_6() {
        let gl = GitLab::new(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_7() {
        let gl = GitLab::new(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_8() {
        let gl = GitLab::new("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_10() {
        let gl = GitLab::new("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_token() {
        let gl = GitLab::new("gitlab.com", "");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "X");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());
    }
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};
use std::ops::{Deref, DerefMut};

use object::{Handle, Obj};

pub const HANDLE_SIZE: usize = 256;

pub struct HandleMemory {
    /// all buffers, Box is important since HandleBuffer
    /// is a big struct that needs to get moved/copied on resizes
    buffers: RefCell<Vec<Box<HandleBuffer>>>,

    // store addresses of inserted borders
    borders: RefCell<Vec<BorderData>>,

    // index of next free position in buffer
    free: Cell<usize>,
}

impl HandleMemory {
    pub fn new() -> HandleMemory {
        let buffer = box HandleBuffer::new();

        HandleMemory {
            buffers: RefCell::new(vec![buffer]),
            borders: RefCell::new(Vec::new()),
            free: Cell::new(0),
        }
    }

    pub fn root<T>(&self, obj: Handle<T>) -> Rooted<T> {
        if self.free.get() >= HANDLE_SIZE {
            self.push_buffer();
            self.free.set(0);
        }

        let mut buffers = self.buffers.borrow_mut();
        let buffer = buffers.last_mut().unwrap();

        let idx = self.free.get();
        let elem = &mut buffer.elements[idx];
        self.free.set(idx+1);

        *elem = obj.cast::<Obj>();

        Rooted(elem as *mut Handle<Obj> as *mut Handle<T>)
    }

    fn push_buffer(&self) {
        self.buffers.borrow_mut().push(box HandleBuffer::new());
    }

    pub fn push_border(&self) {
        let buffer = self.buffers.borrow().len();
        let element = self.free.get();

        self.borders.borrow_mut().push(BorderData {
            buffer: buffer,
            element: element,
        });
    }

    pub fn pop_border(&self) {
        let border = self.borders.borrow_mut().pop().expect("no border left");

        self.buffers.borrow_mut().truncate(border.buffer);
        self.free.set(border.element);
    }

    pub fn iter(&self) -> HandleMemoryIter {
        let len = self.buffers.borrow().len();

        HandleMemoryIter {
            mem: self,
            buffer_idx: 0,
            element_idx: 0,
            full_buffer_len: if len == 0 { 0 } else { len - 1 },
            last_buffer_len: self.free.get(),
        }
    }
}

struct HandleBuffer {
    elements: [Handle<Obj>; HANDLE_SIZE],
}

impl HandleBuffer {
    fn new() -> HandleBuffer {
        HandleBuffer { elements: [Handle::null(); HANDLE_SIZE] }
    }
}

struct BorderData {
    buffer: usize,
    element: usize,
}

#[derive(Copy, Clone)]
pub struct Rooted<T>(*mut Handle<T>);

impl<T> Rooted<T> {
    pub fn direct(self) -> Handle<T> {
        unsafe { *self.0 }
    }

    pub fn raw(self) -> *mut Handle<T> {
        self.0
    }

    pub fn cast<R>(&self) -> Rooted<R> {
        Rooted(self.0 as *mut Handle<R>)
    }
}

impl<T> Deref for Rooted<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.0 }.deref()
    }
}

impl<T> DerefMut for Rooted<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.0 }.deref_mut()
    }
}

pub struct HandleMemoryIter<'a> {
    mem: &'a HandleMemory,
    buffer_idx: usize,
    element_idx: usize,
    full_buffer_len: usize,
    last_buffer_len: usize,
}

impl<'a> Iterator for HandleMemoryIter<'a> {
    type Item = Rooted<Obj>;

    fn next(&mut self) -> Option<Rooted<Obj>> {
        if self.buffer_idx < self.full_buffer_len {
            if self.element_idx < HANDLE_SIZE {
                let idx = self.element_idx;
                self.element_idx += 1;

                let mut buffers = self.mem.buffers.borrow_mut();
                let buffer = &mut buffers[self.buffer_idx];
                return Some(Rooted(&mut buffer.elements[idx] as *mut Handle<Obj>));

            } else {
                self.buffer_idx += 1;
                self.element_idx = 0;
            }
        }

        if self.buffer_idx == self.full_buffer_len {
            if self.element_idx < self.last_buffer_len {
                let idx = self.element_idx;
                self.element_idx += 1;

                let mut buffers = self.mem.buffers.borrow_mut();
                let buffer = &mut buffers[self.buffer_idx];
                return Some(Rooted(&mut buffer.elements[idx] as *mut Handle<Obj>));

            } else {
                return None;
            }
        }

        None
    }
}<commit_msg>implement Copy and Clone for Rooted<commit_after>use std::cell::{Cell, RefCell};
use std::ops::{Deref, DerefMut};

use object::{Handle, Obj};

pub const HANDLE_SIZE: usize = 256;

pub struct HandleMemory {
    /// all buffers, Box is important since HandleBuffer
    /// is a big struct that needs to get moved/copied on resizes
    buffers: RefCell<Vec<Box<HandleBuffer>>>,

    // store addresses of inserted borders
    borders: RefCell<Vec<BorderData>>,

    // index of next free position in buffer
    free: Cell<usize>,
}

impl HandleMemory {
    pub fn new() -> HandleMemory {
        let buffer = box HandleBuffer::new();

        HandleMemory {
            buffers: RefCell::new(vec![buffer]),
            borders: RefCell::new(Vec::new()),
            free: Cell::new(0),
        }
    }

    pub fn root<T>(&self, obj: Handle<T>) -> Rooted<T> {
        if self.free.get() >= HANDLE_SIZE {
            self.push_buffer();
            self.free.set(0);
        }

        let mut buffers = self.buffers.borrow_mut();
        let buffer = buffers.last_mut().unwrap();

        let idx = self.free.get();
        let elem = &mut buffer.elements[idx];
        self.free.set(idx+1);

        *elem = obj.cast::<Obj>();

        Rooted(elem as *mut Handle<Obj> as *mut Handle<T>)
    }

    fn push_buffer(&self) {
        self.buffers.borrow_mut().push(box HandleBuffer::new());
    }

    pub fn push_border(&self) {
        let buffer = self.buffers.borrow().len();
        let element = self.free.get();

        self.borders.borrow_mut().push(BorderData {
            buffer: buffer,
            element: element,
        });
    }

    pub fn pop_border(&self) {
        let border = self.borders.borrow_mut().pop().expect("no border left");

        self.buffers.borrow_mut().truncate(border.buffer);
        self.free.set(border.element);
    }

    pub fn iter(&self) -> HandleMemoryIter {
        let len = self.buffers.borrow().len();

        HandleMemoryIter {
            mem: self,
            buffer_idx: 0,
            element_idx: 0,
            full_buffer_len: if len == 0 { 0 } else { len - 1 },
            last_buffer_len: self.free.get(),
        }
    }
}

struct HandleBuffer {
    elements: [Handle<Obj>; HANDLE_SIZE],
}

impl HandleBuffer {
    fn new() -> HandleBuffer {
        HandleBuffer { elements: [Handle::null(); HANDLE_SIZE] }
    }
}

struct BorderData {
    buffer: usize,
    element: usize,
}

pub struct Rooted<T>(*mut Handle<T>);

impl<T> Rooted<T> {
    pub fn direct(self) -> Handle<T> {
        unsafe { *self.0 }
    }

    pub fn raw(self) -> *mut Handle<T> {
        self.0
    }

    pub fn cast<R>(self) -> Rooted<R> {
        Rooted(self.0 as *mut Handle<R>)
    }
}

impl<T> Deref for Rooted<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.0 }.deref()
    }
}

impl<T> DerefMut for Rooted<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.0 }.deref_mut()
    }
}

// known limitation of #[derive(Copy, Clone)]
// traits need to be implemented manually
impl<T> Copy for Rooted<T> {}
impl<T> Clone for Rooted<T> {
    fn clone(&self) -> Rooted<T> {
        *self
    }
}

pub struct HandleMemoryIter<'a> {
    mem: &'a HandleMemory,
    buffer_idx: usize,
    element_idx: usize,
    full_buffer_len: usize,
    last_buffer_len: usize,
}

impl<'a> Iterator for HandleMemoryIter<'a> {
    type Item = Rooted<Obj>;

    fn next(&mut self) -> Option<Rooted<Obj>> {
        if self.buffer_idx < self.full_buffer_len {
            if self.element_idx < HANDLE_SIZE {
                let idx = self.element_idx;
                self.element_idx += 1;

                let mut buffers = self.mem.buffers.borrow_mut();
                let buffer = &mut buffers[self.buffer_idx];
                return Some(Rooted(&mut buffer.elements[idx] as *mut Handle<Obj>));

            } else {
                self.buffer_idx += 1;
                self.element_idx = 0;
            }
        }

        if self.buffer_idx == self.full_buffer_len {
            if self.element_idx < self.last_buffer_len {
                let idx = self.element_idx;
                self.element_idx += 1;

                let mut buffers = self.mem.buffers.borrow_mut();
                let buffer = &mut buffers[self.buffer_idx];
                return Some(Rooted(&mut buffer.elements[idx] as *mut Handle<Obj>));

            } else {
                return None;
            }
        }

        None
    }
}<|endoftext|>


<commit_before>use crate::decoder::{decode, decode_regular, decode_slice};
use crate::encoder::{encode, Encodable};
use crate::errors::{Error, Result};
use crate::jsontypes::{FacebookScopeMapping, FacebookSources, RawSourceMap};
use crate::types::{DecodedMap, RewriteOptions, SourceMap};
use crate::vlq::parse_vlq_segment;
use std::cmp::Ordering;
use std::io::{Read, Write};
use std::ops::{Deref, DerefMut};

/// These are starting locations of scopes.
/// The `name_index` represents the index into the `HermesFunctionMap.names` vec,
/// which represents the function names/scopes.
pub struct HermesScopeOffset {
    line: u32,
    column: u32,
    name_index: u32,
}

pub struct HermesFunctionMap {
    names: Vec<String>,
    mappings: Vec<HermesScopeOffset>,
}

/// Represents a `react-native`-style SourceMap, which has additional scope
/// information embedded.
pub struct SourceMapHermes {
    pub(crate) sm: SourceMap,
    // There should be one `HermesFunctionMap` per each `sources` entry in the main SourceMap.
    function_maps: Vec<Option<HermesFunctionMap>>,
    // XXX: right now, I am too lazy to actually serialize the above `funciton_maps`
    // back into json types, so just keep the original json. Might be a bit inefficient, but meh.
    raw_facebook_sources: FacebookSources,
}

impl Deref for SourceMapHermes {
    type Target = SourceMap;

    fn deref(&self) -> &Self::Target {
        &self.sm
    }
}

impl DerefMut for SourceMapHermes {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.sm
    }
}

impl Encodable for SourceMapHermes {
    fn as_raw_sourcemap(&self) -> RawSourceMap {
        // TODO: need to serialize the `HermesFunctionMap` mappings
        let mut rsm = self.sm.as_raw_sourcemap();
        rsm.x_facebook_sources = self.raw_facebook_sources.clone();
        rsm
    }
}

impl SourceMapHermes {
    /// Creates a sourcemap from a reader over a JSON stream in UTF-8
    /// format.
    ///
    /// See [`SourceMap::from_reader`](struct.SourceMap.html#method.from_reader)
    pub fn from_reader<R: Read>(rdr: R) -> Result<Self> {
        match decode(rdr)? {
            DecodedMap::Hermes(sm) => Ok(sm),
            _ => Err(Error::IncompatibleSourceMap),
        }
    }

    /// Creates a sourcemap from a reader over a JSON byte slice in UTF-8
    /// format.
    ///
    /// See [`SourceMap::from_slice`](struct.SourceMap.html#method.from_slice)
    pub fn from_slice(slice: &[u8]) -> Result<Self> {
        match decode_slice(slice)? {
            DecodedMap::Hermes(sm) => Ok(sm),
            _ => Err(Error::IncompatibleSourceMap),
        }
    }

    /// Writes a sourcemap into a writer.
    ///
    /// See [`SourceMap::to_writer`](struct.SourceMap.html#method.to_writer)
    pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
        encode(self, w)
    }

    /// Given a bytecode offset, this will find the enclosing scopes function
    /// name.
    pub fn get_original_function_name(&self, bytecode_offset: u32) -> Option<&str> {
        let token = self.sm.lookup_token(0, bytecode_offset)?;

        let function_map = &self.function_maps[token.get_src_id() as usize].as_ref()?;

        // Find the closest mapping, just like here:
        // https://github.com/facebook/metro/blob/63b523eb20e7bdf62018aeaf195bb5a3a1a67f36/packages/metro-symbolicate/src/SourceMetadataMapConsumer.js#L204-L231
        let mapping =
            function_map
                .mappings
                .binary_search_by(|o| match o.line.cmp(&token.get_src_line()) {
                    Ordering::Equal => o.column.cmp(&token.get_src_col()),
                    x => x,
                });
        let name_index = function_map
            .mappings
            .get(match mapping {
                Ok(a) => a,
                Err(a) => a.saturating_sub(1),
            })?
            .name_index;

        function_map
            .names
            .get(name_index as usize)
            .map(|n| n.as_str())
    }

    /// This rewrites the sourcemap according to the provided rewrite
    /// options.
    ///
    /// See [`SourceMap::rewrite`](struct.SourceMap.html#method.rewrite)
    pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<Self> {
        let Self {
            sm,
            function_maps,
            raw_facebook_sources,
        } = self;
        let sm = sm.rewrite(options)?;
        Ok(Self {
            sm,
            function_maps,
            raw_facebook_sources,
        })
    }
}

pub fn decode_hermes(mut rsm: RawSourceMap) -> Result<SourceMapHermes> {
    let x_facebook_sources = rsm
        .x_facebook_sources
        .take()
        .ok_or(Error::IncompatibleSourceMap)?;

    // This is basically the logic from here:
    // https://github.com/facebook/metro/blob/63b523eb20e7bdf62018aeaf195bb5a3a1a67f36/packages/metro-symbolicate/src/SourceMetadataMapConsumer.js#L182-L202

    let function_maps = x_facebook_sources
        .iter()
        .map(|v| {
            let FacebookScopeMapping {
                names,
                mappings: raw_mappings,
            } = v.as_ref()?.iter().next()?;

            let mut mappings = vec![];
            let mut line = 1;
            let mut name_index = 0;

            for line_mapping in raw_mappings.split(';') {
                if line_mapping.is_empty() {
                    continue;
                }

                let mut column = 0;

                for mapping in line_mapping.split(',') {
                    if mapping.is_empty() {
                        continue;
                    }

                    let mut nums = parse_vlq_segment(mapping).ok()?.into_iter();

                    column = (i64::from(column) + nums.next()?) as u32;
                    name_index = (i64::from(name_index) + nums.next().unwrap_or(0)) as u32;
                    line = (i64::from(line) + nums.next().unwrap_or(0)) as u32;
                    mappings.push(HermesScopeOffset {
                        column,
                        line,
                        name_index,
                    });
                }
            }
            Some(HermesFunctionMap {
                names: names.clone(),
                mappings,
            })
        })
        .collect();

    let sm = decode_regular(rsm)?;
    Ok(SourceMapHermes {
        sm,
        function_maps,
        raw_facebook_sources: Some(x_facebook_sources),
    })
}
<commit_msg>avoid one last painc :-)<commit_after>use crate::decoder::{decode, decode_regular, decode_slice};
use crate::encoder::{encode, Encodable};
use crate::errors::{Error, Result};
use crate::jsontypes::{FacebookScopeMapping, FacebookSources, RawSourceMap};
use crate::types::{DecodedMap, RewriteOptions, SourceMap};
use crate::vlq::parse_vlq_segment;
use std::cmp::Ordering;
use std::io::{Read, Write};
use std::ops::{Deref, DerefMut};

/// These are starting locations of scopes.
/// The `name_index` represents the index into the `HermesFunctionMap.names` vec,
/// which represents the function names/scopes.
pub struct HermesScopeOffset {
    line: u32,
    column: u32,
    name_index: u32,
}

pub struct HermesFunctionMap {
    names: Vec<String>,
    mappings: Vec<HermesScopeOffset>,
}

/// Represents a `react-native`-style SourceMap, which has additional scope
/// information embedded.
pub struct SourceMapHermes {
    pub(crate) sm: SourceMap,
    // There should be one `HermesFunctionMap` per each `sources` entry in the main SourceMap.
    function_maps: Vec<Option<HermesFunctionMap>>,
    // XXX: right now, I am too lazy to actually serialize the above `function_maps`
    // back into json types, so just keep the original json. Might be a bit inefficient, but meh.
    raw_facebook_sources: FacebookSources,
}

impl Deref for SourceMapHermes {
    type Target = SourceMap;

    fn deref(&self) -> &Self::Target {
        &self.sm
    }
}

impl DerefMut for SourceMapHermes {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.sm
    }
}

impl Encodable for SourceMapHermes {
    fn as_raw_sourcemap(&self) -> RawSourceMap {
        // TODO: need to serialize the `HermesFunctionMap` mappings
        let mut rsm = self.sm.as_raw_sourcemap();
        rsm.x_facebook_sources = self.raw_facebook_sources.clone();
        rsm
    }
}

impl SourceMapHermes {
    /// Creates a sourcemap from a reader over a JSON stream in UTF-8
    /// format.
    ///
    /// See [`SourceMap::from_reader`](struct.SourceMap.html#method.from_reader)
    pub fn from_reader<R: Read>(rdr: R) -> Result<Self> {
        match decode(rdr)? {
            DecodedMap::Hermes(sm) => Ok(sm),
            _ => Err(Error::IncompatibleSourceMap),
        }
    }

    /// Creates a sourcemap from a reader over a JSON byte slice in UTF-8
    /// format.
    ///
    /// See [`SourceMap::from_slice`](struct.SourceMap.html#method.from_slice)
    pub fn from_slice(slice: &[u8]) -> Result<Self> {
        match decode_slice(slice)? {
            DecodedMap::Hermes(sm) => Ok(sm),
            _ => Err(Error::IncompatibleSourceMap),
        }
    }

    /// Writes a sourcemap into a writer.
    ///
    /// See [`SourceMap::to_writer`](struct.SourceMap.html#method.to_writer)
    pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
        encode(self, w)
    }

    /// Given a bytecode offset, this will find the enclosing scopes function
    /// name.
    pub fn get_original_function_name(&self, bytecode_offset: u32) -> Option<&str> {
        let token = self.sm.lookup_token(0, bytecode_offset)?;

        let function_map = (self.function_maps.get(token.get_src_id() as usize))?.as_ref()?;

        // Find the closest mapping, just like here:
        // https://github.com/facebook/metro/blob/63b523eb20e7bdf62018aeaf195bb5a3a1a67f36/packages/metro-symbolicate/src/SourceMetadataMapConsumer.js#L204-L231
        let mapping =
            function_map
                .mappings
                .binary_search_by(|o| match o.line.cmp(&token.get_src_line()) {
                    Ordering::Equal => o.column.cmp(&token.get_src_col()),
                    x => x,
                });
        let name_index = function_map
            .mappings
            .get(match mapping {
                Ok(a) => a,
                Err(a) => a.saturating_sub(1),
            })?
            .name_index;

        function_map
            .names
            .get(name_index as usize)
            .map(|n| n.as_str())
    }

    /// This rewrites the sourcemap according to the provided rewrite
    /// options.
    ///
    /// See [`SourceMap::rewrite`](struct.SourceMap.html#method.rewrite)
    pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<Self> {
        let Self {
            sm,
            function_maps,
            raw_facebook_sources,
        } = self;
        let sm = sm.rewrite(options)?;
        Ok(Self {
            sm,
            function_maps,
            raw_facebook_sources,
        })
    }
}

pub fn decode_hermes(mut rsm: RawSourceMap) -> Result<SourceMapHermes> {
    let x_facebook_sources = rsm
        .x_facebook_sources
        .take()
        .ok_or(Error::IncompatibleSourceMap)?;

    // This is basically the logic from here:
    // https://github.com/facebook/metro/blob/63b523eb20e7bdf62018aeaf195bb5a3a1a67f36/packages/metro-symbolicate/src/SourceMetadataMapConsumer.js#L182-L202

    let function_maps = x_facebook_sources
        .iter()
        .map(|v| {
            let FacebookScopeMapping {
                names,
                mappings: raw_mappings,
            } = v.as_ref()?.iter().next()?;

            let mut mappings = vec![];
            let mut line = 1;
            let mut name_index = 0;

            for line_mapping in raw_mappings.split(';') {
                if line_mapping.is_empty() {
                    continue;
                }

                let mut column = 0;

                for mapping in line_mapping.split(',') {
                    if mapping.is_empty() {
                        continue;
                    }

                    let mut nums = parse_vlq_segment(mapping).ok()?.into_iter();

                    column = (i64::from(column) + nums.next()?) as u32;
                    name_index = (i64::from(name_index) + nums.next().unwrap_or(0)) as u32;
                    line = (i64::from(line) + nums.next().unwrap_or(0)) as u32;
                    mappings.push(HermesScopeOffset {
                        column,
                        line,
                        name_index,
                    });
                }
            }
            Some(HermesFunctionMap {
                names: names.clone(),
                mappings,
            })
        })
        .collect();

    let sm = decode_regular(rsm)?;
    Ok(SourceMapHermes {
        sm,
        function_maps,
        raw_facebook_sources: Some(x_facebook_sources),
    })
}
<|endoftext|>