<commit_before>use rustc_index::vec::Idx;
use rustc_middle::mir::*;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_target::abi::VariantIdx;

use std::iter::TrustedLen;

/// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields.
///
/// Produces something like
///
/// (lhs as Variant).field0 = arg0;     // We only have a downcast if this is an enum
/// (lhs as Variant).field1 = arg1;
/// discriminant(lhs) = variant_index;  // If lhs is an enum or generator.
pub fn expand_aggregate<'tcx>(
    mut lhs: Place<'tcx>,
    operands: impl Iterator<Item = (Operand<'tcx>, Ty<'tcx>)> + TrustedLen,
    kind: AggregateKind<'tcx>,
    source_info: SourceInfo,
    tcx: TyCtxt<'tcx>,
) -> impl Iterator<Item = Statement<'tcx>> + TrustedLen {
    let mut set_discriminant = None;
    let active_field_index = match kind {
        AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
            if adt_def.is_enum() {
                set_discriminant = Some(Statement {
                    kind: StatementKind::SetDiscriminant { place: box (lhs), variant_index },
                    source_info,
                });
                lhs = tcx.mk_place_downcast(lhs, adt_def, variant_index);
            }
            active_field_index
        }
        AggregateKind::Generator(..) => {
            // Right now we only support initializing generators to
            // variant 0 (Unresumed).
            let variant_index = VariantIdx::new(0);
            set_discriminant = Some(Statement {
                kind: StatementKind::SetDiscriminant { place: box (lhs), variant_index },
                source_info,
            });

            // Operands are upvars stored on the base place, so no
            // downcast is necessary.

            None
        }
        _ => None,
    };

    operands
        .enumerate()
        .map(move |(i, (op, ty))| {
            let lhs_field = if let AggregateKind::Array(_) = kind {
                let offset = i as u64;
                assert_eq!(offset as usize, i);
                tcx.mk_place_elem(
                    lhs,
                    ProjectionElem::ConstantIndex {
                        offset,
                        min_length: offset + 1,
                        from_end: false,
                    },
                )
            } else {
                let field = Field::new(active_field_index.unwrap_or(i));
                tcx.mk_place_field(lhs, field, ty)
            };
            Statement { source_info, kind: StatementKind::Assign(box (lhs_field, Rvalue::Use(op))) }
        })
        .chain(set_discriminant)
}
<commit_msg>removed not-needed conversion<commit_after>use rustc_index::vec::Idx;
use rustc_middle::mir::*;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_target::abi::VariantIdx;

use std::iter::TrustedLen;

/// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields.
///
/// Produces something like
///
/// (lhs as Variant).field0 = arg0;     // We only have a downcast if this is an enum
/// (lhs as Variant).field1 = arg1;
/// discriminant(lhs) = variant_index;  // If lhs is an enum or generator.
pub fn expand_aggregate<'tcx>(
    mut lhs: Place<'tcx>,
    operands: impl Iterator<Item = (Operand<'tcx>, Ty<'tcx>)> + TrustedLen,
    kind: AggregateKind<'tcx>,
    source_info: SourceInfo,
    tcx: TyCtxt<'tcx>,
) -> impl Iterator<Item = Statement<'tcx>> + TrustedLen {
    let mut set_discriminant = None;
    let active_field_index = match kind {
        AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
            if adt_def.is_enum() {
                set_discriminant = Some(Statement {
                    kind: StatementKind::SetDiscriminant { place: box (lhs), variant_index },
                    source_info,
                });
                lhs = tcx.mk_place_downcast(lhs, adt_def, variant_index);
            }
            active_field_index
        }
        AggregateKind::Generator(..) => {
            // Right now we only support initializing generators to
            // variant 0 (Unresumed).
            let variant_index = VariantIdx::new(0);
            set_discriminant = Some(Statement {
                kind: StatementKind::SetDiscriminant { place: box (lhs), variant_index },
                source_info,
            });

            // Operands are upvars stored on the base place, so no
            // downcast is necessary.

            None
        }
        _ => None,
    };

    operands
        .enumerate()
        .map(move |(i, (op, ty))| {
            let lhs_field = if let AggregateKind::Array(_) = kind {
                let offset = u64::try_from(i).unwrap();
                tcx.mk_place_elem(
                    lhs,
                    ProjectionElem::ConstantIndex {
                        offset,
                        min_length: offset + 1,
                        from_end: false,
                    },
                )
            } else {
                let field = Field::new(active_field_index.unwrap_or(i));
                tcx.mk_place_field(lhs, field, ty)
            };
            Statement { source_info, kind: StatementKind::Assign(box (lhs_field, Rvalue::Use(op))) }
        })
        .chain(set_discriminant)
}
<|endoftext|>


<commit_before>// Copyright (c) 2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use thiserror::Error;

use std::sync::Arc;

use crate::api::{ChromaSampling, Context, ContextInner};
use crate::cpu_features::CpuFeatureLevel;
use crate::rayon::{ThreadPool, ThreadPoolBuilder};
use crate::tiling::TilingInfo;
use crate::util::Pixel;

mod encoder;
pub use encoder::*;

mod rate;
pub use rate::Error as RateControlError;
pub use rate::{RateControlConfig, RateControlSummary};

mod speedsettings;
pub use speedsettings::*;

/// Enumeration of possible invalid configuration errors.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
#[non_exhaustive]
pub enum InvalidConfig {
  /// The width is invalid.
  #[error("invalid width {0} (expected >= 16, <= 65535)")]
  InvalidWidth(usize),
  /// The height is invalid.
  #[error("invalid height {0} (expected >= 16, <= 65535)")]
  InvalidHeight(usize),
  /// Aspect ratio numerator is invalid.
  #[error("invalid aspect ratio numerator {0} (expected > 0)")]
  InvalidAspectRatioNum(usize),
  /// Aspect ratio denominator is invalid.
  #[error("invalid aspect ratio denominator {0} (expected > 0)")]
  InvalidAspectRatioDen(usize),
  /// The render width (width adjusted based on the aspect ratio) is invalid.
  #[error("invalid render width {0} (expected >= 1, <= 65535")]
  InvalidRenderWidth(usize),
  /// The render height (height adjusted based on the aspect ratio) is invalid.
  #[error("invalid render height {0} (expected >= 1, <= 65535")]
  InvalidRenderHeight(usize),
  /// RDO lookahead frame count is invalid.
  #[error(
    "invalid rdo lookahead frames {actual} (expected <= {max} and >= {min})"
  )]
  InvalidRdoLookaheadFrames {
    /// The actual value.
    actual: usize,
    /// The maximal supported value.
    max: usize,
    /// The minimal supported value.
    min: usize,
  },
  /// Maximal keyframe interval is invalid.
  #[error("invalid max keyframe interval {actual} (expected <= {max})")]
  InvalidMaxKeyFrameInterval {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Tile columns is invalid.
  #[error("invalid tile cols {0} (expected power of 2)")]
  InvalidTileCols(usize),
  /// Tile rows is invalid.
  #[error("invalid tile rows {0} (expected power of 2)")]
  InvalidTileRows(usize),
  /// Framerate numerator is invalid.
  #[error("invalid framerate numerator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateNum {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Framerate denominator is invalid.
  #[error("invalid framerate denominator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateDen {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Reservoir frame delay is invalid.
  #[error("invalid reservoir frame delay {0} (expected >= 12, <= 131072)")]
  InvalidReservoirFrameDelay(i32),
  /// Reservoir frame delay is invalid.
  #[error(
    "invalid switch frame interval {0} (must only be used with low latency mode)"
  )]
  InvalidSwitchFrameInterval(u64),

  /// An option unsupported in still picture mode was enabled along with it.
  #[error("invalid option {0} specified with still picture mode")]
  InvalidOptionWithStillPicture(&'static str),

  /// The rate control needs a target bitrate in order to produce results
  #[error("The rate control requires a target bitrate")]
  TargetBitrateNeeded,

  /// The configuration
  #[error("Mismatch in the rate control configuration")]
  RateControlConfigurationMismatch,
}

/// Contains the encoder configuration.
#[derive(Clone, Debug, Default)]
pub struct Config {
  /// Settings which impact the produced bitstream.
  pub(crate) enc: EncoderConfig,
  /// Rate control configuration
  pub(crate) rate_control: RateControlConfig,
  /// The number of threads in the threadpool.
  pub(crate) threads: usize,
  /// Shared thread pool
  pub(crate) pool: Option<Arc<ThreadPool>>,
}

impl Config {
  /// Create a default configuration
  ///
  /// same as Default::default()
  pub fn new() -> Self {
    Config::default()
  }

  /// Set the encoder configuration
  ///
  /// EncoderConfig contains the settings impacting the
  /// codec features used in the produced bitstream.
  pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
    self.enc = enc;
    self
  }

  /// Set the number of workers in the threadpool
  ///
  /// The threadpool is shared across all the different parallel
  /// components in the encoder.
  pub fn with_threads(mut self, threads: usize) -> Self {
    self.threads = threads;
    self
  }

  /// Set the rate control configuration
  ///
  /// The default configuration is single pass
  pub fn with_rate_control(mut self, rate_control: RateControlConfig) -> Self {
    self.rate_control = rate_control;
    self
  }

  #[cfg(feature = "unstable")]
  /// Use the provided threadpool
  pub fn with_thread_pool(mut self, pool: Arc<ThreadPool>) -> Self {
    self.pool = Some(pool);
    self
  }
}

fn check_tile_log2(n: usize) -> bool {
  let tile_log2 = TilingInfo::tile_log2(1, n);
  if tile_log2.is_none() {
    return false;
  }
  let tile_log2 = tile_log2.unwrap();

  ((1 << tile_log2) - n) == 0 || n == 0
}

impl Config {
  pub(crate) fn new_inner<T: Pixel>(
    &self,
  ) -> Result<ContextInner<T>, InvalidConfig> {
    assert!(
      8 * std::mem::size_of::<T>() >= self.enc.bit_depth,
      "The Pixel u{} does not match the Config bit_depth {}",
      8 * std::mem::size_of::<T>(),
      self.enc.bit_depth
    );

    self.validate()?;

    // Because we don't have a FrameInvariants yet,
    // this is the only way to get the CpuFeatureLevel in use.
    // Since we only call this once, this shouldn't cause
    // performance issues.
    info!("CPU Feature Level: {}", CpuFeatureLevel::default());

    let mut config = self.enc;
    config.set_key_frame_interval(
      config.min_key_frame_interval,
      config.max_key_frame_interval,
    );

    // FIXME: inter unsupported with 4:2:2 and 4:4:4 chroma sampling
    let chroma_sampling = config.chroma_sampling;

    // FIXME: tx partition for intra not supported for chroma 422
    if chroma_sampling == ChromaSampling::Cs422 {
      config.speed_settings.rdo_tx_decision = false;
    }

    let mut inner = ContextInner::new(&config);

    if self.rate_control.emit_pass_data {
      let params = inner.rc_state.get_twopass_out_params(&inner, 0);
      inner.rc_state.init_first_pass(params.pass1_log_base_q);
    }

    if let Some(ref s) = self.rate_control.summary {
      inner.rc_state.init_second_pass();
      inner.rc_state.setup_second_pass(s);
    }

    Ok(inner)
  }
  /// Creates a [`Context`] with this configuration.
  ///
  /// # Examples
  ///
  /// ```
  /// use rav1e::prelude::*;
  ///
  /// # fn main() -> Result<(), InvalidConfig> {
  /// let cfg = Config::default();
  /// let ctx: Context<u8> = cfg.new_context()?;
  /// # Ok(())
  /// # }
  /// ```
  ///
  /// [`Context`]: struct.Context.html
  pub fn new_context<T: Pixel>(&self) -> Result<Context<T>, InvalidConfig> {
    let inner = self.new_inner()?;
    let config = inner.config;
    let pool = if let Some(ref p) = self.pool {
      p.clone()
    } else {
      let pool =
        ThreadPoolBuilder::new().num_threads(self.threads).build().unwrap();
      Arc::new(pool)
    };

    Ok(Context { is_flushing: false, inner, pool, config })
  }

  /// Validates the configuration.
  pub fn validate(&self) -> Result<(), InvalidConfig> {
    use InvalidConfig::*;

    let config = &self.enc;

    if config.width < 16 || config.width > u16::max_value() as usize {
      return Err(InvalidWidth(config.width));
    }
    if config.height < 16 || config.height > u16::max_value() as usize {
      return Err(InvalidHeight(config.height));
    }

    if config.sample_aspect_ratio.num == 0 {
      return Err(InvalidAspectRatioNum(
        config.sample_aspect_ratio.num as usize,
      ));
    }
    if config.sample_aspect_ratio.den == 0 {
      return Err(InvalidAspectRatioDen(
        config.sample_aspect_ratio.den as usize,
      ));
    }

    let (render_width, render_height) = config.render_size();
    if render_width == 0 || render_width > u16::max_value() as usize {
      return Err(InvalidRenderWidth(render_width));
    }
    if render_height == 0 || render_height > u16::max_value() as usize {
      return Err(InvalidRenderHeight(render_height));
    }

    if config.rdo_lookahead_frames > MAX_RDO_LOOKAHEAD_FRAMES
      || config.rdo_lookahead_frames < 1
    {
      return Err(InvalidRdoLookaheadFrames {
        actual: config.rdo_lookahead_frames,
        max: MAX_RDO_LOOKAHEAD_FRAMES,
        min: 1,
      });
    }
    if config.max_key_frame_interval > MAX_MAX_KEY_FRAME_INTERVAL {
      return Err(InvalidMaxKeyFrameInterval {
        actual: config.max_key_frame_interval,
        max: MAX_MAX_KEY_FRAME_INTERVAL,
      });
    }

    if !check_tile_log2(config.tile_cols) {
      return Err(InvalidTileCols(config.tile_cols));
    }
    if !check_tile_log2(config.tile_rows) {
      return Err(InvalidTileRows(config.tile_rows));
    }

    if config.time_base.num == 0
      || config.time_base.num > u32::max_value() as u64
    {
      return Err(InvalidFrameRateNum {
        actual: config.time_base.num,
        max: u32::max_value() as u64,
      });
    }
    if config.time_base.den == 0
      || config.time_base.den > u32::max_value() as u64
    {
      return Err(InvalidFrameRateDen {
        actual: config.time_base.den,
        max: u32::max_value() as u64,
      });
    }

    if let Some(delay) = config.reservoir_frame_delay {
      if delay < 12 || delay > 131_072 {
        return Err(InvalidReservoirFrameDelay(delay));
      }
    }

    if config.switch_frame_interval > 0 && !config.low_latency {
      return Err(InvalidSwitchFrameInterval(config.switch_frame_interval));
    }

    if config.enable_timing_info && config.still_picture {
      return Err(InvalidOptionWithStillPicture("enable_timing_info"));
    }

    // TODO: add more validation
    let rc = &self.rate_control;

    if (rc.emit_pass_data || rc.summary.is_some()) && config.bitrate == 0 {
      return Err(TargetBitrateNeeded);
    }

    Ok(())
  }
}
<commit_msg>Change the minimum frame size from 16x16 to 1x1 for still picture mode<commit_after>// Copyright (c) 2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use thiserror::Error;

use std::sync::Arc;

use crate::api::{ChromaSampling, Context, ContextInner};
use crate::cpu_features::CpuFeatureLevel;
use crate::rayon::{ThreadPool, ThreadPoolBuilder};
use crate::tiling::TilingInfo;
use crate::util::Pixel;

mod encoder;
pub use encoder::*;

mod rate;
pub use rate::Error as RateControlError;
pub use rate::{RateControlConfig, RateControlSummary};

mod speedsettings;
pub use speedsettings::*;

/// Enumeration of possible invalid configuration errors.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
#[non_exhaustive]
pub enum InvalidConfig {
  /// The width is invalid.
  #[error("invalid width {0} (expected >= 16, <= 65535)")]
  InvalidWidth(usize),
  /// The height is invalid.
  #[error("invalid height {0} (expected >= 16, <= 65535)")]
  InvalidHeight(usize),
  /// Aspect ratio numerator is invalid.
  #[error("invalid aspect ratio numerator {0} (expected > 0)")]
  InvalidAspectRatioNum(usize),
  /// Aspect ratio denominator is invalid.
  #[error("invalid aspect ratio denominator {0} (expected > 0)")]
  InvalidAspectRatioDen(usize),
  /// The render width (width adjusted based on the aspect ratio) is invalid.
  #[error("invalid render width {0} (expected >= 1, <= 65535")]
  InvalidRenderWidth(usize),
  /// The render height (height adjusted based on the aspect ratio) is invalid.
  #[error("invalid render height {0} (expected >= 1, <= 65535")]
  InvalidRenderHeight(usize),
  /// RDO lookahead frame count is invalid.
  #[error(
    "invalid rdo lookahead frames {actual} (expected <= {max} and >= {min})"
  )]
  InvalidRdoLookaheadFrames {
    /// The actual value.
    actual: usize,
    /// The maximal supported value.
    max: usize,
    /// The minimal supported value.
    min: usize,
  },
  /// Maximal keyframe interval is invalid.
  #[error("invalid max keyframe interval {actual} (expected <= {max})")]
  InvalidMaxKeyFrameInterval {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Tile columns is invalid.
  #[error("invalid tile cols {0} (expected power of 2)")]
  InvalidTileCols(usize),
  /// Tile rows is invalid.
  #[error("invalid tile rows {0} (expected power of 2)")]
  InvalidTileRows(usize),
  /// Framerate numerator is invalid.
  #[error("invalid framerate numerator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateNum {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Framerate denominator is invalid.
  #[error("invalid framerate denominator {actual} (expected > 0, <= {max})")]
  InvalidFrameRateDen {
    /// The actual value.
    actual: u64,
    /// The maximal supported value.
    max: u64,
  },
  /// Reservoir frame delay is invalid.
  #[error("invalid reservoir frame delay {0} (expected >= 12, <= 131072)")]
  InvalidReservoirFrameDelay(i32),
  /// Reservoir frame delay is invalid.
  #[error(
    "invalid switch frame interval {0} (must only be used with low latency mode)"
  )]
  InvalidSwitchFrameInterval(u64),

  /// An option unsupported in still picture mode was enabled along with it.
  #[error("invalid option {0} specified with still picture mode")]
  InvalidOptionWithStillPicture(&'static str),

  /// The rate control needs a target bitrate in order to produce results
  #[error("The rate control requires a target bitrate")]
  TargetBitrateNeeded,

  /// The configuration
  #[error("Mismatch in the rate control configuration")]
  RateControlConfigurationMismatch,
}

/// Contains the encoder configuration.
#[derive(Clone, Debug, Default)]
pub struct Config {
  /// Settings which impact the produced bitstream.
  pub(crate) enc: EncoderConfig,
  /// Rate control configuration
  pub(crate) rate_control: RateControlConfig,
  /// The number of threads in the threadpool.
  pub(crate) threads: usize,
  /// Shared thread pool
  pub(crate) pool: Option<Arc<ThreadPool>>,
}

impl Config {
  /// Create a default configuration
  ///
  /// same as Default::default()
  pub fn new() -> Self {
    Config::default()
  }

  /// Set the encoder configuration
  ///
  /// EncoderConfig contains the settings impacting the
  /// codec features used in the produced bitstream.
  pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
    self.enc = enc;
    self
  }

  /// Set the number of workers in the threadpool
  ///
  /// The threadpool is shared across all the different parallel
  /// components in the encoder.
  pub fn with_threads(mut self, threads: usize) -> Self {
    self.threads = threads;
    self
  }

  /// Set the rate control configuration
  ///
  /// The default configuration is single pass
  pub fn with_rate_control(mut self, rate_control: RateControlConfig) -> Self {
    self.rate_control = rate_control;
    self
  }

  #[cfg(feature = "unstable")]
  /// Use the provided threadpool
  pub fn with_thread_pool(mut self, pool: Arc<ThreadPool>) -> Self {
    self.pool = Some(pool);
    self
  }
}

fn check_tile_log2(n: usize) -> bool {
  let tile_log2 = TilingInfo::tile_log2(1, n);
  if tile_log2.is_none() {
    return false;
  }
  let tile_log2 = tile_log2.unwrap();

  ((1 << tile_log2) - n) == 0 || n == 0
}

impl Config {
  pub(crate) fn new_inner<T: Pixel>(
    &self,
  ) -> Result<ContextInner<T>, InvalidConfig> {
    assert!(
      8 * std::mem::size_of::<T>() >= self.enc.bit_depth,
      "The Pixel u{} does not match the Config bit_depth {}",
      8 * std::mem::size_of::<T>(),
      self.enc.bit_depth
    );

    self.validate()?;

    // Because we don't have a FrameInvariants yet,
    // this is the only way to get the CpuFeatureLevel in use.
    // Since we only call this once, this shouldn't cause
    // performance issues.
    info!("CPU Feature Level: {}", CpuFeatureLevel::default());

    let mut config = self.enc;
    config.set_key_frame_interval(
      config.min_key_frame_interval,
      config.max_key_frame_interval,
    );

    // FIXME: inter unsupported with 4:2:2 and 4:4:4 chroma sampling
    let chroma_sampling = config.chroma_sampling;

    // FIXME: tx partition for intra not supported for chroma 422
    if chroma_sampling == ChromaSampling::Cs422 {
      config.speed_settings.rdo_tx_decision = false;
    }

    let mut inner = ContextInner::new(&config);

    if self.rate_control.emit_pass_data {
      let params = inner.rc_state.get_twopass_out_params(&inner, 0);
      inner.rc_state.init_first_pass(params.pass1_log_base_q);
    }

    if let Some(ref s) = self.rate_control.summary {
      inner.rc_state.init_second_pass();
      inner.rc_state.setup_second_pass(s);
    }

    Ok(inner)
  }
  /// Creates a [`Context`] with this configuration.
  ///
  /// # Examples
  ///
  /// ```
  /// use rav1e::prelude::*;
  ///
  /// # fn main() -> Result<(), InvalidConfig> {
  /// let cfg = Config::default();
  /// let ctx: Context<u8> = cfg.new_context()?;
  /// # Ok(())
  /// # }
  /// ```
  ///
  /// [`Context`]: struct.Context.html
  pub fn new_context<T: Pixel>(&self) -> Result<Context<T>, InvalidConfig> {
    let inner = self.new_inner()?;
    let config = inner.config;
    let pool = if let Some(ref p) = self.pool {
      p.clone()
    } else {
      let pool =
        ThreadPoolBuilder::new().num_threads(self.threads).build().unwrap();
      Arc::new(pool)
    };

    Ok(Context { is_flushing: false, inner, pool, config })
  }

  /// Validates the configuration.
  pub fn validate(&self) -> Result<(), InvalidConfig> {
    use InvalidConfig::*;

    let config = &self.enc;

    if (config.still_picture && config.width < 1)
      || (!config.still_picture && config.width < 16)
      || config.width > u16::max_value() as usize
    {
      return Err(InvalidWidth(config.width));
    }
    if (config.still_picture && config.height < 1)
      || (!config.still_picture && config.height < 16)
      || config.height > u16::max_value() as usize
    {
      return Err(InvalidHeight(config.height));
    }

    if config.sample_aspect_ratio.num == 0 {
      return Err(InvalidAspectRatioNum(
        config.sample_aspect_ratio.num as usize,
      ));
    }
    if config.sample_aspect_ratio.den == 0 {
      return Err(InvalidAspectRatioDen(
        config.sample_aspect_ratio.den as usize,
      ));
    }

    let (render_width, render_height) = config.render_size();
    if render_width == 0 || render_width > u16::max_value() as usize {
      return Err(InvalidRenderWidth(render_width));
    }
    if render_height == 0 || render_height > u16::max_value() as usize {
      return Err(InvalidRenderHeight(render_height));
    }

    if config.rdo_lookahead_frames > MAX_RDO_LOOKAHEAD_FRAMES
      || config.rdo_lookahead_frames < 1
    {
      return Err(InvalidRdoLookaheadFrames {
        actual: config.rdo_lookahead_frames,
        max: MAX_RDO_LOOKAHEAD_FRAMES,
        min: 1,
      });
    }
    if config.max_key_frame_interval > MAX_MAX_KEY_FRAME_INTERVAL {
      return Err(InvalidMaxKeyFrameInterval {
        actual: config.max_key_frame_interval,
        max: MAX_MAX_KEY_FRAME_INTERVAL,
      });
    }

    if !check_tile_log2(config.tile_cols) {
      return Err(InvalidTileCols(config.tile_cols));
    }
    if !check_tile_log2(config.tile_rows) {
      return Err(InvalidTileRows(config.tile_rows));
    }

    if config.time_base.num == 0
      || config.time_base.num > u32::max_value() as u64
    {
      return Err(InvalidFrameRateNum {
        actual: config.time_base.num,
        max: u32::max_value() as u64,
      });
    }
    if config.time_base.den == 0
      || config.time_base.den > u32::max_value() as u64
    {
      return Err(InvalidFrameRateDen {
        actual: config.time_base.den,
        max: u32::max_value() as u64,
      });
    }

    if let Some(delay) = config.reservoir_frame_delay {
      if delay < 12 || delay > 131_072 {
        return Err(InvalidReservoirFrameDelay(delay));
      }
    }

    if config.switch_frame_interval > 0 && !config.low_latency {
      return Err(InvalidSwitchFrameInterval(config.switch_frame_interval));
    }

    if config.enable_timing_info && config.still_picture {
      return Err(InvalidOptionWithStillPicture("enable_timing_info"));
    }

    // TODO: add more validation
    let rc = &self.rate_control;

    if (rc.emit_pass_data || rc.summary.is_some()) && config.bitrate == 0 {
      return Err(TargetBitrateNeeded);
    }

    Ok(())
  }
}
<|endoftext|>


<commit_before>use crate::ast::Ident;
use crate::ext::base::ExtCtxt;
use crate::ext::expand::Marker;
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
use crate::ext::tt::quoted;
use crate::mut_visit::noop_visit_tt;
use crate::parse::token::{self, NtTT, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};

use smallvec::{smallvec, SmallVec};
use syntax_pos::DUMMY_SP;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use std::mem;
use std::ops::Add;
use std::rc::Rc;

/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
    Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
    Sequence { forest: Lrc<quoted::SequenceRepetition>, idx: usize, sep: Option<Token> },
}

impl Frame {
    fn new(tts: Vec<quoted::TokenTree>) -> Frame {
        let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts: tts });
        Frame::Delimited { forest: forest, idx: 0, span: DelimSpan::dummy() }
    }
}

impl Iterator for Frame {
    type Item = quoted::TokenTree;

    fn next(&mut self) -> Option<quoted::TokenTree> {
        match *self {
            Frame::Delimited { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
            Frame::Sequence { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
        }
    }
}

/// This can do Macro-By-Example transcription. On the other hand, if `src` contains no
/// `TokenTree::{Sequence, MetaVar, MetaVarDecl}`s, `interp` can (and should) be `None`.
pub fn transcribe(
    cx: &ExtCtxt<'_>,
    interp: Option<FxHashMap<Ident, Rc<NamedMatch>>>,
    src: Vec<quoted::TokenTree>,
) -> TokenStream {
    let mut stack: SmallVec<[Frame; 1]> = smallvec![Frame::new(src)];
    let interpolations = interp.unwrap_or_else(FxHashMap::default); /* just a convenience */
    let mut repeats = Vec::new();
    let mut result: Vec<TreeAndJoint> = Vec::new();
    let mut result_stack = Vec::new();

    loop {
        let tree = if let Some(tree) = stack.last_mut().unwrap().next() {
            tree
        } else {
            if let Frame::Sequence { ref mut idx, ref sep, .. } = *stack.last_mut().unwrap() {
                let (ref mut repeat_idx, repeat_len) = *repeats.last_mut().unwrap();
                *repeat_idx += 1;
                if *repeat_idx < repeat_len {
                    *idx = 0;
                    if let Some(sep) = sep.clone() {
                        // repeat same span, I guess
                        let prev_span = match result.last() {
                            Some((tt, _)) => tt.span(),
                            None => DUMMY_SP,
                        };
                        result.push(TokenTree::Token(prev_span, sep).into());
                    }
                    continue;
                }
            }

            match stack.pop().unwrap() {
                Frame::Sequence { .. } => {
                    repeats.pop();
                }
                Frame::Delimited { forest, span, .. } => {
                    if result_stack.is_empty() {
                        return TokenStream::new(result);
                    }
                    let tree =
                        TokenTree::Delimited(span, forest.delim, TokenStream::new(result).into());
                    result = result_stack.pop().unwrap();
                    result.push(tree.into());
                }
            }
            continue;
        };

        match tree {
            quoted::TokenTree::Sequence(sp, seq) => {
                // FIXME(pcwalton): Bad copy.
                match lockstep_iter_size(
                    &quoted::TokenTree::Sequence(sp, seq.clone()),
                    &interpolations,
                    &repeats,
                ) {
                    LockstepIterSize::Unconstrained => {
                        cx.span_fatal(
                            sp.entire(), /* blame macro writer */
                            "attempted to repeat an expression \
                             containing no syntax \
                             variables matched as repeating at this depth",
                        );
                    }
                    LockstepIterSize::Contradiction(ref msg) => {
                        // FIXME #2887 blame macro invoker instead
                        cx.span_fatal(sp.entire(), &msg[..]);
                    }
                    LockstepIterSize::Constraint(len, _) => {
                        if len == 0 {
                            if seq.op == quoted::KleeneOp::OneOrMore {
                                // FIXME #2887 blame invoker
                                cx.span_fatal(sp.entire(), "this must repeat at least once");
                            }
                        } else {
                            repeats.push((0, len));
                            stack.push(Frame::Sequence {
                                idx: 0,
                                sep: seq.separator.clone(),
                                forest: seq,
                            });
                        }
                    }
                }
            }
            // FIXME #2887: think about span stuff here
            quoted::TokenTree::MetaVar(mut sp, ident) => {
                if let Some(cur_matched) = lookup_cur_matched(ident, &interpolations, &repeats) {
                    if let MatchedNonterminal(ref nt) = *cur_matched {
                        if let NtTT(ref tt) = **nt {
                            result.push(tt.clone().into());
                        } else {
                            sp = sp.apply_mark(cx.current_expansion.mark);
                            let token = TokenTree::Token(sp, Token::Interpolated(nt.clone()));
                            result.push(token.into());
                        }
                    } else {
                        cx.span_fatal(
                            sp, /* blame the macro writer */
                            &format!("variable '{}' is still repeating at this depth", ident),
                        );
                    }
                } else {
                    let ident =
                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.mark));
                    sp = sp.apply_mark(cx.current_expansion.mark);
                    result.push(TokenTree::Token(sp, token::Dollar).into());
                    result.push(TokenTree::Token(sp, token::Token::from_ast_ident(ident)).into());
                }
            }
            quoted::TokenTree::Delimited(mut span, delimited) => {
                span = span.apply_mark(cx.current_expansion.mark);
                stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span });
                result_stack.push(mem::replace(&mut result, Vec::new()));
            }
            quoted::TokenTree::Token(sp, tok) => {
                let mut marker = Marker(cx.current_expansion.mark);
                let mut tt = TokenTree::Token(sp, tok);
                noop_visit_tt(&mut tt, &mut marker);
                result.push(tt.into());
            }
            quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
        }
    }
}

fn lookup_cur_matched(
    ident: Ident,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> Option<Rc<NamedMatch>> {
    interpolations.get(&ident).map(|matched| {
        let mut matched = matched.clone();
        for &(idx, _) in repeats {
            let m = matched.clone();
            match *m {
                MatchedNonterminal(_) => break,
                MatchedSeq(ref ads, _) => matched = Rc::new(ads[idx].clone()),
            }
        }

        matched
    })
}

#[derive(Clone)]
enum LockstepIterSize {
    Unconstrained,
    Constraint(usize, Ident),
    Contradiction(String),
}

impl Add for LockstepIterSize {
    type Output = LockstepIterSize;

    fn add(self, other: LockstepIterSize) -> LockstepIterSize {
        match self {
            LockstepIterSize::Unconstrained => other,
            LockstepIterSize::Contradiction(_) => self,
            LockstepIterSize::Constraint(l_len, ref l_id) => match other {
                LockstepIterSize::Unconstrained => self,
                LockstepIterSize::Contradiction(_) => other,
                LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
                LockstepIterSize::Constraint(r_len, r_id) => {
                    let msg = format!(
                        "inconsistent lockstep iteration: \
                         '{}' has {} items, but '{}' has {}",
                        l_id, l_len, r_id, r_len
                    );
                    LockstepIterSize::Contradiction(msg)
                }
            },
        }
    }
}

fn lockstep_iter_size(
    tree: &quoted::TokenTree,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> LockstepIterSize {
    use quoted::TokenTree;
    match *tree {
        TokenTree::Delimited(_, ref delimed) => {
            delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        }
        TokenTree::Sequence(_, ref seq) => {
            seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        }
        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
            match lookup_cur_matched(name, interpolations, repeats) {
                Some(matched) => match *matched {
                    MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
                    MatchedSeq(ref ads, _) => LockstepIterSize::Constraint(ads.len(), name),
                },
                _ => LockstepIterSize::Unconstrained,
            }
        }
        TokenTree::Token(..) => LockstepIterSize::Unconstrained,
    }
}
<commit_msg>avoid extra copy<commit_after>use crate::ast::Ident;
use crate::ext::base::ExtCtxt;
use crate::ext::expand::Marker;
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
use crate::ext::tt::quoted;
use crate::mut_visit::noop_visit_tt;
use crate::parse::token::{self, NtTT, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};

use smallvec::{smallvec, SmallVec};
use syntax_pos::DUMMY_SP;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use std::mem;
use std::ops::Add;
use std::rc::Rc;

/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
    Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
    Sequence { forest: Lrc<quoted::SequenceRepetition>, idx: usize, sep: Option<Token> },
}

impl Frame {
    fn new(tts: Vec<quoted::TokenTree>) -> Frame {
        let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts: tts });
        Frame::Delimited { forest: forest, idx: 0, span: DelimSpan::dummy() }
    }
}

impl Iterator for Frame {
    type Item = quoted::TokenTree;

    fn next(&mut self) -> Option<quoted::TokenTree> {
        match *self {
            Frame::Delimited { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
            Frame::Sequence { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
        }
    }
}

/// This can do Macro-By-Example transcription. On the other hand, if `src` contains no
/// `TokenTree::{Sequence, MetaVar, MetaVarDecl}`s, `interp` can (and should) be `None`.
pub fn transcribe(
    cx: &ExtCtxt<'_>,
    interp: Option<FxHashMap<Ident, Rc<NamedMatch>>>,
    src: Vec<quoted::TokenTree>,
) -> TokenStream {
    let mut stack: SmallVec<[Frame; 1]> = smallvec![Frame::new(src)];
    let interpolations = interp.unwrap_or_else(FxHashMap::default); /* just a convenience */
    let mut repeats = Vec::new();
    let mut result: Vec<TreeAndJoint> = Vec::new();
    let mut result_stack = Vec::new();

    loop {
        let tree = if let Some(tree) = stack.last_mut().unwrap().next() {
            tree
        } else {
            if let Frame::Sequence { ref mut idx, ref sep, .. } = *stack.last_mut().unwrap() {
                let (ref mut repeat_idx, repeat_len) = *repeats.last_mut().unwrap();
                *repeat_idx += 1;
                if *repeat_idx < repeat_len {
                    *idx = 0;
                    if let Some(sep) = sep.clone() {
                        // repeat same span, I guess
                        let prev_span = match result.last() {
                            Some((tt, _)) => tt.span(),
                            None => DUMMY_SP,
                        };
                        result.push(TokenTree::Token(prev_span, sep).into());
                    }
                    continue;
                }
            }

            match stack.pop().unwrap() {
                Frame::Sequence { .. } => {
                    repeats.pop();
                }
                Frame::Delimited { forest, span, .. } => {
                    if result_stack.is_empty() {
                        return TokenStream::new(result);
                    }
                    let tree =
                        TokenTree::Delimited(span, forest.delim, TokenStream::new(result).into());
                    result = result_stack.pop().unwrap();
                    result.push(tree.into());
                }
            }
            continue;
        };

        match tree {
            seq @ quoted::TokenTree::Sequence(..) => {
                match lockstep_iter_size(&seq, interp, &repeats) {
                    LockstepIterSize::Unconstrained => {
                        cx.span_fatal(
                            seq.span(), /* blame macro writer */
                            "attempted to repeat an expression \
                             containing no syntax \
                             variables matched as repeating at this depth",
                        );
                    }
                    LockstepIterSize::Contradiction(ref msg) => {
                        // FIXME #2887 blame macro invoker instead
                        cx.span_fatal(seq.span(), &msg[..]);
                    }
                    LockstepIterSize::Constraint(len, _) => {
                        let (sp, seq) = if let quoted::TokenTree::Sequence(sp, seq) = seq {
                            (sp, seq)
                        } else {
                            unreachable!()
                        };

                        if len == 0 {
                            if seq.op == quoted::KleeneOp::OneOrMore {
                                // FIXME #2887 blame invoker
                                cx.span_fatal(sp.entire(), "this must repeat at least once");
                            }
                        } else {
                            repeats.push((0, len));
                            stack.push(Frame::Sequence {
                                idx: 0,
                                sep: seq.separator.clone(),
                                forest: seq,
                            });
                        }
                    }
                }
            }
            // FIXME #2887: think about span stuff here
            quoted::TokenTree::MetaVar(mut sp, ident) => {
                if let Some(cur_matched) = lookup_cur_matched(ident, &interpolations, &repeats) {
                    if let MatchedNonterminal(ref nt) = *cur_matched {
                        if let NtTT(ref tt) = **nt {
                            result.push(tt.clone().into());
                        } else {
                            sp = sp.apply_mark(cx.current_expansion.mark);
                            let token = TokenTree::Token(sp, Token::Interpolated(nt.clone()));
                            result.push(token.into());
                        }
                    } else {
                        cx.span_fatal(
                            sp, /* blame the macro writer */
                            &format!("variable '{}' is still repeating at this depth", ident),
                        );
                    }
                } else {
                    let ident =
                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.mark));
                    sp = sp.apply_mark(cx.current_expansion.mark);
                    result.push(TokenTree::Token(sp, token::Dollar).into());
                    result.push(TokenTree::Token(sp, token::Token::from_ast_ident(ident)).into());
                }
            }
            quoted::TokenTree::Delimited(mut span, delimited) => {
                span = span.apply_mark(cx.current_expansion.mark);
                stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span });
                result_stack.push(mem::replace(&mut result, Vec::new()));
            }
            quoted::TokenTree::Token(sp, tok) => {
                let mut marker = Marker(cx.current_expansion.mark);
                let mut tt = TokenTree::Token(sp, tok);
                noop_visit_tt(&mut tt, &mut marker);
                result.push(tt.into());
            }
            quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
        }
    }
}

fn lookup_cur_matched(
    ident: Ident,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> Option<Rc<NamedMatch>> {
    interpolations.get(&ident).map(|matched| {
        let mut matched = matched.clone();
        for &(idx, _) in repeats {
            let m = matched.clone();
            match *m {
                MatchedNonterminal(_) => break,
                MatchedSeq(ref ads, _) => matched = Rc::new(ads[idx].clone()),
            }
        }

        matched
    })
}

#[derive(Clone)]
enum LockstepIterSize {
    Unconstrained,
    Constraint(usize, Ident),
    Contradiction(String),
}

impl LockstepIterSize {
    fn with(self, other: LockstepIterSize) -> LockstepIterSize {
        match self {
            LockstepIterSize::Unconstrained => other,
            LockstepIterSize::Contradiction(_) => self,
            LockstepIterSize::Constraint(l_len, ref l_id) => match other {
                LockstepIterSize::Unconstrained => self,
                LockstepIterSize::Contradiction(_) => other,
                LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
                LockstepIterSize::Constraint(r_len, r_id) => {
                    let msg = format!(
                        "inconsistent lockstep iteration: \
                         '{}' has {} items, but '{}' has {}",
                        l_id, l_len, r_id, r_len
                    );
                    LockstepIterSize::Contradiction(msg)
                }
            },
        }
    }
}

fn lockstep_iter_size(
    tree: &quoted::TokenTree,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> LockstepIterSize {
    use quoted::TokenTree;
    match *tree {
        TokenTree::Delimited(_, ref delimed) => {
            delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        }
        TokenTree::Sequence(_, ref seq) => {
            seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        }
        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
            match lookup_cur_matched(name, interpolations, repeats) {
                Some(matched) => match *matched {
                    MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
                    MatchedSeq(ref ads, _) => LockstepIterSize::Constraint(ads.len(), name),
                },
                _ => LockstepIterSize::Unconstrained,
            }
        }
        TokenTree::Token(..) => LockstepIterSize::Unconstrained,
    }
}
<|endoftext|>


<commit_before>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl From<Lexeme> for Expression {
    fn from(lexeme: Lexeme) -> Expression {
        match lexeme.token {
            Token::Number(..) =>
                Expression::Number(
                    NumberValue::from_scalar(lexeme)
                ),
            Token::Plus | Token::Minus | Token::Star |
                          Token::Slash | Token::Percent =>
                Expression::Operator(lexeme),
            _ => Expression::String(lexeme),
        }
    }
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    fn parse_parenthetical<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::RightParen => {
                    return Ok(Expression::List(list))
                },
                Token::LeftParen => {
                    list.push(
                        try!(Expression::parse_parenthetical(tokenizer))
                    );
                },
                Token::Comment(_) => {},
                _ => list.push(lexeme.into()),
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset.unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected right paren while parsing a value expression; reached EOF instead."
            ),
        })
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::LeftParen => {
                    list.push(
                        try!(Expression::parse_parenthetical(tokenizer))
                    );
                },
                Token::Comment(_) => {},
                _ => list.push(lexeme.into()),
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset.unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(exprs: Vec<Expression>, context: &Context, force_slash: bool) -> Expression {

        let mut last_was_an_operator = true;
        // TODO: I'd love to have more types here, to ensure only values are
        // on the value stack and only operators are on the operator stack...
        let mut value_stack: Vec<Expression> = Vec::new();
        let mut op_stack: Vec<Expression> = Vec::new();

        // Split into value stacks and operator stacks

        let mut exprs = exprs.into_iter();
        while let Some(part) = exprs.next() {
            debug!("Processing list item {:#?}", part);
            match part {
                Expression::Number(nv) => {
                    if last_was_an_operator {
                        debug!("Push on value stack {:#?}", nv);
                        value_stack.push(Expression::Number(nv));
                    } else {
                        let list = create_list(
                            value_stack.pop(),
                            Expression::Number(nv),
                        );
                        debug!("Push on list on value stack {:#?}", list);
                        value_stack.push(list);
                    }
                    last_was_an_operator = false;
                },
                Expression::Operator(Lexeme {
                    token: Token::Slash, offset: o }) if !force_slash => {
                    let list = create_list(
                        value_stack.pop(),
                        Expression::Operator(Lexeme {
                            token: Token::Slash, offset: o
                        }),
                    );
                    debug!("Push on list on value stack {:#?}", list);
                    value_stack.push(list);
                },
                Expression::Operator(op) => {
                    debug!("Push on op stack {:#?}", op);
                    op_stack.push(Expression::Operator(op));
                    last_was_an_operator = true;
                },
                Expression::String(lex) => {
                    let var_eval = context.get_variable(&lex)
                                    .unwrap_or(Expression::String(lex));

                    if last_was_an_operator {
                        value_stack.push(var_eval);
                    } else {
                        let list = create_list(
                            value_stack.pop(),
                            var_eval,
                        );
                        value_stack.push(list);
                    }
                    last_was_an_operator = false;
                },
                Expression::List(list) => {
                    debug!("Push list on value stack {:#?}", list);
                    value_stack.push(Expression::List(list));
                    last_was_an_operator = false;
                },
            }
        }

        // Process the stacks
        while !op_stack.is_empty() {
            let op = op_stack.pop().unwrap();
            let second = value_stack.pop().expect("Expected a second argument on the value stack");
            let first = value_stack.pop().expect("Expected a first argument on the value stack");

            let math_result = first.apply_math(op, second, context, force_slash);
            debug!("Math result: {:#?}", math_result);

            value_stack.push(math_result);
        }

        value_stack.pop().unwrap()
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::Number(nv) => Expression::Number(nv),
            Expression::Operator(op) => Expression::Operator(op),
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::evaluate_list(exprs, context, false)
            },
        }
    }

    fn is_slash(&self) -> bool {
        match self {
            &Expression::Operator(ref o) if o.token == Token::Slash => true,
            _ => false,
        }
    }

    fn is_plus(&self) -> bool {
        match self {
            &Expression::Operator(ref o) if o.token == Token::Plus => true,
            _ => false,
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        debug!("Applying math to:\nfirst: {:#?}\nop: {:#?}\nsecond: {:#?}", self, operator, second);
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(Lexeme {
                        token: Token::Slash, offset: o }) => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(
                                &Lexeme {
                                    token: Token::Slash,
                                    offset: o
                                },
                                &s
                            );
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::evaluate_list(f, context, true);
                let eval_second = Expression::evaluate_list(s, context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::evaluate_list(f, context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                if s.iter().any(|e| e.is_slash()) {
                    let evaled_list = Expression::evaluate_list(s, context, true);
                    Expression::Number(f).apply_math(
                        operator,
                        evaled_list,
                        context,
                        true
                    )
                } else {
                    if let Some((ref first_in_list, rest_of_list)) =
                            s.split_first() {
                        let new_first = if operator.is_plus() {
                            Expression::String(
                                Lexeme {
                                    token: Token::String(
                                        format!("{}{}", f, first_in_list.to_string(SassOutputStyle::Expanded))
                                    ),
                                    offset: f.offset,
                                }
                            )
                        } else {
                            Expression::Number(f).apply_math(
                                operator,
                                (*first_in_list).clone(),
                                context,
                                true
                            )
                        };

                        let mut new_list = vec![new_first];
                        new_list.extend_from_slice(rest_of_list);
                        Expression::List(new_list)
                    } else {
                        panic!("Trying to perform an operation on a number and a list; expected to get a list with something in it");
                    }
                }
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one_lexeme() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn one() -> NumberValue {
        NumberValue::from_scalar(one_lexeme())
    }

    fn two_lexeme() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn two() -> NumberValue {
        NumberValue::from_scalar(two_lexeme())
    }

    fn one_px_lexeme() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    fn one_px() -> NumberValue {
        NumberValue::from_scalar(one_px_lexeme())
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one_lexeme()),
            Ok(plus()),
            Ok(one_lexeme()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(one()),
                Expression::Operator(plus()),
                Expression::Number(one()),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![
            Ok(one_lexeme()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(one()))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![
            Ok(one_px_lexeme()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(one_px()))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::Number(one()),
            Expression::Operator(slash()),
            Expression::Number(two()),
            Expression::Operator(plus()),
            Expression::Number(one()),
            Expression::Operator(slash()),
            Expression::Number(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue {
                scalar: 1.0,
                computed: true,
                units: None,
                offset: None,
            })
        );
    }
}
<commit_msg>Shorten a match<commit_after>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl From<Lexeme> for Expression {
    fn from(lexeme: Lexeme) -> Expression {
        match lexeme.token {
            Token::Number(..) =>
                Expression::Number(
                    NumberValue::from_scalar(lexeme)
                ),
            Token::Plus | Token::Minus | Token::Star |
                          Token::Slash | Token::Percent =>
                Expression::Operator(lexeme),
            _ => Expression::String(lexeme),
        }
    }
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    fn parse_parenthetical<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::RightParen => {
                    return Ok(Expression::List(list))
                },
                Token::LeftParen => {
                    list.push(
                        try!(Expression::parse_parenthetical(tokenizer))
                    );
                },
                Token::Comment(_) => {},
                _ => list.push(lexeme.into()),
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset.unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected right paren while parsing a value expression; reached EOF instead."
            ),
        })
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::LeftParen => {
                    list.push(
                        try!(Expression::parse_parenthetical(tokenizer))
                    );
                },
                Token::Comment(_) => {},
                _ => list.push(lexeme.into()),
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset.unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(exprs: Vec<Expression>, context: &Context, force_slash: bool) -> Expression {

        let mut last_was_an_operator = true;
        // TODO: I'd love to have more types here, to ensure only values are
        // on the value stack and only operators are on the operator stack...
        let mut value_stack: Vec<Expression> = Vec::new();
        let mut op_stack: Vec<Expression> = Vec::new();

        // Split into value stacks and operator stacks

        let mut exprs = exprs.into_iter();
        while let Some(part) = exprs.next() {
            debug!("Processing list item {:#?}", part);
            match part {
                Expression::Number(nv) => {
                    if last_was_an_operator {
                        debug!("Push on value stack {:#?}", nv);
                        value_stack.push(Expression::Number(nv));
                    } else {
                        let list = create_list(
                            value_stack.pop(),
                            Expression::Number(nv),
                        );
                        debug!("Push on list on value stack {:#?}", list);
                        value_stack.push(list);
                    }
                    last_was_an_operator = false;
                },
                Expression::Operator(Lexeme {
                    token: Token::Slash, offset: o }) if !force_slash => {
                    let list = create_list(
                        value_stack.pop(),
                        Expression::Operator(Lexeme {
                            token: Token::Slash, offset: o
                        }),
                    );
                    debug!("Push on list on value stack {:#?}", list);
                    value_stack.push(list);
                },
                Expression::Operator(op) => {
                    debug!("Push on op stack {:#?}", op);
                    op_stack.push(Expression::Operator(op));
                    last_was_an_operator = true;
                },
                Expression::String(lex) => {
                    let var_eval = context.get_variable(&lex)
                                    .unwrap_or(Expression::String(lex));

                    if last_was_an_operator {
                        value_stack.push(var_eval);
                    } else {
                        let list = create_list(
                            value_stack.pop(),
                            var_eval,
                        );
                        value_stack.push(list);
                    }
                    last_was_an_operator = false;
                },
                Expression::List(list) => {
                    debug!("Push list on value stack {:#?}", list);
                    value_stack.push(Expression::List(list));
                    last_was_an_operator = false;
                },
            }
        }

        // Process the stacks
        while !op_stack.is_empty() {
            let op = op_stack.pop().unwrap();
            let second = value_stack.pop().expect("Expected a second argument on the value stack");
            let first = value_stack.pop().expect("Expected a first argument on the value stack");

            let math_result = first.apply_math(op, second, context, force_slash);
            debug!("Math result: {:#?}", math_result);

            value_stack.push(math_result);
        }

        value_stack.pop().unwrap()
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::evaluate_list(exprs, context, false)
            },
            other => other,
        }
    }

    fn is_slash(&self) -> bool {
        match self {
            &Expression::Operator(ref o) if o.token == Token::Slash => true,
            _ => false,
        }
    }

    fn is_plus(&self) -> bool {
        match self {
            &Expression::Operator(ref o) if o.token == Token::Plus => true,
            _ => false,
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        debug!("Applying math to:\nfirst: {:#?}\nop: {:#?}\nsecond: {:#?}", self, operator, second);
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(Lexeme {
                        token: Token::Slash, offset: o }) => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(
                                &Lexeme {
                                    token: Token::Slash,
                                    offset: o
                                },
                                &s
                            );
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::evaluate_list(f, context, true);
                let eval_second = Expression::evaluate_list(s, context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::evaluate_list(f, context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                if s.iter().any(|e| e.is_slash()) {
                    let evaled_list = Expression::evaluate_list(s, context, true);
                    Expression::Number(f).apply_math(
                        operator,
                        evaled_list,
                        context,
                        true
                    )
                } else {
                    if let Some((ref first_in_list, rest_of_list)) =
                            s.split_first() {
                        let new_first = if operator.is_plus() {
                            Expression::String(
                                Lexeme {
                                    token: Token::String(
                                        format!("{}{}", f, first_in_list.to_string(SassOutputStyle::Expanded))
                                    ),
                                    offset: f.offset,
                                }
                            )
                        } else {
                            Expression::Number(f).apply_math(
                                operator,
                                (*first_in_list).clone(),
                                context,
                                true
                            )
                        };

                        let mut new_list = vec![new_first];
                        new_list.extend_from_slice(rest_of_list);
                        Expression::List(new_list)
                    } else {
                        panic!("Trying to perform an operation on a number and a list; expected to get a list with something in it");
                    }
                }
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one_lexeme() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn one() -> NumberValue {
        NumberValue::from_scalar(one_lexeme())
    }

    fn two_lexeme() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn two() -> NumberValue {
        NumberValue::from_scalar(two_lexeme())
    }

    fn one_px_lexeme() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    fn one_px() -> NumberValue {
        NumberValue::from_scalar(one_px_lexeme())
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one_lexeme()),
            Ok(plus()),
            Ok(one_lexeme()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(one()),
                Expression::Operator(plus()),
                Expression::Number(one()),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![
            Ok(one_lexeme()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(one()))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![
            Ok(one_px_lexeme()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(one_px()))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::Number(one()),
            Expression::Operator(slash()),
            Expression::Number(two()),
            Expression::Operator(plus()),
            Expression::Number(one()),
            Expression::Operator(slash()),
            Expression::Number(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue {
                scalar: 1.0,
                computed: true,
                units: None,
                offset: None,
            })
        );
    }
}
<|endoftext|>


<commit_before>extern crate time;
extern crate http_event_store as hes;
extern crate uuid;

use hes::event::Event;
use hes::error::HesError::*;
use hes::error::UserErrorKind::*;
use hes::expected_version::ExpectedVersion;

#[macro_use]
mod support;

#[test]
fn should_retrun_stream_not_found_error_attempting_to_read_nonexistent_stream() {
    let client = hes::client::Client::default();
    let nonexistent_stream_name = "some-nonexistent";
    let result = client.read_stream_events_forward(&nonexistent_stream_name, 0, 1, true);

    assert_error!(UserError, StreamNotFound, result.unwrap_err());
}

#[test]
fn should_retrun_stream_deleted_error_attempting_to_read_deleted_stream() {
    let events: Vec<Event> = vec![Event { event_id: Some(uuid::Uuid::new_v4()),
                                          event_type: "created".to_string(),
                                          data: Some("{a:1}".to_string()) }];

    let client = hes::client::Client::default();
    let stream_name = format!("stream-{}", uuid::Uuid::new_v4().simple());
    client.append_to_stream(&stream_name, ExpectedVersion::NoStream, events).unwrap();

    println!("{:?}", client.read_stream_events_forward(&stream_name, 0, 1, true).is_ok());
    assert!(client.read_stream_events_forward(&stream_name, 0, 1, true).is_ok());
    assert!(client.hard_delete_stream(&stream_name, ExpectedVersion::Any).is_ok());

    let result = client.read_stream_events_forward(&stream_name, 0, 1, true);
    assert_error!(UserError, StreamDeleted, result.unwrap_err());
}
<commit_msg>Do cleanup: remove `println!` call<commit_after>extern crate time;
extern crate http_event_store as hes;
extern crate uuid;

use hes::event::Event;
use hes::error::HesError::*;
use hes::error::UserErrorKind::*;
use hes::expected_version::ExpectedVersion;

#[macro_use]
mod support;

#[test]
fn should_retrun_stream_not_found_error_attempting_to_read_nonexistent_stream() {
    let client = hes::client::Client::default();
    let nonexistent_stream_name = "some-nonexistent";
    let result = client.read_stream_events_forward(&nonexistent_stream_name, 0, 1, true);

    assert_error!(UserError, StreamNotFound, result.unwrap_err());
}

#[test]
fn should_retrun_stream_deleted_error_attempting_to_read_deleted_stream() {
    let events: Vec<Event> = vec![Event { event_id: Some(uuid::Uuid::new_v4()),
                                          event_type: "created".to_string(),
                                          data: Some("{a:1}".to_string()) }];

    let client = hes::client::Client::default();
    let stream_name = format!("stream-{}", uuid::Uuid::new_v4().simple());
    client.append_to_stream(&stream_name, ExpectedVersion::NoStream, events).unwrap();

    assert!(client.read_stream_events_forward(&stream_name, 0, 1, true).is_ok());
    assert!(client.hard_delete_stream(&stream_name, ExpectedVersion::Any).is_ok());

    let result = client.read_stream_events_forward(&stream_name, 0, 1, true);
    assert_error!(UserError, StreamDeleted, result.unwrap_err());
}
<|endoftext|>


<commit_before>extern crate libc;

pub mod anoncreds;
pub mod crypto;
pub mod sovrin;
pub mod wallet;

use std::collections::HashMap;
use std::mem;
use std::sync::{Arc, Mutex, Once, ONCE_INIT};

use self::libc::{c_char, c_uchar};
use super::SovrinClient;

#[derive(Clone)]
pub struct SingletonClients {
    inner: Arc<Mutex<(HashMap<i32, SovrinClient>, i32)>>
}

pub fn get_active_clients() -> SingletonClients {
    static mut SINGLETON: *const SingletonClients = 0 as *const SingletonClients;
    static ONCE: Once = ONCE_INIT;

    unsafe {
        ONCE.call_once(|| {
            let singleton = SingletonClients {
                inner: Arc::new(Mutex::new((HashMap::new(), 1)))
            };
            SINGLETON = mem::transmute(Box::new(singleton));
        });
        (*SINGLETON).clone()
    }
}

#[no_mangle]
pub extern fn init_client(host_and_port: *const c_char) -> i32 {
    let s = get_active_clients();
    let (ref mut clients, mut cl_id): (HashMap<i32, SovrinClient>, i32) = *s.inner.lock().unwrap();

    while clients.contains_key(&cl_id) {
        cl_id += 1;
        if cl_id < 0 {
            cl_id = 1;
        }
    }

    clients.insert(cl_id, SovrinClient::new());

    cl_id
}

#[no_mangle]
pub extern fn release_client(client_id: i32) -> i32 {
    let s = get_active_clients();
    let ref mut clients: HashMap<i32, SovrinClient> = (*s.inner.lock().unwrap()).0;

    if clients.contains_key(&client_id) {
        clients.remove(&client_id);
        0
    } else {
        -1
    }
}

#[no_mangle]
pub extern fn free_str(c_ptr: *mut c_char) {
    unimplemented!();
}<commit_msg>Add tests for init/release client in C API.<commit_after>extern crate libc;

pub mod anoncreds;
pub mod crypto;
pub mod sovrin;
pub mod wallet;

use std::collections::HashMap;
use std::mem;
use std::sync::{Arc, Mutex, Once, ONCE_INIT};

use self::libc::{c_char, c_uchar};
use super::SovrinClient;

#[derive(Clone)]
pub struct SingletonClients {
    inner: Arc<Mutex<(HashMap<i32, SovrinClient>, i32)>>
}

pub fn get_active_clients() -> SingletonClients {
    static mut SINGLETON: *const SingletonClients = 0 as *const SingletonClients;
    static ONCE: Once = ONCE_INIT;

    unsafe {
        ONCE.call_once(|| {
            let singleton = SingletonClients {
                inner: Arc::new(Mutex::new((HashMap::new(), 1)))
            };
            SINGLETON = mem::transmute(Box::new(singleton));
        });
        (*SINGLETON).clone()
    }
}

#[no_mangle]
pub extern fn init_client(host_and_port: *const c_char) -> i32 {
    let s = get_active_clients();
    let (ref mut clients, mut cl_id): (HashMap<i32, SovrinClient>, i32) = *s.inner.lock().unwrap();

    while clients.contains_key(&cl_id) {
        cl_id += 1;
        if cl_id < 0 {
            cl_id = 1;
        }
    }

    clients.insert(cl_id, SovrinClient::new());

    cl_id
}

#[no_mangle]
pub extern fn release_client(client_id: i32) -> i32 {
    let s = get_active_clients();
    let ref mut clients: HashMap<i32, SovrinClient> = (*s.inner.lock().unwrap()).0;

    if clients.contains_key(&client_id) {
        clients.remove(&client_id);
        0
    } else {
        -1
    }
}

#[no_mangle]
pub extern fn free_str(c_ptr: *mut c_char) {
    unimplemented!();
}

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

    #[test]
    fn sovrin_client_can_be_created() {
        let empty = CString::new("").unwrap();
        init_client(empty.as_ptr());
    }

    #[test]
    fn sovrin_client_can_be_created_and_freed() {
        let empty = CString::new("").unwrap();
        let id = init_client(empty.as_ptr());
        let other_id = id + 1;
        assert_eq!(0, release_client(id));
        assert_eq!(-1, release_client(other_id));
        //TODO create more complex example: use different threads
    }

//        TODO: check memory consumption
//        #[test]
//        fn sovrin_client_no_leak() {
//            let empty = CString::new("").unwrap();
//            for i in 1..1000000 {
//                let id = init_client(empty.as_ptr());
//                assert_eq!(0, release_client(id));
//            }
//        }
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::fs::create_dir;

use git2;

use util::{process, CargoResult};

pub struct HgRepo;
pub struct GitRepo;
pub struct PijulRepo;
pub struct FossilRepo;

impl GitRepo {
    pub fn init(path: &Path, _: &Path) -> CargoResult<GitRepo> {
        git2::Repository::init(path)?;
        Ok(GitRepo)
    }
    pub fn discover(path: &Path, _: &Path) -> Result<git2::Repository, git2::Error> {
        git2::Repository::discover(path)
    }
}

impl HgRepo {
    pub fn init(path: &Path, cwd: &Path) -> CargoResult<HgRepo> {
        process("hg").cwd(cwd).arg("init").arg(path).exec()?;
        Ok(HgRepo)
    }
    pub fn discover(path: &Path, cwd: &Path) -> CargoResult<HgRepo> {
        process("hg")
            .cwd(cwd)
            .arg("root")
            .cwd(path)
            .exec_with_output()?;
        Ok(HgRepo)
    }
}

impl PijulRepo {
    pub fn init(path: &Path, cwd: &Path) -> CargoResult<PijulRepo> {
        process("pijul").cwd(cwd).arg("init").arg(path).exec()?;
        Ok(PijulRepo)
    }
}

impl FossilRepo {
    pub fn init(path: &Path, cwd: &Path) -> CargoResult<FossilRepo> {
        // fossil doesn't create the directory so we'll do that first
        create_dir(path)?;

        // set up the paths we'll use
        let db_fname = ".fossil";
        let mut db_path = path.to_owned();
        db_path.push(db_fname);

        // then create the fossil DB in that location
        process("fossil").cwd(cwd).arg("init").arg(&db_path).exec()?;

        // open it in that new directory
        process("fossil")
            .cwd(&path)
            .arg("open")
            .arg(db_fname)
            .exec()?;

        // set `target` as ignoreable and cleanable
        process("fossil")
            .cwd(cwd)
            .arg("settings")
            .arg("ignore-glob")
            .arg("target");
        process("fossil")
            .cwd(cwd)
            .arg("settings")
            .arg("clean-glob")
            .arg("target");
        Ok(FossilRepo)
    }
}
<commit_msg>Fix HgRepo::discover<commit_after>use std::path::Path;
use std::fs::create_dir;

use git2;

use util::{process, CargoResult};

pub struct HgRepo;
pub struct GitRepo;
pub struct PijulRepo;
pub struct FossilRepo;

impl GitRepo {
    pub fn init(path: &Path, _: &Path) -> CargoResult<GitRepo> {
        git2::Repository::init(path)?;
        Ok(GitRepo)
    }
    pub fn discover(path: &Path, _: &Path) -> Result<git2::Repository, git2::Error> {
        git2::Repository::discover(path)
    }
}

impl HgRepo {
    pub fn init(path: &Path, cwd: &Path) -> CargoResult<HgRepo> {
        process("hg").cwd(cwd).arg("init").arg(path).exec()?;
        Ok(HgRepo)
    }
    pub fn discover(path: &Path, cwd: &Path) -> CargoResult<HgRepo> {
        process("hg")
            .cwd(cwd)
            .arg("--cwd")
            .arg(path)
            .arg("root")
            .exec_with_output()?;
        Ok(HgRepo)
    }
}

impl PijulRepo {
    pub fn init(path: &Path, cwd: &Path) -> CargoResult<PijulRepo> {
        process("pijul").cwd(cwd).arg("init").arg(path).exec()?;
        Ok(PijulRepo)
    }
}

impl FossilRepo {
    pub fn init(path: &Path, cwd: &Path) -> CargoResult<FossilRepo> {
        // fossil doesn't create the directory so we'll do that first
        create_dir(path)?;

        // set up the paths we'll use
        let db_fname = ".fossil";
        let mut db_path = path.to_owned();
        db_path.push(db_fname);

        // then create the fossil DB in that location
        process("fossil").cwd(cwd).arg("init").arg(&db_path).exec()?;

        // open it in that new directory
        process("fossil")
            .cwd(&path)
            .arg("open")
            .arg(db_fname)
            .exec()?;

        // set `target` as ignoreable and cleanable
        process("fossil")
            .cwd(cwd)
            .arg("settings")
            .arg("ignore-glob")
            .arg("target");
        process("fossil")
            .cwd(cwd)
            .arg("settings")
            .arg("clean-glob")
            .arg("target");
        Ok(FossilRepo)
    }
}
<|endoftext|>