<commit_before>///! Basic tests for instructions/constructions, missing in wabt tests

use builder::module;
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
	InitExpr, ValueType, Opcodes, Opcode};
use interpreter::Error;
use interpreter::module::ModuleInstanceInterface;
use interpreter::program::ProgramInstance;
use interpreter::value::RuntimeValue;

#[test]
fn import_function() {
	let module1 = module()
		.with_export(ExportEntry::new("external_func".into(), Internal::Function(0)))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::I32Const(3),
				Opcode::End,
			])).build()
			.build()
		.build();

	let module2 = module()
		.with_import(ImportEntry::new("external_module".into(), "external_func".into(), External::Function(0)))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::Call(0),
				Opcode::I32Const(7),
				Opcode::I32Add,
				Opcode::End,
			])).build()
			.build()
		.build();

	let program = ProgramInstance::new().unwrap();
	let external_module = program.add_module("external_module", module1).unwrap();
	let main_module = program.add_module("main", module2).unwrap();

	assert_eq!(external_module.execute_index(0, vec![]).unwrap().unwrap(), RuntimeValue::I32(3));
	assert_eq!(main_module.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(10));
}

#[test]
fn global_get_set() {
	let module = module()
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(42)])))
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(777)])))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::GetGlobal(0),
				Opcode::I32Const(8),
				Opcode::I32Add,
				Opcode::SetGlobal(0),
				Opcode::GetGlobal(0),
				Opcode::End,
			])).build()
			.build()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::GetGlobal(1),
				Opcode::I32Const(8),
				Opcode::I32Add,
				Opcode::SetGlobal(1),
				Opcode::GetGlobal(1),
				Opcode::End,
			])).build()
			.build()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::I64Const(8),
				Opcode::SetGlobal(0),
				Opcode::GetGlobal(0),
				Opcode::End,
			])).build()
			.build()
		.build();

	let program = ProgramInstance::new().unwrap();
	let module = program.add_module("main", module).unwrap();
	assert_eq!(module.execute_index(0, vec![]).unwrap().unwrap(), RuntimeValue::I32(50));
	assert_eq!(module.execute_index(1, vec![]).unwrap_err(), Error::Variable("trying to update immutable variable".into()));
	assert_eq!(module.execute_index(2, vec![]).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into()));
}
<commit_msg>interpretter unhappy test<commit_after>///! Basic tests for instructions/constructions, missing in wabt tests

use builder::module;
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
	InitExpr, ValueType, Opcodes, Opcode};
use interpreter::Error;
use interpreter::module::ModuleInstanceInterface;
use interpreter::program::ProgramInstance;
use interpreter::value::RuntimeValue;

#[test]
fn import_function() {
	let module1 = module()
		.with_export(ExportEntry::new("external_func".into(), Internal::Function(0)))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::I32Const(3),
				Opcode::End,
			])).build()
			.build()
		.build();

	let module2 = module()
		.with_import(ImportEntry::new("external_module".into(), "external_func".into(), External::Function(0)))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::Call(0),
				Opcode::I32Const(7),
				Opcode::I32Add,
				Opcode::End,
			])).build()
			.build()
		.build();

	let program = ProgramInstance::new().unwrap();
	let external_module = program.add_module("external_module", module1).unwrap();
	let main_module = program.add_module("main", module2).unwrap();

	assert_eq!(external_module.execute_index(0, vec![]).unwrap().unwrap(), RuntimeValue::I32(3));
	assert_eq!(main_module.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(10));
}

#[test]
fn wrong_import() {
	let side_module = module()
		.with_export(ExportEntry::new("cool_func".into(), Internal::Function(0)))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::I32Const(3),
				Opcode::End,
			])).build()
			.build()
		.build();

	let module = module()
		.with_import(ImportEntry::new("side_module".into(), "not_cool_func".into(), External::Function(0)))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::Call(0),
				Opcode::I32Const(7),
				Opcode::I32Add,
				Opcode::End,
			])).build()
			.build()
		.build();

	let program = ProgramInstance::new().unwrap();
	let _side_module_instance = program.add_module("side_module", side_module).unwrap();
	let module_instance = program.add_module("main", module).unwrap();

	assert!(module_instance.execute_index(1, vec![]).is_err());
}

#[test]
fn global_get_set() {
	let module = module()
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(42)])))
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(777)])))
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::GetGlobal(0),
				Opcode::I32Const(8),
				Opcode::I32Add,
				Opcode::SetGlobal(0),
				Opcode::GetGlobal(0),
				Opcode::End,
			])).build()
			.build()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::GetGlobal(1),
				Opcode::I32Const(8),
				Opcode::I32Add,
				Opcode::SetGlobal(1),
				Opcode::GetGlobal(1),
				Opcode::End,
			])).build()
			.build()
		.function()
			.signature().return_type().i32().build()
			.body().with_opcodes(Opcodes::new(vec![
				Opcode::I64Const(8),
				Opcode::SetGlobal(0),
				Opcode::GetGlobal(0),
				Opcode::End,
			])).build()
			.build()
		.build();

	let program = ProgramInstance::new().unwrap();
	let module = program.add_module("main", module).unwrap();
	assert_eq!(module.execute_index(0, vec![]).unwrap().unwrap(), RuntimeValue::I32(50));
	assert_eq!(module.execute_index(1, vec![]).unwrap_err(), Error::Variable("trying to update immutable variable".into()));
	assert_eq!(module.execute_index(2, vec![]).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into()));
}
<|endoftext|>


<commit_before>use std::fmt;
use crate::decoders::basics::*;

const DECODE_CACHE_BITS: u32 = 13;

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // Represent the weird shifts that are needed for some NEF files
  pub shiftval: [u32;256],

  // Enable the workaround for 16 bit decodes in DNG that need to consume those
  // bits instead of the value being implied
  pub dng_bug: bool,

  // In CRW we only use the len code so the cache is not needed
  pub disable_cache: bool,

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private

  // The max number of bits in a huffman code and the table that converts those
  // bits into how many bits to consume and the decoded length and shift
  nbits: u32,
  hufftable: Vec<(u8,u8,u8)>,

  // A pregenerated table that goes straight to decoding a diff without first
  // finding a length, fetching bits, and sign extending them. The table is
  // sized by DECODE_CACHE_BITS and can have 99%+ hit rate with 13 bits
  decodecache: [Option<(u8,i16)>; 1<< DECODE_CACHE_BITS],

  initialized: bool,
}

struct MockPump {
  bits: u64,
  nbits: u32,
}

impl MockPump {
  pub fn empty() -> Self {
    MockPump {
      bits: 0,
      nbits: 0,
    }
  }

  pub fn set(&mut self, bits: u32, nbits: u32) {
    self.bits = (bits as u64) << 32;
    self.nbits = nbits + 32;
  }

  pub fn validbits(&self) -> i32 {
    self.nbits as i32 - 32
  }
}

impl BitPump for MockPump {
  fn peek_bits(&mut self, num: u32) -> u32 {
    (self.bits >> (self.nbits-num)) as u32
  }

  fn consume_bits(&mut self, num: u32) {
    self.nbits -= num;
    self.bits &= (1 << self.nbits) - 1;
  }
}

impl HuffTable {
  pub fn empty() -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      shiftval: [0;256],
      dng_bug: false,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    }
  }

  pub fn new(bits: [u32;17], huffval: [u32;256], dng_bug: bool) -> Result<HuffTable,String> {
    let mut tbl = HuffTable {
      bits: bits,
      huffval: huffval,
      shiftval: [0;256],
      dng_bug: dng_bug,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    };
    tbl.initialize()?;
    Ok(tbl)
  }

  pub fn initialize(&mut self) -> Result<(), String> {
    // Find out the max code length and allocate a table with that size
    self.nbits = 16;
    for i in 0..16 {
      if self.bits[16-i] != 0 {
        break;
      }
      self.nbits -= 1;
    }
    self.hufftable = vec![(0,0,0); 1 << self.nbits];

    // Fill in the table itself
    let mut h = 0;
    let mut pos = 0;
    for len in 0..self.nbits {
      for _ in 0..self.bits[len as usize + 1] {
        for _ in 0..(1 << (self.nbits-len-1)) {
          self.hufftable[h] = (len as u8 + 1, self.huffval[pos] as u8, self.shiftval[pos] as u8);
          h += 1;
        }
        pos += 1;
      }
    }

    // Create the decode cache by running the slow code over all the possible
    // values DECODE_CACHE_BITS wide
    if !self.disable_cache {
      let mut pump = MockPump::empty();
      let mut i = 0;
      loop {
        pump.set(i, DECODE_CACHE_BITS);
        let (bits, decode) = self.huff_decode_slow(&mut pump);
        if pump.validbits() >= 0 {
          self.decodecache[i as usize] = Some((bits, decode as i16));
        }
        i += 1;
        if i >= 1 << DECODE_CACHE_BITS {
          break;
        }
      }
    }

    self.initialized = true;
    Ok(())
  }

  pub fn huff_decode(&self, pump: &mut dyn BitPump) -> Result<i32,String> {
    let code = pump.peek_bits(DECODE_CACHE_BITS) as usize;
    if let Some((bits,decode)) = self.decodecache[code] {
      pump.consume_bits(bits as u32);
      Ok(decode as i32)
    } else {
      let decode = self.huff_decode_slow(pump);
      Ok(decode.1)
    }
  }

  pub fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u8,i32) {
    let len = self.huff_len(pump);
    (len.0+len.1, self.huff_diff(pump, len))
  }

  pub fn huff_len(&self, pump: &mut dyn BitPump) -> (u8,u8,u8) {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, shift) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    (bits, len, shift)
  }

  pub fn huff_get_bits(&self, pump: &mut dyn BitPump) -> u32 {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, _) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    len as u32
  }

  pub fn huff_diff(&self, pump: &mut dyn BitPump, input: (u8,u8,u8)) -> i32 {
    let (_, len, shift) = input;

    match len {
      0 => 0,
      16 => {
        if self.dng_bug {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // decode the difference and extend sign bit
        let fulllen: i32 = len as i32 + shift as i32;
        let shift: i32 = shift as i32;
        let bits = pump.get_bits(len as u32) as i32;
        let mut diff: i32 = ((bits << 1) + 1) << shift >> 1;
        if (diff & (1 << (fulllen - 1))) == 0 {
          diff -= (1 << fulllen) - ((shift == 0) as i32);
        }
        diff
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}
<commit_msg>Force inline of huffman decoding functions<commit_after>use std::fmt;
use crate::decoders::basics::*;

const DECODE_CACHE_BITS: u32 = 13;

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // Represent the weird shifts that are needed for some NEF files
  pub shiftval: [u32;256],

  // Enable the workaround for 16 bit decodes in DNG that need to consume those
  // bits instead of the value being implied
  pub dng_bug: bool,

  // In CRW we only use the len code so the cache is not needed
  pub disable_cache: bool,

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private

  // The max number of bits in a huffman code and the table that converts those
  // bits into how many bits to consume and the decoded length and shift
  nbits: u32,
  hufftable: Vec<(u8,u8,u8)>,

  // A pregenerated table that goes straight to decoding a diff without first
  // finding a length, fetching bits, and sign extending them. The table is
  // sized by DECODE_CACHE_BITS and can have 99%+ hit rate with 13 bits
  decodecache: [Option<(u8,i16)>; 1<< DECODE_CACHE_BITS],

  initialized: bool,
}

struct MockPump {
  bits: u64,
  nbits: u32,
}

impl MockPump {
  pub fn empty() -> Self {
    MockPump {
      bits: 0,
      nbits: 0,
    }
  }

  pub fn set(&mut self, bits: u32, nbits: u32) {
    self.bits = (bits as u64) << 32;
    self.nbits = nbits + 32;
  }

  pub fn validbits(&self) -> i32 {
    self.nbits as i32 - 32
  }
}

impl BitPump for MockPump {
  fn peek_bits(&mut self, num: u32) -> u32 {
    (self.bits >> (self.nbits-num)) as u32
  }

  fn consume_bits(&mut self, num: u32) {
    self.nbits -= num;
    self.bits &= (1 << self.nbits) - 1;
  }
}

impl HuffTable {
  pub fn empty() -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      shiftval: [0;256],
      dng_bug: false,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    }
  }

  pub fn new(bits: [u32;17], huffval: [u32;256], dng_bug: bool) -> Result<HuffTable,String> {
    let mut tbl = HuffTable {
      bits: bits,
      huffval: huffval,
      shiftval: [0;256],
      dng_bug: dng_bug,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    };
    tbl.initialize()?;
    Ok(tbl)
  }

  pub fn initialize(&mut self) -> Result<(), String> {
    // Find out the max code length and allocate a table with that size
    self.nbits = 16;
    for i in 0..16 {
      if self.bits[16-i] != 0 {
        break;
      }
      self.nbits -= 1;
    }
    self.hufftable = vec![(0,0,0); 1 << self.nbits];

    // Fill in the table itself
    let mut h = 0;
    let mut pos = 0;
    for len in 0..self.nbits {
      for _ in 0..self.bits[len as usize + 1] {
        for _ in 0..(1 << (self.nbits-len-1)) {
          self.hufftable[h] = (len as u8 + 1, self.huffval[pos] as u8, self.shiftval[pos] as u8);
          h += 1;
        }
        pos += 1;
      }
    }

    // Create the decode cache by running the slow code over all the possible
    // values DECODE_CACHE_BITS wide
    if !self.disable_cache {
      let mut pump = MockPump::empty();
      let mut i = 0;
      loop {
        pump.set(i, DECODE_CACHE_BITS);
        let (bits, decode) = self.huff_decode_slow(&mut pump);
        if pump.validbits() >= 0 {
          self.decodecache[i as usize] = Some((bits, decode as i16));
        }
        i += 1;
        if i >= 1 << DECODE_CACHE_BITS {
          break;
        }
      }
    }

    self.initialized = true;
    Ok(())
  }

  #[inline(always)]
  pub fn huff_decode(&self, pump: &mut dyn BitPump) -> Result<i32,String> {
    let code = pump.peek_bits(DECODE_CACHE_BITS) as usize;
    if let Some((bits,decode)) = self.decodecache[code] {
      pump.consume_bits(bits as u32);
      Ok(decode as i32)
    } else {
      let decode = self.huff_decode_slow(pump);
      Ok(decode.1)
    }
  }

  #[inline(always)]
  pub fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u8,i32) {
    let len = self.huff_len(pump);
    (len.0+len.1, self.huff_diff(pump, len))
  }

  #[inline(always)]
  pub fn huff_len(&self, pump: &mut dyn BitPump) -> (u8,u8,u8) {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, shift) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    (bits, len, shift)
  }

  #[inline(always)]
  pub fn huff_get_bits(&self, pump: &mut dyn BitPump) -> u32 {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, _) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    len as u32
  }

  #[inline(always)]
  pub fn huff_diff(&self, pump: &mut dyn BitPump, input: (u8,u8,u8)) -> i32 {
    let (_, len, shift) = input;

    match len {
      0 => 0,
      16 => {
        if self.dng_bug {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // decode the difference and extend sign bit
        let fulllen: i32 = len as i32 + shift as i32;
        let shift: i32 = shift as i32;
        let bits = pump.get_bits(len as u32) as i32;
        let mut diff: i32 = ((bits << 1) + 1) << shift >> 1;
        if (diff & (1 << (fulllen - 1))) == 0 {
          diff -= (1 << fulllen) - ((shift == 0) as i32);
        }
        diff
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}
<|endoftext|>


<commit_before><commit_msg>Print the right function name in drivers::setup_late<commit_after><|endoftext|>


<commit_before><commit_msg>Display conditions properly in dissasembly<commit_after><|endoftext|>


<commit_before><commit_msg>Removed nedless borrows as suggested by clippy<commit_after><|endoftext|>


<commit_before>
use std::rc::Rc;
use syn::*;
use quote::Tokens;

use ast_converters::*;

/// Defines tokens for converting COM types into Rust types
pub struct ComToRust
{
    /// Optional expression for storing a temporary value in the stack
    /// for the duration of the Rust call.
    pub stack: Option<Tokens>,

    /// Expression that converts the COM type into Rust type.
    pub conversion: Tokens
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> ComToRust
    {
        ComToRust {
            stack: None,
            conversion: quote!( #ident.into() )
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> Tokens
    {
        quote!( #ident.into() )
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> Tokens
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.as_ref();
                match name {
                    "c_void"
                        | "RawComPtr"
                        => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            _ => quote!( Default::default() )
        }
    }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam( Type );

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.0.clone() }

    /// Gets the default value for the type.
    fn default_value( &self ) -> Tokens
    {
        quote!( ComItf::null_itf() )
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam( Type );
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.0.clone() }

    fn com_ty( &self ) -> Type
    {
        parse_quote!( ::intercom::BStr )
    }

    fn com_to_rust( &self, ident : &Ident ) -> ComToRust
    {
        ComToRust {
            stack: None,
            conversion: quote!( #ident.into() )
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> Tokens
    {
        quote!( #ident.into() )
    }
}

/// String parameter handler. Converts between Rust &str and COM BSTR types.
struct StringRefParam( Type );
impl TypeHandler for StringRefParam
{
    fn rust_ty( &self ) -> Type { self.0.clone() }

    fn com_ty( &self ) -> Type
    {
        parse_quote!( ::intercom::BStr )
    }

    fn com_to_rust( &self, ident : &Ident ) -> ComToRust
    {
        ComToRust {
            stack: Some( quote!( let as_string: String = #ident.into(); ) ),
            conversion: quote!( as_string.as_ref() )
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> Tokens
    {
        quote!( #ident.into() )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .expect( &format!( "Type {:?} could not be parsed.", arg_ty  ) );

    map_by_name( type_info.get_name().as_ref(), type_info.original.clone() )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam( original_type ) ),
        "String" => Rc::new( StringParam( original_type ) ),
        "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<commit_msg>Generate unique names for stack variables in string conversion<commit_after>
use std::rc::Rc;
use syn::*;
use quote::Tokens;

use ast_converters::*;

/// Defines tokens for converting COM types into Rust types
pub struct ComToRust
{
    /// Optional expression for storing a temporary value in the stack
    /// for the duration of the Rust call.
    pub stack: Option<Tokens>,

    /// Expression that converts the COM type into Rust type.
    pub conversion: Tokens
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> ComToRust
    {
        ComToRust {
            stack: None,
            conversion: quote!( #ident.into() )
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> Tokens
    {
        quote!( #ident.into() )
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> Tokens
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.as_ref();
                match name {
                    "c_void"
                        | "RawComPtr"
                        => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            _ => quote!( Default::default() )
        }
    }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam( Type );

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.0.clone() }

    /// Gets the default value for the type.
    fn default_value( &self ) -> Tokens
    {
        quote!( ComItf::null_itf() )
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam( Type );
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.0.clone() }

    fn com_ty( &self ) -> Type
    {
        parse_quote!( ::intercom::BStr )
    }

    fn com_to_rust( &self, ident : &Ident ) -> ComToRust
    {
        ComToRust {
            stack: None,
            conversion: quote!( #ident.into() )
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> Tokens
    {
        quote!( #ident.into() )
    }
}

/// String parameter handler. Converts between Rust &str and COM BSTR types.
struct StringRefParam( Type );
impl TypeHandler for StringRefParam
{
    fn rust_ty( &self ) -> Type { self.0.clone() }

    fn com_ty( &self ) -> Type
    {
        parse_quote!( ::intercom::BStr )
    }

    fn com_to_rust( &self, ident : &Ident ) -> ComToRust
    {
        // Generate unique name for each stack variable to avoid conflicts with function
        // thay may have multiple parameters.
        let as_string_ident = Ident::from( format!( "{}_as_string", ident ) );
        ComToRust {
            stack: Some( quote!( let #as_string_ident: String = #ident.into(); ) ),
            conversion: quote!( #as_string_ident.as_ref() )
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> Tokens
    {
        quote!( #ident.into() )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .expect( &format!( "Type {:?} could not be parsed.", arg_ty  ) );

    map_by_name( type_info.get_name().as_ref(), type_info.original.clone() )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam( original_type ) ),
        "String" => Rc::new( StringParam( original_type ) ),
        "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<|endoftext|>


<commit_before>use luminance::{Depth32F, Dim2, Flat, RGBA32F};
use luminance_gl::gl33::{Framebuffer, Pipe, Pipeline, RenderCommand, ShadingCommand, Texture};

use camera::{Camera, Freefly};
use extra::shaders::default::{DEFAULT_3D_INST, DEFAULT_3D_PROJ, DEFAULT_3D_VIEW, DefaultProgram3D};
use object::Object;
use projection::Projectable;
use renderer::Renderer;
use scene::Scene;
use transform::Transformable;

pub type Texture2D<A> = Texture<Flat, Dim2, A>;

/// Simple renderer that takes a camera, a set of model and applies a shader on them. This renderer
/// outputs a single color map along with the depth map.
pub struct SimpleRenderer<'a> {
  program: DefaultProgram3D<'a>,
  framebuffer: Framebuffer<Flat, Dim2, Texture2D<RGBA32F>, Texture2D<Depth32F>>
}

impl<'a> SimpleRenderer<'a> {
  pub fn new_from(w: u32, h: u32, scene: &mut Scene<'a>) -> Self {
    SimpleRenderer {
      program: DefaultProgram3D::new_from(scene).unwrap(),
      framebuffer: Framebuffer::new((w, h), 0).unwrap()
    }
  }
}

impl<'a, 'b, 'c> Renderer<'a, 'b, 'c, (&'a Camera<Freefly>, &'a [&'a Object<'c>]), (&'a Texture2D<RGBA32F>, &'a Texture2D<Depth32F>)> for SimpleRenderer<'c> {
  fn render(&'a self, scene: &'b mut Scene<'c>, input: (&'a Camera<Freefly>, &'a [&'a Object<'c>])) -> (&'a Texture2D<RGBA32F>, &'a Texture2D<Depth32F>) {
    let (camera, objects) = input;
    let program = scene.get_by_id(&self.program).unwrap();

    // reify objects
    let objects: Vec<_> = objects.iter().map(|object| {
      (object, scene.get_by_id(&object.model).unwrap())
    }).collect();

    let tessellations = objects.iter().flat_map(|&(object, ref model)| {
      model.parts.iter().map(move |part| {
        Pipe::new(move |program| {
                    program.update(&DEFAULT_3D_INST, *object.transform().as_ref());
                  },
                  &part.tess)
      })
    }).collect();

    Pipeline::new(&self.framebuffer, [0., 0., 0., 0.], &[], &[], vec![
      Pipe::new(|program| {
                  // update the camera
                  program.update(&DEFAULT_3D_PROJ, *camera.projection().as_ref());
                  program.update(&DEFAULT_3D_VIEW, *camera.transform().as_ref());
                },
                ShadingCommand::new(&program, vec![
                  Pipe::new(|_| {}, RenderCommand::new(None, true, tessellations, 1, None))
                ]))
    ]).run();

    (&self.framebuffer.color_slot, &self.framebuffer.depth_slot)
  }
}
<commit_msg>SimpleRenderer::new_from -> SimpleRenderer::new.<commit_after>use luminance::{Depth32F, Dim2, Flat, RGBA32F};
use luminance_gl::gl33::{Framebuffer, Pipe, Pipeline, RenderCommand, ShadingCommand, Texture};

use camera::{Camera, Freefly};
use extra::shaders::default::{DEFAULT_3D_INST, DEFAULT_3D_PROJ, DEFAULT_3D_VIEW, DefaultProgram3D};
use object::Object;
use projection::Projectable;
use renderer::Renderer;
use scene::Scene;
use transform::Transformable;

pub type Texture2D<A> = Texture<Flat, Dim2, A>;

/// Simple renderer that takes a camera, a set of model and applies a shader on them. This renderer
/// outputs a single color map along with the depth map.
pub struct SimpleRenderer<'a> {
  program: DefaultProgram3D<'a>,
  framebuffer: Framebuffer<Flat, Dim2, Texture2D<RGBA32F>, Texture2D<Depth32F>>
}

impl<'a> SimpleRenderer<'a> {
  pub fn new(w: u32, h: u32, scene: &mut Scene<'a>) -> Self {
    SimpleRenderer {
      program: DefaultProgram3D::new_from(scene).unwrap(),
      framebuffer: Framebuffer::new((w, h), 0).unwrap()
    }
  }
}

impl<'a, 'b, 'c> Renderer<'a, 'b, 'c, (&'a Camera<Freefly>, &'a [&'a Object<'c>]), (&'a Texture2D<RGBA32F>, &'a Texture2D<Depth32F>)> for SimpleRenderer<'c> {
  fn render(&'a self, scene: &'b mut Scene<'c>, input: (&'a Camera<Freefly>, &'a [&'a Object<'c>])) -> (&'a Texture2D<RGBA32F>, &'a Texture2D<Depth32F>) {
    let (camera, objects) = input;
    let program = scene.get_by_id(&self.program).unwrap();

    // reify objects
    let objects: Vec<_> = objects.iter().map(|object| {
      (object, scene.get_by_id(&object.model).unwrap())
    }).collect();

    let tessellations = objects.iter().flat_map(|&(object, ref model)| {
      model.parts.iter().map(move |part| {
        Pipe::new(move |program| {
                    program.update(&DEFAULT_3D_INST, *object.transform().as_ref());
                  },
                  &part.tess)
      })
    }).collect();

    Pipeline::new(&self.framebuffer, [0., 0., 0., 0.], &[], &[], vec![
      Pipe::new(|program| {
                  // update the camera
                  program.update(&DEFAULT_3D_PROJ, *camera.projection().as_ref());
                  program.update(&DEFAULT_3D_VIEW, *camera.transform().as_ref());
                },
                ShadingCommand::new(&program, vec![
                  Pipe::new(|_| {}, RenderCommand::new(None, true, tessellations, 1, None))
                ]))
    ]).run();

    (&self.framebuffer.color_slot, &self.framebuffer.depth_slot)
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Fixed-function hardware state.
//!
//! Configures the primitive assembly (PA), rasterizer, and output merger (OM) blocks.

use std::default::Default;
use std::fmt;

use target;

/// The front face winding order of a set of vertices.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum FrontFace {
    /// Clockwise winding order.
    Clockwise,
    /// Counter-clockwise winding order.
    CounterClockwise,
}

/// Width of a line.
pub type LineWidth = f32;
#[allow(missing_docs)]
pub type OffsetFactor = f32;
#[allow(missing_docs)]
pub type OffsetUnits = u32;

/// How to offset vertices in screen space, if at all.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Offset(pub OffsetFactor, pub OffsetUnits);

/// Which face, if any, to cull.
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum CullFace {
    Nothing,
    Front,
    Back,
}

/// How to rasterize a primitive.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum RasterMethod {
    /// Rasterize as a point.
    Point,
    /// Rasterize as a line with the given width.
    Line(LineWidth),
    /// Rasterize as a face with a given cull face mode.
    Fill(CullFace),
}

/// Primitive rasterization state. Note that GL allows different raster
/// method to be used for front and back, while this abstraction does not.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Primitive {
    /// Which vertex winding is considered to be the front face for culling.
    pub front_face: FrontFace,
    /// How to rasterize this primitive.
    pub method: RasterMethod,
    /// Any polygon offset to apply.
    pub offset: Option<Offset>,
}

impl Primitive {
    /// Get the cull face, if any, for this primitive state.
    pub fn get_cull_face(&self) -> CullFace {
        match self.method {
            RasterMethod::Fill(mode) => mode,
            _ => CullFace::Nothing,
        }
    }
}

impl Default for Primitive {
    fn default() -> Primitive {
        Primitive {
            front_face: FrontFace::CounterClockwise,
            method: RasterMethod::Fill(CullFace::Nothing),
            offset: None,
        }
    }
}

/// Multi-sampling rasterization mode
#[derive(Eq, Ord, PartialEq, PartialOrd, Copy, Clone, Debug)]
pub struct MultiSample;
    //sample_mask: u16,
    //alpha_to_coverage: bool,

/// A pixel-wise comparison function.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum Comparison {
    /// `false`
    Never,
    /// `x < y`
    Less,
    /// `x <= y`
    LessEqual,
    /// `x == y`
    Equal,
    /// `x >= y`
    GreaterEqual,
    /// `x > y`
    Greater,
    /// `x != y`
    NotEqual,
    /// `true`
    Always,
}

/// Stencil mask operation.
#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum StencilOp {
    /// Keep the current value in the stencil buffer (no change).
    Keep,
    /// Set the value in the stencil buffer to zero.
    Zero,
    /// Set the stencil buffer value to `value` from `StencilSide`
    Replace,
    /// Increment the stencil buffer value, clamping to its maximum value.
    IncrementClamp,
    /// Increment the stencil buffer value, wrapping around to 0 on overflow.
    IncrementWrap,
    /// Decrement the stencil buffer value, clamping to its minimum value.
    DecrementClamp,
    /// Decrement the stencil buffer value, wrapping around to the maximum value on overflow.
    DecrementWrap,
    /// Bitwise invert the current value in the stencil buffer.
    Invert,
}

/// Complete stencil state for a given side of a face.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct StencilSide {
    /// Comparison function to use to determine if the stencil test passes.
    pub fun: Comparison,
    /// Reference value to compare the value in the stencil buffer with.
    pub value: target::Stencil,
    /// A mask that is ANDd with both the stencil buffer value and the reference value when they
    /// are read before doing the stencil test.
    pub mask_read: target::Stencil,
    /// A mask that is ANDd with the stencil value before writing to the stencil buffer.
    pub mask_write: target::Stencil,
    /// What operation to do if the stencil test fails.
    pub op_fail: StencilOp,
    /// What operation to do if the stenil test passes but the depth test fails.
    pub op_depth_fail: StencilOp,
    /// What operation to do if both the depth and stencil test pass.
    pub op_pass: StencilOp,
}

impl Default for StencilSide {
    fn default() -> StencilSide {
        StencilSide {
            fun: Comparison::Always,
            value: 0,
            mask_read: -1,
            mask_write: -1,
            op_fail: StencilOp::Keep,
            op_depth_fail: StencilOp::Keep,
            op_pass: StencilOp::Keep,
        }
    }
}

/// Complete stencil state, specifying how to handle the front and back side of a face.
#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct Stencil {
    pub front: StencilSide,
    pub back: StencilSide,
}

/// Depth test state.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct Depth {
    /// Comparison function to use.
    pub fun: Comparison,
    /// Specify whether to write to the depth buffer or not.
    pub write: bool,
}

impl Default for Depth {
    fn default() -> Depth {
        Depth {
            fun: Comparison::Always,
            write: false,
        }
    }
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum Equation {
    Add,
    Sub,
    RevSub,
    Min,
    Max,
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum InverseFlag {
    Normal,
    Inverse,
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum BlendValue {
    Zero,
    SourceColor,
    SourceAlpha,
    SourceAlphaSaturated,
    DestColor,
    DestAlpha,
    ConstColor,
    ConstAlpha,
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct Factor(pub InverseFlag, pub BlendValue);

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct BlendChannel {
    pub equation: Equation,
    pub source: Factor,
    pub destination: Factor,
}

impl Default for BlendChannel {
    fn default() -> BlendChannel {
        BlendChannel {
            equation: Equation::Add,
            source: Factor(InverseFlag::Inverse, BlendValue::Zero),
            destination: Factor(InverseFlag::Normal, BlendValue::Zero),
        }
    }
}

#[allow(missing_docs)]
#[derive(Copy)]
pub struct Blend {
    pub color: BlendChannel,
    pub alpha: BlendChannel,
    pub value: target::ColorValue,
}

impl Default for Blend {
    fn default() -> Blend {
        Blend {
            color: Default::default(),
            alpha: Default::default(),
            value: [0.0, 0.0, 0.0, 0.0],
        }
    }
}

impl PartialEq for Blend {
    fn eq(&self, other: &Blend) -> bool {
        self.color == other.color &&
        self.alpha == other.alpha &&
        self.value == other.value
    }
}

impl Clone for Blend {
    fn clone(&self) -> Blend {
        Blend {
            color: self.color.clone(),
            alpha: self.alpha.clone(),
            value: self.value,
        }
    }
}

impl fmt::Debug for Blend {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Blend {{ color: {:?}, alpha: {:?}, value: {:?} }}",
               self.color, self.alpha, &self.value[..])
    }
}

bitflags!(
    #[allow(missing_docs)]
    flags ColorMask: u32 {  //u8 is preferred, but doesn't seem to work well
        const RED     = 0x1,
        const GREEN   = 0x2,
        const BLUE    = 0x4,
        const ALPHA   = 0x8,
        const MASK_ALL = 0xF
    }
);

impl fmt::Debug for ColorMask {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let values = [
            (RED,   "Red"  ),
            (GREEN, "Green"),
            (BLUE,  "Blue" ),
            (ALPHA, "Alpha"),
        ];

        try!(write!(f, "ColorMask("));
        for (i, &(_, name)) in values.iter()
            .filter(|&&(flag, _)| self.contains(flag))
            .enumerate()
        {
            if i == 0 {
                try!(write!(f, "{}", name))
            } else {
                try!(write!(f, " | {}", name))
            }
        }
        try!(write!(f, ")"));

        Ok(())
    }
}
<commit_msg>Added docs to blending `Equation`<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Fixed-function hardware state.
//!
//! Configures the primitive assembly (PA), rasterizer, and output merger (OM) blocks.

use std::default::Default;
use std::fmt;

use target;

/// The front face winding order of a set of vertices.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum FrontFace {
    /// Clockwise winding order.
    Clockwise,
    /// Counter-clockwise winding order.
    CounterClockwise,
}

/// Width of a line.
pub type LineWidth = f32;
#[allow(missing_docs)]
pub type OffsetFactor = f32;
#[allow(missing_docs)]
pub type OffsetUnits = u32;

/// How to offset vertices in screen space, if at all.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Offset(pub OffsetFactor, pub OffsetUnits);

/// Which face, if any, to cull.
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum CullFace {
    Nothing,
    Front,
    Back,
}

/// How to rasterize a primitive.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum RasterMethod {
    /// Rasterize as a point.
    Point,
    /// Rasterize as a line with the given width.
    Line(LineWidth),
    /// Rasterize as a face with a given cull face mode.
    Fill(CullFace),
}

/// Primitive rasterization state. Note that GL allows different raster
/// method to be used for front and back, while this abstraction does not.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Primitive {
    /// Which vertex winding is considered to be the front face for culling.
    pub front_face: FrontFace,
    /// How to rasterize this primitive.
    pub method: RasterMethod,
    /// Any polygon offset to apply.
    pub offset: Option<Offset>,
}

impl Primitive {
    /// Get the cull face, if any, for this primitive state.
    pub fn get_cull_face(&self) -> CullFace {
        match self.method {
            RasterMethod::Fill(mode) => mode,
            _ => CullFace::Nothing,
        }
    }
}

impl Default for Primitive {
    fn default() -> Primitive {
        Primitive {
            front_face: FrontFace::CounterClockwise,
            method: RasterMethod::Fill(CullFace::Nothing),
            offset: None,
        }
    }
}

/// Multi-sampling rasterization mode
#[derive(Eq, Ord, PartialEq, PartialOrd, Copy, Clone, Debug)]
pub struct MultiSample;
    //sample_mask: u16,
    //alpha_to_coverage: bool,

/// A pixel-wise comparison function.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum Comparison {
    /// `false`
    Never,
    /// `x < y`
    Less,
    /// `x <= y`
    LessEqual,
    /// `x == y`
    Equal,
    /// `x >= y`
    GreaterEqual,
    /// `x > y`
    Greater,
    /// `x != y`
    NotEqual,
    /// `true`
    Always,
}

/// Stencil mask operation.
#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum StencilOp {
    /// Keep the current value in the stencil buffer (no change).
    Keep,
    /// Set the value in the stencil buffer to zero.
    Zero,
    /// Set the stencil buffer value to `value` from `StencilSide`
    Replace,
    /// Increment the stencil buffer value, clamping to its maximum value.
    IncrementClamp,
    /// Increment the stencil buffer value, wrapping around to 0 on overflow.
    IncrementWrap,
    /// Decrement the stencil buffer value, clamping to its minimum value.
    DecrementClamp,
    /// Decrement the stencil buffer value, wrapping around to the maximum value on overflow.
    DecrementWrap,
    /// Bitwise invert the current value in the stencil buffer.
    Invert,
}

/// Complete stencil state for a given side of a face.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct StencilSide {
    /// Comparison function to use to determine if the stencil test passes.
    pub fun: Comparison,
    /// Reference value to compare the value in the stencil buffer with.
    pub value: target::Stencil,
    /// A mask that is ANDd with both the stencil buffer value and the reference value when they
    /// are read before doing the stencil test.
    pub mask_read: target::Stencil,
    /// A mask that is ANDd with the stencil value before writing to the stencil buffer.
    pub mask_write: target::Stencil,
    /// What operation to do if the stencil test fails.
    pub op_fail: StencilOp,
    /// What operation to do if the stenil test passes but the depth test fails.
    pub op_depth_fail: StencilOp,
    /// What operation to do if both the depth and stencil test pass.
    pub op_pass: StencilOp,
}

impl Default for StencilSide {
    fn default() -> StencilSide {
        StencilSide {
            fun: Comparison::Always,
            value: 0,
            mask_read: -1,
            mask_write: -1,
            op_fail: StencilOp::Keep,
            op_depth_fail: StencilOp::Keep,
            op_pass: StencilOp::Keep,
        }
    }
}

/// Complete stencil state, specifying how to handle the front and back side of a face.
#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct Stencil {
    pub front: StencilSide,
    pub back: StencilSide,
}

/// Depth test state.
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct Depth {
    /// Comparison function to use.
    pub fun: Comparison,
    /// Specify whether to write to the depth buffer or not.
    pub write: bool,
}

impl Default for Depth {
    fn default() -> Depth {
        Depth {
            fun: Comparison::Always,
            write: false,
        }
    }
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum Equation {
    /// Adds source and destination.
    /// Source and destination are multiplied by blending parameters before addition.
    Add,
    /// Subtracts destination from source.
    /// Source and destination are multiplied by blending parameters before subtraction.
    Sub,
    /// Subtracts source from destination.
    /// Source and destination are multiplied by blending parameters before subtraction.
    RevSub,
    /// Component-wise minimum value of source and destination.
    /// Blending parameters are ignored.
    Min,
    /// Component-wise maximum value of source and destination.
    /// Blending parameters are ignored.
    Max,
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum InverseFlag {
    Normal,
    Inverse,
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub enum BlendValue {
    Zero,
    SourceColor,
    SourceAlpha,
    SourceAlphaSaturated,
    DestColor,
    DestAlpha,
    ConstColor,
    ConstAlpha,
}

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct Factor(pub InverseFlag, pub BlendValue);

#[allow(missing_docs)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Hash, Copy, Clone, Debug)]
pub struct BlendChannel {
    pub equation: Equation,
    pub source: Factor,
    pub destination: Factor,
}

impl Default for BlendChannel {
    fn default() -> BlendChannel {
        BlendChannel {
            equation: Equation::Add,
            source: Factor(InverseFlag::Inverse, BlendValue::Zero),
            destination: Factor(InverseFlag::Normal, BlendValue::Zero),
        }
    }
}

#[allow(missing_docs)]
#[derive(Copy)]
pub struct Blend {
    pub color: BlendChannel,
    pub alpha: BlendChannel,
    pub value: target::ColorValue,
}

impl Default for Blend {
    fn default() -> Blend {
        Blend {
            color: Default::default(),
            alpha: Default::default(),
            value: [0.0, 0.0, 0.0, 0.0],
        }
    }
}

impl PartialEq for Blend {
    fn eq(&self, other: &Blend) -> bool {
        self.color == other.color &&
        self.alpha == other.alpha &&
        self.value == other.value
    }
}

impl Clone for Blend {
    fn clone(&self) -> Blend {
        Blend {
            color: self.color.clone(),
            alpha: self.alpha.clone(),
            value: self.value,
        }
    }
}

impl fmt::Debug for Blend {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Blend {{ color: {:?}, alpha: {:?}, value: {:?} }}",
               self.color, self.alpha, &self.value[..])
    }
}

bitflags!(
    #[allow(missing_docs)]
    flags ColorMask: u32 {  //u8 is preferred, but doesn't seem to work well
        const RED     = 0x1,
        const GREEN   = 0x2,
        const BLUE    = 0x4,
        const ALPHA   = 0x8,
        const MASK_ALL = 0xF
    }
);

impl fmt::Debug for ColorMask {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let values = [
            (RED,   "Red"  ),
            (GREEN, "Green"),
            (BLUE,  "Blue" ),
            (ALPHA, "Alpha"),
        ];

        try!(write!(f, "ColorMask("));
        for (i, &(_, name)) in values.iter()
            .filter(|&&(flag, _)| self.contains(flag))
            .enumerate()
        {
            if i == 0 {
                try!(write!(f, "{}", name))
            } else {
                try!(write!(f, " | {}", name))
            }
        }
        try!(write!(f, ")"));

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


<commit_before>use std::path::PathBuf;

use rayon::prelude::*;
use syntect::parsing::SyntaxSet;

use crate::config::Config;
use crate::page::source::Source;
use crate::page::Page;

pub fn build(in_dir: PathBuf) -> Result<(), String> {
    let in_dir = std::fs::canonicalize(in_dir).map_err(|e| e.to_string())?;
    let config_path = in_dir.join(PathBuf::from("_data/config.json5"));
    let config = Config::from_file(&config_path)?;

    let syntax_set = load_syntaxes();

    get_files_to_load(in_dir)
        .into_par_iter()
        .map(|path| {
            std::fs::read_to_string(&path)
                .map(|contents| Source {
                    path: path.clone(),
                    contents,
                })
                .map_err(|e| format!("{}: {}", path.display(), e))
        })
        .map(|result| {
            result.and_then(|source| {
                Page::new(&source, &syntax_set)
                    .map_err(|e| format!("{}: {}", source.path.display(), e))
            })
        })
        .map(|result| {
            result.and_then(|page| {
                let path = page.path(&config.output);
                println!(
                    "built final path {} from {} and {}",
                    &path.display(),
                    &page.metadata.slug,
                    &config.output.display()
                );
                let containing_dir = path
                    .parent()
                    .ok_or_else(|| format!("{} should have a containing dir!", path.display()))?;
                std::fs::create_dir_all(containing_dir)
                    .map_err(|e| format!("{}: {}", path.display(), e.to_string()))?;
                println!("writing {}", path.display());
                std::fs::write(&path, page.contents)
                    .map_err(|e| format!("{}: {}", path.display(), e))
            })
        })
        .fold(
            || Ok(()),
            |so_far, result| match (so_far, result) {
                (Ok(_), Ok(_)) => Ok(()),
                (Err(s), Ok(_)) => Err(s),
                (Ok(_), Err(e)) => Err(e),
                (Err(s), Err(e)) => Err(s + &e),
            },
        )
        .collect()
}

fn get_files_to_load(in_dir: PathBuf) -> Vec<PathBuf> {
    let content_dir = in_dir.join("content");
    let content_glob = content_dir.to_string_lossy() + "/**/*.md";

    let (ok_files, err_files): (Vec<PathBuf>, Vec<String>) = glob::glob(&content_glob)
        .unwrap_or_else(|_| panic!("bad glob: '{}'", &content_glob))
        .fold((vec![], vec![]), |(mut good, mut bad), result| {
            match result {
                Ok(path) => good.push(path),
                Err(e) => bad.push(e.to_string()),
            };

            (good, bad)
        });

    for err in err_files {
        eprintln!("problem with {}", err);
    }

    ok_files
}

fn load_syntaxes() -> SyntaxSet {
    // let mut extra_syntaxes_dir = std::env::current_dir().map_err(|e| format!("{}", e))?;
    // extra_syntaxes_dir.push("syntaxes");

    let syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // let mut syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // syntax_builder
    //     .add_from_folder(&extra_syntaxes_dir, false)
    //     .map_err(|e| format!("could not load {}: {}", &extra_syntaxes_dir.display(), e))?;

    syntax_builder.build()
}
<commit_msg>Output `.html`<commit_after>use std::path::PathBuf;

use rayon::prelude::*;
use syntect::parsing::SyntaxSet;

use crate::config::Config;
use crate::page::source::Source;
use crate::page::Page;

pub fn build(in_dir: PathBuf) -> Result<(), String> {
    let in_dir = std::fs::canonicalize(in_dir).map_err(|e| e.to_string())?;
    let config_path = in_dir.join(PathBuf::from("_data/config.json5"));
    let config = Config::from_file(&config_path)?;

    let syntax_set = load_syntaxes();

    get_files_to_load(in_dir)
        .into_par_iter()
        .map(|path| {
            std::fs::read_to_string(&path)
                .map(|contents| Source {
                    path: path.clone(),
                    contents,
                })
                .map_err(|e| format!("{}: {}", path.display(), e))
        })
        .map(|result| {
            result.and_then(|source| {
                Page::new(&source, &syntax_set)
                    .map_err(|e| format!("{}: {}", source.path.display(), e))
            })
        })
        .map(|result| {
            result.and_then(|page| {
                let path = page.path(&config.output);
                println!(
                    "built final path {} from {} and {}",
                    &path.display(),
                    &page.metadata.slug,
                    &config.output.display()
                );
                let containing_dir = path
                    .parent()
                    .ok_or_else(|| format!("{} should have a containing dir!", path.display()))?;
                std::fs::create_dir_all(containing_dir)
                    .map_err(|e| format!("{}: {}", path.display(), e.to_string()))?;
                println!("writing {}", path.display());
                std::fs::write(&path.with_extension("html"), page.contents)
                    .map_err(|e| format!("{}: {}", path.display(), e))
            })
        })
        .fold(
            || Ok(()),
            |so_far, result| match (so_far, result) {
                (Ok(_), Ok(_)) => Ok(()),
                (Err(s), Ok(_)) => Err(s),
                (Ok(_), Err(e)) => Err(e),
                (Err(s), Err(e)) => Err(s + &e),
            },
        )
        .collect()
}

fn get_files_to_load(in_dir: PathBuf) -> Vec<PathBuf> {
    let content_dir = in_dir.join("content");
    let content_glob = content_dir.to_string_lossy() + "/**/*.md";

    let (ok_files, err_files): (Vec<PathBuf>, Vec<String>) = glob::glob(&content_glob)
        .unwrap_or_else(|_| panic!("bad glob: '{}'", &content_glob))
        .fold((vec![], vec![]), |(mut good, mut bad), result| {
            match result {
                Ok(path) => good.push(path),
                Err(e) => bad.push(e.to_string()),
            };

            (good, bad)
        });

    for err in err_files {
        eprintln!("problem with {}", err);
    }

    ok_files
}

fn load_syntaxes() -> SyntaxSet {
    // let mut extra_syntaxes_dir = std::env::current_dir().map_err(|e| format!("{}", e))?;
    // extra_syntaxes_dir.push("syntaxes");

    let syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // let mut syntax_builder = SyntaxSet::load_defaults_newlines().into_builder();
    // syntax_builder
    //     .add_from_folder(&extra_syntaxes_dir, false)
    //     .map_err(|e| format!("could not load {}: {}", &extra_syntaxes_dir.display(), e))?;

    syntax_builder.build()
}
<|endoftext|>


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

use std::path::Path;

use std::io;

use error;
use graph;
use rules::Rules;
use resource;
use task;
use node::Task;

/// Represents a build. This holds the context necessary for all build
/// operations.
pub struct Build<'a> {
    /// Root of the build. This is the directory containing the "button.json"
    /// file and is the default path from which all subprocesses are spawned.
    /// The working directories of tasks are relative to this path.
    root: &'a Path,

    /// Whether or not this is a dry run. This needs to be passed to child build
    /// systems.
    dryrun: bool,
}

impl<'a> Build<'a> {
    pub fn new(root: &'a Path, dryrun: bool) -> Build<'a> {
        Build {
            root: root,
            dryrun: dryrun,
        }
    }

    /// Runs a build.
    pub fn build<'b>(&self,
                     rules: &'b Rules,
                     threads: usize)
                     -> Result<(), error::Error<'b>> {

        println!("Root directory: {:?}", self.root);

        if self.dryrun {
            println!("Note: This is a dry run. Nothing is affected.");
        }

        let g = graph::from_rules(rules)?;

        if let Err(err) = graph::traverse(&g,
                                          |id, node| self.visit(id, node),
                                          threads) {
            // TODO: Propagate error.
            println!("{:?}", err);
        }

        Ok(())
    }

    /// Visitor function for a node.
    fn visit(&self, id: usize, node: graph::Node) -> Result<bool, String> {
        match node {
            graph::Node::Resource(r) => self.visit_resource(id, r),
            graph::Node::Task(t) => self.visit_task(id, t),
        }
    }

    /// Called when visiting a resource type node in the build graph.
    fn visit_resource(&self,
                      id: usize,
                      node: &resource::Res)
                      -> Result<bool, String> {
        println!("thread {} :: {:?}", id, node);

        // TODO: Determine if this resource has changed.

        // Only visit child nodes if this node's state has changed. For example,
        // when compiling an object file, if the generated object file has not
        // changed, there is no need to perform linking.
        Ok(true)
    }

    /// Called when visiting a task type node in the build graph.
    fn visit_task(&self,
                  id: usize,
                  node: &[task::Task])
                  -> Result<bool, String> {
        println!("thread {} :: {:?}", id, node);

        let mut stdout = io::stdout();

        for task in node {
            // TODO:
            //  1. Handle errors
            //  2. Handle retries
            //  3. Handle sending build events
            task.run(&mut stdout);
        }

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

use std::path::Path;

use std::io;

use error;
use graph;
use rules::Rules;
use resource;
use task;
use node::Task;

/// Represents a build. This holds the context necessary for all build
/// operations.
pub struct Build<'a> {
    /// Root of the build. This is the directory containing the "button.json"
    /// file and is the default path from which all subprocesses are spawned.
    /// The working directories of tasks are relative to this path.
    root: &'a Path,

    /// Whether or not this is a dry run. This needs to be passed to child build
    /// systems.
    dryrun: bool,
}

impl<'a> Build<'a> {
    pub fn new(root: &'a Path, dryrun: bool) -> Build<'a> {
        Build {
            root: root,
            dryrun: dryrun,
        }
    }

    /// Runs a build.
    pub fn build<'b>(&self,
                     rules: &'b Rules,
                     threads: usize)
                     -> Result<(), error::Error<'b>> {

        println!("Root directory: {:?}", self.root);

        if self.dryrun {
            println!("Note: This is a dry run. Nothing is affected.");
        }

        let g = graph::from_rules(rules)?;

        if let Err(err) = graph::traverse(&g,
                                          |id, node| self.visit(id, node),
                                          threads) {
            // TODO: Propagate error.
            println!("{:?}", err);
        }

        Ok(())
    }

    /// Visitor function for a node.
    fn visit(&self, id: usize, node: graph::Node) -> Result<bool, io::Error> {
        match node {
            graph::Node::Resource(r) => self.visit_resource(id, r),
            graph::Node::Task(t) => self.visit_task(id, t),
        }
    }

    /// Called when visiting a resource type node in the build graph.
    fn visit_resource(&self,
                      id: usize,
                      node: &resource::Res)
                      -> Result<bool, io::Error> {
        println!("thread {} :: {:?}", id, node);

        // TODO: Determine if this resource has changed.

        // Only visit child nodes if this node's state has changed. For example,
        // when compiling an object file, if the generated object file has not
        // changed, there is no need to perform linking.
        Ok(true)
    }

    /// Called when visiting a task type node in the build graph.
    fn visit_task(&self,
                  id: usize,
                  node: &[task::Task])
                  -> Result<bool, io::Error> {
        println!("thread {} :: {:?}", id, node);

        let mut stdout = io::stdout();

        for task in node {
            // TODO:
            //  1. Handle retries
            //  2. Handle sending build events
            task.run(&mut stdout)?;
        }

        Ok(true)
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::fs;
use std::env;
use std::path::PathBuf;

#[cfg(unix)] use std::os::unix::fs::MetadataExt;

use flate2::read::GzDecoder;
use tar::Archive;
use curl::http;
use walkdir::{WalkDir, WalkDirIterator, DirEntry};
use time;

use error::TldrError::{self, CacheError, UpdateError};
use types::OsType;

#[derive(Debug)]
pub struct Cache {
    url: String,
    os: OsType,
}

impl Cache {

    pub fn new<S>(url: S, os: OsType) -> Cache where S: Into<String> {
        Cache {
            url: url.into(),
            os: os,
        }
    }

    /// Return the path to the cache directory.
    fn get_cache_dir(&self) -> Result<PathBuf, TldrError> {
        let home_dir = try!(env::home_dir().ok_or(CacheError("Could not determine home directory".into())));
        Ok(home_dir.join(".cache").join("tldr-rs"))
    }

    /// Download the archive
    fn download(&self) -> Result<http::Response, TldrError> {
        let resp = try!(
            http::handle()
                 .follow_location(1)
                 .get(&self.url[..])
                 .exec()
        );
        Ok(resp)
    }

    /// Decompress and open the archive
    fn decompress<R: Read>(&self, reader: R) -> Result<Archive<GzDecoder<R>>, TldrError> {
        let decoder = try!(GzDecoder::new(reader).map_err(|_| UpdateError("Could not decode gzip data".into())));
        Ok(Archive::new(decoder))
    }

    /// Update the pages cache.
    pub fn update(&self) -> Result<(), TldrError> {
        // First, download the compressed data
        let response = try!(self.download());

        // Decompress the response body into an `Archive`
        let mut archive = try!(self.decompress(response.get_body()));

        // Determine paths
        let cache_dir = try!(self.get_cache_dir());

        // Clear cache directory
        // Note: This is not the best solution. Ideally we would download the
        // archive to a temporary directory and then swap the two directories.
        // But renaming a directory doesn't work across filesystems and Rust
        // does not yet offer a recursive directory copying function. So for
        // now, we'll use this approach.
        try!(self.clear());

        // Extract archive
        try!(archive.unpack(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not unpack compressed data: {}", e))
        }));

        Ok(())
    }

    #[cfg(unix)]
    /// Return the number of seconds since the cache directory was last modified.
    pub fn last_update(&self) -> Option<i64> {
        if let Ok(cache_dir) = self.get_cache_dir() {
            if let Ok(metadata) = fs::metadata(cache_dir.join("tldr-master")) {
                let mtime = metadata.mtime();
                let now = time::now_utc().to_timespec();
                return Some(now.sec - mtime)
            };
        };
        None
    }

    /// Return the platform directory.
    fn get_platform_dir(&self) -> Option<&'static str> {
        match self.os {
            OsType::Linux => Some("linux"),
            OsType::OsX => Some("osx"),
            OsType::SunOs => None, // TODO: Does Rust support SunOS?
            OsType::Other => None,
        }
    }

    /// Search for a page and return the path to it.
    pub fn find_page(&self, name: &str) -> Option<PathBuf> {
        // Build page file name
        let page_filename = format!("{}.md", name);

        // Get platform dir
        let platforms_dir = match self.get_cache_dir() {
            Ok(cache_dir) => cache_dir.join("tldr-master").join("pages"),
            _ => return None,
        };

        // Determine platform
        let platform = self.get_platform_dir();

        // Search for the page in the platform specific directory
        if let Some(pf) = platform {
            let path = platforms_dir.join(&pf).join(&page_filename);
            if path.exists() && path.is_file() {
                return Some(path);
            }
        }

        // If platform is not supported or if platform specific page does not exist,
        // look up the page in the "common" directory.
        let path = platforms_dir.join("common").join(&page_filename);

        // Return it if it exists, otherwise give up and return `None`
        if path.exists() && path.is_file() {
            Some(path)
        } else {
            None
        }
    }

    /// Return the available pages.
    pub fn list_pages(&self) -> Result<Vec<String>, TldrError> {

        // Determine platforms directory and platform
        let cache_dir = try!(self.get_cache_dir());
        let platforms_dir = cache_dir.join("tldr-master").join("pages");
        let platform_dir = self.get_platform_dir();

        // Closure that allows the WalkDir instance to traverse platform
        // specific and common page directories, but not others.
        let should_walk = |entry: &DirEntry| -> bool {
            let file_type = entry.file_type();
            let file_name = match entry.file_name().to_str() {
                Some(name) => name,
                None => return false,
            };
            if file_type.is_dir() {
                if file_name == "common" {
                    return true;
                }
                if let Some(platform) = platform_dir {
                    return file_name == platform;
                }
            } else if file_type.is_file() {
                return true
            }
            false
        };

        // Recursively walk through common and (if applicable) platform specific directory
        let mut pages = WalkDir::new(platforms_dir)
                                .min_depth(1) // Skip root directory
                                .into_iter()
                                .filter_entry(|e| should_walk(e)) // Filter out pages for other architectures
                                .filter_map(|e| e.ok()) // Convert results to options, filter out errors
                                .filter_map(|e| {
                                    let path = e.path();
                                    let extension = &path.extension().and_then(|s| s.to_str()).unwrap_or("");
                                    if e.file_type().is_file() && extension == &"md" {
                                        path.file_stem().and_then(|stem| stem.to_str().map(|s| s.into()))
                                    } else {
                                        None
                                    }
                                })
                                .collect::<Vec<String>>();
        pages.sort();
        pages.dedup();
        Ok(pages)
    }

    /// Delete the cache directory.
    pub fn clear(&self) -> Result<(), TldrError> {
        let path = try!(self.get_cache_dir());
        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(|_| {
                CacheError(format!("Could not remove cache directory ({}).", path.display()))
            }));
        } else if path.exists() {
            return Err(CacheError(format!("Cache path ({}) is not a directory.", path.display())));
        } else {
            return Err(CacheError(format!("Cache path ({}) does not exist.", path.display())));
        };
        Ok(())
    }

}
<commit_msg>Allow overriding of cache dir<commit_after>use std::io::Read;
use std::fs;
use std::env;
use std::path::PathBuf;

#[cfg(unix)] use std::os::unix::fs::MetadataExt;

use flate2::read::GzDecoder;
use tar::Archive;
use curl::http;
use walkdir::{WalkDir, WalkDirIterator, DirEntry};
use time;

use error::TldrError::{self, CacheError, UpdateError};
use types::OsType;

#[derive(Debug)]
pub struct Cache {
    url: String,
    os: OsType,
}

impl Cache {

    pub fn new<S>(url: S, os: OsType) -> Cache where S: Into<String> {
        Cache {
            url: url.into(),
            os: os,
        }
    }

    /// Return the path to the cache directory.
    fn get_cache_dir(&self) -> Result<PathBuf, TldrError> {
        // Allow overriding the cache directory by setting the
        // $TLDR_RS_CACHE_DIR env variable.
        if let Ok(value) = env::var("TLDR_RS_CACHE_DIR") {
            let path = PathBuf::from(value);
            if path.exists() && path.is_dir() {
                return Ok(path)
            } else {
                return Err(CacheError(
                    "Path specified by $TLDR_RS_CACHE_DIR \
                     does not exist or is not a directory.".into()
                ));
            }
        };

        // Otherwise, fall back to ~/.cache/tldr-rs
        let home_dir = try!(env::home_dir().ok_or(
            CacheError("Could not determine home directory".into())
        ));
        Ok(home_dir.join(".cache").join("tldr-rs"))
    }

    /// Download the archive
    fn download(&self) -> Result<http::Response, TldrError> {
        let resp = try!(
            http::handle()
                 .follow_location(1)
                 .get(&self.url[..])
                 .exec()
        );
        Ok(resp)
    }

    /// Decompress and open the archive
    fn decompress<R: Read>(&self, reader: R) -> Result<Archive<GzDecoder<R>>, TldrError> {
        let decoder = try!(GzDecoder::new(reader).map_err(|_| UpdateError("Could not decode gzip data".into())));
        Ok(Archive::new(decoder))
    }

    /// Update the pages cache.
    pub fn update(&self) -> Result<(), TldrError> {
        // First, download the compressed data
        let response = try!(self.download());

        // Decompress the response body into an `Archive`
        let mut archive = try!(self.decompress(response.get_body()));

        // Determine paths
        let cache_dir = try!(self.get_cache_dir());

        // Clear cache directory
        // Note: This is not the best solution. Ideally we would download the
        // archive to a temporary directory and then swap the two directories.
        // But renaming a directory doesn't work across filesystems and Rust
        // does not yet offer a recursive directory copying function. So for
        // now, we'll use this approach.
        try!(self.clear());

        // Extract archive
        try!(archive.unpack(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not unpack compressed data: {}", e))
        }));

        Ok(())
    }

    #[cfg(unix)]
    /// Return the number of seconds since the cache directory was last modified.
    pub fn last_update(&self) -> Option<i64> {
        if let Ok(cache_dir) = self.get_cache_dir() {
            if let Ok(metadata) = fs::metadata(cache_dir.join("tldr-master")) {
                let mtime = metadata.mtime();
                let now = time::now_utc().to_timespec();
                return Some(now.sec - mtime)
            };
        };
        None
    }

    /// Return the platform directory.
    fn get_platform_dir(&self) -> Option<&'static str> {
        match self.os {
            OsType::Linux => Some("linux"),
            OsType::OsX => Some("osx"),
            OsType::SunOs => None, // TODO: Does Rust support SunOS?
            OsType::Other => None,
        }
    }

    /// Search for a page and return the path to it.
    pub fn find_page(&self, name: &str) -> Option<PathBuf> {
        // Build page file name
        let page_filename = format!("{}.md", name);

        // Get platform dir
        let platforms_dir = match self.get_cache_dir() {
            Ok(cache_dir) => cache_dir.join("tldr-master").join("pages"),
            _ => return None,
        };

        // Determine platform
        let platform = self.get_platform_dir();

        // Search for the page in the platform specific directory
        if let Some(pf) = platform {
            let path = platforms_dir.join(&pf).join(&page_filename);
            if path.exists() && path.is_file() {
                return Some(path);
            }
        }

        // If platform is not supported or if platform specific page does not exist,
        // look up the page in the "common" directory.
        let path = platforms_dir.join("common").join(&page_filename);

        // Return it if it exists, otherwise give up and return `None`
        if path.exists() && path.is_file() {
            Some(path)
        } else {
            None
        }
    }

    /// Return the available pages.
    pub fn list_pages(&self) -> Result<Vec<String>, TldrError> {

        // Determine platforms directory and platform
        let cache_dir = try!(self.get_cache_dir());
        let platforms_dir = cache_dir.join("tldr-master").join("pages");
        let platform_dir = self.get_platform_dir();

        // Closure that allows the WalkDir instance to traverse platform
        // specific and common page directories, but not others.
        let should_walk = |entry: &DirEntry| -> bool {
            let file_type = entry.file_type();
            let file_name = match entry.file_name().to_str() {
                Some(name) => name,
                None => return false,
            };
            if file_type.is_dir() {
                if file_name == "common" {
                    return true;
                }
                if let Some(platform) = platform_dir {
                    return file_name == platform;
                }
            } else if file_type.is_file() {
                return true
            }
            false
        };

        // Recursively walk through common and (if applicable) platform specific directory
        let mut pages = WalkDir::new(platforms_dir)
                                .min_depth(1) // Skip root directory
                                .into_iter()
                                .filter_entry(|e| should_walk(e)) // Filter out pages for other architectures
                                .filter_map(|e| e.ok()) // Convert results to options, filter out errors
                                .filter_map(|e| {
                                    let path = e.path();
                                    let extension = &path.extension().and_then(|s| s.to_str()).unwrap_or("");
                                    if e.file_type().is_file() && extension == &"md" {
                                        path.file_stem().and_then(|stem| stem.to_str().map(|s| s.into()))
                                    } else {
                                        None
                                    }
                                })
                                .collect::<Vec<String>>();
        pages.sort();
        pages.dedup();
        Ok(pages)
    }

    /// Delete the cache directory.
    pub fn clear(&self) -> Result<(), TldrError> {
        let path = try!(self.get_cache_dir());
        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(|_| {
                CacheError(format!("Could not remove cache directory ({}).", path.display()))
            }));
        } else if path.exists() {
            return Err(CacheError(format!("Cache path ({}) is not a directory.", path.display())));
        } else {
            return Err(CacheError(format!("Cache path ({}) does not exist.", path.display())));
        };
        Ok(())
    }

}
<|endoftext|>


<commit_before>use std::io::Read;
use std::fs;
use std::env;
use std::path::PathBuf;

#[cfg(unix)] use std::os::unix::fs::MetadataExt;

use flate2::read::GzDecoder;
use tar::Archive;
use curl::http;
use walkdir::{WalkDir, WalkDirIterator, DirEntry};
use time;

use error::TldrError::{self, CacheError, UpdateError};
use types::OsType;

#[derive(Debug)]
pub struct Cache {
    url: String,
    os: OsType,
}

impl Cache {

    pub fn new<S>(url: S, os: OsType) -> Cache where S: Into<String> {
        Cache {
            url: url.into(),
            os: os,
        }
    }

    /// Return the path to the cache directory.
    fn get_cache_dir(&self) -> Result<PathBuf, TldrError> {
        let home_dir = try!(env::home_dir().ok_or(CacheError("Could not determine home directory".into())));
        Ok(home_dir.join(".cache").join("tldr-rs"))
    }

    /// Download the archive
    fn download(&self) -> Result<http::Response, TldrError> {
        let resp = try!(
            http::handle()
                 .follow_location(1)
                 .get(&self.url[..])
                 .exec()
        );
        Ok(resp)
    }

    /// Decompress and open the archive
    fn decompress<R: Read>(&self, reader: R) -> Result<Archive<GzDecoder<R>>, TldrError> {
        let decoder = try!(GzDecoder::new(reader).map_err(|_| UpdateError("Could not decode gzip data".into())));
        Ok(Archive::new(decoder))
    }

    /// Update the pages cache. Return the number of cached pages.
    pub fn update(&self) -> Result<(), TldrError> {
        // First, download the compressed data
        let response = try!(self.download());

        // Decompress the response body into an `Archive`
        let mut archive = try!(self.decompress(response.get_body()));

        // Determine paths
        let cache_dir = try!(self.get_cache_dir());

        // Extract archive
        try!(archive.unpack(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not unpack compressed data: {}", e))
        }));

        // Make sure that cache directory exists
        debug!("Ensure cache directory {:?} exists", &cache_dir);
        try!(fs::create_dir_all(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not create cache directory: {}", e))
        }));

        Ok(())
    }

    #[cfg(unix)]
    /// Return the number of seconds since the cache directory was last modified.
    pub fn last_update(&self) -> Option<i64> {
        if let Ok(cache_dir) = self.get_cache_dir() {
            if let Ok(metadata) = fs::metadata(cache_dir) {
                let mtime = metadata.mtime();
                let now = time::now_utc().to_timespec();
                return Some(now.sec - mtime)
            };
        };
        None
    }

    /// Return the platform directory.
    fn get_platform_dir(&self) -> Option<&'static str> {
        match self.os {
            OsType::Linux => Some("linux"),
            OsType::OsX => Some("osx"),
            OsType::SunOs => None, // TODO: Does Rust support SunOS?
            OsType::Other => None,
        }
    }

    /// Search for a page and return the path to it.
    pub fn find_page(&self, name: &str) -> Option<PathBuf> {
        // Build page file name
        let page_filename = format!("{}.md", name);

        // Get platform dir
        let platforms_dir = match self.get_cache_dir() {
            Ok(cache_dir) => cache_dir.join("tldr-master").join("pages"),
            _ => return None,
        };

        // Determine platform
        let platform = self.get_platform_dir();

        // Search for the page in the platform specific directory
        if let Some(pf) = platform {
            let path = platforms_dir.join(&pf).join(&page_filename);
            if path.exists() && path.is_file() {
                return Some(path);
            }
        }

        // If platform is not supported or if platform specific page does not exist,
        // look up the page in the "common" directory.
        let path = platforms_dir.join("common").join(&page_filename);

        // Return it if it exists, otherwise give up and return `None`
        if path.exists() && path.is_file() {
            Some(path)
        } else {
            None
        }
    }

    /// Return the available pages.
    pub fn list_pages(&self) -> Result<Vec<String>, TldrError> {

        // Determine platforms directory and platform
        let cache_dir = try!(self.get_cache_dir());
        let platforms_dir = cache_dir.join("tldr-master").join("pages");
        let platform_dir = self.get_platform_dir();

        // Closure that allows the WalkDir instance to traverse platform
        // specific and common page directories, but not others.
        let should_walk = |entry: &DirEntry| -> bool {
            let file_type = entry.file_type();
            let file_name = match entry.file_name().to_str() {
                Some(name) => name,
                None => return false,
            };
            if file_type.is_dir() {
                if file_name == "common" {
                    return true;
                }
                if let Some(platform) = platform_dir {
                    return file_name == platform;
                }
            } else if file_type.is_file() {
                return true
            }
            false
        };

        // Recursively walk through common and (if applicable) platform specific directory
        let mut pages = WalkDir::new(platforms_dir)
                                .min_depth(1) // Skip root directory
                                .into_iter()
                                .filter_entry(|e| should_walk(e)) // Filter out pages for other architectures
                                .filter_map(|e| e.ok()) // Convert results to options, filter out errors
                                .filter_map(|e| {
                                    let path = e.path();
                                    let extension = &path.extension().and_then(|s| s.to_str()).unwrap_or("");
                                    if e.file_type().is_file() && extension == &"md" {
                                        path.file_stem().and_then(|stem| stem.to_str().map(|s| s.into()))
                                    } else {
                                        None
                                    }
                                })
                                .collect::<Vec<String>>();
        pages.sort();
        pages.dedup();
        Ok(pages)
    }

    /// Delete the cache directory.
    pub fn clear(&self) -> Result<(), TldrError> {
        let path = try!(self.get_cache_dir());
        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(|_| {
                CacheError(format!("Could not remove cache directory ({}).", path.display()))
            }));
        } else if path.exists() {
            return Err(CacheError(format!("Cache path ({}) is not a directory.", path.display())));
        } else {
            return Err(CacheError(format!("Cache path ({}) does not exist.", path.display())));
        };
        Ok(())
    }

}
<commit_msg>Fix detection of outdated cache<commit_after>use std::io::Read;
use std::fs;
use std::env;
use std::path::PathBuf;

#[cfg(unix)] use std::os::unix::fs::MetadataExt;

use flate2::read::GzDecoder;
use tar::Archive;
use curl::http;
use walkdir::{WalkDir, WalkDirIterator, DirEntry};
use time;

use error::TldrError::{self, CacheError, UpdateError};
use types::OsType;

#[derive(Debug)]
pub struct Cache {
    url: String,
    os: OsType,
}

impl Cache {

    pub fn new<S>(url: S, os: OsType) -> Cache where S: Into<String> {
        Cache {
            url: url.into(),
            os: os,
        }
    }

    /// Return the path to the cache directory.
    fn get_cache_dir(&self) -> Result<PathBuf, TldrError> {
        let home_dir = try!(env::home_dir().ok_or(CacheError("Could not determine home directory".into())));
        Ok(home_dir.join(".cache").join("tldr-rs"))
    }

    /// Download the archive
    fn download(&self) -> Result<http::Response, TldrError> {
        let resp = try!(
            http::handle()
                 .follow_location(1)
                 .get(&self.url[..])
                 .exec()
        );
        Ok(resp)
    }

    /// Decompress and open the archive
    fn decompress<R: Read>(&self, reader: R) -> Result<Archive<GzDecoder<R>>, TldrError> {
        let decoder = try!(GzDecoder::new(reader).map_err(|_| UpdateError("Could not decode gzip data".into())));
        Ok(Archive::new(decoder))
    }

    /// Update the pages cache. Return the number of cached pages.
    pub fn update(&self) -> Result<(), TldrError> {
        // First, download the compressed data
        let response = try!(self.download());

        // Decompress the response body into an `Archive`
        let mut archive = try!(self.decompress(response.get_body()));

        // Determine paths
        let cache_dir = try!(self.get_cache_dir());

        // Extract archive
        try!(archive.unpack(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not unpack compressed data: {}", e))
        }));

        // Make sure that cache directory exists
        debug!("Ensure cache directory {:?} exists", &cache_dir);
        try!(fs::create_dir_all(&cache_dir).map_err(|e| {
            UpdateError(format!("Could not create cache directory: {}", e))
        }));

        Ok(())
    }

    #[cfg(unix)]
    /// Return the number of seconds since the cache directory was last modified.
    pub fn last_update(&self) -> Option<i64> {
        if let Ok(cache_dir) = self.get_cache_dir() {
            if let Ok(metadata) = fs::metadata(cache_dir.join("tldr-master")) {
                let mtime = metadata.mtime();
                let now = time::now_utc().to_timespec();
                return Some(now.sec - mtime)
            };
        };
        None
    }

    /// Return the platform directory.
    fn get_platform_dir(&self) -> Option<&'static str> {
        match self.os {
            OsType::Linux => Some("linux"),
            OsType::OsX => Some("osx"),
            OsType::SunOs => None, // TODO: Does Rust support SunOS?
            OsType::Other => None,
        }
    }

    /// Search for a page and return the path to it.
    pub fn find_page(&self, name: &str) -> Option<PathBuf> {
        // Build page file name
        let page_filename = format!("{}.md", name);

        // Get platform dir
        let platforms_dir = match self.get_cache_dir() {
            Ok(cache_dir) => cache_dir.join("tldr-master").join("pages"),
            _ => return None,
        };

        // Determine platform
        let platform = self.get_platform_dir();

        // Search for the page in the platform specific directory
        if let Some(pf) = platform {
            let path = platforms_dir.join(&pf).join(&page_filename);
            if path.exists() && path.is_file() {
                return Some(path);
            }
        }

        // If platform is not supported or if platform specific page does not exist,
        // look up the page in the "common" directory.
        let path = platforms_dir.join("common").join(&page_filename);

        // Return it if it exists, otherwise give up and return `None`
        if path.exists() && path.is_file() {
            Some(path)
        } else {
            None
        }
    }

    /// Return the available pages.
    pub fn list_pages(&self) -> Result<Vec<String>, TldrError> {

        // Determine platforms directory and platform
        let cache_dir = try!(self.get_cache_dir());
        let platforms_dir = cache_dir.join("tldr-master").join("pages");
        let platform_dir = self.get_platform_dir();

        // Closure that allows the WalkDir instance to traverse platform
        // specific and common page directories, but not others.
        let should_walk = |entry: &DirEntry| -> bool {
            let file_type = entry.file_type();
            let file_name = match entry.file_name().to_str() {
                Some(name) => name,
                None => return false,
            };
            if file_type.is_dir() {
                if file_name == "common" {
                    return true;
                }
                if let Some(platform) = platform_dir {
                    return file_name == platform;
                }
            } else if file_type.is_file() {
                return true
            }
            false
        };

        // Recursively walk through common and (if applicable) platform specific directory
        let mut pages = WalkDir::new(platforms_dir)
                                .min_depth(1) // Skip root directory
                                .into_iter()
                                .filter_entry(|e| should_walk(e)) // Filter out pages for other architectures
                                .filter_map(|e| e.ok()) // Convert results to options, filter out errors
                                .filter_map(|e| {
                                    let path = e.path();
                                    let extension = &path.extension().and_then(|s| s.to_str()).unwrap_or("");
                                    if e.file_type().is_file() && extension == &"md" {
                                        path.file_stem().and_then(|stem| stem.to_str().map(|s| s.into()))
                                    } else {
                                        None
                                    }
                                })
                                .collect::<Vec<String>>();
        pages.sort();
        pages.dedup();
        Ok(pages)
    }

    /// Delete the cache directory.
    pub fn clear(&self) -> Result<(), TldrError> {
        let path = try!(self.get_cache_dir());
        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(|_| {
                CacheError(format!("Could not remove cache directory ({}).", path.display()))
            }));
        } else if path.exists() {
            return Err(CacheError(format!("Cache path ({}) is not a directory.", path.display())));
        } else {
            return Err(CacheError(format!("Cache path ({}) does not exist.", path.display())));
        };
        Ok(())
    }

}
<|endoftext|>