<commit_before>#![feature(try_trait)]

#[macro_use]
extern crate failure;
extern crate num;
extern crate nalgebra;
#[cfg(test)]
#[macro_use]
extern crate proptest;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
#[macro_use]
extern crate smallvec;
#[cfg(test)]
extern crate rand;
extern crate array_ext;

use num::traits::{Zero, WrappingAdd, WrappingSub, WrappingMul, CheckedShl, CheckedShr, Signed};
use std::mem;
use std::cmp::{min};
use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign, Shl, Shr, Neg, Index};
use std::fmt::{Debug, Display};

pub trait Integer:
  'static + num::PrimInt + num::Integer + num::FromPrimitive +
  AddAssign <Self> + SubAssign <Self> + MulAssign <Self> +
  WrappingAdd + WrappingSub + WrappingMul +
  for<'a> Add <&'a Self, Output = Self> + for<'a> Sub <&'a Self, Output = Self> + for<'a> Mul <&'a Self, Output = Self> +
  CheckedShl + CheckedShr + Shl <u32, Output = Self> + Shr <u32, Output = Self> +
  Debug + Display + Send + Sync {}
impl <T: 'static + num::PrimInt + num::Integer + num::FromPrimitive + AddAssign <Self> + SubAssign <Self> + MulAssign <Self> + WrappingAdd + WrappingSub + WrappingMul + for<'a> Add <&'a Self, Output = Self> + for<'a> Sub <&'a Self, Output = Self> + for<'a> Mul <&'a Self, Output = Self> + CheckedShl + CheckedShr + Shl <u32, Output = Self> + Shr <u32, Output = Self> + Debug + Display + Send + Sync> Integer for T {}

pub trait HasCoordinates
   {
  type Coordinate: Integer + Signed + Vector + HasCoordinates <Coordinate = Self::Coordinate>;}

pub trait Vector:
  'static + Sized + Copy + Clone + HasCoordinates +
  Add <Self, Output = Self> + Sub <Self, Output = Self> + Mul <<Self as HasCoordinates>::Coordinate, Output = Self> +
  for<'a> Add <&'a Self, Output = Self> + for<'a> Sub <&'a Self, Output = Self> + //for<'a> Mul <&'a <Self as HasCoordinates>::Coordinate, Output = Self> +
  AddAssign <Self> + SubAssign <Self> + MulAssign <<Self as HasCoordinates>::Coordinate> +
  for<'a> AddAssign <&'a Self> + for<'a> SubAssign <&'a Self> + //for<'a> MulAssign <&'a <Self as HasCoordinates>::Coordinate> +
  Zero + Neg <Output = Self>
  {
  const DIMENSIONS: usize;
  fn coordinate (&self, which: usize)->Self::Coordinate;
  fn set_coordinate (&mut self, which: usize, value: Self::Coordinate);
  fn map_coordinates <F: Fn(Self::Coordinate)->Self::Coordinate> (mut self, transform: F)->Self {
    for index in 0..Self::DIMENSIONS {
      let new_coordinate = (transform) (self.coordinate (index));
      self.set_coordinate (index, new_coordinate);
    }
    self
  }
}

pub mod impls {
  use super::*;
  use nalgebra::*;
  macro_rules! impl_vector {
    ($([$coordinates: expr, $Vector: ident],)*) => {
      $(
        impl <T: Integer + Signed + Vector + HasCoordinates <Coordinate = T>> HasCoordinates for $Vector <T> {type Coordinate = T;}
        impl <T: Integer + Signed + Vector + HasCoordinates <Coordinate = T>> Vector for $Vector <T> {
          const DIMENSIONS: usize = $coordinates;
          fn coordinate (&self, which: usize)->Self::Coordinate {self [which]}
          fn set_coordinate (&mut self, which: usize, value: Self::Coordinate) {self [which] = value}
        }
      )*
    }
  }
  macro_rules! impl_integer {
    ($($Integer: ident,)*) => {
      $(
        impl HasCoordinates for $Integer {type Coordinate = $Integer;}
        impl Vector for $Integer {
          const DIMENSIONS: usize = 1;
          fn coordinate (&self, which: usize)->Self::Coordinate {*self}
          fn set_coordinate (&mut self, which: usize, value: Self::Coordinate) {*self = value}
        }
      )*
    }
  }
  impl_vector! ([1, Vector1], [2, Vector2], [3, Vector3], [4, Vector4], [5, Vector5], [6, Vector6],);
  impl_integer! (i8, i16, i32, i64, isize,);
}

/// Right-shift an integer, but round to nearest, with ties rounding to even.
///
/// This minimizes error, and avoids a directional bias.
pub fn shr_nicely_rounded <T: Integer> (input: T, shift: u32)->T {
  let divisor = match T::one().checked_shl ( shift ) {Some (value) => value, None => return T::zero()};
  let mask = divisor.wrapping_sub (&T::one());
  let shifted = input >> shift;
  let half = divisor >> 1u32;
  shifted + if (input & mask)+(shifted & if shift != 0 {T::one()} else {T::zero()}) > half {T::one()} else {T::zero()}
}

/// Right-shift an integer, but round to even.
///
/// This avoids a directional bias.
pub fn shr_round_to_even <T: Integer> (input: T, shift: u32)->T {
  let divisor = match T::one().checked_shl ( shift ) {Some (value) => value, None => return T::zero()};
  let mask = divisor.wrapping_sub (&T::one());
  let shifted = input >> shift;
  shifted + if (input & mask) != T::zero() {shifted & T::one()} else {T::zero()}
}

/// Right-shift an integer, but round towards positive infinity.
pub fn shr_ceil <T: Integer> (input: T, shift: u32)->T {
  let divisor = match T::one().checked_shl ( shift ) {Some (value) => value, None => return T::zero()};
  let mask = divisor.wrapping_sub (&T::one());
  (input >> shift) + if input & mask != T::zero() {T::one()} else {T::zero()}
}

/// Right-shift an integer, but round towards 0.
pub fn shr_round_towards_zero <T: Integer> (input: T, shift: u32)->T {
  (input + if input <T::zero() {(T::one() << shift).wrapping_sub (& T::one())} else {T::zero()}) >> shift
}

/// Left-shift an integer, returning Some(input*(2^shift)) if it fits within the type, None otherwise.
pub fn overflow_checked_shl <T: Integer> (input: T, shift: u32)->Option <T> {
  if input == T::zero() {return Some (T::zero())}
  let maximum = match T::max_value().checked_shr (shift) {
    None => return None,
    Some (value) => value,
  };
  if input > maximum {return None}
  let minimum = T::min_value() >> shift;
  if input < minimum {return None}
  Some (input << shift)
}

/// Compute the arithmetic mean of two integers, rounded towards negative infinity. Never overflows.
pub fn mean_floor <T: Integer> (first: T, second: T)->T {
  (first >> 1u32) + (second >> 1u32) + (first & second & T::one())
}

/// Compute the arithmetic mean of two integers, rounded towards positive infinity. Never overflows.
pub fn mean_ceil <T: Integer> (first: T, second: T)->T {
  (first >> 1u32) + (second >> 1u32) + ((first | second) & T::one())
}

pub mod polynomial;

#[cfg (test)]
mod tests {
  use super::*;
  use num::{One, ToPrimitive, Integer};
  use num::bigint::BigInt;
  use num::rational::{Ratio};
  use std::cmp::Ordering;

  fn perfect_shr_nicely_rounded <T: Integer> (input: T, shift: u32)->BigInt where BigInt: From <T> {
    let perfect_result = Ratio::new (BigInt::from (input), BigInt::one() << shift as usize);
    let rounded_down = perfect_result.floor();
    let fraction = & perfect_result - & rounded_down;
    let rounded_down = rounded_down.to_integer();
    match fraction.cmp (& Ratio::new (BigInt::one(), BigInt::one() << 1)) {
      Ordering::Less => rounded_down, Ordering::Greater => rounded_down + BigInt::one() ,
      Ordering::Equal => & rounded_down + rounded_down.mod_floor (& (BigInt::one() << 1)),
    }
  }

  fn perfect_shr_round_to_even <T: Integer> (input: T, shift: u32)->BigInt where BigInt: From <T> {
    let perfect_result = Ratio::new (BigInt::from (input), BigInt::one() << shift as usize);
    let rounded_down = perfect_result.floor();
    if perfect_result == rounded_down {rounded_down.to_integer()}
    else {
      let rounded_down = rounded_down.to_integer();
      if &rounded_down >> 1 << 1 == rounded_down {
        rounded_down
      }
      else {rounded_down + 1}
    }
  }

  #[test]
  fn test_shr_nicely_rounded() {
    let inputs: Vec<(i64, u32, i64)> = vec![
      (0, 0, 0), (0, 5, 0), (5, 0, 5), (1, 3, 0), (4, 3, 0), (5, 3, 1),
      (999, 1, 500), (998, 1, 499), (997, 1, 498)
    ];
    for (input, shift, result) in inputs {
      println!( "{:?}", (input, shift, result));
      assert_eq!(shr_nicely_rounded (input, shift), result);
      assert_eq!(shr_nicely_rounded (-input, shift), -result);
    }
  }

  #[test]
  fn test_shr_round_to_even() {
    let inputs: Vec<(i64, u32, i64)> = vec![
      (0, 0, 0), (0, 5, 0), (5, 0, 5), (1, 3, 0), (4, 3, 0), (5, 3, 0),
      (999, 1, 500), (998, 1, 499), (997, 1, 498)
    ];
    for (input, shift, result) in inputs {
      println!( "{:?}", (input, shift, result));
      assert_eq!(shr_round_to_even (input, shift), result);
      assert_eq!(shr_round_to_even (-input, shift), -result);
    }
  }

  quickcheck! {
    fn quickcheck_shr_nicely_rounded_signed (input: i32, shift: u8)->bool {
      let result = shr_nicely_rounded (input, shift as u32);
      let perfect_result = perfect_shr_nicely_rounded (input, shift as u32);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      perfect_result == BigInt::from (result)
    }

    fn quickcheck_shr_nicely_rounded_unsigned (input: u32, shift: u8)->bool {
      let result = shr_nicely_rounded (input, shift as u32);
      let perfect_result = perfect_shr_nicely_rounded (input, shift as u32);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      perfect_result == BigInt::from (result)
    }

    fn quickcheck_shr_round_to_even_signed (input: i32, shift: u8)->bool {
      let result = shr_round_to_even (input, shift as u32);
      let perfect_result = perfect_shr_round_to_even (input, shift as u32);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      perfect_result == BigInt::from (result)
    }

    fn quickcheck_shr_round_to_even_unsigned (input: u32, shift: u8)->bool {
      let result = shr_round_to_even (input, shift as u32);
      let perfect_result = perfect_shr_round_to_even (input, shift as u32);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      perfect_result == BigInt::from (result)
    }

    fn quickcheck_overflow_checked_shl (input: i32, shift: u8)->bool {
      let result = overflow_checked_shl (input, shift as u32);
      let perfect_result = BigInt::from (input) << shift as usize;
      result == perfect_result.to_i32()
    }
  }
}
<commit_msg>Converted some of the tests to use propstest instead of quickcheck<commit_after>#![feature(try_trait)]

#[macro_use]
extern crate failure;
extern crate num;
extern crate nalgebra;
#[cfg(test)]
#[macro_use]
extern crate proptest;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
#[macro_use]
extern crate smallvec;
#[cfg(test)]
extern crate rand;
extern crate array_ext;

use num::traits::{Zero, WrappingAdd, WrappingSub, WrappingMul, CheckedShl, CheckedShr, Signed};
use std::mem;
use std::cmp::{min};
use std::ops::{Add, Sub, Mul, AddAssign, SubAssign, MulAssign, Shl, Shr, Neg, Index};
use std::fmt::{Debug, Display};

pub trait Integer:
  'static + num::PrimInt + num::Integer + num::FromPrimitive +
  AddAssign <Self> + SubAssign <Self> + MulAssign <Self> +
  WrappingAdd + WrappingSub + WrappingMul +
  for<'a> Add <&'a Self, Output = Self> + for<'a> Sub <&'a Self, Output = Self> + for<'a> Mul <&'a Self, Output = Self> +
  CheckedShl + CheckedShr + Shl <u32, Output = Self> + Shr <u32, Output = Self> +
  Debug + Display + Send + Sync {}
impl <T: 'static + num::PrimInt + num::Integer + num::FromPrimitive + AddAssign <Self> + SubAssign <Self> + MulAssign <Self> + WrappingAdd + WrappingSub + WrappingMul + for<'a> Add <&'a Self, Output = Self> + for<'a> Sub <&'a Self, Output = Self> + for<'a> Mul <&'a Self, Output = Self> + CheckedShl + CheckedShr + Shl <u32, Output = Self> + Shr <u32, Output = Self> + Debug + Display + Send + Sync> Integer for T {}

pub trait HasCoordinates
   {
  type Coordinate: Integer + Signed + Vector + HasCoordinates <Coordinate = Self::Coordinate>;}

pub trait Vector:
  'static + Sized + Copy + Clone + HasCoordinates +
  Add <Self, Output = Self> + Sub <Self, Output = Self> + Mul <<Self as HasCoordinates>::Coordinate, Output = Self> +
  for<'a> Add <&'a Self, Output = Self> + for<'a> Sub <&'a Self, Output = Self> + //for<'a> Mul <&'a <Self as HasCoordinates>::Coordinate, Output = Self> +
  AddAssign <Self> + SubAssign <Self> + MulAssign <<Self as HasCoordinates>::Coordinate> +
  for<'a> AddAssign <&'a Self> + for<'a> SubAssign <&'a Self> + //for<'a> MulAssign <&'a <Self as HasCoordinates>::Coordinate> +
  Zero + Neg <Output = Self>
  {
  const DIMENSIONS: usize;
  fn coordinate (&self, which: usize)->Self::Coordinate;
  fn set_coordinate (&mut self, which: usize, value: Self::Coordinate);
  fn map_coordinates <F: Fn(Self::Coordinate)->Self::Coordinate> (mut self, transform: F)->Self {
    for index in 0..Self::DIMENSIONS {
      let new_coordinate = (transform) (self.coordinate (index));
      self.set_coordinate (index, new_coordinate);
    }
    self
  }
}

pub mod impls {
  use super::*;
  use nalgebra::*;
  macro_rules! impl_vector {
    ($([$coordinates: expr, $Vector: ident],)*) => {
      $(
        impl <T: Integer + Signed + Vector + HasCoordinates <Coordinate = T>> HasCoordinates for $Vector <T> {type Coordinate = T;}
        impl <T: Integer + Signed + Vector + HasCoordinates <Coordinate = T>> Vector for $Vector <T> {
          const DIMENSIONS: usize = $coordinates;
          fn coordinate (&self, which: usize)->Self::Coordinate {self [which]}
          fn set_coordinate (&mut self, which: usize, value: Self::Coordinate) {self [which] = value}
        }
      )*
    }
  }
  macro_rules! impl_integer {
    ($($Integer: ident,)*) => {
      $(
        impl HasCoordinates for $Integer {type Coordinate = $Integer;}
        impl Vector for $Integer {
          const DIMENSIONS: usize = 1;
          fn coordinate (&self, which: usize)->Self::Coordinate {*self}
          fn set_coordinate (&mut self, which: usize, value: Self::Coordinate) {*self = value}
        }
      )*
    }
  }
  impl_vector! ([1, Vector1], [2, Vector2], [3, Vector3], [4, Vector4], [5, Vector5], [6, Vector6],);
  impl_integer! (i8, i16, i32, i64, isize,);
}

/// Right-shift an integer, but round to nearest, with ties rounding to even.
///
/// This minimizes error, and avoids a directional bias.
pub fn shr_nicely_rounded <T: Integer> (input: T, shift: u32)->T {
  let divisor = match T::one().checked_shl ( shift ) {Some (value) => value, None => return T::zero()};
  let mask = divisor.wrapping_sub (&T::one());
  let shifted = input >> shift;
  let half = divisor >> 1u32;
  shifted + if (input & mask)+(shifted & if shift != 0 {T::one()} else {T::zero()}) > half {T::one()} else {T::zero()}
}

/// Right-shift an integer, but round to even.
///
/// This avoids a directional bias.
pub fn shr_round_to_even <T: Integer> (input: T, shift: u32)->T {
  let divisor = match T::one().checked_shl ( shift ) {Some (value) => value, None => return T::zero()};
  let mask = divisor.wrapping_sub (&T::one());
  let shifted = input >> shift;
  shifted + if (input & mask) != T::zero() {shifted & T::one()} else {T::zero()}
}

/// Right-shift an integer, but round towards positive infinity.
pub fn shr_ceil <T: Integer> (input: T, shift: u32)->T {
  let divisor = match T::one().checked_shl ( shift ) {Some (value) => value, None => return T::zero()};
  let mask = divisor.wrapping_sub (&T::one());
  (input >> shift) + if input & mask != T::zero() {T::one()} else {T::zero()}
}

/// Right-shift an integer, but round towards 0.
pub fn shr_round_towards_zero <T: Integer> (input: T, shift: u32)->T {
  (input + if input <T::zero() {(T::one() << shift).wrapping_sub (& T::one())} else {T::zero()}) >> shift
}

/// Left-shift an integer, returning Some(input*(2^shift)) if it fits within the type, None otherwise.
pub fn overflow_checked_shl <T: Integer> (input: T, shift: u32)->Option <T> {
  if input == T::zero() {return Some (T::zero())}
  let maximum = match T::max_value().checked_shr (shift) {
    None => return None,
    Some (value) => value,
  };
  if input > maximum {return None}
  let minimum = T::min_value() >> shift;
  if input < minimum {return None}
  Some (input << shift)
}

/// Compute the arithmetic mean of two integers, rounded towards negative infinity. Never overflows.
pub fn mean_floor <T: Integer> (first: T, second: T)->T {
  (first >> 1u32) + (second >> 1u32) + (first & second & T::one())
}

/// Compute the arithmetic mean of two integers, rounded towards positive infinity. Never overflows.
pub fn mean_ceil <T: Integer> (first: T, second: T)->T {
  (first >> 1u32) + (second >> 1u32) + ((first | second) & T::one())
}

pub mod polynomial;

#[cfg (test)]
mod tests {
  use super::*;
  use num::{One, ToPrimitive, Integer};
  use num::bigint::BigInt;
  use num::rational::{Ratio};
  use std::cmp::Ordering;
  use proptest::prelude::*;

  fn perfect_shr_nicely_rounded <T: Integer> (input: T, shift: u32)->BigInt where BigInt: From <T> {
    let perfect_result = Ratio::new (BigInt::from (input), BigInt::one() << shift as usize);
    let rounded_down = perfect_result.floor();
    let fraction = & perfect_result - & rounded_down;
    let rounded_down = rounded_down.to_integer();
    match fraction.cmp (& Ratio::new (BigInt::one(), BigInt::one() << 1)) {
      Ordering::Less => rounded_down, Ordering::Greater => rounded_down + BigInt::one() ,
      Ordering::Equal => & rounded_down + rounded_down.mod_floor (& (BigInt::one() << 1)),
    }
  }

  fn perfect_shr_round_to_even <T: Integer> (input: T, shift: u32)->BigInt where BigInt: From <T> {
    let perfect_result = Ratio::new (BigInt::from (input), BigInt::one() << shift as usize);
    let rounded_down = perfect_result.floor();
    if perfect_result == rounded_down {rounded_down.to_integer()}
    else {
      let rounded_down = rounded_down.to_integer();
      if &rounded_down >> 1 << 1 == rounded_down {
        rounded_down
      }
      else {rounded_down + 1}
    }
  }

  #[test]
  fn test_shr_nicely_rounded() {
    let inputs: Vec<(i64, u32, i64)> = vec![
      (0, 0, 0), (0, 5, 0), (5, 0, 5), (1, 3, 0), (4, 3, 0), (5, 3, 1),
      (999, 1, 500), (998, 1, 499), (997, 1, 498)
    ];
    for (input, shift, result) in inputs {
      println!( "{:?}", (input, shift, result));
      assert_eq!(shr_nicely_rounded (input, shift), result);
      assert_eq!(shr_nicely_rounded (-input, shift), -result);
    }
  }

  #[test]
  fn test_shr_round_to_even() {
    let inputs: Vec<(i64, u32, i64)> = vec![
      (0, 0, 0), (0, 5, 0), (5, 0, 5), (1, 3, 0), (4, 3, 0), (5, 3, 0),
      (999, 1, 500), (998, 1, 499), (997, 1, 498)
    ];
    for (input, shift, result) in inputs {
      println!( "{:?}", (input, shift, result));
      assert_eq!(shr_round_to_even (input, shift), result);
      assert_eq!(shr_round_to_even (-input, shift), -result);
    }
  }

  proptest! {
    #[test]
    fn randomly_test_shr_nicely_rounded_signed (input in any::<i32>(), shift in 0u32..40) {
      let result = shr_nicely_rounded (input, shift);
      let perfect_result = perfect_shr_nicely_rounded (input, shift);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      prop_assert_eq!(perfect_result, BigInt::from (result))
    }

    #[test]
    fn randomly_test_shr_nicely_rounded_unsigned (input in any::<u32>(), shift in 0u32..40) {
      let result = shr_nicely_rounded (input, shift);
      let perfect_result = perfect_shr_nicely_rounded (input, shift);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      prop_assert_eq!(perfect_result, BigInt::from (result))
    }

    #[test]
    fn randomly_test_shr_round_to_even_signed (input in any::<i32>(), shift in 0u32..40) {
      let result = shr_round_to_even (input, shift);
      let perfect_result = perfect_shr_round_to_even (input, shift);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      prop_assert_eq!(perfect_result, BigInt::from (result))
    }

    #[test]
    fn randomly_test_shr_round_to_even_unsigned (input in any::<u32>(), shift in 0u32..40) {
      let result = shr_round_to_even (input, shift);
      let perfect_result = perfect_shr_round_to_even (input, shift);
      println!( "{:?}", (result, & perfect_result.to_str_radix (10)));
      prop_assert_eq!(perfect_result, BigInt::from (result))
    }

    #[test]
    fn randomly_test_overflow_checked_shl (input in any::<i32>(), shift in 0u32..40) {
      let result = overflow_checked_shl (input, shift);
      let perfect_result = BigInt::from (input) << shift as usize;
      prop_assert_eq!(result, perfect_result.to_i32())
    }
  }
}
<|endoftext|>


<commit_before>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The reference loader implementation for the `gltf` crate.
//!
//! # Examples
//!
//! ### Importing some `glTF` 2.0
//!
//! ```rust
//! use gltf_importer::import;
//! # #[allow(unused_variables)]
//! let path = "path/to/asset.gltf";
//! # let path = "../examples/Box.gltf";
//! match import(path) {
//!     Ok((gltf, _)) => println!("{:#?}", gltf.as_json()),
//!     Err(err) => println!("error: {:?}", err),
//! }
//! ```

extern crate base64;
extern crate gltf;
extern crate gltf_utils;

use gltf::json::{self, validation};
use std::{fmt, fs, io};

use gltf::Gltf;
use gltf_utils::Source;
use std::error::Error as StdError;
use std::path::Path;

/// Contains parameters for import configuration.
pub mod config;

pub use self::config::Config;
pub use self::config::ValidationStrategy;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A loaded glTF buffer is not of the required length.
    BufferLength(json::Path),

    /// Base 64 decoding error.
    Base64Decoding(base64::DecodeError),

    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(std::io::Error),

    /// `gltf` crate error.
    Gltf(gltf::Error),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// Buffer data returned from `import`.
#[derive(Clone, Debug)]
pub struct Buffers(Vec<Vec<u8>>);

impl Source for Buffers {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.0[buffer.index()]
    }
}

impl Buffers {
    /// Obtain the contents of a loaded buffer.
    pub fn buffer(&self, buffer: &gltf::Buffer) -> Option<&[u8]> {
        self.0.get(buffer.index()).map(Vec::as_slice)
    }

    /// Obtain the contents of a loaded buffer view.
    pub fn view(&self, view: &gltf::buffer::View) -> Option<&[u8]> {
        self.buffer(&view.buffer())
            .map(|data| {
                let begin = view.offset();
                let end = begin + view.length();
                &data[begin..end]
            })
    }

    /// Take the loaded buffer data.
    pub fn take(self) -> Vec<Vec<u8>> {
        self.0
    }
}

fn import_impl(path: &Path, config: &Config) -> Result<(Gltf, Buffers), Error> {
    let data = read_to_end(path)?;
    import_data_slice(&data, path, config)
}

pub fn import_data_slice<'a>(data: &'a [u8], path: &Path, config: Config) -> Result<(Gltf, Buffers), Error> {
    if data.starts_with(b"glTF") {
        import_binary(&data, config, path)
    } else {
        import_standard(&data, config, path)
    }
}

/// Imports glTF 2.0 with custom configuration.
pub fn import_with_config<P>(path: P, config: &Config) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), config)
}

/// Imports glTF 2.0 with default configuration.
pub fn import<P>(path: P) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), &Default::default())
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    let file = fs::File::open(path)?;
    let mut reader = io::BufReader::new(file);
    let mut buffer = vec![];
    let _ = reader.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn parse_data_uri(uri: &str) -> Result<Vec<u8>, Error> {
    let encoded = uri.split(',').nth(1).unwrap();
    let decoded = base64::decode(&encoded)?;
    Ok(decoded)
}

fn load_external_buffers(
    base_path: &Path,
    gltf: &Gltf,
    mut bin: Option<Vec<u8>>,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut buffers = vec![];
    for (index, buffer) in gltf.buffers().enumerate() {
        let uri = buffer.uri();
        let data = if uri == "#bin" {
            Ok(bin.take().unwrap())
        } else if uri.starts_with("data:") {
            Ok(parse_data_uri(uri)?)
        } else {
            let path = base_path.parent().unwrap_or_else(|| Path::new("./")).join(uri);
            read_to_end(&path)
        }?;
        if data.len() < buffer.length() {
            let path = json::Path::new().field("buffers").index(index);
            return Err(Error::BufferLength(path));
        }
        buffers.push(data);
    }
    Ok(buffers)
}

fn validate_standard(
    unvalidated: gltf::Unvalidated,
    config: &Config,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    Ok(match config.validation_strategy {
        ValidationStrategy::Skip => unsafe { unvalidated.skip_validation() },
        ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
        ValidationStrategy::Complete => unvalidated.validate_completely()?,
    })
}

fn validate_binary(
    unvalidated: gltf::Unvalidated,
    config: &Config,
    has_bin: bool,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    use json::validation::Error as Reason;

    if config.validation_strategy == ValidationStrategy::Skip {
        return Ok(unsafe { unvalidated.skip_validation() });
    }

    let mut errs = vec![];
    {
        let json = unvalidated.as_json();
        for (index, buffer) in json.buffers.iter().enumerate() {
            let path = || {
                json::Path::new()
                    .field("buffers")
                    .index(index)
                    .field("uri")
            };
            match index {
                0 if has_bin => if buffer.uri.is_some() {
                    errs.push((path(), Reason::Missing));
                },
                _ if buffer.uri.is_none() => {
                    errs.push((path(), Reason::Missing));
                },
                _ => {},
            }
        }
    }

    if errs.is_empty() {
        Ok(match config.validation_strategy {
            ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
            ValidationStrategy::Complete => unvalidated.validate_completely()?,
            ValidationStrategy::Skip => unreachable!(),
        })
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let unvalidated = Gltf::from_slice(data)?;
    let gltf = validate_standard(unvalidated, config)?;
    let bin = None;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let gltf::Glb { json, bin, .. } = gltf::Glb::from_slice(data)?;
    let unvalidated = Gltf::from_slice(json)?;
    let bin = bin.map(|x| x.to_vec());
    let gltf = validate_binary(unvalidated, config, bin.is_some())?;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

impl From<gltf::Error> for Error {
    fn from(err: gltf::Error) -> Error {
        match err {
            gltf::Error::Validation(errs) => Error::Validation(errs),
            _ => Error::Gltf(err),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        Error::Base64Decoding(err)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            Base64Decoding(_) => "Base 64 decoding failed",
            BufferLength(_) => "Loaded buffer does not match required length",
            ExtensionDisabled(_) => "Asset requires a disabled extension",
            ExtensionUnsupported(_) => "Assets requires an unsupported extension",
            IncompatibleVersion(_) => "Asset is not glTF version 2.0",
            Io(_) => "I/O error",
            Gltf(_) => "Error from gltf crate",
            MalformedJson(_) => "Malformed .gltf / .glb JSON",
            Validation(_) => "Asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match *self {
            MalformedJson(ref err) => Some(err),
            Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<commit_msg>Remove redundant unsafes<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The reference loader implementation for the `gltf` crate.
//!
//! # Examples
//!
//! ### Importing some `glTF` 2.0
//!
//! ```rust
//! use gltf_importer::import;
//! # #[allow(unused_variables)]
//! let path = "path/to/asset.gltf";
//! # let path = "../examples/Box.gltf";
//! match import(path) {
//!     Ok((gltf, _)) => println!("{:#?}", gltf.as_json()),
//!     Err(err) => println!("error: {:?}", err),
//! }
//! ```

extern crate base64;
extern crate gltf;
extern crate gltf_utils;

use gltf::json::{self, validation};
use std::{fmt, fs, io};

use gltf::Gltf;
use gltf_utils::Source;
use std::error::Error as StdError;
use std::path::Path;

/// Contains parameters for import configuration.
pub mod config;

pub use self::config::Config;
pub use self::config::ValidationStrategy;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A loaded glTF buffer is not of the required length.
    BufferLength(json::Path),

    /// Base 64 decoding error.
    Base64Decoding(base64::DecodeError),

    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(std::io::Error),

    /// `gltf` crate error.
    Gltf(gltf::Error),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// Buffer data returned from `import`.
#[derive(Clone, Debug)]
pub struct Buffers(Vec<Vec<u8>>);

impl Source for Buffers {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.0[buffer.index()]
    }
}

impl Buffers {
    /// Obtain the contents of a loaded buffer.
    pub fn buffer(&self, buffer: &gltf::Buffer) -> Option<&[u8]> {
        self.0.get(buffer.index()).map(Vec::as_slice)
    }

    /// Obtain the contents of a loaded buffer view.
    pub fn view(&self, view: &gltf::buffer::View) -> Option<&[u8]> {
        self.buffer(&view.buffer())
            .map(|data| {
                let begin = view.offset();
                let end = begin + view.length();
                &data[begin..end]
            })
    }

    /// Take the loaded buffer data.
    pub fn take(self) -> Vec<Vec<u8>> {
        self.0
    }
}

fn import_impl(path: &Path, config: &Config) -> Result<(Gltf, Buffers), Error> {
    let data = read_to_end(path)?;
    import_data_slice(&data, path, config)
}

pub fn import_data_slice<'a>(data: &'a [u8], path: &Path, config: Config) -> Result<(Gltf, Buffers), Error> {
    if data.starts_with(b"glTF") {
        import_binary(&data, config, path)
    } else {
        import_standard(&data, config, path)
    }
}

/// Imports glTF 2.0 with custom configuration.
pub fn import_with_config<P>(path: P, config: &Config) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), config)
}

/// Imports glTF 2.0 with default configuration.
pub fn import<P>(path: P) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), &Default::default())
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    let file = fs::File::open(path)?;
    let mut reader = io::BufReader::new(file);
    let mut buffer = vec![];
    let _ = reader.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn parse_data_uri(uri: &str) -> Result<Vec<u8>, Error> {
    let encoded = uri.split(',').nth(1).unwrap();
    let decoded = base64::decode(&encoded)?;
    Ok(decoded)
}

fn load_external_buffers(
    base_path: &Path,
    gltf: &Gltf,
    mut bin: Option<Vec<u8>>,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut buffers = vec![];
    for (index, buffer) in gltf.buffers().enumerate() {
        let uri = buffer.uri();
        let data = if uri == "#bin" {
            Ok(bin.take().unwrap())
        } else if uri.starts_with("data:") {
            Ok(parse_data_uri(uri)?)
        } else {
            let path = base_path.parent().unwrap_or_else(|| Path::new("./")).join(uri);
            read_to_end(&path)
        }?;
        if data.len() < buffer.length() {
            let path = json::Path::new().field("buffers").index(index);
            return Err(Error::BufferLength(path));
        }
        buffers.push(data);
    }
    Ok(buffers)
}

fn validate_standard(
    unvalidated: gltf::Unvalidated,
    config: &Config,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    Ok(match config.validation_strategy {
        ValidationStrategy::Skip => unvalidated.skip_validation(),
        ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
        ValidationStrategy::Complete => unvalidated.validate_completely()?,
    })
}

fn validate_binary(
    unvalidated: gltf::Unvalidated,
    config: &Config,
    has_bin: bool,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    use json::validation::Error as Reason;

    if config.validation_strategy == ValidationStrategy::Skip {
        return Ok(unvalidated.skip_validation());
    }

    let mut errs = vec![];
    {
        let json = unvalidated.as_json();
        for (index, buffer) in json.buffers.iter().enumerate() {
            let path = || {
                json::Path::new()
                    .field("buffers")
                    .index(index)
                    .field("uri")
            };
            match index {
                0 if has_bin => if buffer.uri.is_some() {
                    errs.push((path(), Reason::Missing));
                },
                _ if buffer.uri.is_none() => {
                    errs.push((path(), Reason::Missing));
                },
                _ => {},
            }
        }
    }

    if errs.is_empty() {
        Ok(match config.validation_strategy {
            ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
            ValidationStrategy::Complete => unvalidated.validate_completely()?,
            ValidationStrategy::Skip => unreachable!(),
        })
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let unvalidated = Gltf::from_slice(data)?;
    let gltf = validate_standard(unvalidated, config)?;
    let bin = None;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let gltf::Glb { json, bin, .. } = gltf::Glb::from_slice(data)?;
    let unvalidated = Gltf::from_slice(json)?;
    let bin = bin.map(|x| x.to_vec());
    let gltf = validate_binary(unvalidated, config, bin.is_some())?;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

impl From<gltf::Error> for Error {
    fn from(err: gltf::Error) -> Error {
        match err {
            gltf::Error::Validation(errs) => Error::Validation(errs),
            _ => Error::Gltf(err),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        Error::Base64Decoding(err)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            Base64Decoding(_) => "Base 64 decoding failed",
            BufferLength(_) => "Loaded buffer does not match required length",
            ExtensionDisabled(_) => "Asset requires a disabled extension",
            ExtensionUnsupported(_) => "Assets requires an unsupported extension",
            IncompatibleVersion(_) => "Asset is not glTF version 2.0",
            Io(_) => "I/O error",
            Gltf(_) => "Error from gltf crate",
            MalformedJson(_) => "Malformed .gltf / .glb JSON",
            Validation(_) => "Asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match *self {
            MalformedJson(ref err) => Some(err),
            Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>//! A module providing an enum for a message prefix.
use std::borrow::ToOwned;
use std::string;
use std::str::FromStr;
use std::fmt;

/// The Prefix indicates "the true origin of the message", according to the server.
///
/// Warning: Avoid constructing a `Nickname(nickname, None, Some(hostname))`, but
/// `Nickname(nickname, Some("".to_owned()), Some(hostname))` works reliably.
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Prefix {
    /// servername
    ServerName(String),
    /// nickname [ ["!" username] "@" hostname ]
    Nickname(String, Option<String>, Option<String>),
}

impl Prefix {
    /// Creates a prefix by parsing a string.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// Prefix::new_from_str("nickname!username@hostname");
    /// Prefix::new_from_str("example.com");
    /// # }
    /// ```
    pub fn new_from_str(s: &str) -> Prefix {
        let mut name = String::new();
        let mut user = None;
        let mut host = None;

        for c in s.chars() {
            match c {
                // We consider the '.' to be a ServerName except if a ! has already
                // been encountered.
                '.' if user.is_none() => return Prefix::ServerName(s.to_owned()),

                '!' if user.is_none() => {
                    user = Some(String::new())
                },

                // The '@' is not special until we've started the username
                // portion
                '@' if user.is_some() && host.is_none() => {
                    host = Some(String::new())
                },

                _ => {
                    // Push onto the latest buffer
                    if host.is_some() {
                        host.as_mut().unwrap().push(c)
                    } else if user.is_some() {
                        user.as_mut().unwrap().push(c)
                    } else {
                        name.push(c)
                    }
                }
            }
        }

        Prefix::Nickname(name, user, host)
    }
}

/// This implementation never returns an error and is isomorphic with `Display`.
impl FromStr for Prefix {
    type Err = string::ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Prefix::new_from_str(s))
    }
}

/// This is isomorphic with `FromStr`, except when called on `Prefix::Nickname(nickname, None, Some(hostname))`
impl fmt::Display for Prefix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Prefix::ServerName(name) => write!(f, "{}", name),
            Prefix::Nickname(name, None, None) => write!(f, "{}", name),
            Prefix::Nickname(name, Some(user), None) => write!(f, "{}!{}", name, user),
            Prefix::Nickname(name, Some(user), Some(host)) => write!(f, "{}!{}@{}", name, user, host),

            // This is an issue with the type using two Option values when really hostname implies username
            Prefix::Nickname(name, None, Some(host)) => write!(f, "{}!@{}", name, host),
        }
    }
}

impl<'a> From<&'a str> for Prefix {
    fn from(s: &str) -> Self {
        Prefix::new_from_str(s)
    }
}

#[cfg(test)]
mod test {
    use super::Prefix::{self, ServerName, Nickname};

    // Checks that str -> parsed -> Display doesn't lose data
    fn test_parse(s: &str) -> Prefix {
        let prefix = Prefix::new_from_str(s);
        let s2 = format!("{}", prefix);
        assert_eq!(s, &s2);
        prefix
    }

    #[test]
    fn parse_word() {
        assert_eq!(
            test_parse("only_nick"),
            Nickname("only_nick".into(), None, None)
        )
    }

    #[test]
    fn parse_host() {
        assert_eq!(
            test_parse("host.tld"),
            ServerName("host.tld".into())
        )
    }

    #[test]
    fn parse_nick_user() {
        assert_eq!(
            test_parse("test!nick"),
            Nickname("test".into(), Some("nick".into()), None)
        )
    }

    #[test]
    fn parse_nick_user_host() {
        assert_eq!(
            test_parse("test!nick@host"),
            Nickname("test".into(), Some("nick".into()), Some("host".into()))
        )
    }

    #[test]
    fn parse_dot_and_symbols() {
        assert_eq!(
            test_parse("test.net@something"),
            ServerName("test.net@something".into())
        )
    }

    #[test]
    fn parse_danger_cases() {
        assert_eq!(
            test_parse("name@name!user"),
            Nickname("name@name".into(), Some("user".into()), None)
        );
        assert_eq!(
            test_parse("name!@"),
            Nickname("name".into(), Some("".into()), Some("".into()))
        );
        assert_eq!(
            test_parse("name!@hostname"),
            Nickname("name".into(), Some("".into()), Some("hostname".into()))
        );
        assert_eq!(
            test_parse("name!.user"),
            Nickname("name".into(), Some(".user".into()), None)
        );
        assert_eq!(
            test_parse("name!user.user"),
            Nickname("name".into(), Some("user.user".into()), None)
        );
        assert_eq!(
            test_parse("name!user@host.host"),
            Nickname("name".into(), Some("user".into()), Some("host.host".into()))
        );
        assert_eq!(
            test_parse("!user"),
            Nickname("".into(), Some("user".into()), None)
        );
        assert_eq!(
            test_parse("!@host.host"),
            Nickname("".into(), Some("".into()), Some("host.host".into()))
        );
    }
}
<commit_msg>prefix uses Nickname(String, String, String)<commit_after>//! A module providing an enum for a message prefix.
use std::borrow::ToOwned;
use std::string;
use std::str::FromStr;
use std::fmt;

/// The Prefix indicates "the true origin of the message", according to the server.
///
/// Warning: Avoid constructing a `Nickname(nickname, None, Some(hostname))`, but
/// `Nickname(nickname, Some("".to_owned()), Some(hostname))` works reliably.
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Prefix {
    /// servername
    ServerName(String),
    /// nickname [ ["!" username] "@" hostname ]
    Nickname(String, String, String),
}

impl Prefix {
    /// Creates a prefix by parsing a string.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// Prefix::new_from_str("nickname!username@hostname");
    /// Prefix::new_from_str("example.com");
    /// # }
    /// ```
    pub fn new_from_str(s: &str) -> Prefix {
        #[derive(Copy, Clone, Eq, PartialEq)]
        enum Active {
            Name = 0,
            User = 1,
            Host = 2,
        }

        let mut name = String::new();
        let mut user = String::new();
        let mut host = String::new();
        let mut active = Active::Name;

        for c in s.chars() {
            match c {
                // We consider the '.' to be a ServerName except if a ! has already
                // been encountered.
                '.' if active == Active::Name => return Prefix::ServerName(s.to_owned()),

                '!' if active == Active::Name => {
                    active = Active::User;
                },

                // The '@' is not special until we've started the username
                // portion
                '@' if active == Active::User => {
                    active = Active::Host;
                },

                _ => {
                    // Push onto the latest buffer
                    match active {
                        Active::Name => &mut name,
                        Active::User => &mut user,
                        Active::Host => &mut host,
                    }.push(c)
                }
            }
        }

        Prefix::Nickname(name, user, host)
    }
}

/// This implementation never returns an error and is isomorphic with `Display`.
impl FromStr for Prefix {
    type Err = string::ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Prefix::new_from_str(s))
    }
}

/// This is isomorphic with `FromStr`
impl fmt::Display for Prefix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Prefix::ServerName(name) => write!(f, "{}", name),
            Prefix::Nickname(name, user, host) => match (&name[..], &user[..], &host[..]) {
                ("", "", "") => write!(f, ""),
                (name, "", "") => write!(f, "{}", name),
                (name, user, "") => write!(f, "{}!{}", name, user),
                // This case shouldn't happen normally, but user!@host is invalid, so we
                // format it without the host
                (name, "", _host) => write!(f, "{}", name),
                (name, user, host) => write!(f, "{}!{}@{}", name, user, host),
            },
        }
    }
}

impl<'a> From<&'a str> for Prefix {
    fn from(s: &str) -> Self {
        Prefix::new_from_str(s)
    }
}

#[cfg(test)]
mod test {
    use super::Prefix::{self, ServerName, Nickname};

    // Checks that str -> parsed -> Display doesn't lose data
    fn test_parse(s: &str) -> Prefix {
        let prefix = Prefix::new_from_str(s);
        let s2 = format!("{}", prefix);
        assert_eq!(s, &s2);
        prefix
    }

    #[test]
    fn print() {
        let s = format!("{}", Nickname("nick".into(), "".into(), "".into()));
        assert_eq!(&s, "nick");
        let s = format!("{}", Nickname("nick".into(), "user".into(), "".into()));
        assert_eq!(&s, "nick!user");
        let s = format!("{}", Nickname("nick".into(), "user".into(), "host".into()));
        assert_eq!(&s, "nick!user@host");
    }

    #[test]
    fn parse_word() {
        assert_eq!(
            test_parse("only_nick"),
            Nickname("only_nick".into(), String::new(), String::new())
        )
    }

    #[test]
    fn parse_host() {
        assert_eq!(
            test_parse("host.tld"),
            ServerName("host.tld".into())
        )
    }

    #[test]
    fn parse_nick_user() {
        assert_eq!(
            test_parse("test!nick"),
            Nickname("test".into(), "nick".into(), String::new())
        )
    }

    #[test]
    fn parse_nick_user_host() {
        assert_eq!(
            test_parse("test!nick@host"),
            Nickname("test".into(), "nick".into(), "host".into())
        )
    }

    #[test]
    fn parse_dot_and_symbols() {
        assert_eq!(
            test_parse("test.net@something"),
            ServerName("test.net@something".into())
        )
    }

    #[test]
    fn parse_danger_cases() {
        assert_eq!(
            test_parse("name@name!user"),
            Nickname("name@name".into(), "user".into(), String::new())
        );
        assert_eq!(
            test_parse("name!@"),
            Nickname("name".into(), "".into(), "".into())
        );
        assert_eq!(
            test_parse("name!@hostname"),
            Nickname("name".into(), "".into(), "hostname".into())
        );
        assert_eq!(
            test_parse("name!.user"),
            Nickname("name".into(), ".user".into(), String::new())
        );
        assert_eq!(
            test_parse("name!user.user"),
            Nickname("name".into(), "user.user".into(), String::new())
        );
        assert_eq!(
            test_parse("name!user@host.host"),
            Nickname("name".into(), "user".into(), "host.host".into())
        );
        assert_eq!(
            test_parse("!user"),
            Nickname("".into(), "user".into(), String::new())
        );
        assert_eq!(
            test_parse("!@host.host"),
            Nickname("".into(), "".into(), "host.host".into())
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::CString;
use std::ptr;
use std::mem;
use std::sync::atomic::AtomicBool;
use std::any::TypeId;
use std::collections::BTreeMap;
use std::sync::Mutex;

use glib_ffi;
use gobject_ffi;

use glib;
use glib::translate::*;

use properties::*;

pub trait ObjectImpl<T: ObjectType>: Send + Sync + 'static {
    fn set_property(&self, _obj: &glib::Object, _id: u32, _value: &glib::Value) {
        unimplemented!()
    }

    fn get_property(&self, _obj: &glib::Object, _id: u32) -> Result<glib::Value, ()> {
        unimplemented!()
    }

    fn notify(&self, obj: &glib::Object, name: &str) {
        unsafe {
            gobject_ffi::g_object_notify(obj.to_glib_none().0, name.to_glib_none().0);
        }
    }
}

#[macro_export]
macro_rules! box_object_impl(
    ($name:ident) => {
        impl<T: ObjectType> ObjectImpl<T> for Box<$name<T>> {
            fn set_property(&self, obj: &glib::Object, id: u32, value: &glib::Value) {
                let imp: &$name<T> = self.as_ref();
                imp.set_property(obj, id, value);
            }

            fn get_property(&self, obj: &glib::Object, id: u32) -> Result<glib::Value, ()> {
                let imp: &$name<T> = self.as_ref();
                imp.get_property(obj, id)
            }
        }
    };
);

pub trait ImplTypeStatic<T: ObjectType>: Send + Sync + 'static {
    fn get_name(&self) -> &str;
    fn new(&self, &T) -> T::ImplType;
    fn class_init(&self, &mut ClassStruct<T>);
    fn type_init(&self, _: &TypeInitToken, _type_: glib::Type) {}
}

pub struct ClassInitToken(());
pub struct TypeInitToken(());

pub trait ObjectType: FromGlibPtrBorrow<*mut InstanceStruct<Self>> + 'static
where
    Self: Sized,
{
    const NAME: &'static str;
    type GlibType;
    type GlibClassType;
    type ImplType: ObjectImpl<Self>;

    fn glib_type() -> glib::Type;

    fn class_init(token: &ClassInitToken, klass: &mut ClassStruct<Self>);

    fn set_property(_obj: &Self, _id: u32, _value: &glib::Value) {
        unimplemented!()
    }

    fn get_property(_obj: &Self, _id: u32) -> Result<glib::Value, ()> {
        unimplemented!()
    }

    unsafe fn get_instance(&self) -> *mut InstanceStruct<Self>;

    fn get_impl(&self) -> &Self::ImplType {
        unsafe { (*self.get_instance()).get_impl() }
    }

    unsafe fn get_class(&self) -> *const ClassStruct<Self> {
        (*self.get_instance()).get_class()
    }
}

#[macro_export]
macro_rules! object_type_fns(
    () => {
        unsafe fn get_instance(&self) -> *mut InstanceStruct<Self> {
            self.to_glib_none().0
        }
    }
);

#[repr(C)]
pub struct InstanceStruct<T: ObjectType> {
    pub parent: T::GlibType,
    pub imp: *const T::ImplType,
    pub panicked: AtomicBool,
}

impl<T: ObjectType> InstanceStruct<T> {
    pub fn get_impl(&self) -> &T::ImplType {
        unsafe { &*self.imp }
    }

    pub unsafe fn get_class(&self) -> *const ClassStruct<T> {
        *(self as *const _ as *const *const ClassStruct<T>)
    }
}

#[repr(C)]
pub struct ClassStruct<T: ObjectType> {
    pub parent: T::GlibClassType,
    pub imp_static: *const Box<ImplTypeStatic<T>>,
    pub parent_class: *const T::GlibClassType,
    pub interfaces_static: *const Vec<(glib_ffi::GType, glib_ffi::gpointer)>,
}

impl<T: ObjectType> ClassStruct<T> {
    pub unsafe fn get_parent_class(&self) -> *const T::GlibClassType {
        self.parent_class
    }
}

impl<T: ObjectType> ClassStruct<T> {
    pub fn get_interface_static(&self, type_: glib_ffi::GType) -> glib_ffi::gpointer {
        unsafe {
            if self.interfaces_static.is_null() {
                return ptr::null_mut();
            }

            for &(t, p) in &(*self.interfaces_static) {
                if t == type_ {
                    return p;
                }
            }

            ptr::null_mut()
        }
    }
}

pub unsafe trait ObjectClass {
    fn install_properties(&mut self, properties: &[Property]) {
        if properties.is_empty() {
            return;
        }

        let mut pspecs = Vec::with_capacity(properties.len());

        pspecs.push(ptr::null_mut());

        for property in properties {
            pspecs.push(property.into());
        }

        unsafe {
            gobject_ffi::g_object_class_install_properties(
                self as *mut _ as *mut gobject_ffi::GObjectClass,
                pspecs.len() as u32,
                pspecs.as_mut_ptr(),
            );
        }
    }
}

unsafe impl<T: ObjectType> ObjectClass for ClassStruct<T> {}

unsafe extern "C" fn class_init<T: ObjectType>(
    klass: glib_ffi::gpointer,
    _klass_data: glib_ffi::gpointer,
) {
    callback_guard!();
    {
        let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);

        gobject_klass.finalize = Some(finalize::<T>);
        gobject_klass.set_property = Some(set_property::<T>);
        gobject_klass.get_property = Some(get_property::<T>);
    }

    {
        let klass = &mut *(klass as *mut ClassStruct<T>);
        klass.parent_class = gobject_ffi::g_type_class_peek_parent(
            klass as *mut _ as glib_ffi::gpointer,
        ) as *const T::GlibClassType;
        T::class_init(&ClassInitToken(()), klass);
    }
}

unsafe extern "C" fn finalize<T: ObjectType>(obj: *mut gobject_ffi::GObject) {
    callback_guard!();
    let instance = &mut *(obj as *mut InstanceStruct<T>);

    drop(Box::from_raw(instance.imp as *mut T::ImplType));
    instance.imp = ptr::null_mut();

    let klass = *(obj as *const glib_ffi::gpointer);
    let parent_klass = gobject_ffi::g_type_class_peek_parent(klass);
    let parent_klass =
        &*(gobject_ffi::g_type_class_peek_parent(parent_klass) as *const gobject_ffi::GObjectClass);
    parent_klass.finalize.map(|f| f(obj));
}

unsafe extern "C" fn get_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    match T::get_property(&from_glib_borrow(obj as *mut InstanceStruct<T>), id - 1) {
        Ok(v) => {
            gobject_ffi::g_value_unset(value);
            ptr::write(value, ptr::read(v.to_glib_none().0));
            mem::forget(v);
        }
        Err(_) => unimplemented!(),
    }
}

unsafe extern "C" fn set_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    T::set_property(
        &from_glib_borrow(obj as *mut InstanceStruct<T>),
        id - 1,
        &*(value as *mut glib::Value),
    );
}

static mut TYPES: *mut Mutex<BTreeMap<TypeId, glib::Type>> = 0 as *mut _;

pub unsafe fn get_type<T: ObjectType>() -> glib_ffi::GType {
    use std::sync::{Once, ONCE_INIT};

    static ONCE: Once = ONCE_INIT;

    ONCE.call_once(|| {
        TYPES = Box::into_raw(Box::new(Mutex::new(BTreeMap::new())));
    });

    let mut types = (*TYPES).lock().unwrap();
    types
        .entry(TypeId::of::<T>())
        .or_insert_with(|| {
            let type_info = gobject_ffi::GTypeInfo {
                class_size: mem::size_of::<ClassStruct<T>>() as u16,
                base_init: None,
                base_finalize: None,
                class_init: Some(class_init::<T>),
                class_finalize: None,
                class_data: ptr::null_mut(),
                instance_size: mem::size_of::<InstanceStruct<T>>() as u16,
                n_preallocs: 0,
                instance_init: None,
                value_table: ptr::null(),
            };

            let type_name = {
                let mut idx = 0;

                loop {
                    let type_name = CString::new(format!("{}-{}", T::NAME, idx)).unwrap();
                    if gobject_ffi::g_type_from_name(type_name.as_ptr())
                        == gobject_ffi::G_TYPE_INVALID
                    {
                        break type_name;
                    }
                    idx += 1;
                }
            };

            from_glib(gobject_ffi::g_type_register_static(
                T::glib_type().to_glib(),
                type_name.as_ptr(),
                &type_info,
                gobject_ffi::G_TYPE_FLAG_ABSTRACT,
            ))
        })
        .to_glib()
}

unsafe extern "C" fn sub_class_init<T: ObjectType>(
    klass: glib_ffi::gpointer,
    klass_data: glib_ffi::gpointer,
) {
    callback_guard!();
    {
        let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);

        gobject_klass.set_property = Some(sub_set_property::<T>);
        gobject_klass.get_property = Some(sub_get_property::<T>);
    }
    {
        let klass = &mut *(klass as *mut ClassStruct<T>);
        let imp_static = klass_data as *const Box<ImplTypeStatic<T>>;
        klass.imp_static = imp_static;
        klass.interfaces_static = Box::into_raw(Box::new(Vec::new()));

        (*imp_static).class_init(klass);
    }
}

unsafe extern "C" fn sub_get_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    let instance = &*(obj as *mut InstanceStruct<T>);
    let imp = instance.get_impl();

    match imp.get_property(&from_glib_borrow(obj), id - 1) {
        Ok(v) => {
            gobject_ffi::g_value_unset(value);
            ptr::write(value, ptr::read(v.to_glib_none().0));
            mem::forget(v);
        }
        Err(_) => unimplemented!(),
    }
}

unsafe extern "C" fn sub_set_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    let instance = &*(obj as *mut InstanceStruct<T>);
    let imp = instance.get_impl();
    imp.set_property(
        &from_glib_borrow(obj),
        id - 1,
        &*(value as *mut glib::Value),
    );
}

unsafe extern "C" fn sub_init<T: ObjectType>(
    obj: *mut gobject_ffi::GTypeInstance,
    _klass: glib_ffi::gpointer,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    let instance = &mut *(obj as *mut InstanceStruct<T>);
    let klass = &**(obj as *const *const ClassStruct<T>);
    let rs_instance: T = from_glib_borrow(obj as *mut InstanceStruct<T>);

    let imp = (*klass.imp_static).new(&rs_instance);
    instance.imp = Box::into_raw(Box::new(imp));
}

pub fn register_type<T: ObjectType, I: ImplTypeStatic<T>>(imp: I) -> glib::Type {
    unsafe {
        let parent_type = get_type::<T>();
        let type_name = format!("{}-{}", T::NAME, imp.get_name());

        let imp: Box<ImplTypeStatic<T>> = Box::new(imp);
        let imp_ptr = Box::into_raw(Box::new(imp));

        let type_info = gobject_ffi::GTypeInfo {
            class_size: mem::size_of::<ClassStruct<T>>() as u16,
            base_init: None,
            base_finalize: None,
            class_init: Some(sub_class_init::<T>),
            class_finalize: None,
            class_data: imp_ptr as glib_ffi::gpointer,
            instance_size: mem::size_of::<InstanceStruct<T>>() as u16,
            n_preallocs: 0,
            instance_init: Some(sub_init::<T>),
            value_table: ptr::null(),
        };

        let type_ = from_glib(gobject_ffi::g_type_register_static(
            parent_type,
            type_name.to_glib_none().0,
            &type_info,
            gobject_ffi::GTypeFlags::empty(),
        ));

        (*imp_ptr).type_init(&TypeInitToken(()), type_);

        type_
    }
}
<commit_msg>Don't panic when getting a property failed<commit_after>// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::CString;
use std::ptr;
use std::mem;
use std::sync::atomic::AtomicBool;
use std::any::TypeId;
use std::collections::BTreeMap;
use std::sync::Mutex;

use glib_ffi;
use gobject_ffi;

use glib;
use glib::translate::*;

use properties::*;

pub trait ObjectImpl<T: ObjectType>: Send + Sync + 'static {
    fn set_property(&self, _obj: &glib::Object, _id: u32, _value: &glib::Value) {
        unimplemented!()
    }

    fn get_property(&self, _obj: &glib::Object, _id: u32) -> Result<glib::Value, ()> {
        unimplemented!()
    }

    fn notify(&self, obj: &glib::Object, name: &str) {
        unsafe {
            gobject_ffi::g_object_notify(obj.to_glib_none().0, name.to_glib_none().0);
        }
    }
}

#[macro_export]
macro_rules! box_object_impl(
    ($name:ident) => {
        impl<T: ObjectType> ObjectImpl<T> for Box<$name<T>> {
            fn set_property(&self, obj: &glib::Object, id: u32, value: &glib::Value) {
                let imp: &$name<T> = self.as_ref();
                imp.set_property(obj, id, value);
            }

            fn get_property(&self, obj: &glib::Object, id: u32) -> Result<glib::Value, ()> {
                let imp: &$name<T> = self.as_ref();
                imp.get_property(obj, id)
            }
        }
    };
);

pub trait ImplTypeStatic<T: ObjectType>: Send + Sync + 'static {
    fn get_name(&self) -> &str;
    fn new(&self, &T) -> T::ImplType;
    fn class_init(&self, &mut ClassStruct<T>);
    fn type_init(&self, _: &TypeInitToken, _type_: glib::Type) {}
}

pub struct ClassInitToken(());
pub struct TypeInitToken(());

pub trait ObjectType: FromGlibPtrBorrow<*mut InstanceStruct<Self>> + 'static
where
    Self: Sized,
{
    const NAME: &'static str;
    type GlibType;
    type GlibClassType;
    type ImplType: ObjectImpl<Self>;

    fn glib_type() -> glib::Type;

    fn class_init(token: &ClassInitToken, klass: &mut ClassStruct<Self>);

    fn set_property(_obj: &Self, _id: u32, _value: &glib::Value) {
        unimplemented!()
    }

    fn get_property(_obj: &Self, _id: u32) -> Result<glib::Value, ()> {
        unimplemented!()
    }

    unsafe fn get_instance(&self) -> *mut InstanceStruct<Self>;

    fn get_impl(&self) -> &Self::ImplType {
        unsafe { (*self.get_instance()).get_impl() }
    }

    unsafe fn get_class(&self) -> *const ClassStruct<Self> {
        (*self.get_instance()).get_class()
    }
}

#[macro_export]
macro_rules! object_type_fns(
    () => {
        unsafe fn get_instance(&self) -> *mut InstanceStruct<Self> {
            self.to_glib_none().0
        }
    }
);

#[repr(C)]
pub struct InstanceStruct<T: ObjectType> {
    pub parent: T::GlibType,
    pub imp: *const T::ImplType,
    pub panicked: AtomicBool,
}

impl<T: ObjectType> InstanceStruct<T> {
    pub fn get_impl(&self) -> &T::ImplType {
        unsafe { &*self.imp }
    }

    pub unsafe fn get_class(&self) -> *const ClassStruct<T> {
        *(self as *const _ as *const *const ClassStruct<T>)
    }
}

#[repr(C)]
pub struct ClassStruct<T: ObjectType> {
    pub parent: T::GlibClassType,
    pub imp_static: *const Box<ImplTypeStatic<T>>,
    pub parent_class: *const T::GlibClassType,
    pub interfaces_static: *const Vec<(glib_ffi::GType, glib_ffi::gpointer)>,
}

impl<T: ObjectType> ClassStruct<T> {
    pub unsafe fn get_parent_class(&self) -> *const T::GlibClassType {
        self.parent_class
    }
}

impl<T: ObjectType> ClassStruct<T> {
    pub fn get_interface_static(&self, type_: glib_ffi::GType) -> glib_ffi::gpointer {
        unsafe {
            if self.interfaces_static.is_null() {
                return ptr::null_mut();
            }

            for &(t, p) in &(*self.interfaces_static) {
                if t == type_ {
                    return p;
                }
            }

            ptr::null_mut()
        }
    }
}

pub unsafe trait ObjectClass {
    fn install_properties(&mut self, properties: &[Property]) {
        if properties.is_empty() {
            return;
        }

        let mut pspecs = Vec::with_capacity(properties.len());

        pspecs.push(ptr::null_mut());

        for property in properties {
            pspecs.push(property.into());
        }

        unsafe {
            gobject_ffi::g_object_class_install_properties(
                self as *mut _ as *mut gobject_ffi::GObjectClass,
                pspecs.len() as u32,
                pspecs.as_mut_ptr(),
            );
        }
    }
}

unsafe impl<T: ObjectType> ObjectClass for ClassStruct<T> {}

unsafe extern "C" fn class_init<T: ObjectType>(
    klass: glib_ffi::gpointer,
    _klass_data: glib_ffi::gpointer,
) {
    callback_guard!();
    {
        let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);

        gobject_klass.finalize = Some(finalize::<T>);
        gobject_klass.set_property = Some(set_property::<T>);
        gobject_klass.get_property = Some(get_property::<T>);
    }

    {
        let klass = &mut *(klass as *mut ClassStruct<T>);
        klass.parent_class = gobject_ffi::g_type_class_peek_parent(
            klass as *mut _ as glib_ffi::gpointer,
        ) as *const T::GlibClassType;
        T::class_init(&ClassInitToken(()), klass);
    }
}

unsafe extern "C" fn finalize<T: ObjectType>(obj: *mut gobject_ffi::GObject) {
    callback_guard!();
    let instance = &mut *(obj as *mut InstanceStruct<T>);

    drop(Box::from_raw(instance.imp as *mut T::ImplType));
    instance.imp = ptr::null_mut();

    let klass = *(obj as *const glib_ffi::gpointer);
    let parent_klass = gobject_ffi::g_type_class_peek_parent(klass);
    let parent_klass =
        &*(gobject_ffi::g_type_class_peek_parent(parent_klass) as *const gobject_ffi::GObjectClass);
    parent_klass.finalize.map(|f| f(obj));
}

unsafe extern "C" fn get_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    match T::get_property(&from_glib_borrow(obj as *mut InstanceStruct<T>), id - 1) {
        Ok(v) => {
            gobject_ffi::g_value_unset(value);
            ptr::write(value, ptr::read(v.to_glib_none().0));
            mem::forget(v);
        }
        Err(()) => eprintln!("Failed to get property"),
    }
}

unsafe extern "C" fn set_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    T::set_property(
        &from_glib_borrow(obj as *mut InstanceStruct<T>),
        id - 1,
        &*(value as *mut glib::Value),
    );
}

static mut TYPES: *mut Mutex<BTreeMap<TypeId, glib::Type>> = 0 as *mut _;

pub unsafe fn get_type<T: ObjectType>() -> glib_ffi::GType {
    use std::sync::{Once, ONCE_INIT};

    static ONCE: Once = ONCE_INIT;

    ONCE.call_once(|| {
        TYPES = Box::into_raw(Box::new(Mutex::new(BTreeMap::new())));
    });

    let mut types = (*TYPES).lock().unwrap();
    types
        .entry(TypeId::of::<T>())
        .or_insert_with(|| {
            let type_info = gobject_ffi::GTypeInfo {
                class_size: mem::size_of::<ClassStruct<T>>() as u16,
                base_init: None,
                base_finalize: None,
                class_init: Some(class_init::<T>),
                class_finalize: None,
                class_data: ptr::null_mut(),
                instance_size: mem::size_of::<InstanceStruct<T>>() as u16,
                n_preallocs: 0,
                instance_init: None,
                value_table: ptr::null(),
            };

            let type_name = {
                let mut idx = 0;

                loop {
                    let type_name = CString::new(format!("{}-{}", T::NAME, idx)).unwrap();
                    if gobject_ffi::g_type_from_name(type_name.as_ptr())
                        == gobject_ffi::G_TYPE_INVALID
                    {
                        break type_name;
                    }
                    idx += 1;
                }
            };

            from_glib(gobject_ffi::g_type_register_static(
                T::glib_type().to_glib(),
                type_name.as_ptr(),
                &type_info,
                gobject_ffi::G_TYPE_FLAG_ABSTRACT,
            ))
        })
        .to_glib()
}

unsafe extern "C" fn sub_class_init<T: ObjectType>(
    klass: glib_ffi::gpointer,
    klass_data: glib_ffi::gpointer,
) {
    callback_guard!();
    {
        let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);

        gobject_klass.set_property = Some(sub_set_property::<T>);
        gobject_klass.get_property = Some(sub_get_property::<T>);
    }
    {
        let klass = &mut *(klass as *mut ClassStruct<T>);
        let imp_static = klass_data as *const Box<ImplTypeStatic<T>>;
        klass.imp_static = imp_static;
        klass.interfaces_static = Box::into_raw(Box::new(Vec::new()));

        (*imp_static).class_init(klass);
    }
}

unsafe extern "C" fn sub_get_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    let instance = &*(obj as *mut InstanceStruct<T>);
    let imp = instance.get_impl();

    match imp.get_property(&from_glib_borrow(obj), id - 1) {
        Ok(v) => {
            gobject_ffi::g_value_unset(value);
            ptr::write(value, ptr::read(v.to_glib_none().0));
            mem::forget(v);
        }
        Err(()) => eprintln!("Failed to get property"),
    }
}

unsafe extern "C" fn sub_set_property<T: ObjectType>(
    obj: *mut gobject_ffi::GObject,
    id: u32,
    value: *mut gobject_ffi::GValue,
    _pspec: *mut gobject_ffi::GParamSpec,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    let instance = &*(obj as *mut InstanceStruct<T>);
    let imp = instance.get_impl();
    imp.set_property(
        &from_glib_borrow(obj),
        id - 1,
        &*(value as *mut glib::Value),
    );
}

unsafe extern "C" fn sub_init<T: ObjectType>(
    obj: *mut gobject_ffi::GTypeInstance,
    _klass: glib_ffi::gpointer,
) {
    callback_guard!();
    floating_reference_guard!(obj);
    let instance = &mut *(obj as *mut InstanceStruct<T>);
    let klass = &**(obj as *const *const ClassStruct<T>);
    let rs_instance: T = from_glib_borrow(obj as *mut InstanceStruct<T>);

    let imp = (*klass.imp_static).new(&rs_instance);
    instance.imp = Box::into_raw(Box::new(imp));
}

pub fn register_type<T: ObjectType, I: ImplTypeStatic<T>>(imp: I) -> glib::Type {
    unsafe {
        let parent_type = get_type::<T>();
        let type_name = format!("{}-{}", T::NAME, imp.get_name());

        let imp: Box<ImplTypeStatic<T>> = Box::new(imp);
        let imp_ptr = Box::into_raw(Box::new(imp));

        let type_info = gobject_ffi::GTypeInfo {
            class_size: mem::size_of::<ClassStruct<T>>() as u16,
            base_init: None,
            base_finalize: None,
            class_init: Some(sub_class_init::<T>),
            class_finalize: None,
            class_data: imp_ptr as glib_ffi::gpointer,
            instance_size: mem::size_of::<InstanceStruct<T>>() as u16,
            n_preallocs: 0,
            instance_init: Some(sub_init::<T>),
            value_table: ptr::null(),
        };

        let type_ = from_glib(gobject_ffi::g_type_register_static(
            parent_type,
            type_name.to_glib_none().0,
            &type_info,
            gobject_ffi::GTypeFlags::empty(),
        ));

        (*imp_ptr).type_init(&TypeInitToken(()), type_);

        type_
    }
}
<|endoftext|>


<commit_before>
//! Prepend another stream.

use event;
use modifier;
use transform;
use builder;

/// Prepends another stream after the current.
pub struct Prepend<S, N> {
    stream: modifier::Combine<N, S>,
}

impl<S, N> Prepend<S, N>
where
    S: event::Stream,
    N: event::Stream,
{
    pub(crate) fn new(stream: S, new_stream: N) -> Prepend<S, N> {
        Prepend {
            stream: modifier::Combine::new(new_stream, stream),
        }
    }
}

impl<S, N> event::Stream for Prepend<S, N>
where
    S: event::Stream,
    N: event::Stream,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.stream.next_event()
    }
}

/// Prepends another stream to the contents of the current element.
pub struct PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    stream: modifier::select::SelectContent<S, Builder<N>>,
}

impl<S, N> PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    pub(crate) fn new(stream: S, new_stream: N) -> PrependContent<S, N> {
        PrependContent {
            stream: modifier::select::SelectContent::new(stream, Builder { new_stream }),
        }
    }
}

impl<S, N> event::ElementStream for PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{}

impl<S, N> event::Stream for PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.stream.next_event()
    }
}

struct Builder<N> {
    new_stream: N,
}

impl<S, N> builder::BuildOnce<modifier::select::CurrentContent<S>> for Builder<N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    type Stream = Prepend<modifier::select::CurrentContent<S>, N>;

    fn build_once(self, stream: transform::Api<modifier::select::CurrentContent<S>>)
    -> transform::Api<Self::Stream> {
        transform::Api::pack(Prepend::new(stream.unpack(), self.new_stream))
    }
}

#[cfg(test)]
mod tests {

    test_group!(prepend_contents:
        "prepend to existing" => transform_test!(
            "23<c>42</c>93",
            "23<c>foo42</c>93",
            |html| html.select("c", |html| html.prepend_contents("foo")),
        ),
        "prepend to existing" => transform_test!(
            "23<c></c>93",
            "23<c>foo</c>93",
            |html| html.select("c", |html| html.prepend_contents("foo")),
        ),
    );

    test_group!(prepend:
        "prepend" => transform_test!(
            "23<c>42</c>93",
            "23foo<c>42</c>93",
            |html| html.select("c", |html| html.prepend("foo")),
        ),
    );
}
<commit_msg>added element stream tests to prepend<commit_after>
//! Prepend another stream.

use event;
use modifier;
use transform;
use builder;

/// Prepends another stream after the current.
pub struct Prepend<S, N> {
    stream: modifier::Combine<N, S>,
}

impl<S, N> Prepend<S, N>
where
    S: event::Stream,
    N: event::Stream,
{
    pub(crate) fn new(stream: S, new_stream: N) -> Prepend<S, N> {
        Prepend {
            stream: modifier::Combine::new(new_stream, stream),
        }
    }
}

impl<S, N> event::Stream for Prepend<S, N>
where
    S: event::Stream,
    N: event::Stream,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.stream.next_event()
    }
}

/// Prepends another stream to the contents of the current element.
pub struct PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    stream: modifier::select::SelectContent<S, Builder<N>>,
}

impl<S, N> PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    pub(crate) fn new(stream: S, new_stream: N) -> PrependContent<S, N> {
        PrependContent {
            stream: modifier::select::SelectContent::new(stream, Builder { new_stream }),
        }
    }
}

impl<S, N> event::ElementStream for PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{}

impl<S, N> event::Stream for PrependContent<S, N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.stream.next_event()
    }
}

struct Builder<N> {
    new_stream: N,
}

impl<S, N> builder::BuildOnce<modifier::select::CurrentContent<S>> for Builder<N>
where
    S: event::ElementStream,
    N: event::Stream,
{
    type Stream = Prepend<modifier::select::CurrentContent<S>, N>;

    fn build_once(self, stream: transform::Api<modifier::select::CurrentContent<S>>)
    -> transform::Api<Self::Stream> {
        transform::Api::pack(Prepend::new(stream.unpack(), self.new_stream))
    }
}

#[cfg(test)]
mod tests {

    test_group!(prepend_contents:
        "prepend to existing" => transform_test!(
            "23<c>42</c>93",
            "23<c>foo42</c>93",
            |html| html.select("c", |html| html.prepend_contents("foo")),
        ),
        "prepend to existing" => transform_test!(
            "23<c></c>93",
            "23<c>foo</c>93",
            |html| html.select("c", |html| html.prepend_contents("foo")),
        ),
        "ensure element stream" => transform_test!(
            "23<c></c>93",
            "23<c>foo</c>93",
            |html| html.select("c", |html| html.prepend_contents("foo").into_boxed_element()),
        ),
    );

    test_group!(prepend:
        "prepend" => transform_test!(
            "23<c>42</c>93",
            "23foo<c>42</c>93",
            |html| html.select("c", |html| html.prepend("foo")),
        ),
    );
}
<|endoftext|>