<commit_before>use nom::{
  IResult,
  ErrorCode, Err,
  Needed,
};

use frame;
use frame::{subframe, ChannelAssignment};
use frame::SubFrame;
use frame::subframe::{CodingMethod, PartitionedRiceContents};

fn leading_zeros(input: (&[u8], usize)) -> IResult<(&[u8], usize), u32> {
  let (bytes, mut offset) = input;

  let mut index     = 0;
  let mut count     = 0;
  let mut is_parsed = false;
  let bytes_len     = bytes.len();

  for i in 0..bytes_len {
    // Clear the number of offset bits
    let byte  = bytes[i] << offset;
    let zeros = byte.leading_zeros() as usize;

    if byte > 0 {
      index     = i;
      is_parsed = true;
      count    += zeros;
      offset   += zeros + 1;

      if offset >= 8 {
        index  += 1;
        offset -= 8;
      }

      break;
    } else {
      count += zeros - offset;
      offset = 0;
    }
  }

  if is_parsed {
    IResult::Done((&bytes[index..], offset), count as u32)
  } else if index + 1 > bytes_len {
    IResult::Incomplete(Needed::Size(index + 1))
  } else {
    IResult::Error(Err::Position(ErrorCode::TakeUntil as u32, bytes))
  }
}

fn adjust_bits_per_sample(frame_header: &frame::Header,
                          channel: usize)
                          -> usize {
  let bits_per_sample = frame_header.bits_per_sample;

  match frame_header.channel_assignment {
    // Independent doesn't adjust bits per sample.
    ChannelAssignment::Independent => bits_per_sample,
    ChannelAssignment::LeftSide    => {
      if channel == 1 {
        bits_per_sample + 1
      } else {
        bits_per_sample
      }
    }
    ChannelAssignment::RightSide   => {
      if channel == 0 {
        bits_per_sample + 1
      } else {
        bits_per_sample
      }
    }
    ChannelAssignment::MiddleSide  => {
      if channel == 1 {
        bits_per_sample + 1
      } else {
        bits_per_sample
      }
    }
  }
}

pub fn subframe_parser<'a>(input: (&'a [u8], usize),
                           channel: &mut usize,
                           frame_header: &frame::Header)
                           -> IResult<'a, (&'a [u8], usize), SubFrame> {
  let block_size      = frame_header.block_size as usize;
  let bits_per_sample = adjust_bits_per_sample(frame_header, *channel);

  chain!(input,
    subframe_header: header ~
    wasted_bits: map!(
      cond!(subframe_header.1, leading_zeros),
      |option: Option<u32>| option.map_or(0, |zeros| zeros + 1)
    ) ~
    subframe_data: apply!(data,
      bits_per_sample - (wasted_bits as usize),
      block_size, subframe_header.0),
    || {
      // Iterate over the current channel being parsed. This probably should
      // be abstracted away, but for now this is the solution.
      *channel += 1;

      SubFrame {
        data: subframe_data,
        wasted_bits: wasted_bits,
      }
    }
  )
}

fn header(input: (&[u8], usize)) -> IResult<(&[u8], usize), (usize, bool)> {
  match take_bits!(input, u8, 8) {
    IResult::Done(i, byte)    => {
      let is_valid        = (byte >> 7) == 0;
      let subframe_type   = (byte >> 1) & 0b111111;
      let has_wasted_bits = (byte & 0b01) == 1;

      if is_valid {
        IResult::Done(i, (subframe_type as usize, has_wasted_bits))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input.0))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn data(input: (&[u8], usize),
        bits_per_sample: usize,
        block_size: usize,
        subframe_type: usize)
        -> IResult<(&[u8], usize), subframe::Data> {
  match subframe_type {
    0b000000            => constant(input, bits_per_sample),
    0b000001            => verbatim(input, bits_per_sample, block_size),
    0b001000...0b001100 => fixed(input, subframe_type & 0b0111,
                                 bits_per_sample, block_size),
    0b100000...0b111111 => lpc(input, (subframe_type & 0b011111) + 1,
                               bits_per_sample, block_size),
    _                   => IResult::Error(Err::Position(
                             ErrorCode::Alt as u32, input.0))
  }
}

fn constant(input: (&[u8], usize), bits_per_sample: usize)
            -> IResult<(&[u8], usize), subframe::Data> {
  map!(input, take_bits!(i32, bits_per_sample), subframe::Data::Constant)
}

fn fixed(input: (&[u8], usize),
         order: usize,
         bits_per_sample: usize,
         block_size: usize)
         -> IResult<(&[u8], usize), subframe::Data> {
  let mut warmup = [0; subframe::MAX_FIXED_ORDER];

  chain!(input,
    count_slice!(take_bits!(i32, bits_per_sample), &mut warmup[0..order]) ~
    tuple: apply!(residual, order, block_size),
    || {
      let data = tuple;

      subframe::Data::Fixed(subframe::Fixed {
        entropy_coding_method: data.0,
        order: order as u8,
        warmup: warmup,
        residual: data.1,
      })
    }
  )
}

fn qlp_coefficient_precision(input: (&[u8], usize))
                             -> IResult<(&[u8], usize), u8> {
  match take_bits!(input, u8, 4) {
    IResult::Done(i, precision) => {
      if precision == 0b1111 {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input.0))
      } else {
        IResult::Done(i, precision + 1)
      }
    }
    IResult::Error(error)       => IResult::Error(error),
    IResult::Incomplete(need)   => IResult::Incomplete(need),
  }
}

fn lpc(input: (&[u8], usize),
       order: usize,
       bits_per_sample: usize,
       block_size: usize)
       -> IResult<(&[u8], usize), subframe::Data> {
  let mut warmup           = [0; subframe::MAX_LPC_ORDER];
  let mut qlp_coefficients = [0; subframe::MAX_LPC_ORDER];

  chain!(input,
    count_slice!(take_bits!(i32, bits_per_sample), &mut warmup[0..order]) ~
    qlp_coeff_precision: qlp_coefficient_precision ~
    quantization_level: take_bits!(i8, 5) ~
    count_slice!(
      take_bits!(i32, qlp_coeff_precision as usize),
      &mut qlp_coefficients[0..order]
    ) ~
    tuple: apply!(residual, order, block_size),
    || {
      let data = tuple;

      subframe::Data::LPC(subframe::LPC {
        entropy_coding_method: data.0,
        order: order as u8,
        qlp_coeff_precision: qlp_coeff_precision,
        quantization_level: quantization_level,
        qlp_coefficients: qlp_coefficients,
        warmup: warmup,
        residual: data.1,
      })
    }
  )
}

fn verbatim(input: (&[u8], usize), bits_per_sample: usize, block_size: usize)
            -> IResult<(&[u8], usize), subframe::Data> {
  // TODO: Use nom's `count!` macro as soon as it is fixed for bit parsers.
  map!(input, count_bits!(take_bits!(i32, bits_per_sample), block_size),
       subframe::Data::Verbatim)
}

fn coding_method(input: (&[u8], usize))
                 -> IResult<(&[u8], usize), CodingMethod> {
  match take_bits!(input, u8, 2) {
    IResult::Done(i, method)  => {
      match method {
        0 => IResult::Done(i, CodingMethod::PartitionedRice),
        1 => IResult::Done(i, CodingMethod::PartitionedRice2),
        _ => IResult::Error(Err::Position(ErrorCode::Alt as u32, input.0)),
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn residual(input: (&[u8], usize),
            predictor_order: usize,
            block_size: usize)
            -> IResult<(&[u8], usize),
                       (subframe::EntropyCodingMethod, Vec<i32>)> {
  match pair!(input, coding_method, take_bits!(u32, 4)) {
    IResult::Done(i, data)    => {
      let (method, order) = data;

      rice_partition(i, order, predictor_order, block_size, method)
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn rice_partition(input: (&[u8], usize),
                  partition_order: u32,
                  predictor_order: usize,
                  block_size: usize,
                  method: CodingMethod)
                  -> IResult<(&[u8], usize),
                             (subframe::EntropyCodingMethod, Vec<i32>)> {
  let (param_size, escape_code) = match method {
    CodingMethod::PartitionedRice  => (4, 0b1111),
    CodingMethod::PartitionedRice2 => (5, 0b11111),
  };

  // Adjust block size to not include allocation for warm up samples
  let residual_size = block_size - predictor_order;

  let mut mut_input = input;
  let mut residual  = Vec::with_capacity(residual_size);
  let mut sample    = 0;
  let mut contents  = PartitionedRiceContents::new(partition_order);

  unsafe { residual.set_len(residual_size) }

  let partitions = 2_usize.pow(partition_order);

  for partition in 0..partitions {
    let offset = if partition_order == 0 {
      block_size - predictor_order
    } else if partition > 0 {
      block_size / partitions
    } else {
      (block_size / partitions) - predictor_order
    };
    let start = sample;
    let end   = sample + offset;

    let result = chain!(mut_input,
      rice_parameter: take_bits!(u32, param_size) ~
      size: cond!(rice_parameter == escape_code, take_bits!(usize, 5)) ~
      data: apply!(residual_data,
        size, rice_parameter,
        &mut contents.raw_bits[partition],
        &mut residual[start..end]
      ),
      || { rice_parameter }
    );

    match result {
      IResult::Done(i, parameter) => {
        mut_input = i;
        sample    = end;

        contents.parameters[partition] = parameter;
      }
      IResult::Error(error)       => return IResult::Error(error),
      IResult::Incomplete(need)   => return IResult::Incomplete(need),
    }
  }

  let entropy_coding_method = subframe::EntropyCodingMethod {
    method_type: method,
    data: subframe::PartitionedRice {
      order: partition_order,
      contents: contents,
    },
  };

  IResult::Done(mut_input, (entropy_coding_method, residual))
}

fn residual_data<'a>(input: (&'a [u8], usize),
                     option: Option<usize>,
                     rice_parameter: u32,
                     raw_bit: &mut u32,
                     samples: &mut [i32])
                     -> IResult<'a, (&'a [u8], usize), ()> {
  if let Some(size) = option {
    unencoded_residuals(input, size, raw_bit, samples)
  } else {
    encoded_residuals(input, rice_parameter, raw_bit, samples)
  }
}

fn unencoded_residuals<'a>(input: (&'a [u8], usize),
                           bits_per_sample: usize,
                           raw_bit: &mut u32,
                           samples: &mut [i32])
                           -> IResult<'a, (&'a[u8], usize), ()> {
  *raw_bit = bits_per_sample as u32;

  count_slice!(input, take_bits!(i32, bits_per_sample), &mut samples[..])
}

fn encoded_residuals<'a>(input: (&'a [u8], usize),
                         parameter: u32,
                         raw_bit: &mut u32,
                         samples: &mut [i32])
                         -> IResult<'a, (&'a[u8], usize), ()> {
  let length = samples.len();

  let mut count     = 0;
  let mut is_error  = false;
  let mut mut_input = input;

  *raw_bit = 0;

  for sample in samples {
    let result = chain!(mut_input,
      quotient: leading_zeros ~
      remainder: take_bits!(u32, parameter as usize),
      || {
        let value = quotient * parameter + remainder;

        ((value as i32) >> 1) ^ -((value as i32) & 1)
      });

    match result {
      IResult::Done(i, value) => {
        mut_input = i;
        count    += 1;

        *sample = value
      }
      IResult::Error(_)       => {
        is_error = true;

        break;
      }
      IResult::Incomplete(_)  => break,
    }
  }

  if is_error {
    IResult::Error(Err::Position(ErrorCode::Count as u32, input.0))
  } else if count == length {
    IResult::Done(mut_input, ())
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}
<commit_msg>Make `leading_zeros` public for testing<commit_after>use nom::{
  IResult,
  ErrorCode, Err,
  Needed,
};

use frame;
use frame::{subframe, ChannelAssignment};
use frame::SubFrame;
use frame::subframe::{CodingMethod, PartitionedRiceContents};

pub fn leading_zeros(input: (&[u8], usize)) -> IResult<(&[u8], usize), u32> {
  let (bytes, mut offset) = input;

  let mut index     = 0;
  let mut count     = 0;
  let mut is_parsed = false;
  let bytes_len     = bytes.len();

  for i in 0..bytes_len {
    // Clear the number of offset bits
    let byte  = bytes[i] << offset;
    let zeros = byte.leading_zeros() as usize;

    if byte > 0 {
      index     = i;
      is_parsed = true;
      count    += zeros;
      offset   += zeros + 1;

      if offset >= 8 {
        index  += 1;
        offset -= 8;
      }

      break;
    } else {
      count += zeros - offset;
      offset = 0;
    }
  }

  if is_parsed {
    IResult::Done((&bytes[index..], offset), count as u32)
  } else if index + 1 > bytes_len {
    IResult::Incomplete(Needed::Size(index + 1))
  } else {
    IResult::Error(Err::Position(ErrorCode::TakeUntil as u32, bytes))
  }
}

fn adjust_bits_per_sample(frame_header: &frame::Header,
                          channel: usize)
                          -> usize {
  let bits_per_sample = frame_header.bits_per_sample;

  match frame_header.channel_assignment {
    // Independent doesn't adjust bits per sample.
    ChannelAssignment::Independent => bits_per_sample,
    ChannelAssignment::LeftSide    => {
      if channel == 1 {
        bits_per_sample + 1
      } else {
        bits_per_sample
      }
    }
    ChannelAssignment::RightSide   => {
      if channel == 0 {
        bits_per_sample + 1
      } else {
        bits_per_sample
      }
    }
    ChannelAssignment::MiddleSide  => {
      if channel == 1 {
        bits_per_sample + 1
      } else {
        bits_per_sample
      }
    }
  }
}

pub fn subframe_parser<'a>(input: (&'a [u8], usize),
                           channel: &mut usize,
                           frame_header: &frame::Header)
                           -> IResult<'a, (&'a [u8], usize), SubFrame> {
  let block_size      = frame_header.block_size as usize;
  let bits_per_sample = adjust_bits_per_sample(frame_header, *channel);

  chain!(input,
    subframe_header: header ~
    wasted_bits: map!(
      cond!(subframe_header.1, leading_zeros),
      |option: Option<u32>| option.map_or(0, |zeros| zeros + 1)
    ) ~
    subframe_data: apply!(data,
      bits_per_sample - (wasted_bits as usize),
      block_size, subframe_header.0),
    || {
      // Iterate over the current channel being parsed. This probably should
      // be abstracted away, but for now this is the solution.
      *channel += 1;

      SubFrame {
        data: subframe_data,
        wasted_bits: wasted_bits,
      }
    }
  )
}

fn header(input: (&[u8], usize)) -> IResult<(&[u8], usize), (usize, bool)> {
  match take_bits!(input, u8, 8) {
    IResult::Done(i, byte)    => {
      let is_valid        = (byte >> 7) == 0;
      let subframe_type   = (byte >> 1) & 0b111111;
      let has_wasted_bits = (byte & 0b01) == 1;

      if is_valid {
        IResult::Done(i, (subframe_type as usize, has_wasted_bits))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input.0))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn data(input: (&[u8], usize),
        bits_per_sample: usize,
        block_size: usize,
        subframe_type: usize)
        -> IResult<(&[u8], usize), subframe::Data> {
  match subframe_type {
    0b000000            => constant(input, bits_per_sample),
    0b000001            => verbatim(input, bits_per_sample, block_size),
    0b001000...0b001100 => fixed(input, subframe_type & 0b0111,
                                 bits_per_sample, block_size),
    0b100000...0b111111 => lpc(input, (subframe_type & 0b011111) + 1,
                               bits_per_sample, block_size),
    _                   => IResult::Error(Err::Position(
                             ErrorCode::Alt as u32, input.0))
  }
}

fn constant(input: (&[u8], usize), bits_per_sample: usize)
            -> IResult<(&[u8], usize), subframe::Data> {
  map!(input, take_bits!(i32, bits_per_sample), subframe::Data::Constant)
}

fn fixed(input: (&[u8], usize),
         order: usize,
         bits_per_sample: usize,
         block_size: usize)
         -> IResult<(&[u8], usize), subframe::Data> {
  let mut warmup = [0; subframe::MAX_FIXED_ORDER];

  chain!(input,
    count_slice!(take_bits!(i32, bits_per_sample), &mut warmup[0..order]) ~
    tuple: apply!(residual, order, block_size),
    || {
      let data = tuple;

      subframe::Data::Fixed(subframe::Fixed {
        entropy_coding_method: data.0,
        order: order as u8,
        warmup: warmup,
        residual: data.1,
      })
    }
  )
}

fn qlp_coefficient_precision(input: (&[u8], usize))
                             -> IResult<(&[u8], usize), u8> {
  match take_bits!(input, u8, 4) {
    IResult::Done(i, precision) => {
      if precision == 0b1111 {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input.0))
      } else {
        IResult::Done(i, precision + 1)
      }
    }
    IResult::Error(error)       => IResult::Error(error),
    IResult::Incomplete(need)   => IResult::Incomplete(need),
  }
}

fn lpc(input: (&[u8], usize),
       order: usize,
       bits_per_sample: usize,
       block_size: usize)
       -> IResult<(&[u8], usize), subframe::Data> {
  let mut warmup           = [0; subframe::MAX_LPC_ORDER];
  let mut qlp_coefficients = [0; subframe::MAX_LPC_ORDER];

  chain!(input,
    count_slice!(take_bits!(i32, bits_per_sample), &mut warmup[0..order]) ~
    qlp_coeff_precision: qlp_coefficient_precision ~
    quantization_level: take_bits!(i8, 5) ~
    count_slice!(
      take_bits!(i32, qlp_coeff_precision as usize),
      &mut qlp_coefficients[0..order]
    ) ~
    tuple: apply!(residual, order, block_size),
    || {
      let data = tuple;

      subframe::Data::LPC(subframe::LPC {
        entropy_coding_method: data.0,
        order: order as u8,
        qlp_coeff_precision: qlp_coeff_precision,
        quantization_level: quantization_level,
        qlp_coefficients: qlp_coefficients,
        warmup: warmup,
        residual: data.1,
      })
    }
  )
}

fn verbatim(input: (&[u8], usize), bits_per_sample: usize, block_size: usize)
            -> IResult<(&[u8], usize), subframe::Data> {
  // TODO: Use nom's `count!` macro as soon as it is fixed for bit parsers.
  map!(input, count_bits!(take_bits!(i32, bits_per_sample), block_size),
       subframe::Data::Verbatim)
}

fn coding_method(input: (&[u8], usize))
                 -> IResult<(&[u8], usize), CodingMethod> {
  match take_bits!(input, u8, 2) {
    IResult::Done(i, method)  => {
      match method {
        0 => IResult::Done(i, CodingMethod::PartitionedRice),
        1 => IResult::Done(i, CodingMethod::PartitionedRice2),
        _ => IResult::Error(Err::Position(ErrorCode::Alt as u32, input.0)),
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn residual(input: (&[u8], usize),
            predictor_order: usize,
            block_size: usize)
            -> IResult<(&[u8], usize),
                       (subframe::EntropyCodingMethod, Vec<i32>)> {
  match pair!(input, coding_method, take_bits!(u32, 4)) {
    IResult::Done(i, data)    => {
      let (method, order) = data;

      rice_partition(i, order, predictor_order, block_size, method)
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn rice_partition(input: (&[u8], usize),
                  partition_order: u32,
                  predictor_order: usize,
                  block_size: usize,
                  method: CodingMethod)
                  -> IResult<(&[u8], usize),
                             (subframe::EntropyCodingMethod, Vec<i32>)> {
  let (param_size, escape_code) = match method {
    CodingMethod::PartitionedRice  => (4, 0b1111),
    CodingMethod::PartitionedRice2 => (5, 0b11111),
  };

  // Adjust block size to not include allocation for warm up samples
  let residual_size = block_size - predictor_order;

  let mut mut_input = input;
  let mut residual  = Vec::with_capacity(residual_size);
  let mut sample    = 0;
  let mut contents  = PartitionedRiceContents::new(partition_order);

  unsafe { residual.set_len(residual_size) }

  let partitions = 2_usize.pow(partition_order);

  for partition in 0..partitions {
    let offset = if partition_order == 0 {
      block_size - predictor_order
    } else if partition > 0 {
      block_size / partitions
    } else {
      (block_size / partitions) - predictor_order
    };
    let start = sample;
    let end   = sample + offset;

    let result = chain!(mut_input,
      rice_parameter: take_bits!(u32, param_size) ~
      size: cond!(rice_parameter == escape_code, take_bits!(usize, 5)) ~
      data: apply!(residual_data,
        size, rice_parameter,
        &mut contents.raw_bits[partition],
        &mut residual[start..end]
      ),
      || { rice_parameter }
    );

    match result {
      IResult::Done(i, parameter) => {
        mut_input = i;
        sample    = end;

        contents.parameters[partition] = parameter;
      }
      IResult::Error(error)       => return IResult::Error(error),
      IResult::Incomplete(need)   => return IResult::Incomplete(need),
    }
  }

  let entropy_coding_method = subframe::EntropyCodingMethod {
    method_type: method,
    data: subframe::PartitionedRice {
      order: partition_order,
      contents: contents,
    },
  };

  IResult::Done(mut_input, (entropy_coding_method, residual))
}

fn residual_data<'a>(input: (&'a [u8], usize),
                     option: Option<usize>,
                     rice_parameter: u32,
                     raw_bit: &mut u32,
                     samples: &mut [i32])
                     -> IResult<'a, (&'a [u8], usize), ()> {
  if let Some(size) = option {
    unencoded_residuals(input, size, raw_bit, samples)
  } else {
    encoded_residuals(input, rice_parameter, raw_bit, samples)
  }
}

fn unencoded_residuals<'a>(input: (&'a [u8], usize),
                           bits_per_sample: usize,
                           raw_bit: &mut u32,
                           samples: &mut [i32])
                           -> IResult<'a, (&'a[u8], usize), ()> {
  *raw_bit = bits_per_sample as u32;

  count_slice!(input, take_bits!(i32, bits_per_sample), &mut samples[..])
}

fn encoded_residuals<'a>(input: (&'a [u8], usize),
                         parameter: u32,
                         raw_bit: &mut u32,
                         samples: &mut [i32])
                         -> IResult<'a, (&'a[u8], usize), ()> {
  let length = samples.len();

  let mut count     = 0;
  let mut is_error  = false;
  let mut mut_input = input;

  *raw_bit = 0;

  for sample in samples {
    let result = chain!(mut_input,
      quotient: leading_zeros ~
      remainder: take_bits!(u32, parameter as usize),
      || {
        let value = quotient * parameter + remainder;

        ((value as i32) >> 1) ^ -((value as i32) & 1)
      });

    match result {
      IResult::Done(i, value) => {
        mut_input = i;
        count    += 1;

        *sample = value
      }
      IResult::Error(_)       => {
        is_error = true;

        break;
      }
      IResult::Incomplete(_)  => break,
    }
  }

  if is_error {
    IResult::Error(Err::Position(ErrorCode::Count as u32, input.0))
  } else if count == length {
    IResult::Done(mut_input, ())
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}
<|endoftext|>


<commit_before>extern crate specs;
extern crate futures;
extern crate serde_json;

use self::specs::{Join, ReadStorage, WriteStorage, System, Entities};
use self::futures::{Future, Sink};
use std::collections::HashMap;

use model::comp::{Pos, Vel, Acc, Bounciness, Ping, Player as PlayerComp};
use model::client::{ClientState, Ball, Player};
use model::game::Id;
use engine::OwnedMessage;
use util::timestamp;

pub struct Sending;
impl<'a> System<'a> for Sending {
    #[allow(type_complexity)]
    type SystemData = (ReadStorage<'a, Pos>,
     ReadStorage<'a, Vel>,
     ReadStorage<'a, Acc>,
     ReadStorage<'a, Bounciness>,
     WriteStorage<'a, PlayerComp>,
     WriteStorage<'a, Ping>,
     Entities<'a>);

    fn run(
        &mut self,
        (pos, vel, acc, bounciness, mut player, mut ping, entities): Self::SystemData,
    ) {
        let (ball_pos, ball_vel, _) = (&pos, &vel, &bounciness).join().take(1).next().unwrap();
        let ball = Ball {
            pos: ball_pos.clone(),
            vel: ball_vel.clone(),
        };

        let mut players = HashMap::new();
        for (pos, vel, acc, player) in (&pos, &vel, &acc, &player).join() {
            players.insert(
                player.id,
                Player {
                    pos: pos.clone(),
                    vel: vel.clone(),
                    acc: acc.clone(),
                },
            );
        }
        let state = ClientState {
            ball,
            players,
            timestamp: timestamp(),
        };
        for player in &mut player.join() {
            send(player, &state);
        }

        let mut pinged_players = Vec::new();
        for (mut player, entity, _) in (&mut player, &*entities, &mut ping).join() {

            send_ping(&mut player);
            pinged_players.push(entity);
        }

        for pinged_player in pinged_players {
            ping.remove(pinged_player);
        }
    }
}

fn send(player: &PlayerComp, state: &ClientState) {
    let msg = serde_json::to_string(&state).unwrap();
    let send_channel = player.send_channel.clone();
    send_channel.send(OwnedMessage::Text(msg)).wait().expect(
        "Failed to send message",
    );
}

fn send_ping(player: &mut PlayerComp) {
    let send_channel = player.send_channel.clone();
    let timestamp = timestamp();
    let id = Id::new_v4();
    player.pingpongs.insert(id, (timestamp, None));
    send_channel.send(OwnedMessage::Ping(id.as_bytes().to_vec()));
}
<commit_msg>Core: Actually send pings<commit_after>extern crate specs;
extern crate futures;
extern crate serde_json;

use self::specs::{Join, ReadStorage, WriteStorage, System, Entities};
use self::futures::{Future, Sink};
use std::collections::HashMap;

use model::comp::{Pos, Vel, Acc, Bounciness, Ping, Player as PlayerComp};
use model::client::{ClientState, Ball, Player};
use model::game::Id;
use engine::OwnedMessage;
use util::timestamp;

pub struct Sending;
impl<'a> System<'a> for Sending {
    #[allow(type_complexity)]
    type SystemData = (
        ReadStorage<'a, Pos>,
        ReadStorage<'a, Vel>,
        ReadStorage<'a, Acc>,
        ReadStorage<'a, Bounciness>,
        WriteStorage<'a, PlayerComp>,
        WriteStorage<'a, Ping>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (pos, vel, acc, bounciness, mut player, mut ping, entities): Self::SystemData,
    ) {
        let (ball_pos, ball_vel, _) = (&pos, &vel, &bounciness).join().take(1).next().unwrap();
        let ball = Ball {
            pos: ball_pos.clone(),
            vel: ball_vel.clone(),
        };

        let mut players = HashMap::new();
        for (pos, vel, acc, player) in (&pos, &vel, &acc, &player).join() {
            players.insert(
                player.id,
                Player {
                    pos: pos.clone(),
                    vel: vel.clone(),
                    acc: acc.clone(),
                },
            );
        }
        let state = ClientState {
            ball,
            players,
            timestamp: timestamp(),
        };
        for player in &mut player.join() {
            send(player, &state);
        }

        let mut pinged_players = Vec::new();
        for (mut player, entity, _) in (&mut player, &*entities, &mut ping).join() {

            send_ping(&mut player);
            pinged_players.push(entity);
        }

        for pinged_player in pinged_players {
            ping.remove(pinged_player);
        }
    }
}

fn send(player: &PlayerComp, state: &ClientState) {
    let msg = serde_json::to_string(&state).unwrap();
    let send_channel = player.send_channel.clone();
    send_channel
        .send(OwnedMessage::Text(msg))
        .wait()
        .expect("Failed to send message");
}

fn send_ping(player: &mut PlayerComp) {
    let send_channel = player.send_channel.clone();
    let timestamp = timestamp();
    let id = Id::new_v4();
    player.pingpongs.insert(id, (timestamp, None));
    send_channel
        .send(OwnedMessage::Ping(id.as_bytes().to_vec()))
        .wait()
        .unwrap();
}
<|endoftext|>


<commit_before>use color::PuyoColor;
use field::{self, BitField, FieldHeight};

pub struct CoreField {
    field: BitField,
    height: [i16; 8],
}

impl CoreField {
    pub fn new() -> CoreField {
        CoreField {
            field: BitField::new(),
            height: [0; 8],
        }
    }

    pub fn from_str(s: &str) -> CoreField {
        let mut cf = CoreField {
            field: BitField::from_str(s),
            height: [0; 8],
        };

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. 15 {
                if cf.is_empty(x, y) {
                    cf.height[x] = (y - 1) as i16;
                    break
                }
            }
        }

        cf
    }

    pub fn color(&self, x: usize, y: usize) -> PuyoColor {
        self.field.color(x, y)
    }

    pub fn is_color(&self, x: usize, y: usize, c: PuyoColor) -> bool {
        self.field.is_color(x, y, c)
    }

    pub fn is_empty(&self, x: usize, y: usize) -> bool {
        self.field.is_empty(x, y)
    }
}

impl FieldHeight for CoreField {
    fn height(&self, x: usize) -> usize {
        self.height[x] as usize
    }
}

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

    use color::PuyoColor;
    use field;
    use field::FieldHeight;

    #[test]
    fn test_constructor() {
        let cf = CoreField::new();

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. field::HEIGHT + 1 {
                assert!(cf.is_empty(x, y));
                assert!(cf.is_color(x, y, PuyoColor::EMPTY));
                assert_eq!(PuyoColor::EMPTY, cf.color(x, y));
            }
        }

        for x in 1 .. field::WIDTH + 1 {
            assert_eq!(0, cf.height(x));
        }
    }

    #[test]
    fn test_from_str() {
        let cf = CoreField::from_str(concat!(
            "R.....",
            "RRRB.."));

        assert_eq!(PuyoColor::WALL, cf.color(0, 1));
        assert_eq!(PuyoColor::RED, cf.color(1, 1));
        assert_eq!(PuyoColor::RED, cf.color(2, 1));
        assert_eq!(PuyoColor::RED, cf.color(3, 1));
        assert_eq!(PuyoColor::BLUE, cf.color(4, 1));
        assert_eq!(PuyoColor::EMPTY, cf.color(5, 1));
        assert_eq!(PuyoColor::EMPTY, cf.color(6, 1));
        assert_eq!(PuyoColor::WALL, cf.color(7, 1));

        assert_eq!(2, cf.height(1));
        assert_eq!(1, cf.height(2));
        assert_eq!(1, cf.height(3));
        assert_eq!(1, cf.height(4));
        assert_eq!(0, cf.height(5));
        assert_eq!(0, cf.height(6));
    }
}
<commit_msg>Add field() to CoreField<commit_after>use color::PuyoColor;
use field::{self, BitField, FieldHeight};

pub struct CoreField {
    field: BitField,
    height: [i16; 8],
}

impl CoreField {
    pub fn new() -> CoreField {
        CoreField {
            field: BitField::new(),
            height: [0; 8],
        }
    }

    pub fn from_str(s: &str) -> CoreField {
        let mut cf = CoreField {
            field: BitField::from_str(s),
            height: [0; 8],
        };

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. 15 {
                if cf.is_empty(x, y) {
                    cf.height[x] = (y - 1) as i16;
                    break
                }
            }
        }

        cf
    }

    pub fn color(&self, x: usize, y: usize) -> PuyoColor {
        self.field.color(x, y)
    }

    pub fn is_color(&self, x: usize, y: usize, c: PuyoColor) -> bool {
        self.field.is_color(x, y, c)
    }

    pub fn is_empty(&self, x: usize, y: usize) -> bool {
        self.field.is_empty(x, y)
    }

    pub fn field(&self) -> &BitField {
        &self.field
    }
}

impl FieldHeight for CoreField {
    fn height(&self, x: usize) -> usize {
        self.height[x] as usize
    }
}

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

    use color::PuyoColor;
    use field;
    use field::FieldHeight;

    #[test]
    fn test_constructor() {
        let cf = CoreField::new();

        for x in 1 .. field::WIDTH + 1 {
            for y in 1 .. field::HEIGHT + 1 {
                assert!(cf.is_empty(x, y));
                assert!(cf.is_color(x, y, PuyoColor::EMPTY));
                assert_eq!(PuyoColor::EMPTY, cf.color(x, y));
            }
        }

        for x in 1 .. field::WIDTH + 1 {
            assert_eq!(0, cf.height(x));
        }
    }

    #[test]
    fn test_from_str() {
        let cf = CoreField::from_str(concat!(
            "R.....",
            "RRRB.."));

        assert_eq!(PuyoColor::WALL, cf.color(0, 1));
        assert_eq!(PuyoColor::RED, cf.color(1, 1));
        assert_eq!(PuyoColor::RED, cf.color(2, 1));
        assert_eq!(PuyoColor::RED, cf.color(3, 1));
        assert_eq!(PuyoColor::BLUE, cf.color(4, 1));
        assert_eq!(PuyoColor::EMPTY, cf.color(5, 1));
        assert_eq!(PuyoColor::EMPTY, cf.color(6, 1));
        assert_eq!(PuyoColor::WALL, cf.color(7, 1));

        assert_eq!(2, cf.height(1));
        assert_eq!(1, cf.height(2));
        assert_eq!(1, cf.height(3));
        assert_eq!(1, cf.height(4));
        assert_eq!(0, cf.height(5));
        assert_eq!(0, cf.height(6));
    }
}
<|endoftext|>


<commit_before>use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::thread;

use chatmap::{ChatMap, ClientInfo};
use message::Message;

pub fn create_client(stream: TcpStream, sender_to_router: Sender<Message>, chat_map: &Arc<Mutex<ChatMap>>) {
    println!("New client");
    let mut stream = stream;
    stream.write(&"Welcome to Smazy\n".to_string().into_bytes()).unwrap();
    stream.write(&"Please enter a username:\n".to_string().into_bytes()).unwrap();
    stream.flush().unwrap();
    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut username = "".to_string();
    match reader.read_line(&mut username) {
        Ok(n) => {
            let username = username.trim().to_string();
            let (sender_to_client, receiver_from_router) = channel();
            {
                let mut guard = chat_map.lock().unwrap();
                if n > 0 && !guard.contains_key(&username) {
                    let client_info = ClientInfo{partner: None, sender_to_client: sender_to_client};
                    guard.insert(username.to_string(), client_info);
                    println!("New user: {}", username.to_string());
                }
            }
            let partner = choose_chat_partner(stream.try_clone().unwrap(), username.to_string(), chat_map);
            chat(stream, username, partner, sender_to_router, receiver_from_router);
        },
        Err(e) => println!("{}", e)
    }
}

fn choose_chat_partner(stream: TcpStream, username: String, chat_map: &Arc<Mutex<ChatMap>>) -> String {
    let mut stream = stream;

    stream.write(&"Here are the users available to chat:\n".to_string().into_bytes()).unwrap();
    {
        let guard = chat_map.lock().unwrap();
        for (name, client_info) in guard.iter() {
            if name.as_str() != username.as_str() && client_info.partner == None {
                stream.write(&name.to_string().into_bytes()).unwrap();
                stream.write(&"\n".to_string().into_bytes()).unwrap();
            }
        }
    }

    stream.write(&"Select who you want to chat with:\n".to_string().into_bytes()).unwrap();

    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut partner = "".to_string();

    match reader.read_line(&mut partner) {
        Ok(_) => {
            partner = partner.trim().to_string();

            if !try_select_partner(chat_map, username.to_string(), partner.to_string()) {
                return choose_chat_partner(stream, username, chat_map);
            }
        }
        Err(e) => println!("{}", e)
    }

    partner
}

fn try_select_partner(chat_map: &Arc<Mutex<ChatMap>>, username: String, partner: String) -> bool {
    let success: bool;
    {
        let mut guard = chat_map.lock().unwrap();

        match guard.get_mut(&partner) {
            Some(clientinfo) => {
                if partner.as_str() != username.as_str()
                    && (clientinfo.partner == None
                        || clientinfo.partner == Some(username.to_string())) {
                    clientinfo.partner = Some(username.to_string());
                    success = true;
                } else {
                    success = false;
                }
            },
            None => success = false
        }
    }

    if success {
        let mut guard = chat_map.lock().unwrap();
        println!("Assigning partner {} to {}", partner, username);
        guard.get_mut(&username).unwrap().partner = Some(partner.to_string());
    }
    success
}

// TODO: Receiving a message while in the middle of typing a message
//       inserts received message into middle of your message
fn chat(stream: TcpStream, username: String, partner: String, sender_to_router: Sender<Message>, receiver_from_router: Receiver<Message>) {
    // Send messages
    {
        let username = username.clone();
        let reader = BufReader::new(stream.try_clone().unwrap());
        thread::spawn(move|| {
            let mut lines = reader.lines();
            while let Some(Ok(line)) = lines.next() {
                println!("{}",line);
                let msg = Message::new("Date".to_string(), username.to_string(), partner.to_string(), line.to_string());
                sender_to_router.send(msg).unwrap();
            }
        });
    }

    // Receive Messages
    {
        let username = username.clone();
        thread::spawn(move|| {
            loop {
                let stream = stream.try_clone().unwrap();
                match receiver_from_router.try_recv() {
                    Ok(msg) => {
                        println!("User {} received message: {}", &username, msg.content());
                        deliver_message(stream, msg);
                    }
                    Err(TryRecvError::Empty) => continue,
                    Err(TryRecvError::Disconnected) => panic!("User {} disconnected from router", &username)
                }
            }
        });
    }
}

fn deliver_message(stream: TcpStream, msg: Message) {
    let mut stream = stream;
    let output = msg.from().to_string() + ": " + msg.content().as_str() + "\n";
    stream.write(&output.into_bytes()).unwrap();
}<commit_msg>Changed function name to disambiguate from sending messages<commit_after>use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::thread;

use chatmap::{ChatMap, ClientInfo};
use message::Message;

pub fn create_client(stream: TcpStream, sender_to_router: Sender<Message>, chat_map: &Arc<Mutex<ChatMap>>) {
    println!("New client");
    let mut stream = stream;
    stream.write(&"Welcome to Smazy\n".to_string().into_bytes()).unwrap();
    stream.write(&"Please enter a username:\n".to_string().into_bytes()).unwrap();
    stream.flush().unwrap();
    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut username = "".to_string();
    match reader.read_line(&mut username) {
        Ok(n) => {
            let username = username.trim().to_string();
            let (sender_to_client, receiver_from_router) = channel();
            {
                let mut guard = chat_map.lock().unwrap();
                if n > 0 && !guard.contains_key(&username) {
                    let client_info = ClientInfo{partner: None, sender_to_client: sender_to_client};
                    guard.insert(username.to_string(), client_info);
                    println!("New user: {}", username.to_string());
                }
            }
            let partner = choose_chat_partner(stream.try_clone().unwrap(), username.to_string(), chat_map);
            chat(stream, username, partner, sender_to_router, receiver_from_router);
        },
        Err(e) => println!("{}", e)
    }
}

fn choose_chat_partner(stream: TcpStream, username: String, chat_map: &Arc<Mutex<ChatMap>>) -> String {
    let mut stream = stream;

    stream.write(&"Here are the users available to chat:\n".to_string().into_bytes()).unwrap();
    {
        let guard = chat_map.lock().unwrap();
        for (name, client_info) in guard.iter() {
            if name.as_str() != username.as_str() && client_info.partner == None {
                stream.write(&name.to_string().into_bytes()).unwrap();
                stream.write(&"\n".to_string().into_bytes()).unwrap();
            }
        }
    }

    stream.write(&"Select who you want to chat with:\n".to_string().into_bytes()).unwrap();

    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut partner = "".to_string();

    match reader.read_line(&mut partner) {
        Ok(_) => {
            partner = partner.trim().to_string();

            if !try_select_partner(chat_map, username.to_string(), partner.to_string()) {
                return choose_chat_partner(stream, username, chat_map);
            }
        }
        Err(e) => println!("{}", e)
    }

    partner
}

fn try_select_partner(chat_map: &Arc<Mutex<ChatMap>>, username: String, partner: String) -> bool {
    let success: bool;
    {
        let mut guard = chat_map.lock().unwrap();

        match guard.get_mut(&partner) {
            Some(clientinfo) => {
                if partner.as_str() != username.as_str()
                    && (clientinfo.partner == None
                        || clientinfo.partner == Some(username.to_string())) {
                    clientinfo.partner = Some(username.to_string());
                    success = true;
                } else {
                    success = false;
                }
            },
            None => success = false
        }
    }

    if success {
        let mut guard = chat_map.lock().unwrap();
        println!("Assigning partner {} to {}", partner, username);
        guard.get_mut(&username).unwrap().partner = Some(partner.to_string());
    }
    success
}

// TODO: Receiving a message while in the middle of typing a message
//       inserts received message into middle of your message
fn chat(stream: TcpStream, username: String, partner: String, sender_to_router: Sender<Message>, receiver_from_router: Receiver<Message>) {
    // Send messages
    {
        let username = username.clone();
        let reader = BufReader::new(stream.try_clone().unwrap());
        thread::spawn(move|| {
            let mut lines = reader.lines();
            while let Some(Ok(line)) = lines.next() {
                println!("{}",line);
                let msg = Message::new("Date".to_string(), username.to_string(), partner.to_string(), line.to_string());
                sender_to_router.send(msg).unwrap();
            }
        });
    }

    // Receive Messages
    {
        let username = username.clone();
        thread::spawn(move|| {
            loop {
                let stream = stream.try_clone().unwrap();
                match receiver_from_router.try_recv() {
                    Ok(msg) => {
                        println!("User {} received message: {}", &username, msg.content());
                        receive_message(stream, msg);
                    }
                    Err(TryRecvError::Empty) => continue,
                    Err(TryRecvError::Disconnected) => panic!("User {} disconnected from router", &username)
                }
            }
        });
    }
}

fn receive_message(stream: TcpStream, msg: Message) {
    let mut stream = stream;
    let output = msg.from().to_string() + ": " + msg.content().as_str() + "\n";
    stream.write(&output.into_bytes()).unwrap();
}<|endoftext|>


<commit_before>extern crate chrono;
extern crate clap;
extern crate hostname;
extern crate os_type;
#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_async;
extern crate xdg;

mod command_line_arguments;
mod context;
mod datetime;
mod execution_timer;
mod qork;

use command_line_arguments::CommandLineArguments;
use context::Context;
use execution_timer::ExecutionTimer;
use slog::Logger;

fn main() {
    std::env::set_var("IN_QORK", "1");
    let throw_away_logger = create_root_logger();
    let _timer = ExecutionTimer::new(&throw_away_logger, "Main.Start");

    let args = CommandLineArguments::new();
    let main_logger = create_root_logger();
    info!(&main_logger, "{:?}", args);
    let context = Context::new(main_logger, args);
    context.log_created_message();
}

fn create_root_logger() -> Logger {
    use slog::Drain;

    let decorator = slog_term::TermDecorator::new().build();
    let drain = slog_term::FullFormat::new(decorator).build().fuse();
    let drain = slog_async::Async::new(drain).build().fuse();

    // TODO: We want dynamic configuration here.
    slog::Logger::root(drain, o!())
}
<commit_msg>ADD(Config): Start on loading the user configuration directory.<commit_after>extern crate chrono;
extern crate clap;
extern crate hostname;
extern crate os_type;
#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_async;
extern crate xdg;

mod command_line_arguments;
mod context;
mod datetime;
mod execution_timer;
mod qork;

use command_line_arguments::CommandLineArguments;
use context::Context;
use execution_timer::ExecutionTimer;
use slog::Logger;

fn main() {
    std::env::set_var("IN_QORK", "1");
    let throw_away_logger = create_root_logger();
    let _timer = ExecutionTimer::new(&throw_away_logger, "main.main");

    let args = CommandLineArguments::new();
    let main_logger = create_root_logger();
    info!(&main_logger, "{:?}", args);
    let context = Context::new(main_logger, args);
    context.log_created_message();

    load_user_configuration_if_valid(&context);
}

fn create_root_logger() -> Logger {
    use slog::Drain;

    let decorator = slog_term::TermDecorator::new().build();
    let drain = slog_term::FullFormat::new(decorator).build().fuse();
    let drain = slog_async::Async::new(drain).build().fuse();

    // TODO: We want dynamic configuration here.
    slog::Logger::root(drain, o!())
}

fn load_user_configuration_if_valid(context: &Context) {
    if context.command_line_arguments().load_config() {
        let _timer = ExecutionTimer::new2(&context.logger(), "load_user_configuration");
        let dir = context.xdg().get_config_home();
        if !dir.exists() {
            warn!(&context.logger(), "The config_directory does not exist, no config will be loaded"; "config_directory" => %dir.display());
            return
        }

        if !dir.is_dir() {
            warn!(&context.logger(), "The config_directory is a file, not a directory, no config will be loaded"; "config_directory" => %dir.display());
            return
        }

        load_user_configuration(context);

    } else {
        info!(&context.logger(), "Loading of user configuration is disabled.");
    }
}

fn load_user_configuration(context: &Context) {
}
<|endoftext|>


<commit_before>use std::{
    fs,
    collections::HashSet,
};

use tempfile::tempdir;

use ra_vfs::{Vfs, VfsChange};

#[test]
fn test_vfs_works() -> std::io::Result<()> {
    let files = [
        ("a/foo.rs", "hello"),
        ("a/bar.rs", "world"),
        ("a/b/baz.rs", "nested hello"),
    ];

    let dir = tempdir()?;
    for (path, text) in files.iter() {
        let file_path = dir.path().join(path);
        fs::create_dir_all(file_path.parent().unwrap())?;
        fs::write(file_path, text)?
    }

    let a_root = dir.path().join("a");
    let b_root = dir.path().join("a/b");

    let mut vfs = Vfs::new(vec![a_root, b_root]);
    for _ in 0..2 {
        let task = vfs.task_receiver().recv().unwrap();
        vfs.handle_task(task);
    }
    {
        let files = vfs
            .commit_changes()
            .into_iter()
            .flat_map(|change| {
                let files = match change {
                    VfsChange::AddRoot { files, .. } => files,
                    _ => panic!("unexpected change"),
                };
                files.into_iter().map(|(_id, path, text)| {
                    let text: String = (&*text).clone();
                    (format!("{}", path.display()), text)
                })
            })
            .collect::<HashSet<_>>();

        let expected_files = [
            ("foo.rs", "hello"),
            ("bar.rs", "world"),
            ("baz.rs", "nested hello"),
        ]
        .iter()
        .map(|(path, text)| (path.to_string(), text.to_string()))
        .collect::<HashSet<_>>();

        assert_eq!(files, expected_files);
    }

    vfs.add_file_overlay(&dir.path().join("a/b/baz.rs"), "quux".to_string());
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "quux"),
        _ => panic!("unexpected change"),
    }

    vfs.change_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string());
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "m"),
        _ => panic!("unexpected change"),
    }

    vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs"));
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "nested hello"),
        _ => panic!("unexpected change"),
    }

    vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string());
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::AddFile { text, path, .. } => {
            assert_eq!(&*text, "spam");
            assert_eq!(path, "spam.rs");
        }
        _ => panic!("unexpected change"),
    }

    vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs"));
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::RemoveFile { .. } => (),
        _ => panic!("unexpected change"),
    }

    vfs.shutdown().unwrap();
    Ok(())
}
<commit_msg>fix the test<commit_after>use std::{
    fs,
    collections::HashSet,
};

use tempfile::tempdir;

use ra_vfs::{Vfs, VfsChange};

#[test]
fn test_vfs_works() -> std::io::Result<()> {
    let files = [
        ("a/foo.rs", "hello"),
        ("a/bar.rs", "world"),
        ("a/b/baz.rs", "nested hello"),
    ];

    let dir = tempdir()?;
    for (path, text) in files.iter() {
        let file_path = dir.path().join(path);
        fs::create_dir_all(file_path.parent().unwrap())?;
        fs::write(file_path, text)?
    }

    let a_root = dir.path().join("a");
    let b_root = dir.path().join("a/b");

    let (mut vfs, _) = Vfs::new(vec![a_root, b_root]);
    for _ in 0..2 {
        let task = vfs.task_receiver().recv().unwrap();
        vfs.handle_task(task);
    }
    {
        let files = vfs
            .commit_changes()
            .into_iter()
            .flat_map(|change| {
                let files = match change {
                    VfsChange::AddRoot { files, .. } => files,
                    _ => panic!("unexpected change"),
                };
                files.into_iter().map(|(_id, path, text)| {
                    let text: String = (&*text).clone();
                    (format!("{}", path.display()), text)
                })
            })
            .collect::<HashSet<_>>();

        let expected_files = [
            ("foo.rs", "hello"),
            ("bar.rs", "world"),
            ("baz.rs", "nested hello"),
        ]
        .iter()
        .map(|(path, text)| (path.to_string(), text.to_string()))
        .collect::<HashSet<_>>();

        assert_eq!(files, expected_files);
    }

    vfs.add_file_overlay(&dir.path().join("a/b/baz.rs"), "quux".to_string());
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "quux"),
        _ => panic!("unexpected change"),
    }

    vfs.change_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string());
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "m"),
        _ => panic!("unexpected change"),
    }

    vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs"));
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "nested hello"),
        _ => panic!("unexpected change"),
    }

    vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string());
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::AddFile { text, path, .. } => {
            assert_eq!(&*text, "spam");
            assert_eq!(path, "spam.rs");
        }
        _ => panic!("unexpected change"),
    }

    vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs"));
    let change = vfs.commit_changes().pop().unwrap();
    match change {
        VfsChange::RemoveFile { .. } => (),
        _ => panic!("unexpected change"),
    }

    vfs.shutdown().unwrap();
    Ok(())
}
<|endoftext|>


<commit_before>extern crate config;
extern crate url;

use std::collections::HashMap;
use config::Config;
use std::sync::RwLock;
use utils::error;
use std::path::Path;
use url::{Url, ParseError};

pub static CONFIG_POOL_NAME: &'static str = "pool_name";
pub static CONFIG_POOL_CONFIG_NAME: &'static str = "config_name";
pub static CONFIG_WALLET_NAME: &'static str = "wallet_name";
pub static CONFIG_WALLET_TYPE: &'static str = "wallet_type";
pub static CONFIG_AGENT_ENDPOINT: &'static str = "agent_endpoint";
pub static CONFIG_AGENCY_PAIRWISE_DID: &'static str = "agency_pairwise_did";
pub static CONFIG_AGENCY_PAIRWISE_VERKEY: &'static str = "agency_pairwise_verkey";
pub static CONFIG_AGENT_PAIRWISE_DID: &'static str = "agent_pairwise_did";
pub static CONFIG_AGENT_PAIRWISE_VERKEY: &'static str = "agent_pairwise_verkey";
pub static CONFIG_ENTERPRISE_DID_AGENCY: &'static str = "enterprise_did_agency";
pub static CONFIG_ENTERPRISE_DID_AGENT: &'static str = "enterprise_did_agent";
pub static CONFIG_ENTERPRISE_NAME: &'static str = "enterprise_name";
pub static CONFIG_LOGO_URL: &'static str = "logo_url";

lazy_static! {
    static ref SETTINGS: RwLock<Config> = RwLock::new(Config::default());
}

pub fn set_defaults() -> u32 {
    // if this fails the program should exit
    let mut settings = SETTINGS.write().unwrap();

    settings.set_default(CONFIG_POOL_NAME,"pool1");
    settings.set_default(CONFIG_POOL_CONFIG_NAME,"config1");
    settings.set_default(CONFIG_WALLET_NAME,"wallet1");
    settings.set_default(CONFIG_WALLET_TYPE,"default");
    settings.set_default(CONFIG_AGENT_ENDPOINT,"http://127.0.0.1:8080");
    settings.set_default(CONFIG_AGENCY_PAIRWISE_DID,"default1");
    settings.set_default(CONFIG_AGENCY_PAIRWISE_VERKEY,"default");
    settings.set_default(CONFIG_AGENT_PAIRWISE_DID,"default");
    settings.set_default(CONFIG_AGENT_PAIRWISE_VERKEY,"default");
    settings.set_default(CONFIG_ENTERPRISE_DID_AGENCY,"default");
    settings.set_default(CONFIG_ENTERPRISE_DID_AGENT,"default");
    settings.set_default(CONFIG_ENTERPRISE_NAME,"default");
    settings.set_default(CONFIG_LOGO_URL,"http://www.evernym.com");

    error::SUCCESS.code_num
}

fn is_valid(value: &str) -> bool {
    for c in value.chars() {
        if !c.is_alphanumeric() && c != '_' { return false;}
    }
    true
}

fn validate_config() -> Result<u32, String> {
    let mut error = String::new();

    //if this fails the program should exit
    let config: HashMap<String, String> = SETTINGS.read().unwrap().deserialize::<HashMap<String, String>>().unwrap();

    for setting in config.iter() {
        let mut valid = true;
        if setting.0 == CONFIG_POOL_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_POOL_CONFIG_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_WALLET_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENT_ENDPOINT {
            match Url::parse(setting.1) {
                Err(x) => valid = false,
                Ok(_) => valid = true,
            }
        } else if setting.0 == CONFIG_ENTERPRISE_DID_AGENCY && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_ENTERPRISE_DID_AGENT && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENCY_PAIRWISE_VERKEY && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENT_PAIRWISE_VERKEY && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENCY_PAIRWISE_DID && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENT_PAIRWISE_DID && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_ENTERPRISE_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_LOGO_URL {
            match Url::parse(setting.1) {
                Err(x) => valid = false,
                Ok(_) => valid = true,
            }
        } else {
            //TODO: determine whether we should ignore invalid parameters
            //error.push_str(setting.0);
            //error.push_str("is invalid\n");
        }

        if !valid {
            error.push_str(setting.0);
            error.push_str(" has invalid setting: ");
            error.push_str(setting.1);
        }
    };

    if !error.is_empty() {
        Err(error.to_owned())
    } else {
        Ok(error::SUCCESS.code_num)
    }
}

pub fn process_config_file(path: &str) -> Result<u32, String> {

    if !Path::new(path).is_file() {
        Err("could not find configuration file".to_owned())
    } else {
        // if this fails the program should exit
        SETTINGS.write().unwrap().merge(config::File::with_name(path)).unwrap();

        match validate_config() {
            Err(x) => Err(x),
            Ok(_) => Ok(error::SUCCESS.code_num),
        }
    }
}

pub fn get_config_value(key: &str) -> Result<String, u32> {
    // if this fails the program should exit
    println!("here");
    let config = SETTINGS.read().unwrap();

    match config.get_str(key) {
        Err(_) => Err(error::INVALID_CONFIGURATION.code_num),
        Ok(value) => Ok(value),
    }
}

#[cfg(test)]
pub mod tests {
    use std::error::Error;
    use std::io::prelude::*;
    use std::fs;
    use super::*;

    #[test]
    fn test_invalid_config_value() {
        match get_config_value("garbage") {
            Err(x) => assert_eq!(x, error::INVALID_CONFIGURATION.code_num),
            Ok(v) => assert_eq!(v,"totalgarbage"), //if test gets here it will fail
        };
    }

    #[test]
    fn test_bad_path() {
        //test bad path
        let tmp_string = "garbage.txt";
        match process_config_file(&tmp_string) {
            Err(x) => assert_eq!(x,"could not find configuration file"),
            Ok(x) => assert_eq!(x,error::UNKNOWN_ERROR.code_num),  //if test gets here it will fail
        }
    }

    #[test]
    fn test_process_config_file() {
        let a = "a";
        let b = "b";

        let config_path = "/tmp/test_settings.json";
        let path = Path::new(config_path);

        let mut file = match fs::File::create(&path) {
            Err(why) => panic!("couldn't create sample config file: {}", why.description()),
            Ok(file) => file,
        };

        //throw in some invalid content to test the validation code
        let content = "{ \"a\" : \"a\", \"b\":\"b\", \"pool_name\":\"*98*\" }";

        match file.write_all(content.as_bytes()) {
            Err(why) => panic!("couldn't write to sample config file: {}", why.description()),
            Ok(_) => println!("sample config ready"),
        }

        match process_config_file(&config_path) {
            Err(_) => println!("expected invalid setting"),
            Ok(v) => assert_eq!(v, error::UNKNOWN_ERROR.code_num), //fail if we get here
        }

        match get_config_value(&a) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) => assert_eq!(v,a),
        };

        match get_config_value(&b) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) => assert_eq!(v,b),
        };

        // Leave file around or other concurrent tests will fail
        //fs::remove_file(config_path).unwrap();
    }

    #[test]
    fn test_invalid_url() {
        let a = "logo_url";

        let config_path = "/tmp/test_settings.json";
        let path = Path::new(config_path);

        let mut file = match fs::File::create(&path) {
            Err(why) => panic!("couldn't create sample config file: {}", why.description()),
            Ok(file) => file,
        };

        //throw in some invalid content to test the validation code
        let content = "{ \"logo_url\" : \"wrong_url\" }";

        match file.write_all(content.as_bytes()) {
            Err(why) => panic!("couldn't write to sample config file: {}", why.description()),
            Ok(_) => println!("sample config ready"),
        }

        match process_config_file(&config_path) {
            Err(v) => assert_eq!(v, "logo_url has invalid setting: wrong_url"),
            Ok(_) => println!("expected invalid URL"), //fail if we get here
        }
//        assert!(process_config_file(&config_path) == Err(ParseError::InvalidIpv6Address));
    }

    #[test]
    fn test_process_file_with_pairwise_configs() {
        let a = "agency_pairwise_did";
        let a_rtn = "72x8p4HubxzUK1dwxcc5FU";
        let b = "agent_pairwise_verkey";
        let b_rtn = "U22jM6Cea2YVixjWwHN9wq";
        let config_path = "/tmp/test_init.json";
        let path = Path::new(config_path);

        let mut file = match fs::File::create(&path) {
            Err(why) => panic!("couldn't create sample config file: {}", why.description()),
            Ok(file) => file,
        };

        let content = "{ \"agency_pairwise_did\" : \"72x8p4HubxzUK1dwxcc5FU\", \"agent_pairwise_did\" : \"UJGjM6Cea2YVixjWwHN9wq\", \
        \"enterprise_did_agency\" : \"RF3JM851T4EQmhh8CdagSP\", \"enterprise_did_agent\" : \"AB3JM851T4EQmhh8CdagSP\", \"enterprise_name\" : \"enterprise\",\
        \"logo_url\" : \"https://s19.postimg.org/ykyz4x8jn/evernym.png\", \"agency_pairwise_verkey\" : \"7118p4HubxzUK1dwxcc5FU\",\
        \"agent_pairwise_verkey\" : \"U22jM6Cea2YVixjWwHN9wq\"}";

        match file.write_all(content.as_bytes()) {
            Err(why) => panic!("couldn't write to sample config file: {}", why.description()),
            Ok(_) => println!("sample config ready"),
        }

        match process_config_file(&config_path) {
            Err(_) => println!("expected invalid setting"),
            Ok(v) => assert_eq!(v, error::SUCCESS.code_num), //fail if we get here
        }

        match get_config_value(&a) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) =>  assert_eq!(v,a_rtn),

        };

        match get_config_value(&b) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) =>  assert_eq!(v,b_rtn),
        };
    }
}
<commit_msg>took out print statement<commit_after>extern crate config;
extern crate url;

use std::collections::HashMap;
use config::Config;
use std::sync::RwLock;
use utils::error;
use std::path::Path;
use url::{Url, ParseError};

pub static CONFIG_POOL_NAME: &'static str = "pool_name";
pub static CONFIG_POOL_CONFIG_NAME: &'static str = "config_name";
pub static CONFIG_WALLET_NAME: &'static str = "wallet_name";
pub static CONFIG_WALLET_TYPE: &'static str = "wallet_type";
pub static CONFIG_AGENT_ENDPOINT: &'static str = "agent_endpoint";
pub static CONFIG_AGENCY_PAIRWISE_DID: &'static str = "agency_pairwise_did";
pub static CONFIG_AGENCY_PAIRWISE_VERKEY: &'static str = "agency_pairwise_verkey";
pub static CONFIG_AGENT_PAIRWISE_DID: &'static str = "agent_pairwise_did";
pub static CONFIG_AGENT_PAIRWISE_VERKEY: &'static str = "agent_pairwise_verkey";
pub static CONFIG_ENTERPRISE_DID_AGENCY: &'static str = "enterprise_did_agency";
pub static CONFIG_ENTERPRISE_DID_AGENT: &'static str = "enterprise_did_agent";
pub static CONFIG_ENTERPRISE_NAME: &'static str = "enterprise_name";
pub static CONFIG_LOGO_URL: &'static str = "logo_url";

lazy_static! {
    static ref SETTINGS: RwLock<Config> = RwLock::new(Config::default());
}

pub fn set_defaults() -> u32 {
    // if this fails the program should exit
    let mut settings = SETTINGS.write().unwrap();

    settings.set_default(CONFIG_POOL_NAME,"pool1");
    settings.set_default(CONFIG_POOL_CONFIG_NAME,"config1");
    settings.set_default(CONFIG_WALLET_NAME,"wallet1");
    settings.set_default(CONFIG_WALLET_TYPE,"default");
    settings.set_default(CONFIG_AGENT_ENDPOINT,"http://127.0.0.1:8080");
    settings.set_default(CONFIG_AGENCY_PAIRWISE_DID,"default1");
    settings.set_default(CONFIG_AGENCY_PAIRWISE_VERKEY,"default");
    settings.set_default(CONFIG_AGENT_PAIRWISE_DID,"default");
    settings.set_default(CONFIG_AGENT_PAIRWISE_VERKEY,"default");
    settings.set_default(CONFIG_ENTERPRISE_DID_AGENCY,"default");
    settings.set_default(CONFIG_ENTERPRISE_DID_AGENT,"default");
    settings.set_default(CONFIG_ENTERPRISE_NAME,"default");
    settings.set_default(CONFIG_LOGO_URL,"http://www.evernym.com");

    error::SUCCESS.code_num
}

fn is_valid(value: &str) -> bool {
    for c in value.chars() {
        if !c.is_alphanumeric() && c != '_' { return false;}
    }
    true
}

fn validate_config() -> Result<u32, String> {
    let mut error = String::new();

    //if this fails the program should exit
    let config: HashMap<String, String> = SETTINGS.read().unwrap().deserialize::<HashMap<String, String>>().unwrap();

    for setting in config.iter() {
        let mut valid = true;
        if setting.0 == CONFIG_POOL_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_POOL_CONFIG_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_WALLET_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENT_ENDPOINT {
            match Url::parse(setting.1) {
                Err(x) => valid = false,
                Ok(_) => valid = true,
            }
        } else if setting.0 == CONFIG_ENTERPRISE_DID_AGENCY && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_ENTERPRISE_DID_AGENT && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENCY_PAIRWISE_VERKEY && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENT_PAIRWISE_VERKEY && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENCY_PAIRWISE_DID && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_AGENT_PAIRWISE_DID && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_ENTERPRISE_NAME && !is_valid(setting.1) {
            valid = false;
        } else if setting.0 == CONFIG_LOGO_URL {
            match Url::parse(setting.1) {
                Err(x) => valid = false,
                Ok(_) => valid = true,
            }
        } else {
            //TODO: determine whether we should ignore invalid parameters
            //error.push_str(setting.0);
            //error.push_str("is invalid\n");
        }

        if !valid {
            error.push_str(setting.0);
            error.push_str(" has invalid setting: ");
            error.push_str(setting.1);
        }
    };

    if !error.is_empty() {
        Err(error.to_owned())
    } else {
        Ok(error::SUCCESS.code_num)
    }
}

pub fn process_config_file(path: &str) -> Result<u32, String> {

    if !Path::new(path).is_file() {
        Err("could not find configuration file".to_owned())
    } else {
        // if this fails the program should exit
        SETTINGS.write().unwrap().merge(config::File::with_name(path)).unwrap();

        match validate_config() {
            Err(x) => Err(x),
            Ok(_) => Ok(error::SUCCESS.code_num),
        }
    }
}

pub fn get_config_value(key: &str) -> Result<String, u32> {
    // if this fails the program should exit
    let config = SETTINGS.read().unwrap();

    match config.get_str(key) {
        Err(_) => Err(error::INVALID_CONFIGURATION.code_num),
        Ok(value) => Ok(value),
    }
}

#[cfg(test)]
pub mod tests {
    use std::error::Error;
    use std::io::prelude::*;
    use std::fs;
    use super::*;

    #[test]
    fn test_invalid_config_value() {
        match get_config_value("garbage") {
            Err(x) => assert_eq!(x, error::INVALID_CONFIGURATION.code_num),
            Ok(v) => assert_eq!(v,"totalgarbage"), //if test gets here it will fail
        };
    }

    #[test]
    fn test_bad_path() {
        //test bad path
        let tmp_string = "garbage.txt";
        match process_config_file(&tmp_string) {
            Err(x) => assert_eq!(x,"could not find configuration file"),
            Ok(x) => assert_eq!(x,error::UNKNOWN_ERROR.code_num),  //if test gets here it will fail
        }
    }

    #[test]
    fn test_process_config_file() {
        let a = "a";
        let b = "b";

        let config_path = "/tmp/test_settings.json";
        let path = Path::new(config_path);

        let mut file = match fs::File::create(&path) {
            Err(why) => panic!("couldn't create sample config file: {}", why.description()),
            Ok(file) => file,
        };

        //throw in some invalid content to test the validation code
        let content = "{ \"a\" : \"a\", \"b\":\"b\", \"pool_name\":\"*98*\" }";

        match file.write_all(content.as_bytes()) {
            Err(why) => panic!("couldn't write to sample config file: {}", why.description()),
            Ok(_) => println!("sample config ready"),
        }

        match process_config_file(&config_path) {
            Err(_) => println!("expected invalid setting"),
            Ok(v) => assert_eq!(v, error::UNKNOWN_ERROR.code_num), //fail if we get here
        }

        match get_config_value(&a) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) => assert_eq!(v,a),
        };

        match get_config_value(&b) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) => assert_eq!(v,b),
        };

        // Leave file around or other concurrent tests will fail
        //fs::remove_file(config_path).unwrap();
    }

    #[test]
    fn test_invalid_url() {
        let a = "logo_url";

        let config_path = "/tmp/test_settings.json";
        let path = Path::new(config_path);

        let mut file = match fs::File::create(&path) {
            Err(why) => panic!("couldn't create sample config file: {}", why.description()),
            Ok(file) => file,
        };

        //throw in some invalid content to test the validation code
        let content = "{ \"logo_url\" : \"wrong_url\" }";

        match file.write_all(content.as_bytes()) {
            Err(why) => panic!("couldn't write to sample config file: {}", why.description()),
            Ok(_) => println!("sample config ready"),
        }

        match process_config_file(&config_path) {
            Err(v) => assert_eq!(v, "logo_url has invalid setting: wrong_url"),
            Ok(_) => println!("expected invalid URL"), //fail if we get here
        }
//        assert!(process_config_file(&config_path) == Err(ParseError::InvalidIpv6Address));
    }

    #[test]
    fn test_process_file_with_pairwise_configs() {
        let a = "agency_pairwise_did";
        let a_rtn = "72x8p4HubxzUK1dwxcc5FU";
        let b = "agent_pairwise_verkey";
        let b_rtn = "U22jM6Cea2YVixjWwHN9wq";
        let config_path = "/tmp/test_init.json";
        let path = Path::new(config_path);

        let mut file = match fs::File::create(&path) {
            Err(why) => panic!("couldn't create sample config file: {}", why.description()),
            Ok(file) => file,
        };

        let content = "{ \"agency_pairwise_did\" : \"72x8p4HubxzUK1dwxcc5FU\", \"agent_pairwise_did\" : \"UJGjM6Cea2YVixjWwHN9wq\", \
        \"enterprise_did_agency\" : \"RF3JM851T4EQmhh8CdagSP\", \"enterprise_did_agent\" : \"AB3JM851T4EQmhh8CdagSP\", \"enterprise_name\" : \"enterprise\",\
        \"logo_url\" : \"https://s19.postimg.org/ykyz4x8jn/evernym.png\", \"agency_pairwise_verkey\" : \"7118p4HubxzUK1dwxcc5FU\",\
        \"agent_pairwise_verkey\" : \"U22jM6Cea2YVixjWwHN9wq\"}";

        match file.write_all(content.as_bytes()) {
            Err(why) => panic!("couldn't write to sample config file: {}", why.description()),
            Ok(_) => println!("sample config ready"),
        }

        match process_config_file(&config_path) {
            Err(_) => println!("expected invalid setting"),
            Ok(v) => assert_eq!(v, error::SUCCESS.code_num), //fail if we get here
        }

        match get_config_value(&a) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) =>  assert_eq!(v,a_rtn),

        };

        match get_config_value(&b) {
            Err(x) => assert_eq!(x, error::SUCCESS.code_num), //fail if we get here
            Ok(v) =>  assert_eq!(v,b_rtn),
        };
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::path::PathBuf;
use std::process::Child;
use std::process::Stdio;

use anyhow::{Context, Result};
use chrono::prelude::*;
use crossbeam_channel::Receiver;
use handlebars::Handlebars;
use log::{debug, error, info};

use pueue_lib::log::*;
use pueue_lib::network::message::*;
use pueue_lib::network::protocol::socket_cleanup;
use pueue_lib::state::{GroupStatus, SharedState};
use pueue_lib::task::{Task, TaskResult, TaskStatus};

use crate::pid::cleanup_pid_file;
use crate::platform::process_helper::*;
use crate::state_helper::{reset_state, save_state};

mod callback;
/// Logic for handling dependencies
mod dependencies;
/// Logic for finishing and cleaning up completed tasks.
mod finish_task;
/// This module contains all logic that's triggered by messages received via the mpsc channel.
/// These messages are sent by the threads that handle the client messages.
mod messages;
/// Everything regarding actually spawning task processes.
mod spawn_task;

/// This is a little helper macro, which looks at a critical result and shuts the
/// TaskHandler down, if an error occurred. This is mostly used if the state cannot.
/// be written due to IO errors.
/// Those errors are considered unrecoverable and we should initiate a graceful shutdown
/// immediately.
#[macro_export]
macro_rules! ok_or_shutdown {
    ($task_manager:ident, $result:expr) => {
        match $result {
            Err(err) => {
                error!(
                    "Initializing graceful shutdown. Encountered error in TaskManager: {}",
                    err
                );
                $task_manager.initiate_shutdown(Shutdown::Emergency);
                return;
            }
            Ok(inner) => inner,
        }
    };
}

pub struct TaskHandler {
    state: SharedState,
    receiver: Receiver<Message>,
    children: BTreeMap<usize, Child>,
    callbacks: Vec<Child>,
    full_reset: bool,
    shutdown: Option<Shutdown>,
    // Some static settings that are extracted from `state.settings` for convenience purposes.
    pueue_directory: PathBuf,
    callback: Option<String>,
    callback_log_lines: usize,
}

/// Pueue directly interacts with processes.
/// Since these interactions can vary depending on the current platform, this enum is introduced.
/// The intend is to keep any platform specific code out of the top level code.
/// Even if that implicates adding some layers of abstraction.
#[derive(Debug)]
pub enum ProcessAction {
    Pause,
    Resume,
}

impl TaskHandler {
    pub fn new(state: SharedState, receiver: Receiver<Message>) -> Self {
        // Extract some static settings we often need.
        // This prevents locking the State all the time.
        let (pueue_directory, callback, callback_log_lines) = {
            let state = state.lock().unwrap();
            (
                state.settings.shared.pueue_directory(),
                state.settings.daemon.callback.clone(),
                state.settings.daemon.callback_log_lines,
            )
        };

        TaskHandler {
            state,
            receiver,
            children: BTreeMap::new(),
            callbacks: Vec::new(),
            full_reset: false,
            shutdown: None,
            pueue_directory,
            callback,
            callback_log_lines,
        }
    }

    /// Main loop of the task handler.
    /// In here a few things happen:
    ///
    /// - Receive and handle instructions from the client.
    /// - Handle finished tasks, i.e. cleanup processes, update statuses.
    /// - Callback handling logic. This is rather uncritical.
    /// - Enqueue any stashed processes which are ready for being queued.
    /// - Ensure tasks with dependencies have no failed ancestors
    /// - Whether whe should perform a shutdown.
    /// - If the client requested a reset: reset the state if all children have been killed and handled.
    /// - Check whether we can spawn new tasks.
    pub fn run(&mut self) {
        loop {
            self.receive_messages();
            self.handle_finished_tasks();
            self.check_callbacks();
            self.enqueue_delayed_tasks();
            self.check_failed_dependencies();

            if self.shutdown.is_some() {
                // Check if we're in shutdown.
                // If all tasks are killed, we do some cleanup and exit.
                self.handle_shutdown();
            } else if self.full_reset {
                // Wait until all tasks are killed.
                // Once they are, reset everything and go back to normal
                self.handle_reset();
            } else {
                // Only start new tasks, if we aren't in the middle of a reset or shutdown.
                self.spawn_new();
            }
        }
    }

    /// Initiate shutdown, which includes killing all children and pausing all groups.
    /// We don't have to pause any groups, as no new tasks will be spawned during shutdown anyway.
    /// Any groups with queued tasks, will be automatically paused on state-restoration.
    fn initiate_shutdown(&mut self, shutdown: Shutdown) {
        self.shutdown = Some(shutdown);

        self.kill(vec![], String::new(), true, false, false, None);
    }

    /// Check if all tasks are killed.
    /// If they aren't, we'll wait a little longer.
    /// Once they're, we do some cleanup and exit.
    fn handle_shutdown(&mut self) {
        // There are still active tasks. Continue waiting.
        if !self.children.is_empty() {
            return;
        }

        // Lock the state. This prevents any further connections/alterations from this point on.
        let state = self.state.lock().unwrap();

        // Remove the unix socket.
        if let Err(error) = socket_cleanup(&state.settings.shared) {
            println!("Failed to cleanup socket during shutdown.");
            println!("{}", error);
        }

        // Cleanup the pid file
        if let Err(error) = cleanup_pid_file(&self.pueue_directory) {
            println!("Failed to cleanup pid during shutdown.");
            println!("{}", error);
        }

        // Actually exit the program the way we're supposed to.
        // Depending on the current shutdown type, we exit with different exit codes.
        if matches!(self.shutdown, Some(Shutdown::Emergency)) {
            std::process::exit(1);
        }
        std::process::exit(0);
    }

    /// Users can issue to reset the daemon.
    /// If that's the case, the `self.full_reset` flag is set to true, all children are killed
    /// and no new tasks will be spawned.
    /// This function checks, if all killed children have been handled.
    /// If that's the case, completely reset the state
    fn handle_reset(&mut self) {
        // Don't do any reset logic, if we aren't in reset mode or if some children are still up.
        if !self.children.is_empty() {
            return;
        }

        let mut state = self.state.lock().unwrap();
        if let Err(error) = reset_state(&mut state) {
            error!("Failed to reset state with error: {:?}", error);
        };

        if let Err(error) = reset_task_log_directory(&self.pueue_directory) {
            panic!("Error while resetting task log directory: {}", error);
        };
        self.full_reset = false;
    }

    /// Kill all children by using the `kill` function.
    /// Set the respective group's statuses to `Reset`. This will prevent new tasks from being spawned.
    fn reset(&mut self, kill_children: bool) {
        self.full_reset = true;
        self.kill(vec![], String::new(), true, kill_children, false, None);
    }

    /// As time passes, some delayed tasks may need to be enqueued.
    /// Gather all stashed tasks and enqueue them if it is after the task's enqueue_at
    fn enqueue_delayed_tasks(&mut self) {
        let state_clone = self.state.clone();
        let mut state = state_clone.lock().unwrap();

        let mut changed = false;
        for (_, task) in state.tasks.iter_mut() {
            if let TaskStatus::Stashed {
                enqueue_at: Some(time),
            } = task.status
            {
                if time <= Local::now() {
                    info!("Enqueuing delayed task : {}", task.id);

                    task.status = TaskStatus::Queued;
                    changed = true;
                }
            }
        }
        // Save the state if a task has been enqueued
        if changed {
            ok_or_shutdown!(self, save_state(&state));
        }
    }

    /// This is a small wrapper around the real platform dependant process handling logic
    /// It only ensures, that the process we want to manipulate really does exists.
    fn perform_action(&mut self, id: usize, action: ProcessAction, children: bool) -> Result<bool> {
        match self.children.get(&id) {
            Some(child) => {
                debug!("Executing action {:?} to {}", action, id);
                run_action_on_child(child, &action, children)?;

                Ok(true)
            }
            None => {
                error!(
                    "Tried to execute action {:?} to non existing task {}",
                    action, id
                );
                Ok(false)
            }
        }
    }
}
<commit_msg>docs: Add infos about TaskHandler properties<commit_after>use std::collections::{BTreeMap, HashMap};
use std::path::PathBuf;
use std::process::Child;
use std::process::Stdio;

use anyhow::{Context, Result};
use chrono::prelude::*;
use crossbeam_channel::Receiver;
use handlebars::Handlebars;
use log::{debug, error, info};

use pueue_lib::log::*;
use pueue_lib::network::message::*;
use pueue_lib::network::protocol::socket_cleanup;
use pueue_lib::state::{GroupStatus, SharedState};
use pueue_lib::task::{Task, TaskResult, TaskStatus};

use crate::pid::cleanup_pid_file;
use crate::platform::process_helper::*;
use crate::state_helper::{reset_state, save_state};

mod callback;
/// Logic for handling dependencies
mod dependencies;
/// Logic for finishing and cleaning up completed tasks.
mod finish_task;
/// This module contains all logic that's triggered by messages received via the mpsc channel.
/// These messages are sent by the threads that handle the client messages.
mod messages;
/// Everything regarding actually spawning task processes.
mod spawn_task;

/// This is a little helper macro, which looks at a critical result and shuts the
/// TaskHandler down, if an error occurred. This is mostly used if the state cannot.
/// be written due to IO errors.
/// Those errors are considered unrecoverable and we should initiate a graceful shutdown
/// immediately.
#[macro_export]
macro_rules! ok_or_shutdown {
    ($task_manager:ident, $result:expr) => {
        match $result {
            Err(err) => {
                error!(
                    "Initializing graceful shutdown. Encountered error in TaskManager: {}",
                    err
                );
                $task_manager.initiate_shutdown(Shutdown::Emergency);
                return;
            }
            Ok(inner) => inner,
        }
    };
}

pub struct TaskHandler {
    /// The state that's shared between the TaskHandler and the message handling logic.
    state: SharedState,
    /// The receiver for the MPSC channel that's used to push notificatoins from our message
    /// handling to the TaskHandler.
    receiver: Receiver<Message>,
    /// A map of `task_id` to "spawned task subprocess handle".
    children: BTreeMap<usize, Child>,
    /// These are the currently running callbacks. They're usually very short-lived.
    callbacks: Vec<Child>,
    /// A simple flag which is used to signal that we're currently doing a full reset of the daemon.
    /// This flag prevents new tasks from being spawned.
    full_reset: bool,
    /// Whether we're currently in the process of a graceful shutdown.
    /// Depending on the shutdown type, we're exiting with different exitcodes.
    shutdown: Option<Shutdown>,

    // Some static settings that are extracted from `state.settings` for convenience purposes.
    pueue_directory: PathBuf,
    callback: Option<String>,
    callback_log_lines: usize,
}

/// Pueue directly interacts with processes.
/// Since these interactions can vary depending on the current platform, this enum is introduced.
/// The intend is to keep any platform specific code out of the top level code.
/// Even if that implicates adding some layers of abstraction.
#[derive(Debug)]
pub enum ProcessAction {
    Pause,
    Resume,
}

impl TaskHandler {
    pub fn new(state: SharedState, receiver: Receiver<Message>) -> Self {
        // Extract some static settings we often need.
        // This prevents locking the State all the time.
        let (pueue_directory, callback, callback_log_lines) = {
            let state = state.lock().unwrap();
            (
                state.settings.shared.pueue_directory(),
                state.settings.daemon.callback.clone(),
                state.settings.daemon.callback_log_lines,
            )
        };

        TaskHandler {
            state,
            receiver,
            children: BTreeMap::new(),
            callbacks: Vec::new(),
            full_reset: false,
            shutdown: None,
            pueue_directory,
            callback,
            callback_log_lines,
        }
    }

    /// Main loop of the task handler.
    /// In here a few things happen:
    ///
    /// - Receive and handle instructions from the client.
    /// - Handle finished tasks, i.e. cleanup processes, update statuses.
    /// - Callback handling logic. This is rather uncritical.
    /// - Enqueue any stashed processes which are ready for being queued.
    /// - Ensure tasks with dependencies have no failed ancestors
    /// - Whether whe should perform a shutdown.
    /// - If the client requested a reset: reset the state if all children have been killed and handled.
    /// - Check whether we can spawn new tasks.
    pub fn run(&mut self) {
        loop {
            self.receive_messages();
            self.handle_finished_tasks();
            self.check_callbacks();
            self.enqueue_delayed_tasks();
            self.check_failed_dependencies();

            if self.shutdown.is_some() {
                // Check if we're in shutdown.
                // If all tasks are killed, we do some cleanup and exit.
                self.handle_shutdown();
            } else if self.full_reset {
                // Wait until all tasks are killed.
                // Once they are, reset everything and go back to normal
                self.handle_reset();
            } else {
                // Only start new tasks, if we aren't in the middle of a reset or shutdown.
                self.spawn_new();
            }
        }
    }

    /// Initiate shutdown, which includes killing all children and pausing all groups.
    /// We don't have to pause any groups, as no new tasks will be spawned during shutdown anyway.
    /// Any groups with queued tasks, will be automatically paused on state-restoration.
    fn initiate_shutdown(&mut self, shutdown: Shutdown) {
        self.shutdown = Some(shutdown);

        self.kill(vec![], String::new(), true, false, false, None);
    }

    /// Check if all tasks are killed.
    /// If they aren't, we'll wait a little longer.
    /// Once they're, we do some cleanup and exit.
    fn handle_shutdown(&mut self) {
        // There are still active tasks. Continue waiting.
        if !self.children.is_empty() {
            return;
        }

        // Lock the state. This prevents any further connections/alterations from this point on.
        let state = self.state.lock().unwrap();

        // Remove the unix socket.
        if let Err(error) = socket_cleanup(&state.settings.shared) {
            println!("Failed to cleanup socket during shutdown.");
            println!("{}", error);
        }

        // Cleanup the pid file
        if let Err(error) = cleanup_pid_file(&self.pueue_directory) {
            println!("Failed to cleanup pid during shutdown.");
            println!("{}", error);
        }

        // Actually exit the program the way we're supposed to.
        // Depending on the current shutdown type, we exit with different exit codes.
        if matches!(self.shutdown, Some(Shutdown::Emergency)) {
            std::process::exit(1);
        }
        std::process::exit(0);
    }

    /// Users can issue to reset the daemon.
    /// If that's the case, the `self.full_reset` flag is set to true, all children are killed
    /// and no new tasks will be spawned.
    /// This function checks, if all killed children have been handled.
    /// If that's the case, completely reset the state
    fn handle_reset(&mut self) {
        // Don't do any reset logic, if we aren't in reset mode or if some children are still up.
        if !self.children.is_empty() {
            return;
        }

        let mut state = self.state.lock().unwrap();
        if let Err(error) = reset_state(&mut state) {
            error!("Failed to reset state with error: {:?}", error);
        };

        if let Err(error) = reset_task_log_directory(&self.pueue_directory) {
            panic!("Error while resetting task log directory: {}", error);
        };
        self.full_reset = false;
    }

    /// Kill all children by using the `kill` function.
    /// Set the respective group's statuses to `Reset`. This will prevent new tasks from being spawned.
    fn reset(&mut self, kill_children: bool) {
        self.full_reset = true;
        self.kill(vec![], String::new(), true, kill_children, false, None);
    }

    /// As time passes, some delayed tasks may need to be enqueued.
    /// Gather all stashed tasks and enqueue them if it is after the task's enqueue_at
    fn enqueue_delayed_tasks(&mut self) {
        let state_clone = self.state.clone();
        let mut state = state_clone.lock().unwrap();

        let mut changed = false;
        for (_, task) in state.tasks.iter_mut() {
            if let TaskStatus::Stashed {
                enqueue_at: Some(time),
            } = task.status
            {
                if time <= Local::now() {
                    info!("Enqueuing delayed task : {}", task.id);

                    task.status = TaskStatus::Queued;
                    changed = true;
                }
            }
        }
        // Save the state if a task has been enqueued
        if changed {
            ok_or_shutdown!(self, save_state(&state));
        }
    }

    /// This is a small wrapper around the real platform dependant process handling logic
    /// It only ensures, that the process we want to manipulate really does exists.
    fn perform_action(&mut self, id: usize, action: ProcessAction, children: bool) -> Result<bool> {
        match self.children.get(&id) {
            Some(child) => {
                debug!("Executing action {:?} to {}", action, id);
                run_action_on_child(child, &action, children)?;

                Ok(true)
            }
            None => {
                error!(
                    "Tried to execute action {:?} to non existing task {}",
                    action, id
                );
                Ok(false)
            }
        }
    }
}
<|endoftext|>