<commit_before>use std::fmt;
use approx;
use color::Color;
use encoding::encode::{ColorEncoding, LinearEncoding, EncodableColor};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct EncodedColor<C, E> {
    color: C,
    encoding: E,
}

pub type LinearColor<C> = EncodedColor<C, LinearEncoding>;

impl<C, E> EncodedColor<C, E>
    where C: Color + EncodableColor,
          E: ColorEncoding
{
    pub fn new(color: C, encoding: E) -> Self {
        EncodedColor {
            color: color,
            encoding: encoding,
        }
    }

    pub fn decompose(self) -> (C, E) {
        (self.color, self.encoding)
    }

    pub fn color(&self) -> &C {
        &self.color
    }
    pub fn strip_encoding(self) -> C {
        self.color
    }

    pub fn encoding(&self) -> &E {
        &self.encoding
    }

    pub fn decode(self) -> EncodedColor<C, LinearEncoding> {
        let decoded_color = self.color.decode_color(&self.encoding);
        EncodedColor::new(decoded_color, LinearEncoding::new())
    }

    pub fn transcode<Encoder>(self, new_encoding: Encoder) -> EncodedColor<C, Encoder>
        where Encoder: ColorEncoding
    {
        let decoded_color = self.decode();
        decoded_color.encode(new_encoding)
    }

    pub fn to_tuple(self) -> C::ChannelsTuple {
        self.color.to_tuple()
    }
}

impl<C> EncodedColor<C, LinearEncoding>
    where C: Color + EncodableColor
{
    pub fn encode<Encoder>(self, encoder: Encoder) -> EncodedColor<C, Encoder>
        where Encoder: ColorEncoding
    {
        let encoded_color = self.color.encode_color(&encoder);

        EncodedColor::new(encoded_color, encoder)
    }
}

impl<C, E> approx::ApproxEq for EncodedColor<C, E>
    where C: Color + EncodableColor + approx::ApproxEq,
          E: ColorEncoding + PartialEq
{
    type Epsilon = C::Epsilon;

    fn default_epsilon() -> Self::Epsilon {
        C::default_epsilon()
    }
    fn default_max_relative() -> Self::Epsilon {
        C::default_max_relative()
    }
    fn default_max_ulps() -> u32 {
        C::default_max_ulps()
    }
    fn relative_eq(&self,
                   other: &Self,
                   epsilon: Self::Epsilon,
                   max_relative: Self::Epsilon)
                   -> bool {
        (self.encoding == other.encoding) &&
        self.color.relative_eq(&other.color, epsilon, max_relative)
    }
    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
        (self.encoding == other.encoding) && self.color.ulps_eq(&other.color, epsilon, max_ulps)
    }
}

impl<C, E> Default for EncodedColor<C, E>
    where C: Color + EncodableColor + Default,
          E: ColorEncoding + Default
{
    fn default() -> Self {
        C::default().with_encoding(LinearEncoding::new()).encode(E::default())
    }
}

impl<C, E> fmt::Display for EncodedColor<C, E>
    where C: Color + EncodableColor + fmt::Display,
          E: ColorEncoding + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} @ {}", self.color, self.encoding)
    }
}
<commit_msg>Implemented many color traits for EncodedColor.<commit_after>use std::fmt;
use approx;
use color::{Color, PolarColor, Lerp, Invert, Bounded, HomogeneousColor, FromTuple};
use encoding::encode::{ColorEncoding, LinearEncoding, EncodableColor};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct EncodedColor<C, E> {
    color: C,
    encoding: E,
}

pub type LinearColor<C> = EncodedColor<C, LinearEncoding>;

impl<C, E> EncodedColor<C, E>
    where C: Color + EncodableColor,
          E: ColorEncoding
{
    pub fn new(color: C, encoding: E) -> Self {
        EncodedColor {
            color: color,
            encoding: encoding,
        }
    }

    pub fn decompose(self) -> (C, E) {
        (self.color, self.encoding)
    }

    pub fn color(&self) -> &C {
        &self.color
    }
    pub fn strip_encoding(self) -> C {
        self.color
    }

    pub fn encoding(&self) -> &E {
        &self.encoding
    }

    pub fn decode(self) -> EncodedColor<C, LinearEncoding> {
        let decoded_color = self.color.decode_color(&self.encoding);
        EncodedColor::new(decoded_color, LinearEncoding::new())
    }

    pub fn transcode<Encoder>(self, new_encoding: Encoder) -> EncodedColor<C, Encoder>
        where Encoder: ColorEncoding
    {
        let decoded_color = self.decode();
        decoded_color.encode(new_encoding)
    }
}
impl<C, E> EncodedColor<C, E>
    where C: Color + EncodableColor + HomogeneousColor,
          E: ColorEncoding + PartialEq
{
    pub fn broadcast(value: C::ChannelFormat, encoding: E) -> Self {
        EncodedColor::new(C::broadcast(value), encoding)
    }
}

impl<C, E> EncodedColor<C, E>
    where C: Color + EncodableColor + FromTuple,
          E: ColorEncoding + PartialEq
{
    pub fn from_tuple(values: C::ChannelsTuple, encoding: E) -> Self {
        EncodedColor::new(C::from_tuple(values), encoding)
    }
}

impl<C> EncodedColor<C, LinearEncoding>
    where C: Color + EncodableColor
{
    pub fn encode<Encoder>(self, encoder: Encoder) -> EncodedColor<C, Encoder>
        where Encoder: ColorEncoding
    {
        let encoded_color = self.color.encode_color(&encoder);

        EncodedColor::new(encoded_color, encoder)
    }
}

impl<C, E> Color for EncodedColor<C, E>
    where C: Color + EncodableColor,
          E: ColorEncoding + PartialEq
{
    type Tag = C::Tag;
    type ChannelsTuple = C::ChannelsTuple;

    fn num_channels() -> u32 {
        C::num_channels()
    }
    fn to_tuple(self) -> Self::ChannelsTuple {
        self.color.to_tuple()
    }
}

impl<C, E> PolarColor for EncodedColor<C, E>
    where C: Color + EncodableColor + PolarColor,
          E: ColorEncoding + PartialEq
{
    type Angular = C::Angular;
    type Cartesian = C::Cartesian;
}

impl<C, E> Lerp for EncodedColor<C, E>
    where C: Color + EncodableColor + Lerp,
          E: ColorEncoding + PartialEq + fmt::Debug
{
    type Position = C::Position;

    fn lerp(&self, right: &Self, pos: Self::Position) -> Self {
        assert_eq!(self.encoding, right.encoding);
        EncodedColor::new(self.color.lerp(&right.color(), pos), self.encoding.clone())
    }
}

impl<C, E> Invert for EncodedColor<C, E>
    where C: Color + EncodableColor + Invert,
          E: ColorEncoding + PartialEq
{
    fn invert(self) -> Self {
        EncodedColor::new(self.color.invert(), self.encoding)
    }
}

impl<C, E> Bounded for EncodedColor<C, E>
    where C: Color + EncodableColor + Bounded,
          E: ColorEncoding + PartialEq
{
    fn normalize(self) -> Self {
        EncodedColor::new(self.color.normalize(), self.encoding)
    }
    fn is_normalized(&self) -> bool {
        self.color.is_normalized()
    }
}

impl<C, E> approx::ApproxEq for EncodedColor<C, E>
    where C: Color + EncodableColor + approx::ApproxEq,
          E: ColorEncoding + PartialEq
{
    type Epsilon = C::Epsilon;

    fn default_epsilon() -> Self::Epsilon {
        C::default_epsilon()
    }
    fn default_max_relative() -> Self::Epsilon {
        C::default_max_relative()
    }
    fn default_max_ulps() -> u32 {
        C::default_max_ulps()
    }
    fn relative_eq(&self,
                   other: &Self,
                   epsilon: Self::Epsilon,
                   max_relative: Self::Epsilon)
                   -> bool {
        (self.encoding == other.encoding) &&
        self.color.relative_eq(&other.color, epsilon, max_relative)
    }
    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
        (self.encoding == other.encoding) && self.color.ulps_eq(&other.color, epsilon, max_ulps)
    }
}

impl<C, E> Default for EncodedColor<C, E>
    where C: Color + EncodableColor + Default,
          E: ColorEncoding + Default
{
    fn default() -> Self {
        C::default().with_encoding(LinearEncoding::new()).encode(E::default())
    }
}

impl<C, E> fmt::Display for EncodedColor<C, E>
    where C: Color + EncodableColor + fmt::Display,
          E: ColorEncoding + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} @ {}", self.color, self.encoding)
    }
}
<|endoftext|>


<commit_before>use crate::util::{msb, Aligned};
use std::arch::x86_64::*;

macro_rules! satd_hbd_avx2 {
  ($(($W:expr, $H:expr)),*) => {
    $(
      paste::item! {
        #[target_feature(enable = "avx2")]
        pub(crate) unsafe extern fn [<rav1e_satd_ $W x $H _hbd_avx2>](
          src: *const u16, src_stride: isize, dst: *const u16, dst_stride: isize,
        ) -> u32 {
          // Size of hadamard transform should be 4x4 or 8x8.
          // 4x* and *x4 use 4x4 and all other use 8x8.
          // Because these are constants via the macro system,
          // the branches here will be optimized out.
          let size: usize = $W.min($H).min(8);

          let mut sum = 0u64;

          // Loop over chunks the size of the chosen transform
          for chunk_y in (0isize..$H).step_by(size) {
            for chunk_x in (0isize..$W).step_by(size) {
              let src = src.offset(chunk_y * src_stride + chunk_x);
              let dst = dst.offset(chunk_y * dst_stride + chunk_x);
              if (size == 4) {
                sum += satd_kernel_4x4_hbd_avx2(src, src_stride, dst, dst_stride);
              } else {
                sum += satd_kernel_8x8_hbd_avx2(src, src_stride, dst, dst_stride);
              }
            }
          }

          // Normalize the results
          let ln = msb(size as i32) as u64;
          ((sum + (1 << ln >> 1)) >> ln) as u32
        }
      }
    )*
  }
}

macro_rules! satd_kernel_hbd_avx2 {
  ($(($W:expr, $H:expr)),*) => {
    $(
      paste::item! {
        #[target_feature(enable = "avx2")]
        unsafe extern fn [<satd_kernel_ $W x $H _hbd_avx2>](
          src: *const u16, src_stride: isize, dst: *const u16, dst_stride: isize,
        ) -> u64 {
          // Size of hadamard kernel should be 4x4 or 8x8.
          // Because these are constants via the macro system,
          // the branches here will be optimized out.
          let size: usize = $W.min($H).min(8);
          let sizei = size as isize;

          let buf = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
          let input1 = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
          let input2 = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
          for y in 0..(sizei) {
            for x in 0..(sizei) {
              input1.as_mut_ptr().offset(y * sizei + x).write(src.offset(y * src_stride + x).read() as i32);
              input2.as_mut_ptr().offset(y * sizei + x).write(dst.offset(y * dst_stride + x).read() as i32);
            }
          }

          // Move the difference of the transforms to a buffer
          for y in 0..(sizei) {
            if size == 4 {
              let row_src = _mm_load_si128(input1.as_ptr().offset(y * sizei) as *const __m128i);
              let row_dst = _mm_load_si128(input2.as_ptr().offset(y * sizei) as *const __m128i);
              let diff = _mm_sub_epi32(row_src, row_dst);
              _mm_store_si128(buf.as_mut_ptr().offset(y * sizei) as *mut __m128i, diff);
            } else {
              let row_src = _mm256_load_si256(input1.as_ptr().offset(y * sizei) as *const __m256i);
              let row_dst = _mm256_load_si256(input2.as_ptr().offset(y * sizei) as *const __m256i);
              let diff = _mm256_sub_epi32(row_src, row_dst);
              _mm256_store_si256(buf.as_mut_ptr().offset(y * sizei) as *mut __m256i, diff);
            }
          }

          // Perform the hadamard transform on the differences,
          // Then sum the absolute values of the transformed differences
          if size == 4 {
            hadamard4x4(buf.as_mut_ptr());
            let row1 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(0) as *const __m256i));
            let row2 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(8) as *const __m256i));
            let sum_t = _mm256_add_epi32(row1, row2);
            let mut abs = Aligned::<[u32; 8]>::uninitialized();
            _mm256_store_si256(abs.data.as_mut_ptr() as *mut __m256i, sum_t);
            abs.data.iter().copied().map(|a| a as u64).sum::<u64>()
          } else {
            hadamard8x8(buf.as_mut_ptr());
            let row1 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(0) as *const __m256i));
            let row2 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(8) as *const __m256i));
            let row3 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(16) as *const __m256i));
            let row4 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(24) as *const __m256i));
            let row5 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(32) as *const __m256i));
            let row6 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(40) as *const __m256i));
            let row7 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(48) as *const __m256i));
            let row8 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(56) as *const __m256i));
            let sum1 = _mm256_add_epi32(row1, row2);
            let sum2 = _mm256_add_epi32(row3, row4);
            let sum3 = _mm256_add_epi32(row5, row6);
            let sum4 = _mm256_add_epi32(row7, row8);
            let sum5 = _mm256_add_epi32(sum1, sum2);
            let sum6 = _mm256_add_epi32(sum3, sum4);
            let sum_t = _mm256_add_epi32(sum5, sum6);
            let mut abs_sums = Aligned::<[u32; 8]>::uninitialized();
            _mm256_store_si256(abs_sums.data.as_mut_ptr() as *mut __m256i, sum_t);
            abs_sums.data.iter().copied().map(|a| a as u64).sum::<u64>()
          }
        }
      }
    )*
  }
}

satd_kernel_hbd_avx2!((4, 4), (8, 8));

satd_hbd_avx2!(
  (4, 4),
  (4, 8),
  (4, 16),
  (8, 4),
  (8, 8),
  (8, 16),
  (8, 32),
  (16, 4),
  (16, 8),
  (16, 16),
  (16, 32),
  (16, 64),
  (32, 8),
  (32, 16),
  (32, 32),
  (32, 64),
  (64, 16),
  (64, 32),
  (64, 64),
  (64, 128),
  (128, 64),
  (128, 128)
);

#[inline(always)]
const fn butterfly(a: i32, b: i32) -> (i32, i32) {
  ((a + b), (a - b))
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard4_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);
    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    sub.add(0 * stride1).write(b0);
    sub.add(1 * stride1).write(b1);
    sub.add(2 * stride1).write(b2);
    sub.add(3 * stride1).write(b3);
  }
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard8_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);

    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (a4, a5) =
      butterfly(sub.add(4 * stride1).read(), sub.add(5 * stride1).read());
    let (a6, a7) =
      butterfly(sub.add(6 * stride1).read(), sub.add(7 * stride1).read());

    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    let (b4, b6) = butterfly(a4, a6);
    let (b5, b7) = butterfly(a5, a7);

    let (c0, c4) = butterfly(b0, b4);
    let (c1, c5) = butterfly(b1, b5);
    let (c2, c6) = butterfly(b2, b6);
    let (c3, c7) = butterfly(b3, b7);

    sub.add(0 * stride1).write(c0);
    sub.add(1 * stride1).write(c1);
    sub.add(2 * stride1).write(c2);
    sub.add(3 * stride1).write(c3);
    sub.add(4 * stride1).write(c4);
    sub.add(5 * stride1).write(c5);
    sub.add(6 * stride1).write(c6);
    sub.add(7 * stride1).write(c7);
  }
}

#[inline(always)]
fn hadamard2d(data: *mut i32, (w, h): (usize, usize)) {
  /*Vertical transform.*/
  let vert_func = if h == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    vert_func(data, w, 1, h);
  }
  /*Horizontal transform.*/
  let horz_func = if w == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    horz_func(data, h, w, 1);
  }
}

#[inline(always)]
fn hadamard4x4(data: *mut i32) {
  hadamard2d(data, (4, 4));
}

#[inline(always)]
fn hadamard8x8(data: *mut i32) {
  hadamard2d(data, (8, 8));
}
<commit_msg>Remove unneeded parenteses<commit_after>use crate::util::{msb, Aligned};
use std::arch::x86_64::*;

macro_rules! satd_hbd_avx2 {
  ($(($W:expr, $H:expr)),*) => {
    $(
      paste::item! {
        #[target_feature(enable = "avx2")]
        pub(crate) unsafe extern fn [<rav1e_satd_ $W x $H _hbd_avx2>](
          src: *const u16, src_stride: isize, dst: *const u16, dst_stride: isize,
        ) -> u32 {
          // Size of hadamard transform should be 4x4 or 8x8.
          // 4x* and *x4 use 4x4 and all other use 8x8.
          // Because these are constants via the macro system,
          // the branches here will be optimized out.
          let size: usize = $W.min($H).min(8);

          let mut sum = 0u64;

          // Loop over chunks the size of the chosen transform
          for chunk_y in (0isize..$H).step_by(size) {
            for chunk_x in (0isize..$W).step_by(size) {
              let src = src.offset(chunk_y * src_stride + chunk_x);
              let dst = dst.offset(chunk_y * dst_stride + chunk_x);
              if size == 4 {
                sum += satd_kernel_4x4_hbd_avx2(src, src_stride, dst, dst_stride);
              } else {
                sum += satd_kernel_8x8_hbd_avx2(src, src_stride, dst, dst_stride);
              }
            }
          }

          // Normalize the results
          let ln = msb(size as i32) as u64;
          ((sum + (1 << ln >> 1)) >> ln) as u32
        }
      }
    )*
  }
}

macro_rules! satd_kernel_hbd_avx2 {
  ($(($W:expr, $H:expr)),*) => {
    $(
      paste::item! {
        #[target_feature(enable = "avx2")]
        unsafe extern fn [<satd_kernel_ $W x $H _hbd_avx2>](
          src: *const u16, src_stride: isize, dst: *const u16, dst_stride: isize,
        ) -> u64 {
          // Size of hadamard kernel should be 4x4 or 8x8.
          // Because these are constants via the macro system,
          // the branches here will be optimized out.
          let size: usize = $W.min($H).min(8);
          let sizei = size as isize;

          let buf = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
          let input1 = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
          let input2 = &mut Aligned::<[i32; 8 * 8]>::uninitialized().data[..size * size];
          for y in 0..(sizei) {
            for x in 0..(sizei) {
              input1.as_mut_ptr().offset(y * sizei + x).write(src.offset(y * src_stride + x).read() as i32);
              input2.as_mut_ptr().offset(y * sizei + x).write(dst.offset(y * dst_stride + x).read() as i32);
            }
          }

          // Move the difference of the transforms to a buffer
          for y in 0..(sizei) {
            if size == 4 {
              let row_src = _mm_load_si128(input1.as_ptr().offset(y * sizei) as *const __m128i);
              let row_dst = _mm_load_si128(input2.as_ptr().offset(y * sizei) as *const __m128i);
              let diff = _mm_sub_epi32(row_src, row_dst);
              _mm_store_si128(buf.as_mut_ptr().offset(y * sizei) as *mut __m128i, diff);
            } else {
              let row_src = _mm256_load_si256(input1.as_ptr().offset(y * sizei) as *const __m256i);
              let row_dst = _mm256_load_si256(input2.as_ptr().offset(y * sizei) as *const __m256i);
              let diff = _mm256_sub_epi32(row_src, row_dst);
              _mm256_store_si256(buf.as_mut_ptr().offset(y * sizei) as *mut __m256i, diff);
            }
          }

          // Perform the hadamard transform on the differences,
          // Then sum the absolute values of the transformed differences
          if size == 4 {
            hadamard4x4(buf.as_mut_ptr());
            let row1 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(0) as *const __m256i));
            let row2 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(8) as *const __m256i));
            let sum_t = _mm256_add_epi32(row1, row2);
            let mut abs = Aligned::<[u32; 8]>::uninitialized();
            _mm256_store_si256(abs.data.as_mut_ptr() as *mut __m256i, sum_t);
            abs.data.iter().copied().map(|a| a as u64).sum::<u64>()
          } else {
            hadamard8x8(buf.as_mut_ptr());
            let row1 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(0) as *const __m256i));
            let row2 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(8) as *const __m256i));
            let row3 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(16) as *const __m256i));
            let row4 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(24) as *const __m256i));
            let row5 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(32) as *const __m256i));
            let row6 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(40) as *const __m256i));
            let row7 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(48) as *const __m256i));
            let row8 = _mm256_abs_epi32(_mm256_load_si256(buf.as_ptr().add(56) as *const __m256i));
            let sum1 = _mm256_add_epi32(row1, row2);
            let sum2 = _mm256_add_epi32(row3, row4);
            let sum3 = _mm256_add_epi32(row5, row6);
            let sum4 = _mm256_add_epi32(row7, row8);
            let sum5 = _mm256_add_epi32(sum1, sum2);
            let sum6 = _mm256_add_epi32(sum3, sum4);
            let sum_t = _mm256_add_epi32(sum5, sum6);
            let mut abs_sums = Aligned::<[u32; 8]>::uninitialized();
            _mm256_store_si256(abs_sums.data.as_mut_ptr() as *mut __m256i, sum_t);
            abs_sums.data.iter().copied().map(|a| a as u64).sum::<u64>()
          }
        }
      }
    )*
  }
}

satd_kernel_hbd_avx2!((4, 4), (8, 8));

satd_hbd_avx2!(
  (4, 4),
  (4, 8),
  (4, 16),
  (8, 4),
  (8, 8),
  (8, 16),
  (8, 32),
  (16, 4),
  (16, 8),
  (16, 16),
  (16, 32),
  (16, 64),
  (32, 8),
  (32, 16),
  (32, 32),
  (32, 64),
  (64, 16),
  (64, 32),
  (64, 64),
  (64, 128),
  (128, 64),
  (128, 128)
);

#[inline(always)]
const fn butterfly(a: i32, b: i32) -> (i32, i32) {
  ((a + b), (a - b))
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard4_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);
    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    sub.add(0 * stride1).write(b0);
    sub.add(1 * stride1).write(b1);
    sub.add(2 * stride1).write(b2);
    sub.add(3 * stride1).write(b3);
  }
}

#[inline(always)]
#[allow(clippy::identity_op, clippy::erasing_op)]
unsafe fn hadamard8_1d(
  data: *mut i32, n: usize, stride0: usize, stride1: usize,
) {
  for i in 0..n {
    let sub = data.add(i * stride0);

    let (a0, a1) =
      butterfly(sub.add(0 * stride1).read(), sub.add(1 * stride1).read());
    let (a2, a3) =
      butterfly(sub.add(2 * stride1).read(), sub.add(3 * stride1).read());
    let (a4, a5) =
      butterfly(sub.add(4 * stride1).read(), sub.add(5 * stride1).read());
    let (a6, a7) =
      butterfly(sub.add(6 * stride1).read(), sub.add(7 * stride1).read());

    let (b0, b2) = butterfly(a0, a2);
    let (b1, b3) = butterfly(a1, a3);
    let (b4, b6) = butterfly(a4, a6);
    let (b5, b7) = butterfly(a5, a7);

    let (c0, c4) = butterfly(b0, b4);
    let (c1, c5) = butterfly(b1, b5);
    let (c2, c6) = butterfly(b2, b6);
    let (c3, c7) = butterfly(b3, b7);

    sub.add(0 * stride1).write(c0);
    sub.add(1 * stride1).write(c1);
    sub.add(2 * stride1).write(c2);
    sub.add(3 * stride1).write(c3);
    sub.add(4 * stride1).write(c4);
    sub.add(5 * stride1).write(c5);
    sub.add(6 * stride1).write(c6);
    sub.add(7 * stride1).write(c7);
  }
}

#[inline(always)]
fn hadamard2d(data: *mut i32, (w, h): (usize, usize)) {
  /*Vertical transform.*/
  let vert_func = if h == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    vert_func(data, w, 1, h);
  }
  /*Horizontal transform.*/
  let horz_func = if w == 4 { hadamard4_1d } else { hadamard8_1d };
  unsafe {
    horz_func(data, h, w, 1);
  }
}

#[inline(always)]
fn hadamard4x4(data: *mut i32) {
  hadamard2d(data, (4, 4));
}

#[inline(always)]
fn hadamard8x8(data: *mut i32) {
  hadamard2d(data, (8, 8));
}
<|endoftext|>


<commit_before>use back::machine::MachineCode;
use middle::ir;

struct InstructionSelector<'a> {
    ir: &'a ir::Program,
    code: MachineCode
}

impl<'a> InstructionSelector<'a> {
    fn new(ir: &'a ir::Program) -> InstructionSelector<'a> {
        InstructionSelector {
            ir: ir,
            code: MachineCode::new()
        }
    }

    fn translate(self) -> MachineCode {
        // First, initialize global variables
        // Then initialize the stack management registers
        // Emit a JMP to main
        // Translate all functions

        self.code
    }
}

pub fn select_instructions(ir: &ir::Program) -> MachineCode {
    let is = InstructionSelector::new(ir);
    is.translate()
}<commit_msg>Add note about instruction selector<commit_after>//! The instruction selector
//!
//! Note: For now this will be the simplest instruction selector possible.
//!       I'll be able to improve it later, but I need something that works
//!       before I can get there.

use back::machine::MachineCode;
use middle::ir;

struct InstructionSelector<'a> {
    ir: &'a ir::Program,
    code: MachineCode
}

impl<'a> InstructionSelector<'a> {
    fn new(ir: &'a ir::Program) -> InstructionSelector<'a> {
        InstructionSelector {
            ir: ir,
            code: MachineCode::new()
        }
    }

    fn translate(self) -> MachineCode {
        // First, initialize global variables
        // Then initialize the stack management registers
        // Emit a JMP to main
        // Translate all functions

        self.code
    }
}

pub fn select_instructions(ir: &ir::Program) -> MachineCode {
    let is = InstructionSelector::new(ir);
    is.translate()
}<|endoftext|>


<commit_before>use std::io::{stdin, File};
use std::io::buffered::BufferedReader;
use std::str::{from_utf8};
use std::os;

fn print_usage () {
	println("A tool to keep your reading skills bright and shiny.");
	println("Usage: rustoleum [arguments] path/to/input/file");
	println("-i (I)nteractive mode");
	println("-wpm N Display N words per (m)inute (default: 300)");
	println("-wpl N Display N words per (l)ine (default: 1)");
	println("-h Display this (h)elp");
}

fn get_args_interactive () -> (uint, uint, Path) {
	let mut reader = BufferedReader::new(stdin());

	println("Input path to file to read: ");
	let input = reader.read_line().unwrap_or(~"data/example.txt");
	let p = Path::new(input.trim());

	println("Input WPM: ");
	let input = reader.read_line().unwrap();
	let WPM = from_str::<uint>(input.trim()).unwrap_or(300);

	println("Input the number of words to view at a time: ");
	let input = reader.read_line().unwrap();
	let WPL = from_str::<uint>(input.trim()).unwrap_or(1);

	(WPM, WPL, p)
}

fn get_args () -> (uint, uint, Path){
	let args = os::args();
	if args.contains(&~"-h"){
		print_usage();
	}
	if args.contains(&~"-i"){
		get_args_interactive()
	}  else {
		let fallbackfile = &~"data/example.txt";
		let path = args.tail().last_opt().unwrap_or(fallbackfile);
		let p = Path::new(path.as_slice());
		let WPM = from_str::<uint>(args[1 + args.position_elem(&~"-wpm").unwrap_or(-1)]).unwrap_or(300);
		let WPL = from_str::<uint>(args[1 + args.position_elem(&~"-wpl").unwrap_or(-1)]).unwrap_or(1);
		(WPM,WPL,p)
	}
}

fn main () {
	let (WPM, WPL, p) = get_args();
	let contents = File::open(&p).read_to_end();
	let mut string_iter = from_utf8(contents).words();
	let string_vec = string_iter.to_owned_vec();
	let sleep_time = 60000 / WPM * WPL;
	for words in string_vec.chunks(WPL) {
		println("\x1bc".into_owned().append(words.connect(" ")));
		std::io::timer::sleep(sleep_time.to_u64().unwrap());
	}

}
<commit_msg>Improved support for command-line arguments.<commit_after>use std::io::{stdin, File};
use std::io::buffered::BufferedReader;
use std::str::{from_utf8};
use std::os;

fn print_usage () {
	println("A tool to keep your reading skills bright and shiny.");
	println("Usage: rustoleum [arguments] path/to/input/file");
	println("-i (I)nteractive mode");
	println("-wpm N Display N words per (m)inute (default: 300)");
	println("-wpl N Display N words per (l)ine (default: 1)");
	println("-h Display this (h)elp");
}

fn get_args_interactive () -> (uint, uint, Path) {
	let mut reader = BufferedReader::new(stdin());

	println("Input path to file to read: ");
	let input = reader.read_line().unwrap_or(~"data/example.txt");
	let p = Path::new(input.trim());

	println("Input WPM: ");
	let input = reader.read_line().unwrap();
	let WPM = from_str::<uint>(input.trim()).unwrap_or(300);

	println("Input the number of words to view at a time: ");
	let input = reader.read_line().unwrap();
	let WPL = from_str::<uint>(input.trim()).unwrap_or(1);

	(WPM, WPL, p)
}

fn get_vals (args: ~[~str]) -> (uint, uint, Path){
	if args.contains(&~"-i"){
		get_args_interactive()
	}  else {
		let fallbackfile = &~"data/example.txt";
		let path = args.tail().last_opt().unwrap_or(fallbackfile);
		let p = Path::new(path.as_slice());
		let WPM = from_str::<uint>(args[1 + args.position_elem(&~"-wpm").unwrap_or(-1)]).unwrap_or(300);
		let WPL = from_str::<uint>(args[1 + args.position_elem(&~"-wpl").unwrap_or(-1)]).unwrap_or(1);
		(WPM,WPL,p)
	}
}

fn print_file(WPM: uint, WPL: uint, p: Path){
	if p.exists(){
		let contents = File::open(&p).read_to_end();
		let mut string_iter = from_utf8(contents).words();
		let string_vec = string_iter.to_owned_vec();
		let sleep_time = 60000 / WPM * WPL;
		for words in string_vec.chunks(WPL) {
			println("\x1bc".into_owned().append(words.connect(" ")));
			std::io::timer::sleep(sleep_time.to_u64().unwrap());
		}
	} else {
		println("The path specified does not exist. Please specify a valid path, or run rustoleum -h for help.");
	}
}
fn main () {
	if os::args().contains(&~"-h"){
		print_usage();
	} else {
		let (WPM, WPL, p) = get_vals(os::args());
		print_file(WPM, WPL, p);
	}

}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Jason White
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
use std::str::FromStr;

use nom::{self, AsChar};

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct MakeRule {
    pub targets: Vec<String>,
    pub prereqs: Vec<String>,
}

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct MakeFile(Vec<MakeRule>);

impl FromStr for MakeFile {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match make_rules(s) {
            Ok((_, rules)) => Ok(MakeFile(rules)),
            Err(err) => Err(err.to_string()),
        }
    }
}

impl MakeFile {
    pub fn rules(&self) -> &Vec<MakeRule> {
        &self.0
    }
}

fn is_file_name<C: AsChar>(chr: C) -> bool {
    let chr = chr.as_char();
    !(chr == '\\' || chr == ':' || chr == '\n' || chr == ' ' || chr == '\0')
}

fn is_space<C: AsChar>(chr: C) -> bool {
    let chr = chr.as_char();
    chr == ' ' || chr == '\t'
}

fn is_rule_separator<C: AsChar>(chr: C) -> bool {
    let chr = chr.as_char();
    chr == '\n' || chr == '\r' || is_space(chr)
}

named!(pub filename<&str, String>,
    escaped_transform!(
        take_while1!(is_file_name),
        '\\',
        alt!(
            tag!("\\") => { |_| "\\" }
            | tag!(" ") => { |_| " " }
            | tag!("n") => { |_| "\n" }
            | tag!("t") => { |_| "\t" }
            | nom::line_ending => { |_| "" }
        )
    )
);

named!(make_targets<&str, Vec<String>>,
    separated_list!(
        escaped!(
            take_while1!(is_space),
            '\\',
            nom::line_ending
        ),
        filename
    )
);

named!(make_rule<&str, MakeRule>,
    do_parse!(
        targets: make_targets >>
        take_while!(is_space) >>
        tag!(":") >>
        take_while!(is_space) >>
        prereqs: make_targets >>
        (MakeRule { targets, prereqs })
    )
);

named!(make_rules<&str, Vec<MakeRule>>,
    ws!(separated_list!(
        take_while!(is_rule_separator),
        make_rule
    ))
);

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

    #[test]
    fn it_works() {
        assert_eq!(
            filename("\\nfoo\\\\bar\\t\\ baz.o\0"),
            Ok(("\0", String::from("\nfoo\\bar\t baz.o")))
        );

        assert_eq!(
            filename("foo\\\nbar.o\0"),
            Ok(("\0", String::from("foobar.o")))
        );

        assert_eq!(
            filename("foo\\\r\nbar.o\0"),
            Ok(("\0", String::from("foobar.o")))
        );

        assert_eq!(
            make_targets("foo\\ ooo.o bar.o      baz.o:"),
            Ok((
                ":",
                vec!["foo ooo.o".into(), "bar.o".into(), "baz.o".into()]
            ))
        );

        assert_eq!(
            make_rule("foo.o bar.o   :  foo.c \\\n \\\r\n bar.c \n"),
            Ok((
                " \n",
                MakeRule {
                    targets: vec!["foo.o".into(), "bar.o".into()],
                    prereqs: vec!["foo.c".into(), "bar.c".into()],
                }
            ))
        );

        let makefile =
            "foo.o: foo.c foo.h  \t\n  \n\r\n bar.o: bar.c foo.h\n\0";

        assert_eq!(
            make_rules(makefile),
            Ok((
                "\0",
                vec![
                    MakeRule {
                        targets: vec!["foo.o".into()],
                        prereqs: vec!["foo.c".into(), "foo.h".into()],
                    },
                    MakeRule {
                        targets: vec!["bar.o".into()],
                        prereqs: vec!["bar.c".into(), "foo.h".into()],
                    }
                ]
            ))
        )
    }
}
<commit_msg>Fix makefile parsing when the file ends with \r\n<commit_after>// Copyright (c) 2018 Jason White
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
use std::str::FromStr;

use nom::{self, AsChar};

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct MakeRule {
    pub targets: Vec<String>,
    pub prereqs: Vec<String>,
}

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct MakeFile(Vec<MakeRule>);

impl FromStr for MakeFile {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match make_rules(s) {
            Ok((_, rules)) => Ok(MakeFile(rules)),
            Err(err) => Err(err.to_string()),
        }
    }
}

impl MakeFile {
    pub fn rules(&self) -> &Vec<MakeRule> {
        &self.0
    }
}

fn is_file_name<C: AsChar>(chr: C) -> bool {
    let chr = chr.as_char();
    !(chr == '\\' || chr == ':' || chr == '\r' || chr == '\n' || chr == ' ' ||
      chr == '\0')
}

fn is_space<C: AsChar>(chr: C) -> bool {
    let chr = chr.as_char();
    chr == ' ' || chr == '\t'
}

fn is_rule_separator<C: AsChar>(chr: C) -> bool {
    let chr = chr.as_char();
    chr == '\n' || chr == '\r' || is_space(chr)
}

named!(pub filename<&str, String>,
    escaped_transform!(
        take_while1!(is_file_name),
        '\\',
        alt!(
            tag!("\\") => { |_| "\\" }
            | tag!(" ") => { |_| " " }
            | tag!("r") => { |_| "\r" }
            | tag!("n") => { |_| "\n" }
            | tag!("t") => { |_| "\t" }
            | nom::line_ending => { |_| "" }
        )
    )
);

named!(make_targets<&str, Vec<String>>,
    separated_list!(
        escaped!(
            take_while1!(is_space),
            '\\',
            nom::line_ending
        ),
        filename
    )
);

named!(make_rule<&str, MakeRule>,
    do_parse!(
        targets: make_targets >>
        take_while!(is_space) >>
        tag!(":") >>
        take_while!(is_space) >>
        prereqs: make_targets >>
        (MakeRule { targets, prereqs })
    )
);

named!(make_rules<&str, Vec<MakeRule>>,
    ws!(separated_list!(
        take_while!(is_rule_separator),
        make_rule
    ))
);

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

    #[test]
    fn it_works() {
        assert_eq!(
            filename("\\nfoo\\\\bar\\t\\ baz.o\0"),
            Ok(("\0", String::from("\nfoo\\bar\t baz.o")))
        );

        assert_eq!(
            filename("foo\\\nbar.o\0"),
            Ok(("\0", String::from("foobar.o")))
        );

        assert_eq!(
            filename("foo\\\r\nbar.o\0"),
            Ok(("\0", String::from("foobar.o")))
        );

        assert_eq!(
            make_targets("foo\\ ooo.o bar.o      baz.o:"),
            Ok((
                ":",
                vec!["foo ooo.o".into(), "bar.o".into(), "baz.o".into()]
            ))
        );

        assert_eq!(
            make_rule("foo.o bar.o   :  foo.c \\\n \\\r\n bar.c \n"),
            Ok((
                " \n",
                MakeRule {
                    targets: vec!["foo.o".into(), "bar.o".into()],
                    prereqs: vec!["foo.c".into(), "bar.c".into()],
                }
            ))
        );

        let makefile =
            "foo.o: foo.c foo.h  \t\n  \n\r\n bar.o: bar.c foo.h\n\0";

        assert_eq!(
            make_rules(makefile),
            Ok((
                "\0",
                vec![
                    MakeRule {
                        targets: vec!["foo.o".into()],
                        prereqs: vec!["foo.c".into(), "foo.h".into()],
                    },
                    MakeRule {
                        targets: vec!["bar.o".into()],
                        prereqs: vec!["bar.c".into(), "foo.h".into()],
                    }
                ]
            ))
        );

        assert_eq!(
            make_rules("obj/bar.o: src/bar.c src/foo.h\r\n\0"),
            Ok((
                "\0",
                vec![
                    MakeRule {
                        targets: vec!["obj/bar.o".into()],
                        prereqs: vec!["src/bar.c".into(), "src/foo.h".into()],
                    },
                ]
            ))
        );
    }
}
<|endoftext|>


<commit_before>use crate::syntax::Api;
use proc_macro2::Ident;
use std::collections::BTreeMap;
use std::iter::FromIterator;

pub struct NamespaceEntries<'a> {
    direct: Vec<&'a Api>,
    nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>,
}

impl<'a> NamespaceEntries<'a> {
    pub fn new(apis: &'a [Api]) -> Self {
        let api_refs = Vec::from_iter(apis);
        sort_by_inner_namespace(api_refs, 0)
    }

    pub fn direct_content(&self) -> &[&'a Api] {
        &self.direct
    }

    pub fn nested_content(&self) -> impl Iterator<Item = (&'a Ident, &NamespaceEntries<'a>)> {
        self.nested.iter().map(|(k, entries)| (*k, entries))
    }
}

fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
    let mut direct = Vec::new();
    let mut nested_namespaces = BTreeMap::new();
    for api in &apis {
        if let Some(namespace) = api.namespace() {
            let first_ns_elem = namespace.iter().nth(depth);
            if let Some(first_ns_elem) = first_ns_elem {
                nested_namespaces
                    .entry(first_ns_elem)
                    .or_insert_with(Vec::new)
                    .push(*api);
                continue;
            }
        }
        direct.push(*api);
    }

    let nested = nested_namespaces
        .into_iter()
        .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1)))
        .collect();

    NamespaceEntries { direct, nested }
}

#[cfg(test)]
mod tests {
    use super::NamespaceEntries;
    use crate::syntax::namespace::Namespace;
    use crate::syntax::{Api, Doc, ExternType, Pair};
    use proc_macro2::{Ident, Span};
    use syn::Token;

    #[test]
    fn test_ns_entries_sort() {
        let apis = &[
            make_api(None, "C"),
            make_api(None, "A"),
            make_api(Some("G"), "E"),
            make_api(Some("D"), "F"),
            make_api(Some("G"), "H"),
            make_api(Some("D::K"), "L"),
            make_api(Some("D::K"), "M"),
            make_api(None, "B"),
            make_api(Some("D"), "I"),
            make_api(Some("D"), "J"),
        ];

        let root = NamespaceEntries::new(apis);

        // ::
        let root_direct = root.direct_content();
        assert_eq!(root_direct.len(), 3);
        assert_ident(root_direct[0], "C");
        assert_ident(root_direct[1], "A");
        assert_ident(root_direct[2], "B");

        let mut root_nested = root.nested_content();
        let (id, d) = root_nested.next().unwrap();
        assert_eq!(id, "D");
        let (id, g) = root_nested.next().unwrap();
        assert_eq!(id, "G");
        assert!(root_nested.next().is_none());

        // ::D
        let d_direct = d.direct_content();
        assert_eq!(d_direct.len(), 3);
        assert_ident(d_direct[0], "F");
        assert_ident(d_direct[1], "I");
        assert_ident(d_direct[2], "J");

        let mut d_nested = d.nested_content();
        let (id, k) = d_nested.next().unwrap();
        assert_eq!(id, "K");

        // ::D::K
        let k_direct = k.direct_content();
        assert_eq!(k_direct.len(), 2);
        assert_ident(k_direct[0], "L");
        assert_ident(k_direct[1], "M");

        // ::G
        let g_direct = g.direct_content();
        assert_eq!(g_direct.len(), 2);
        assert_ident(g_direct[0], "E");
        assert_ident(g_direct[1], "H");

        let mut g_nested = g.nested_content();
        assert!(g_nested.next().is_none());
    }

    fn assert_ident(api: &Api, expected: &str) {
        if let Api::CxxType(cxx_type) = api {
            assert_eq!(cxx_type.ident.cxx.ident, expected);
        } else {
            unreachable!()
        }
    }

    fn make_api(ns: Option<&str>, ident: &str) -> Api {
        let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
        Api::CxxType(ExternType {
            doc: Doc::new(),
            type_token: Token![type](Span::call_site()),
            ident: Pair::new(ns, Ident::new(ident, Span::call_site())),
            semi_token: Token![;](Span::call_site()),
            trusted: false,
        })
    }
}
<commit_msg>Flatten a nested if-let<commit_after>use crate::syntax::Api;
use proc_macro2::Ident;
use std::collections::BTreeMap;
use std::iter::FromIterator;

pub struct NamespaceEntries<'a> {
    direct: Vec<&'a Api>,
    nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>,
}

impl<'a> NamespaceEntries<'a> {
    pub fn new(apis: &'a [Api]) -> Self {
        let api_refs = Vec::from_iter(apis);
        sort_by_inner_namespace(api_refs, 0)
    }

    pub fn direct_content(&self) -> &[&'a Api] {
        &self.direct
    }

    pub fn nested_content(&self) -> impl Iterator<Item = (&'a Ident, &NamespaceEntries<'a>)> {
        self.nested.iter().map(|(k, entries)| (*k, entries))
    }
}

fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
    let mut direct = Vec::new();
    let mut nested_namespaces = BTreeMap::new();
    for api in &apis {
        if let Some(first_ns_elem) = api.namespace().and_then(|ns| ns.iter().nth(depth)) {
            nested_namespaces
                .entry(first_ns_elem)
                .or_insert_with(Vec::new)
                .push(*api);
            continue;
        }
        direct.push(*api);
    }

    let nested = nested_namespaces
        .into_iter()
        .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1)))
        .collect();

    NamespaceEntries { direct, nested }
}

#[cfg(test)]
mod tests {
    use super::NamespaceEntries;
    use crate::syntax::namespace::Namespace;
    use crate::syntax::{Api, Doc, ExternType, Pair};
    use proc_macro2::{Ident, Span};
    use syn::Token;

    #[test]
    fn test_ns_entries_sort() {
        let apis = &[
            make_api(None, "C"),
            make_api(None, "A"),
            make_api(Some("G"), "E"),
            make_api(Some("D"), "F"),
            make_api(Some("G"), "H"),
            make_api(Some("D::K"), "L"),
            make_api(Some("D::K"), "M"),
            make_api(None, "B"),
            make_api(Some("D"), "I"),
            make_api(Some("D"), "J"),
        ];

        let root = NamespaceEntries::new(apis);

        // ::
        let root_direct = root.direct_content();
        assert_eq!(root_direct.len(), 3);
        assert_ident(root_direct[0], "C");
        assert_ident(root_direct[1], "A");
        assert_ident(root_direct[2], "B");

        let mut root_nested = root.nested_content();
        let (id, d) = root_nested.next().unwrap();
        assert_eq!(id, "D");
        let (id, g) = root_nested.next().unwrap();
        assert_eq!(id, "G");
        assert!(root_nested.next().is_none());

        // ::D
        let d_direct = d.direct_content();
        assert_eq!(d_direct.len(), 3);
        assert_ident(d_direct[0], "F");
        assert_ident(d_direct[1], "I");
        assert_ident(d_direct[2], "J");

        let mut d_nested = d.nested_content();
        let (id, k) = d_nested.next().unwrap();
        assert_eq!(id, "K");

        // ::D::K
        let k_direct = k.direct_content();
        assert_eq!(k_direct.len(), 2);
        assert_ident(k_direct[0], "L");
        assert_ident(k_direct[1], "M");

        // ::G
        let g_direct = g.direct_content();
        assert_eq!(g_direct.len(), 2);
        assert_ident(g_direct[0], "E");
        assert_ident(g_direct[1], "H");

        let mut g_nested = g.nested_content();
        assert!(g_nested.next().is_none());
    }

    fn assert_ident(api: &Api, expected: &str) {
        if let Api::CxxType(cxx_type) = api {
            assert_eq!(cxx_type.ident.cxx.ident, expected);
        } else {
            unreachable!()
        }
    }

    fn make_api(ns: Option<&str>, ident: &str) -> Api {
        let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
        Api::CxxType(ExternType {
            doc: Doc::new(),
            type_token: Token![type](Span::call_site()),
            ident: Pair::new(ns, Ident::new(ident, Span::call_site())),
            semi_token: Token![;](Span::call_site()),
            trusted: false,
        })
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "clippy", allow(used_underscore_binding))]
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]

// The `quote!` macro requires deep recursion.
#![recursion_limit = "192"]

extern crate serde_codegen_internals as internals;

#[cfg(feature = "with-syntex")]
extern crate syntex;

#[cfg(feature = "with-syntex")]
#[macro_use]
extern crate syntex_syntax as syntax;

#[cfg(not(feature = "with-syntex"))]
#[macro_use]
extern crate syntax;

#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;

extern crate syn;
#[macro_use]
extern crate quote;

#[cfg(feature = "with-syn")]
extern crate post_expansion;

#[cfg(feature = "with-syntex")]
use std::path::Path;

#[cfg(not(feature = "with-syntex"))]
use syntax::feature_gate::AttributeType;

mod bound;
mod de;
mod ser;

#[cfg(feature = "with-syntex")]
fn syntex_registry() -> syntex::Registry {
    use syntax::{ast, fold};

    /// Strip the serde attributes from the crate.
    #[cfg(feature = "with-syntex")]
    fn strip_attributes(krate: ast::Crate) -> ast::Crate {
        /// Helper folder that strips the serde attributes after the extensions have been expanded.
        struct StripAttributeFolder;

        impl fold::Folder for StripAttributeFolder {
            fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
                match attr.node.value.node {
                    ast::MetaItemKind::List(ref n, _) if n == &"serde" => { return None; }
                    _ => {}
                }

                Some(attr)
            }

            fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
                fold::noop_fold_mac(mac, self)
            }
        }

        fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
    }

    let mut reg = syntex::Registry::new();

    reg.add_attr("feature(custom_derive)");
    reg.add_attr("feature(custom_attribute)");

    reg.add_decorator("derive_Serialize", expand_derive_serialize);
    reg.add_decorator("derive_Deserialize", expand_derive_deserialize);

    reg.add_post_expansion_pass(strip_attributes);

    reg
}

#[cfg(feature = "with-syntex")]
pub fn expand_str(src: &str) -> Result<String, syntex::Error> {
    let src = src.to_owned();

    let expand_thread = move || {
        syntex_registry().expand_str("", "", &src)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> Result<(), syntex::Error>
    where S: AsRef<Path>,
          D: AsRef<Path>,
{
    let src = src.as_ref().to_owned();
    let dst = dst.as_ref().to_owned();

    let expand_thread = move || {
        syntex_registry().expand("", src, dst)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(not(feature = "with-syntex"))]
pub fn register(reg: &mut rustc_plugin::Registry) {
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Serialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_serialize)));

    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Deserialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_deserialize)));

    reg.register_attribute("serde".to_owned(), AttributeType::Normal);
}

macro_rules! shim {
    ($name:ident $pkg:ident :: $func:ident) => {
        fn $func(
            cx: &mut ::syntax::ext::base::ExtCtxt,
            span: ::syntax::codemap::Span,
            meta_item: &::syntax::ast::MetaItem,
            annotatable: &::syntax::ext::base::Annotatable,
            push: &mut FnMut(::syntax::ext::base::Annotatable)
        ) {
            let item = match *annotatable {
                ::syntax::ext::base::Annotatable::Item(ref item) => item,
                _ => {
                    cx.span_err(
                        meta_item.span,
                        concat!("`#[derive(",
                                stringify!($name),
                                ")]` may only be applied to structs and enums"));
                    return;
                }
            };

            use syntax::{attr, ast, visit};
            struct MarkSerdeAttributesUsed;
            impl visit::Visitor for MarkSerdeAttributesUsed {
                fn visit_attribute(&mut self, attr: &ast::Attribute) {
                    match attr.node.value.node {
                        ast::MetaItemKind::List(ref name, _) if name == "serde" => {
                            attr::mark_used(attr);
                        }
                        _ => {}
                    }
                }
            }
            visit::walk_item(&mut MarkSerdeAttributesUsed, item);

            use syntax::print::pprust;
            let s = pprust::item_to_string(item);

            let syn_item = syn::parse_macro_input(&s).unwrap();
            let expanded = match $pkg::$func(&syn_item) {
                Ok(expanded) => expanded.to_string(),
                Err(msg) => {
                    cx.span_err(span, &msg);
                    return;
                }
            };

            use syntax::parse;
            let name = stringify!($name).to_string();
            let cfg = Vec::new();
            let sess = cx.parse_sess;
            let impl_item = parse::parse_item_from_source_str(name, expanded, cfg, sess);
            push(::syntax::ext::base::Annotatable::Item(impl_item.unwrap().unwrap()));
        }
    };
}

shim!(Serialize ser::expand_derive_serialize);
shim!(Deserialize de::expand_derive_deserialize);

#[cfg(feature = "with-syn")]
pub fn expand_single_item(item: &str) -> Result<String, String> {
    let syn_item = syn::parse_macro_input(item).unwrap();
    let (ser, de, syn_item) = strip_serde_derives(syn_item);
    let expanded_ser = if ser {
        Some(try!(ser::expand_derive_serialize(&syn_item)))
    } else {
        None
    };
    let expanded_de = if de {
        Some(try!(de::expand_derive_deserialize(&syn_item)))
    } else {
        None::<quote::Tokens>
    };
    let syn_item = post_expansion::strip_attrs_later(syn_item, &["serde"], "serde");
    return Ok(quote!(#expanded_ser #expanded_de #syn_item).to_string());

    fn strip_serde_derives(item: syn::MacroInput) -> (bool, bool, syn::MacroInput) {
        let mut ser = false;
        let mut de = false;
        let item = syn::MacroInput {
            attrs: item.attrs.into_iter().flat_map(|attr| {
                if attr.is_sugared_doc || attr.style != syn::AttrStyle::Outer {
                    return Some(attr);
                }
                let (name, nested) = match attr.value {
                    syn::MetaItem::List(name, nested) => (name, nested),
                    _ => return Some(attr)
                };
                if name != "derive" {
                    return Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, nested),
                        is_sugared_doc: false,
                    });
                }
                let rest: Vec<_> = nested.into_iter().filter(|nested| {
                    match *nested {
                        syn::MetaItem::Word(ref word) if word == "Serialize" => {
                            ser = true;
                            false
                        }
                        syn::MetaItem::Word(ref word) if word == "Deserialize" => {
                            de = true;
                            false
                        }
                        _ => true,
                    }
                }).collect();
                if rest.is_empty() {
                    None
                } else {
                    Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, rest),
                        is_sugared_doc: false,
                    })
                }
            }).collect(),
            ..item
        };
        (ser, de, item)
    }
}
<commit_msg>Remove unneeded type ascription<commit_after>#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "clippy", allow(used_underscore_binding))]
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]

// The `quote!` macro requires deep recursion.
#![recursion_limit = "192"]

extern crate serde_codegen_internals as internals;

#[cfg(feature = "with-syntex")]
extern crate syntex;

#[cfg(feature = "with-syntex")]
#[macro_use]
extern crate syntex_syntax as syntax;

#[cfg(not(feature = "with-syntex"))]
#[macro_use]
extern crate syntax;

#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;

extern crate syn;
#[macro_use]
extern crate quote;

#[cfg(feature = "with-syn")]
extern crate post_expansion;

#[cfg(feature = "with-syntex")]
use std::path::Path;

#[cfg(not(feature = "with-syntex"))]
use syntax::feature_gate::AttributeType;

mod bound;
mod de;
mod ser;

#[cfg(feature = "with-syntex")]
fn syntex_registry() -> syntex::Registry {
    use syntax::{ast, fold};

    /// Strip the serde attributes from the crate.
    #[cfg(feature = "with-syntex")]
    fn strip_attributes(krate: ast::Crate) -> ast::Crate {
        /// Helper folder that strips the serde attributes after the extensions have been expanded.
        struct StripAttributeFolder;

        impl fold::Folder for StripAttributeFolder {
            fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
                match attr.node.value.node {
                    ast::MetaItemKind::List(ref n, _) if n == &"serde" => { return None; }
                    _ => {}
                }

                Some(attr)
            }

            fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
                fold::noop_fold_mac(mac, self)
            }
        }

        fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
    }

    let mut reg = syntex::Registry::new();

    reg.add_attr("feature(custom_derive)");
    reg.add_attr("feature(custom_attribute)");

    reg.add_decorator("derive_Serialize", expand_derive_serialize);
    reg.add_decorator("derive_Deserialize", expand_derive_deserialize);

    reg.add_post_expansion_pass(strip_attributes);

    reg
}

#[cfg(feature = "with-syntex")]
pub fn expand_str(src: &str) -> Result<String, syntex::Error> {
    let src = src.to_owned();

    let expand_thread = move || {
        syntex_registry().expand_str("", "", &src)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> Result<(), syntex::Error>
    where S: AsRef<Path>,
          D: AsRef<Path>,
{
    let src = src.as_ref().to_owned();
    let dst = dst.as_ref().to_owned();

    let expand_thread = move || {
        syntex_registry().expand("", src, dst)
    };

    syntex::with_extra_stack(expand_thread)
}

#[cfg(not(feature = "with-syntex"))]
pub fn register(reg: &mut rustc_plugin::Registry) {
    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Serialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_serialize)));

    reg.register_syntax_extension(
        syntax::parse::token::intern("derive_Deserialize"),
        syntax::ext::base::MultiDecorator(
            Box::new(expand_derive_deserialize)));

    reg.register_attribute("serde".to_owned(), AttributeType::Normal);
}

macro_rules! shim {
    ($name:ident $pkg:ident :: $func:ident) => {
        fn $func(
            cx: &mut ::syntax::ext::base::ExtCtxt,
            span: ::syntax::codemap::Span,
            meta_item: &::syntax::ast::MetaItem,
            annotatable: &::syntax::ext::base::Annotatable,
            push: &mut FnMut(::syntax::ext::base::Annotatable)
        ) {
            let item = match *annotatable {
                ::syntax::ext::base::Annotatable::Item(ref item) => item,
                _ => {
                    cx.span_err(
                        meta_item.span,
                        concat!("`#[derive(",
                                stringify!($name),
                                ")]` may only be applied to structs and enums"));
                    return;
                }
            };

            use syntax::{attr, ast, visit};
            struct MarkSerdeAttributesUsed;
            impl visit::Visitor for MarkSerdeAttributesUsed {
                fn visit_attribute(&mut self, attr: &ast::Attribute) {
                    match attr.node.value.node {
                        ast::MetaItemKind::List(ref name, _) if name == "serde" => {
                            attr::mark_used(attr);
                        }
                        _ => {}
                    }
                }
            }
            visit::walk_item(&mut MarkSerdeAttributesUsed, item);

            use syntax::print::pprust;
            let s = pprust::item_to_string(item);

            let syn_item = syn::parse_macro_input(&s).unwrap();
            let expanded = match $pkg::$func(&syn_item) {
                Ok(expanded) => expanded.to_string(),
                Err(msg) => {
                    cx.span_err(span, &msg);
                    return;
                }
            };

            use syntax::parse;
            let name = stringify!($name).to_string();
            let cfg = Vec::new();
            let sess = cx.parse_sess;
            let impl_item = parse::parse_item_from_source_str(name, expanded, cfg, sess);
            push(::syntax::ext::base::Annotatable::Item(impl_item.unwrap().unwrap()));
        }
    };
}

shim!(Serialize ser::expand_derive_serialize);
shim!(Deserialize de::expand_derive_deserialize);

#[cfg(feature = "with-syn")]
pub fn expand_single_item(item: &str) -> Result<String, String> {
    let syn_item = syn::parse_macro_input(item).unwrap();
    let (ser, de, syn_item) = strip_serde_derives(syn_item);
    let expanded_ser = if ser {
        Some(try!(ser::expand_derive_serialize(&syn_item)))
    } else {
        None
    };
    let expanded_de = if de {
        Some(try!(de::expand_derive_deserialize(&syn_item)))
    } else {
        None
    };
    let syn_item = post_expansion::strip_attrs_later(syn_item, &["serde"], "serde");
    return Ok(quote!(#expanded_ser #expanded_de #syn_item).to_string());

    fn strip_serde_derives(item: syn::MacroInput) -> (bool, bool, syn::MacroInput) {
        let mut ser = false;
        let mut de = false;
        let item = syn::MacroInput {
            attrs: item.attrs.into_iter().flat_map(|attr| {
                if attr.is_sugared_doc || attr.style != syn::AttrStyle::Outer {
                    return Some(attr);
                }
                let (name, nested) = match attr.value {
                    syn::MetaItem::List(name, nested) => (name, nested),
                    _ => return Some(attr)
                };
                if name != "derive" {
                    return Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, nested),
                        is_sugared_doc: false,
                    });
                }
                let rest: Vec<_> = nested.into_iter().filter(|nested| {
                    match *nested {
                        syn::MetaItem::Word(ref word) if word == "Serialize" => {
                            ser = true;
                            false
                        }
                        syn::MetaItem::Word(ref word) if word == "Deserialize" => {
                            de = true;
                            false
                        }
                        _ => true,
                    }
                }).collect();
                if rest.is_empty() {
                    None
                } else {
                    Some(syn::Attribute {
                        style: syn::AttrStyle::Outer,
                        value: syn::MetaItem::List(name, rest),
                        is_sugared_doc: false,
                    })
                }
            }).collect(),
            ..item
        };
        (ser, de, item)
    }
}
<|endoftext|>


<commit_before>/// Custom code within the destructor.
///
/// When a value is no longer needed, Rust will run a "destructor" on that value.
/// The most common way that a value is no longer needed is when it goes out of
/// scope. Destructors may still run in other circumstances, but we're going to
/// focus on scope for the examples here. To learn about some of those other cases,
/// please see [the reference] section on destructors.
///
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// This destructor consists of two components:
/// - A call to `Drop::drop` for that value, if this special `Drop` trait is implemented for its type.
/// - The automatically generated "drop glue" which recursively calls the destructors
///     of the all fields of this value.
///
/// As Rust automatically calls the destructors of all contained fields,
/// you don't have to implement `Drop` in most cases. But there are some cases where
/// it is useful, for example for types which directly manage a resource.
/// That resource may be memory, it may be a file descriptor, it may be a network socket.
/// Once a value of that type is no longer going to be used, it should "clean up" its
/// resource by freeing the memory or closing the file or socket. This is
/// the job of a destructor, and therefore the job of `Drop::drop`.
///
/// ## Examples
///
/// To see destructors in action, let's take a look at the following program:
///
/// ```rust
/// struct HasDrop;
///
/// impl Drop for HasDrop {
///     fn drop(&mut self) {
///         println!("Dropping HasDrop!");
///     }
/// }
///
/// struct HasTwoDrops {
///     one: HasDrop,
///     two: HasDrop,
/// }
///
/// impl Drop for HasTwoDrops {
///     fn drop(&mut self) {
///         println!("Dropping HasTwoDrops!");
///     }
/// }
///
/// fn main() {
///     let _x = HasTwoDrops { one: HasDrop, two: HasDrop };
///     println!("Running!");
/// }
/// ```
///
/// Rust will first call `Drop::drop` for `_x` and then for both `_x.one` and `_x.two`,
/// meaning that running this will print
///
/// ```text
/// Running!
/// Dropping HasTwoDrops!
/// Dropping HasDrop!
/// Dropping HasDrop!
/// ```
///
/// Even if we remove the implementation of `Drop` for `HasTwoDrop`, the destructors of its fields are still called.
/// This would result in
///
/// ```test
/// Running!
/// Dropping HasDrop!
/// Dropping HasDrop!
/// ```
///
/// ## You cannot call `Drop::drop` yourself
///
/// Because `Drop::drop` is used to clean up a value, it may be dangerous to use this value after
/// the method has been called. As `Drop::drop` does not take ownership of its input,
/// Rust prevents misuse by not allowing you to call `Drop::drop` directly.
///
/// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error.
///
/// If you'd like explicitly call the destructor of a value, [`mem::drop`] can be used instead.
///
/// [`mem::drop`]: drop
///
/// ## Drop order
///
/// Which of our two `HasDrop` drops first, though? For structs, it's the same
/// order that they're declared: first `one`, then `two`. If you'd like to try
/// this yourself, you can modify `HasDrop` above to contain some data, like an
/// integer, and then use it in the `println!` inside of `Drop`. This behavior is
/// guaranteed by the language.
///
/// Unlike for structs, local variables are dropped in reverse order:
///
/// ```rust
/// struct Foo;
///
/// impl Drop for Foo {
///     fn drop(&mut self) {
///         println!("Dropping Foo!")
///     }
/// }
///
/// struct Bar;
///
/// impl Drop for Bar {
///     fn drop(&mut self) {
///         println!("Dropping Bar!")
///     }
/// }
///
/// fn main() {
///     let _foo = Foo;
///     let _bar = Bar;
/// }
/// ```
///
/// This will print
///
/// ```text
/// Dropping Bar!
/// Dropping Foo!
/// ```
///
/// Please see [the reference] for the full rules.
///
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// ## `Copy` and `Drop` are exclusive
///
/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that
/// are `Copy` get implicitly duplicated by the compiler, making it very
/// hard to predict when, and how often destructors will be executed. As such,
/// these types cannot have destructors.
#[lang = "drop"]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Drop {
    /// Executes the destructor for this type.
    ///
    /// This method is called implicitly when the value goes out of scope,
    /// and cannot be called explicitly (this is compiler error [E0040]).
    /// However, the [`mem::drop`] function in the prelude can be
    /// used to call the argument's `Drop` implementation.
    ///
    /// When this method has been called, `self` has not yet been deallocated.
    /// That only happens after the method is over.
    /// If this wasn't the case, `self` would be a dangling reference.
    ///
    /// # Panics
    ///
    /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`]
    /// in a `drop` implementation will likely abort.
    ///
    /// Note that even if this panics, the value is considered to be dropped;
    /// you must not cause `drop` to be called again. This is normally automatically
    /// handled by the compiler, but when using unsafe code, can sometimes occur
    /// unintentionally, particularly when using [`ptr::drop_in_place`].
    ///
    /// [E0040]: ../../error-index.html#E0040
    /// [`panic!`]: crate::panic!
    /// [`mem::drop`]: drop
    /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place
    #[stable(feature = "rust1", since = "1.0.0")]
    fn drop(&mut self);
}
<commit_msg>Fix small typo in Drop documentation<commit_after>/// Custom code within the destructor.
///
/// When a value is no longer needed, Rust will run a "destructor" on that value.
/// The most common way that a value is no longer needed is when it goes out of
/// scope. Destructors may still run in other circumstances, but we're going to
/// focus on scope for the examples here. To learn about some of those other cases,
/// please see [the reference] section on destructors.
///
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// This destructor consists of two components:
/// - A call to `Drop::drop` for that value, if this special `Drop` trait is implemented for its type.
/// - The automatically generated "drop glue" which recursively calls the destructors
///     of the all fields of this value.
///
/// As Rust automatically calls the destructors of all contained fields,
/// you don't have to implement `Drop` in most cases. But there are some cases where
/// it is useful, for example for types which directly manage a resource.
/// That resource may be memory, it may be a file descriptor, it may be a network socket.
/// Once a value of that type is no longer going to be used, it should "clean up" its
/// resource by freeing the memory or closing the file or socket. This is
/// the job of a destructor, and therefore the job of `Drop::drop`.
///
/// ## Examples
///
/// To see destructors in action, let's take a look at the following program:
///
/// ```rust
/// struct HasDrop;
///
/// impl Drop for HasDrop {
///     fn drop(&mut self) {
///         println!("Dropping HasDrop!");
///     }
/// }
///
/// struct HasTwoDrops {
///     one: HasDrop,
///     two: HasDrop,
/// }
///
/// impl Drop for HasTwoDrops {
///     fn drop(&mut self) {
///         println!("Dropping HasTwoDrops!");
///     }
/// }
///
/// fn main() {
///     let _x = HasTwoDrops { one: HasDrop, two: HasDrop };
///     println!("Running!");
/// }
/// ```
///
/// Rust will first call `Drop::drop` for `_x` and then for both `_x.one` and `_x.two`,
/// meaning that running this will print
///
/// ```text
/// Running!
/// Dropping HasTwoDrops!
/// Dropping HasDrop!
/// Dropping HasDrop!
/// ```
///
/// Even if we remove the implementation of `Drop` for `HasTwoDrop`, the destructors of its fields are still called.
/// This would result in
///
/// ```test
/// Running!
/// Dropping HasDrop!
/// Dropping HasDrop!
/// ```
///
/// ## You cannot call `Drop::drop` yourself
///
/// Because `Drop::drop` is used to clean up a value, it may be dangerous to use this value after
/// the method has been called. As `Drop::drop` does not take ownership of its input,
/// Rust prevents misuse by not allowing you to call `Drop::drop` directly.
///
/// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error.
///
/// If you'd like to explicitly call the destructor of a value, [`mem::drop`] can be used instead.
///
/// [`mem::drop`]: drop
///
/// ## Drop order
///
/// Which of our two `HasDrop` drops first, though? For structs, it's the same
/// order that they're declared: first `one`, then `two`. If you'd like to try
/// this yourself, you can modify `HasDrop` above to contain some data, like an
/// integer, and then use it in the `println!` inside of `Drop`. This behavior is
/// guaranteed by the language.
///
/// Unlike for structs, local variables are dropped in reverse order:
///
/// ```rust
/// struct Foo;
///
/// impl Drop for Foo {
///     fn drop(&mut self) {
///         println!("Dropping Foo!")
///     }
/// }
///
/// struct Bar;
///
/// impl Drop for Bar {
///     fn drop(&mut self) {
///         println!("Dropping Bar!")
///     }
/// }
///
/// fn main() {
///     let _foo = Foo;
///     let _bar = Bar;
/// }
/// ```
///
/// This will print
///
/// ```text
/// Dropping Bar!
/// Dropping Foo!
/// ```
///
/// Please see [the reference] for the full rules.
///
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// ## `Copy` and `Drop` are exclusive
///
/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that
/// are `Copy` get implicitly duplicated by the compiler, making it very
/// hard to predict when, and how often destructors will be executed. As such,
/// these types cannot have destructors.
#[lang = "drop"]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Drop {
    /// Executes the destructor for this type.
    ///
    /// This method is called implicitly when the value goes out of scope,
    /// and cannot be called explicitly (this is compiler error [E0040]).
    /// However, the [`mem::drop`] function in the prelude can be
    /// used to call the argument's `Drop` implementation.
    ///
    /// When this method has been called, `self` has not yet been deallocated.
    /// That only happens after the method is over.
    /// If this wasn't the case, `self` would be a dangling reference.
    ///
    /// # Panics
    ///
    /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`]
    /// in a `drop` implementation will likely abort.
    ///
    /// Note that even if this panics, the value is considered to be dropped;
    /// you must not cause `drop` to be called again. This is normally automatically
    /// handled by the compiler, but when using unsafe code, can sometimes occur
    /// unintentionally, particularly when using [`ptr::drop_in_place`].
    ///
    /// [E0040]: ../../error-index.html#E0040
    /// [`panic!`]: crate::panic!
    /// [`mem::drop`]: drop
    /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place
    #[stable(feature = "rust1", since = "1.0.0")]
    fn drop(&mut self);
}
<|endoftext|>


<commit_before><commit_msg>Add test for emoji in string literal to test character counting of multi-byte emoji.<commit_after><|endoftext|>


<commit_before>#![feature(proc_macro)]
#![feature(proc_macro_lib)]
#![recursion_limit = "128"]

#[macro_use] extern crate quote;

extern crate proc_macro;
extern crate syn;

use proc_macro::TokenStream;
use std::str::FromStr;

// Yield mock generated code for template
//     Hello, {{name}} ({{age}})
#[proc_macro_derive(StacheDisplay)]
pub fn stach(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).unwrap();

    let type_name = &ast.ident;
    let generics = &ast.generics;

    let gen = quote! {
        impl #generics std::fmt::Display for #type_name #generics {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                use display_html_safe::DisplayHtmlSafe;

                f.write_str("Hello, ")?;
                DisplayHtmlSafe::fmt(&self.name, f)?;
                f.write_str(" (")?;
                DisplayHtmlSafe::fmt(&self.age, f)?;
                f.write_str(")\n")?;
                Ok(())
            }
        }
    };

    gen.parse().unwrap()
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Cleanup<commit_after>#![feature(proc_macro)]
#![feature(proc_macro_lib)]
#![recursion_limit = "128"]

#[macro_use] extern crate quote;

extern crate proc_macro;
extern crate syn;

use proc_macro::TokenStream;

// Yield mock generated code for template
//     Hello, {{name}} ({{age}})
#[proc_macro_derive(StacheDisplay)]
pub fn stache_display(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).unwrap();

    let type_name = &ast.ident;
    let generics = &ast.generics;

    let gen = quote! {
        impl #generics std::fmt::Display for #type_name #generics {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                use display_html_safe::DisplayHtmlSafe;

                f.write_str("Hello, ")?;
                DisplayHtmlSafe::fmt(&self.name, f)?;
                f.write_str(" (")?;
                DisplayHtmlSafe::fmt(&self.age, f)?;
                f.write_str(")\n")?;
                Ok(())
            }
        }
    };

    gen.parse().unwrap()
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>pub mod autotag;
pub mod queue_tag;
pub mod tag_command;
pub mod update_tag;
pub mod update_tags;

pub use self::autotag::AutoTagCommand;
pub use self::queue_tag::QueueTagCommand;
pub use self::tag_command::TagCommand;
pub use self::update_tag::UpdateTagCommand;
pub use self::update_tags::UpdateTagsCommand;

use bot::BotEnv;
use database::models::{ToU64, Tag, NewTag, Verification, Role as DbRole};

use lalafell::error::*;
use lalafell::commands::prelude::*;

use serenity::Error as SError;
use serenity::builder::EditRole;
use serenity::model::guild::Role;
use serenity::model::permissions::Permissions;
use serenity::prelude::Mentionable;
use serenity::model::id::{RoleId, UserId};
use serenity::http::{HttpError, StatusCode};

use diesel::prelude::*;

use url::Url;

use reqwest::Client;

use std::thread;
use std::sync::Mutex;
use std::collections::HashSet;

lazy_static! {
  // Limbo roles are roles that may or may not be added to the Discord bot state.
  static ref LIMBO_ROLES: Mutex<Vec<Role>> = Mutex::default();
}

pub struct Tagger;

impl Tagger {
  fn add_to_xivdb<N: Into<String>, S: Into<String>>(name: N, server: S) {
    let name: String = name.into();
    let server: String = server.into();
    thread::spawn(move || {
      let mut url = Url::parse("https://xivsync.com/character/search").unwrap();
      url.query_pairs_mut()
        .append_pair("name", &name)
        .append_pair("server", &server);
      let client = Client::new();
      client.get(url).send().ok();
    });
  }

  pub fn search_tag(env: &BotEnv, who: UserId, on: GuildId, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let params = &[
      ("one", "characters"),
      ("strict", "on"),
      ("server|et", server)
    ];

    let res = env.xivdb.search(character_name, params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      Tagger::add_to_xivdb(character_name, server);
      return Ok(Some(format!("Could not find any character by the name {} on {} in the XIVDB database.\nIf you typed everything correctly, please wait five minutes for your character to get added to the database, then try again.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => bail!("character ID was not a u64")
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => bail!("character name was not a string")
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(env, who, on, char_id, ignore_verified)
  }

  fn find_or_create_role(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>) -> Result<()> {
    Tagger::find_or_create_role_and(guild, name, add_roles, created_roles, |r| r)
  }

  fn find_or_create_role_and<F>(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>, f: F) -> Result<()>
    where F: FnOnce(EditRole) -> EditRole
  {
    let lower_name = name.to_lowercase();
    let guild = guild.get().chain_err(|| "could not get guild")?;
    match guild.roles.values().find(|x| x.name.to_lowercase() == lower_name) {
      Some(r) => add_roles.push(r.clone()),
      None => {
        let role = guild.create_role(|r| f(r.permissions(Permissions::empty())).name(&lower_name)).chain_err(|| "could not create role")?;
        created_roles.push(role);
      }
    }
    Ok(())
  }

  fn get_roles() -> Result<Vec<String>> {
    let roles: Vec<DbRole> = ::bot::CONNECTION.with(|c| {
      use database::schema::roles::dsl;
      dsl::roles.load(c).chain_err(|| "could not load roles")
    })?;
    Ok(roles.into_iter().map(|x| x.role_name.to_lowercase()).collect())
  }

  pub fn tag(env: &BotEnv, who: UserId, on: GuildId, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    let is_verified = match tag {
      Some(t) => {
        let verification: Verification = ::bot::CONNECTION.with(|c| {
          Verification::belonging_to(&t).first(c).optional().chain_err(|| "could not load verifications")
        })?.unwrap_or_default();
        if verification.verified && !ignore_verified && char_id != *t.character_id {
          return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), t.character, t.server)));
        }
        verification.verified
      },
      None => false
    };

    // This is still a disaster, just slightly less so
    let member = match on.member(who) {
      Ok(m) => m,
      Err(SError::Http(HttpError::UnsuccessfulRequest(ref r))) if r.status == StatusCode::NotFound => {
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags::dsl;
          ::diesel::delete(dsl::tags
            .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64()))))
            .execute(c)
            .chain_err(|| format!("could not remove tag {} on {}, but it was expected to be in database", who.0, on.0))
        })?;
        bail!("could not get user {} as a member of {}: removing their tag", who.0, on.0);
      },
      Err(e) => return Err(e).chain_err(|| "could not get member for tagging")
    };

    let character = env.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    match tag {
      Some(mut t) => {
        let (id, name, server) = (character.lodestone_id, character.name.clone(), character.server.clone());
        t.character_id = id.into();
        t.character = name;
        t.server = server;
        ::bot::CONNECTION.with(|c| t.save_changes::<Tag>(c).chain_err(|| "could not update tag"))?;
      },
      None => {
        let new_tag = NewTag::new(
          who.0,
          on.0,
          character.lodestone_id,
          &character.name,
          &character.server
        );
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags;
          ::diesel::insert_into(tags::table).values(&new_tag).execute(c).chain_err(|| "could not insert tag")
        })?;
      }
    }

    // Get a copy of the roles on the server.
    let mut roles: Vec<Role> = match on.find() {
      Some(g) => g.read().roles.values().cloned().collect(),
      None => on.get().chain_err(|| "could not get guild")?.roles.values().cloned().collect()
    };

    // Check for existing limbo roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for role in &roles {
        // If the server has updated to contain the limbo role, remove it.
        if let Some(i) = limbo.iter().position(|x| x.id == role.id) {
          limbo.remove(i);
        }
      }
    }
    // Get a copy of the limbo roles.
    let limbo = LIMBO_ROLES.lock().unwrap().clone();
    // Extend the server roles with the limbo roles.
    roles.extend(limbo);

    // Find or create the necessary roles
    let mut created_roles = Vec::new();
    let mut add_roles = Vec::new();
    Tagger::find_or_create_role(on, &character.data.race, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(on, &character.data.gender, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role_and(on, &character.server, &mut add_roles, &mut created_roles, |r| r.hoist(true))?;

    if is_verified {
      Tagger::find_or_create_role(on, "verified", &mut add_roles, &mut created_roles)?;
    }

    // If we created any roles, the server may or may not update with them fast enough, so store a copy in the limbo
    // roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for created in &created_roles {
        limbo.push(created.clone());
      }
    }

    debug!("Created the following roles:\n:{:#?}", created_roles);

    debug!("Existing roles:\n{:#?}", add_roles);

    // Extend the roles to add with the roles we created.
    add_roles.extend(created_roles);
    // Get all the roles that are part of groups
    let all_group_roles = Tagger::get_roles()?;
    // Filter all roles on the server to only the roles the member has
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    // Filter all the roles the member has, keeping the ones not in a group. These roles will not be touched when
    // updating the tag.
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&x.name.to_lowercase())).collect();
    debug!("Roles to keep:\n{:#?}", keep);
    // Combine the two sets of roles and map them to IDs
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    // Sort the IDs so we can dedup them
    role_set.sort();
    // Remove the duplicate roles, if any
    role_set.dedup();

    debug!("Final role set:\n{:#?}", role_set);

    // Only update the roles if they are different
    let different = {
      let member_roles: HashSet<u64> = member.roles.iter().map(|x| x.0).collect();
      let actual_role_set: HashSet<u64> = role_set.iter().map(|x| x.0).collect();
      member_roles != actual_role_set
    };
    if different {
      on.edit_member(who, |m| m.roles(&role_set)).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    if member.nick.as_ref() != Some(&character.name) {
      on.edit_member(who, |m| m.nickname(&character.name)).ok();
    }
    Ok(None)
  }
}

#[derive(Debug, Deserialize)]
struct DiscordNotFoundError {
  code: u64,
  message: Option<String>
}
<commit_msg>fix: handle having more than one result<commit_after>pub mod autotag;
pub mod queue_tag;
pub mod tag_command;
pub mod update_tag;
pub mod update_tags;

pub use self::autotag::AutoTagCommand;
pub use self::queue_tag::QueueTagCommand;
pub use self::tag_command::TagCommand;
pub use self::update_tag::UpdateTagCommand;
pub use self::update_tags::UpdateTagsCommand;

use bot::BotEnv;
use database::models::{ToU64, Tag, NewTag, Verification, Role as DbRole};

use lalafell::error::*;
use lalafell::commands::prelude::*;

use serenity::Error as SError;
use serenity::builder::EditRole;
use serenity::model::guild::Role;
use serenity::model::permissions::Permissions;
use serenity::prelude::Mentionable;
use serenity::model::id::{RoleId, UserId};
use serenity::http::{HttpError, StatusCode};

use diesel::prelude::*;

use url::Url;

use reqwest::Client;

use std::thread;
use std::sync::Mutex;
use std::collections::HashSet;

lazy_static! {
  // Limbo roles are roles that may or may not be added to the Discord bot state.
  static ref LIMBO_ROLES: Mutex<Vec<Role>> = Mutex::default();
}

pub struct Tagger;

impl Tagger {
  fn add_to_xivdb<N: Into<String>, S: Into<String>>(name: N, server: S) {
    let name: String = name.into();
    let server: String = server.into();
    thread::spawn(move || {
      let mut url = Url::parse("https://xivsync.com/character/search").unwrap();
      url.query_pairs_mut()
        .append_pair("name", &name)
        .append_pair("server", &server);
      let client = Client::new();
      client.get(url).send().ok();
    });
  }

  pub fn search_tag(env: &BotEnv, who: UserId, on: GuildId, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let params = &[
      ("one", "characters"),
      ("strict", "on"),
      ("server|et", server)
    ];

    let res = env.xivdb.search(character_name, params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;

    let character = match search_chars
      .into_iter()
      .find(|x|
        x["name"]
          .as_str()
          .map(|z| z.to_lowercase()) == Some(character_name.to_lowercase())
      )
    {
      Some(c) => c,
      None => {
        Tagger::add_to_xivdb(character_name, server);
        return Ok(Some(format!("Could not find any character by the name {} on {} in the XIVDB database.\nIf you typed everything correctly, please wait five minutes for your character to get added to the database, then try again.", character_name, server)));
      }
    };

    let char_id = match character["id"].as_u64() {
      Some(u) => u,
      None => bail!("character ID was not a u64")
    };

    let name = match character["name"].as_str() {
      Some(s) => s,
      None => bail!("character name was not a string")
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(env, who, on, char_id, ignore_verified)
  }

  fn find_or_create_role(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>) -> Result<()> {
    Tagger::find_or_create_role_and(guild, name, add_roles, created_roles, |r| r)
  }

  fn find_or_create_role_and<F>(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>, f: F) -> Result<()>
    where F: FnOnce(EditRole) -> EditRole
  {
    let lower_name = name.to_lowercase();
    let guild = guild.get().chain_err(|| "could not get guild")?;
    match guild.roles.values().find(|x| x.name.to_lowercase() == lower_name) {
      Some(r) => add_roles.push(r.clone()),
      None => {
        let role = guild.create_role(|r| f(r.permissions(Permissions::empty())).name(&lower_name)).chain_err(|| "could not create role")?;
        created_roles.push(role);
      }
    }
    Ok(())
  }

  fn get_roles() -> Result<Vec<String>> {
    let roles: Vec<DbRole> = ::bot::CONNECTION.with(|c| {
      use database::schema::roles::dsl;
      dsl::roles.load(c).chain_err(|| "could not load roles")
    })?;
    Ok(roles.into_iter().map(|x| x.role_name.to_lowercase()).collect())
  }

  pub fn tag(env: &BotEnv, who: UserId, on: GuildId, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    let is_verified = match tag {
      Some(t) => {
        let verification: Verification = ::bot::CONNECTION.with(|c| {
          Verification::belonging_to(&t).first(c).optional().chain_err(|| "could not load verifications")
        })?.unwrap_or_default();
        if verification.verified && !ignore_verified && char_id != *t.character_id {
          return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), t.character, t.server)));
        }
        verification.verified
      },
      None => false
    };

    // This is still a disaster, just slightly less so
    let member = match on.member(who) {
      Ok(m) => m,
      Err(SError::Http(HttpError::UnsuccessfulRequest(ref r))) if r.status == StatusCode::NotFound => {
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags::dsl;
          ::diesel::delete(dsl::tags
            .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64()))))
            .execute(c)
            .chain_err(|| format!("could not remove tag {} on {}, but it was expected to be in database", who.0, on.0))
        })?;
        bail!("could not get user {} as a member of {}: removing their tag", who.0, on.0);
      },
      Err(e) => return Err(e).chain_err(|| "could not get member for tagging")
    };

    let character = env.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    match tag {
      Some(mut t) => {
        let (id, name, server) = (character.lodestone_id, character.name.clone(), character.server.clone());
        t.character_id = id.into();
        t.character = name;
        t.server = server;
        ::bot::CONNECTION.with(|c| t.save_changes::<Tag>(c).chain_err(|| "could not update tag"))?;
      },
      None => {
        let new_tag = NewTag::new(
          who.0,
          on.0,
          character.lodestone_id,
          &character.name,
          &character.server
        );
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags;
          ::diesel::insert_into(tags::table).values(&new_tag).execute(c).chain_err(|| "could not insert tag")
        })?;
      }
    }

    // Get a copy of the roles on the server.
    let mut roles: Vec<Role> = match on.find() {
      Some(g) => g.read().roles.values().cloned().collect(),
      None => on.get().chain_err(|| "could not get guild")?.roles.values().cloned().collect()
    };

    // Check for existing limbo roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for role in &roles {
        // If the server has updated to contain the limbo role, remove it.
        if let Some(i) = limbo.iter().position(|x| x.id == role.id) {
          limbo.remove(i);
        }
      }
    }
    // Get a copy of the limbo roles.
    let limbo = LIMBO_ROLES.lock().unwrap().clone();
    // Extend the server roles with the limbo roles.
    roles.extend(limbo);

    // Find or create the necessary roles
    let mut created_roles = Vec::new();
    let mut add_roles = Vec::new();
    Tagger::find_or_create_role(on, &character.data.race, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(on, &character.data.gender, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role_and(on, &character.server, &mut add_roles, &mut created_roles, |r| r.hoist(true))?;

    if is_verified {
      Tagger::find_or_create_role(on, "verified", &mut add_roles, &mut created_roles)?;
    }

    // If we created any roles, the server may or may not update with them fast enough, so store a copy in the limbo
    // roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for created in &created_roles {
        limbo.push(created.clone());
      }
    }

    debug!("Created the following roles:\n:{:#?}", created_roles);

    debug!("Existing roles:\n{:#?}", add_roles);

    // Extend the roles to add with the roles we created.
    add_roles.extend(created_roles);
    // Get all the roles that are part of groups
    let all_group_roles = Tagger::get_roles()?;
    // Filter all roles on the server to only the roles the member has
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    // Filter all the roles the member has, keeping the ones not in a group. These roles will not be touched when
    // updating the tag.
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&x.name.to_lowercase())).collect();
    debug!("Roles to keep:\n{:#?}", keep);
    // Combine the two sets of roles and map them to IDs
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    // Sort the IDs so we can dedup them
    role_set.sort();
    // Remove the duplicate roles, if any
    role_set.dedup();

    debug!("Final role set:\n{:#?}", role_set);

    // Only update the roles if they are different
    let different = {
      let member_roles: HashSet<u64> = member.roles.iter().map(|x| x.0).collect();
      let actual_role_set: HashSet<u64> = role_set.iter().map(|x| x.0).collect();
      member_roles != actual_role_set
    };
    if different {
      on.edit_member(who, |m| m.roles(&role_set)).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    if member.nick.as_ref() != Some(&character.name) {
      on.edit_member(who, |m| m.nickname(&character.name)).ok();
    }
    Ok(None)
  }
}

#[derive(Debug, Deserialize)]
struct DiscordNotFoundError {
  code: u64,
  message: Option<String>
}
<|endoftext|>