<commit_before>#![crate_name = "uu_printf"]
#![allow(dead_code)]

extern crate itertools;
extern crate uucore;

mod cli;
mod memo;
mod tokenize;

static NAME: &str = "printf";
static VERSION: &str = "0.0.1";
static SHORT_USAGE: &str = "printf: usage: printf [-v var] format [arguments]";
static LONGHELP_LEAD: &str = "printf

 USAGE: printf FORMATSTRING [ARGUMENT]...

 basic anonymous string templating:

 prints format string at least once, repeating as long as there are remaining arguments
 output prints escaped literals in the format string as character literals
 output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
  Prints the , replacing escaped character sequences with character literals
    and substitution field sequences with passed arguments

 literally, with the exception of the below
    escaped character sequences, and the substitution sequences described further down.

  ESCAPE SEQUENCES

  The following escape sequences, organized here in alphabetical order,
  will print the corresponding character literal:

  \"   double quote

  \\\\    backslash

  \\a  alert (BEL)

  \\b  backspace

  \\c  End-of-Input

  \\e  escape

  \\f  form feed

  \\n  new line

  \\r  carriage return

  \\t  horizontal tab

  \\v  vertical tab

  \\NNN byte with value expressed in octal value NNN (1 to 3 digits)
        values greater than 256 will be treated

  \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

  \\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

  \\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

  %% a single %

  SUBSTITUTIONS

  SUBSTITUTION QUICK REFERENCE

  Fields

  %s - string
  %b - string parsed for literals
    second parameter is max length

  %c - char
    no second parameter

  %i or %d - 64-bit integer
  %u - 64 bit unsigned integer
  %x or %X - 64-bit unsigned integer as hex
  %o - 64-bit unsigned integer as octal
    second parameter is min-width, integer
     output below that width is padded with leading zeroes

  %f or %F - decimal floating point value
  %e or %E - scientific notation floating point value
  %g or %G - shorter of specially interpreted decimal or SciNote floating point value.
    second parameter is
      -max places after decimal point for floating point output
      -max number of significant digits for scientific notation output

  parameterizing fields

  examples:

  printf '%4.3i' 7
  has a first parameter of 4
     and a second parameter of 3
  will result in ' 007'

  printf '%.1s' abcde
  has no first parameter
     and a second parameter of 1
  will result in 'a'

  printf '%4c' q
  has a first parameter of 4
     and no second parameter
  will result in  '   q'

  The first parameter of a field is the minimum width to pad the output to
   if the output is less than this absolute value of this width,
   it will be padded with leading spaces, or, if the argument is negative,
   with trailing spaces. the default is zero.

  The second parameter of a field is particular to the output field type.
   defaults can be found in the full substitution help below

  special prefixes to numeric arguments
    0 (e.g. 010) - interpret argument as octal (integer output fields only)
    0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
    \' (e.g. \'a) - interpret argument as a character constant

  HOW TO USE SUBSTITUTIONS

  Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
  particular way. E.g.

      printf 'the letter %X comes before the letter %X' 10 11

  will print

     'the letter A comes before the letter B'

  because the substitution field %X means
  'take an integer argument and write it as a hexadecimal number'

  Passing more arguments than are in the format string will cause the format string to be
   repeated for the remaining substitutions

     printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

  will print

     'it is 22 F in Portland
      it is 25 F in Boston
      it is 27 F in Boston
     '
  If a format string is printed but there are less arguments remaining
   than there are substitution fields, substitution fields without
   an argument will default to empty strings, or for numeric fields
   the value 0

  AVAILABLE SUBSTITUTIONS

  This program, like GNU coreutils printf,
  interprets a modified subset of the POSIX C printf spec,
  a quick reference to substitutions is below.

   STRING SUBSTITUTIONS
    All string fields have a 'max width' parameter
    %.3s means 'print no more than three characters of the original input'

   %s - string

   %b - escaped string - the string will be checked for any escaped literals from
         the escaped literal list above, and translate them to literal charcters.
         e.g. \\n will be transformed into a newline character.

        One special rule about %b mode is that octal literals are intepreted differently
        In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
        instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
        still be interpreted and not throw a warning, you will have problems if you use this for a
        literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

   CHAR SUBSTITUTIONS
    The character field does not have a secondary parameter.

   %c - a single character

   INTEGER SUBSTITUTIONS
    All integer fields have a 'pad with zero' parameter
    %.4i means an integer which if it is less than 4 digits in length,
    is padded with leading zeros until it is 4 digits in length.

   %d or %i - 64-bit integer

   %u - 64 bit unsigned integer

   %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
            %X instead of %x means to use uppercase letters for 'a' through 'f'

   %o - 64 bit unsigned integer printed in octal (base 8)

   FLOATING POINT SUBSTITUTIONS

    All floating point fields have a 'max decimal places / max significant digits' parameter
    %.10f means a decimal floating point with 7 decimal places past 0
    %.10e means a scientific notation number with 10 significant digits
    %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
          of each's output.

    Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
    double first before being rendered to text. For both implementations do not expect meaningful
    precision past the 18th decimal place. When using a number of decimal places that is 18 or
    higher, you can expect variation in output between GNU coreutils printf and this printf at the
    18th decimal place of +/- 1

   %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
        default. There is not past-double behavior parity with Coreutils printf, values are not
        estimated or adjusted beyond input values.

   %e or %E - floating point value presented in scientific notation
            7 significant digits by default
            %E means use to use uppercase E for the mantissa.

   %g or %G - floating point value presented in the shorter of decimal and scientific notation
            behaves differently from %f and %E, please see posix printf spec for full details,
            some examples of different behavior:

            Sci Note has 6 significant digits by default
            Trailing zeroes are removed
            Instead of being truncated, digit after last is rounded

   Like other behavior in this utility, the design choices of floating point
    behavior in this utility is selected to reproduce in exact
    the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

  USING PARAMETERS
   Most substitution fields can be parameterized using up to 2 numbers that can
   be passed to the field, between the % sign and the field letter.

   The 1st parameter always indicates the minimum width of output, it is useful for creating
     columnar output. Any output that would be less than this minimum width is padded with
     leading spaces
   The 2nd parameter is proceeded by a dot.
   You do not have to use parameters

  SPECIAL FORMS OF INPUT
   For numeric input, the following additional forms of input are accepted besides decimal:

    Octal (only with integer): if the argument begins with a 0 the proceeding characters
      will be interpreted as octal (base 8) for integer fields

    Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
      will be interpreted as hex (base 16) for any numeric fields
      for float fields, hexadecimal input results in a precision
      limit (in converting input past the decimal point) of 10^-15

    Character Constant: if the argument begins with a single quote character, the first byte
      of the next character will be interpreted as an 8-bit unsigned integer. If there are
      additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
      is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

pub fn uumain(args: Vec<String>) -> i32 {
    let location = &args[0];
    if args.len() <= 1 {
        println!(
            "{0}: missing operand\nTry '{0} --help' for more information.",
            location
        );
        return 1;
    }
    let ref formatstr = args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", NAME, VERSION);
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    0
}
<commit_msg>refactor/polish ~ fix `cargo clippy` complaints ('better' ref use/toplevel_ref_arg)<commit_after>#![crate_name = "uu_printf"]
#![allow(dead_code)]

extern crate itertools;
extern crate uucore;

mod cli;
mod memo;
mod tokenize;

static NAME: &str = "printf";
static VERSION: &str = "0.0.1";
static SHORT_USAGE: &str = "printf: usage: printf [-v var] format [arguments]";
static LONGHELP_LEAD: &str = "printf

 USAGE: printf FORMATSTRING [ARGUMENT]...

 basic anonymous string templating:

 prints format string at least once, repeating as long as there are remaining arguments
 output prints escaped literals in the format string as character literals
 output replaces anonymous fields with the next unused argument, formatted according to the field.

Options:
    --help              display this help and exit
    --version           output version information and exit

";
static LONGHELP_BODY: &str = "
  Prints the , replacing escaped character sequences with character literals
    and substitution field sequences with passed arguments

 literally, with the exception of the below
    escaped character sequences, and the substitution sequences described further down.

  ESCAPE SEQUENCES

  The following escape sequences, organized here in alphabetical order,
  will print the corresponding character literal:

  \"   double quote

  \\\\    backslash

  \\a  alert (BEL)

  \\b  backspace

  \\c  End-of-Input

  \\e  escape

  \\f  form feed

  \\n  new line

  \\r  carriage return

  \\t  horizontal tab

  \\v  vertical tab

  \\NNN byte with value expressed in octal value NNN (1 to 3 digits)
        values greater than 256 will be treated

  \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits)

  \\uHHHH Unicode (IEC 10646) character with value expressed in hexadecimal value HHHH (4 digits)

  \\uHHHH Unicode character with value expressed in hexadecimal value HHHH (8 digits)

  %% a single %

  SUBSTITUTIONS

  SUBSTITUTION QUICK REFERENCE

  Fields

  %s - string
  %b - string parsed for literals
    second parameter is max length

  %c - char
    no second parameter

  %i or %d - 64-bit integer
  %u - 64 bit unsigned integer
  %x or %X - 64-bit unsigned integer as hex
  %o - 64-bit unsigned integer as octal
    second parameter is min-width, integer
     output below that width is padded with leading zeroes

  %f or %F - decimal floating point value
  %e or %E - scientific notation floating point value
  %g or %G - shorter of specially interpreted decimal or SciNote floating point value.
    second parameter is
      -max places after decimal point for floating point output
      -max number of significant digits for scientific notation output

  parameterizing fields

  examples:

  printf '%4.3i' 7
  has a first parameter of 4
     and a second parameter of 3
  will result in ' 007'

  printf '%.1s' abcde
  has no first parameter
     and a second parameter of 1
  will result in 'a'

  printf '%4c' q
  has a first parameter of 4
     and no second parameter
  will result in  '   q'

  The first parameter of a field is the minimum width to pad the output to
   if the output is less than this absolute value of this width,
   it will be padded with leading spaces, or, if the argument is negative,
   with trailing spaces. the default is zero.

  The second parameter of a field is particular to the output field type.
   defaults can be found in the full substitution help below

  special prefixes to numeric arguments
    0 (e.g. 010) - interpret argument as octal (integer output fields only)
    0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only)
    \' (e.g. \'a) - interpret argument as a character constant

  HOW TO USE SUBSTITUTIONS

  Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a
  particular way. E.g.

      printf 'the letter %X comes before the letter %X' 10 11

  will print

     'the letter A comes before the letter B'

  because the substitution field %X means
  'take an integer argument and write it as a hexadecimal number'

  Passing more arguments than are in the format string will cause the format string to be
   repeated for the remaining substitutions

     printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York

  will print

     'it is 22 F in Portland
      it is 25 F in Boston
      it is 27 F in Boston
     '
  If a format string is printed but there are less arguments remaining
   than there are substitution fields, substitution fields without
   an argument will default to empty strings, or for numeric fields
   the value 0

  AVAILABLE SUBSTITUTIONS

  This program, like GNU coreutils printf,
  interprets a modified subset of the POSIX C printf spec,
  a quick reference to substitutions is below.

   STRING SUBSTITUTIONS
    All string fields have a 'max width' parameter
    %.3s means 'print no more than three characters of the original input'

   %s - string

   %b - escaped string - the string will be checked for any escaped literals from
         the escaped literal list above, and translate them to literal charcters.
         e.g. \\n will be transformed into a newline character.

        One special rule about %b mode is that octal literals are intepreted differently
        In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
        instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
        still be interpreted and not throw a warning, you will have problems if you use this for a
        literal whose code begins with zero, as it will be viewed as in \\0NNN form.)

   CHAR SUBSTITUTIONS
    The character field does not have a secondary parameter.

   %c - a single character

   INTEGER SUBSTITUTIONS
    All integer fields have a 'pad with zero' parameter
    %.4i means an integer which if it is less than 4 digits in length,
    is padded with leading zeros until it is 4 digits in length.

   %d or %i - 64-bit integer

   %u - 64 bit unsigned integer

   %x or %X - 64 bit unsigned integer printed in Hexadecimal (base 16)
            %X instead of %x means to use uppercase letters for 'a' through 'f'

   %o - 64 bit unsigned integer printed in octal (base 8)

   FLOATING POINT SUBSTITUTIONS

    All floating point fields have a 'max decimal places / max significant digits' parameter
    %.10f means a decimal floating point with 7 decimal places past 0
    %.10e means a scientific notation number with 10 significant digits
    %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
          of each's output.

    Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
    double first before being rendered to text. For both implementations do not expect meaningful
    precision past the 18th decimal place. When using a number of decimal places that is 18 or
    higher, you can expect variation in output between GNU coreutils printf and this printf at the
    18th decimal place of +/- 1

   %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
        default. There is not past-double behavior parity with Coreutils printf, values are not
        estimated or adjusted beyond input values.

   %e or %E - floating point value presented in scientific notation
            7 significant digits by default
            %E means use to use uppercase E for the mantissa.

   %g or %G - floating point value presented in the shorter of decimal and scientific notation
            behaves differently from %f and %E, please see posix printf spec for full details,
            some examples of different behavior:

            Sci Note has 6 significant digits by default
            Trailing zeroes are removed
            Instead of being truncated, digit after last is rounded

   Like other behavior in this utility, the design choices of floating point
    behavior in this utility is selected to reproduce in exact
    the behavior of GNU coreutils' printf from an inputs and outputs standpoint.

  USING PARAMETERS
   Most substitution fields can be parameterized using up to 2 numbers that can
   be passed to the field, between the % sign and the field letter.

   The 1st parameter always indicates the minimum width of output, it is useful for creating
     columnar output. Any output that would be less than this minimum width is padded with
     leading spaces
   The 2nd parameter is proceeded by a dot.
   You do not have to use parameters

  SPECIAL FORMS OF INPUT
   For numeric input, the following additional forms of input are accepted besides decimal:

    Octal (only with integer): if the argument begins with a 0 the proceeding characters
      will be interpreted as octal (base 8) for integer fields

    Hexadecimal: if the argument begins with 0x the proceeding characters will be interpreted
      will be interpreted as hex (base 16) for any numeric fields
      for float fields, hexadecimal input results in a precision
      limit (in converting input past the decimal point) of 10^-15

    Character Constant: if the argument begins with a single quote character, the first byte
      of the next character will be interpreted as an 8-bit unsigned integer. If there are
      additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
      is set)

WRITTEN BY :
  Nathan E. Ross, et al. for the uutils project

MORE INFO :
  https://github.com/uutils/coreutils

COPYRIGHT :
  Copyright 2015 uutils project.
  Licensed under the MIT License, please see LICENSE file for details

";

pub fn uumain(args: Vec<String>) -> i32 {
    let location = &args[0];
    if args.len() <= 1 {
        println!(
            "{0}: missing operand\nTry '{0} --help' for more information.",
            location
        );
        return 1;
    }
    let formatstr = &args[1];

    if formatstr == "--help" {
        print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY);
    } else if formatstr == "--version" {
        println!("{} {}", NAME, VERSION);
    } else {
        let printf_args = &args[2..];
        memo::Memo::run_all(formatstr, printf_args);
    }
    0
}
<|endoftext|>


<commit_before>use prolog::and_stack::*;
use prolog::instructions::*;

use std::ops::IndexMut;

type Trail = Vec<(Ref, HeapCellValue)>;

pub(crate) trait CopierTarget: IndexMut<usize, Output=HeapCellValue>
{
    fn source(&self) -> usize;
    fn threshold(&self) -> usize;
    fn push(&mut self, HeapCellValue);
    fn store(&self, Addr) -> Addr;
    fn deref(&self, Addr) -> Addr;
    fn stack(&mut self) -> &mut AndStack;
}

pub(crate)
fn copy_term<T: CopierTarget>(target: T, addr: Addr)
{
    let mut copy_term_state = CopyTermState::new(target);
    copy_term_state.copy_term_impl(addr);
}

struct CopyTermState<T: CopierTarget> {
    trail: Trail,
    scan: usize,
    old_h: usize,
    target: T
}

impl<T: CopierTarget> CopyTermState<T> {
    fn new(target: T) -> Self {
        CopyTermState {
            trail: vec![],
            scan:  target.source(),
            old_h: target.threshold(),
            target
        }
    }

    #[inline]
    fn value_at_scan(&mut self) -> &mut HeapCellValue {
        let scan = self.scan;
        &mut self.target[scan]
    }

    fn reinstantiate_var(&mut self, addr: Addr, threshold: usize)
    {
        match addr {
            Addr::HeapCell(h) => {
                self.target[threshold] = HeapCellValue::Addr(Addr::HeapCell(threshold));
                self.target[h] = HeapCellValue::Addr(Addr::HeapCell(threshold));
                self.trail.push((Ref::HeapCell(h), HeapCellValue::Addr(Addr::HeapCell(h))));
            },
            Addr::StackCell(fr, sc) => {
                self.target[threshold] = HeapCellValue::Addr(Addr::HeapCell(threshold));
                self.target.stack()[fr][sc] = Addr::HeapCell(threshold);
                self.trail.push((Ref::StackCell(fr, sc), HeapCellValue::Addr(Addr::StackCell(fr, sc))));
            },
            Addr::AttrVar(h) => {
                self.target[threshold] = HeapCellValue::Addr(Addr::AttrVar(threshold));
                self.target[h] = HeapCellValue::Addr(Addr::AttrVar(threshold));
                self.trail.push((Ref::AttrVar(h), HeapCellValue::Addr(Addr::AttrVar(h))));
            },
            _ => {}
        }
    }

    fn copied_list(&mut self, addr: usize) -> bool {
        if let HeapCellValue::Addr(Addr::Lis(addr)) = self.target[addr].clone() {
            if addr >= self.old_h {
                *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(addr));
                self.scan += 1;
                return true;
            }
        }

        false
    }

    fn copy_list(&mut self, addr: usize) {
        if self.copied_list(addr) {
            return;
        }

        let threshold = self.target.threshold();
        *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(threshold));

        let hcv = self.target[addr].clone();
        self.target.push(hcv.clone());

        let ra = hcv.as_addr(threshold);
        let rd = self.target.store(self.target.deref(ra));

        match rd.clone() {
            Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h =>
                self.target[threshold] = HeapCellValue::Addr(rd),
            ra @ Addr::AttrVar(_) | ra @ Addr::HeapCell(..) | ra @ Addr::StackCell(..) =>
                if ra == rd {
                    self.reinstantiate_var(ra, threshold);
                } else {
                    self.target[threshold] = HeapCellValue::Addr(ra);
                },
            _ => {
                self.trail.push((Ref::HeapCell(addr), self.target[addr].clone()));
                self.target[addr] = HeapCellValue::Addr(Addr::Lis(threshold))
            }
        };

        let hcv = self.target[addr + 1].clone();
        self.target.push(hcv);

        self.scan += 1;
    }

    fn copy_var(&mut self, addr: Addr) {
        let rd = self.target.store(self.target.deref(addr.clone()));

        match rd.clone() {
            Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h => {
                *self.value_at_scan() = HeapCellValue::Addr(rd);
                self.scan += 1;
            },
            Addr::AttrVar(h) if addr == rd => {
                let threshold = self.target.threshold();
                self.target.push(HeapCellValue::Addr(Addr::AttrVar(threshold)));

                let list_val = self.target[h + 1].clone();
                self.target.push(list_val);

                self.reinstantiate_var(addr, threshold);
                *self.value_at_scan() = HeapCellValue::Addr(Addr::AttrVar(threshold));
            },
            _ if addr == rd => {
                let scan = self.scan;
                self.reinstantiate_var(addr, scan);
                self.scan += 1;
            },
            _ => *self.value_at_scan() = HeapCellValue::Addr(rd)
        }
    }

    fn copy_structure(&mut self, addr: usize) {
        match self.target[addr].clone() {
            HeapCellValue::NamedStr(arity, name, fixity) => {
                let threshold = self.target.threshold();

                *self.value_at_scan() = HeapCellValue::Addr(Addr::Str(threshold));
                self.target[addr] = HeapCellValue::Addr(Addr::Str(threshold));

                self.trail.push((Ref::HeapCell(addr),
                                 HeapCellValue::NamedStr(arity, name.clone(), fixity)));

                self.target.push(HeapCellValue::NamedStr(arity, name, fixity));

                for i in 0 .. arity {
                    let hcv = self.target[addr + 1 + i].clone();
                    self.target.push(hcv);
                }
            },
            HeapCellValue::Addr(Addr::Str(addr)) =>
                *self.value_at_scan() = HeapCellValue::Addr(Addr::Str(addr)),
            _ => {}
        }

        self.scan += 1;
    }

    fn copy_term_impl(&mut self, addr: Addr) {
        self.target.push(HeapCellValue::Addr(addr));

        while self.scan < self.target.threshold() {
            match self.value_at_scan().clone() {
                HeapCellValue::NamedStr(..) =>
                    self.scan += 1,
                HeapCellValue::Addr(addr) =>
                    match addr.clone() {
                        Addr::Lis(addr) =>
                            self.copy_list(addr),
                        Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) =>
                            self.copy_var(addr),
                        Addr::Str(addr) =>
                            self.copy_structure(addr),
                        Addr::Con(_) =>
                            self.scan += 1
                    }
            }
        }

        self.unwind_trail();
    }

    fn unwind_trail(&mut self) {
        for (r, value) in self.trail.drain(0 ..) {
            match r {
                Ref::AttrVar(h) | Ref::HeapCell(h) =>
                    self.target[h] = value.clone(),
                Ref::StackCell(fr, sc) =>
                    self.target.stack()[fr][sc] = value.as_addr(0)
            }
        }
    }
}
<commit_msg>remove clone from unwind_trail in copier.rs<commit_after>use prolog::and_stack::*;
use prolog::instructions::*;

use std::ops::IndexMut;

type Trail = Vec<(Ref, HeapCellValue)>;

pub(crate) trait CopierTarget: IndexMut<usize, Output=HeapCellValue>
{
    fn source(&self) -> usize;
    fn threshold(&self) -> usize;
    fn push(&mut self, HeapCellValue);
    fn store(&self, Addr) -> Addr;
    fn deref(&self, Addr) -> Addr;
    fn stack(&mut self) -> &mut AndStack;
}

pub(crate)
fn copy_term<T: CopierTarget>(target: T, addr: Addr)
{
    let mut copy_term_state = CopyTermState::new(target);
    copy_term_state.copy_term_impl(addr);
}

struct CopyTermState<T: CopierTarget> {
    trail: Trail,
    scan: usize,
    old_h: usize,
    target: T
}

impl<T: CopierTarget> CopyTermState<T> {
    fn new(target: T) -> Self {
        CopyTermState {
            trail: vec![],
            scan:  target.source(),
            old_h: target.threshold(),
            target
        }
    }

    #[inline]
    fn value_at_scan(&mut self) -> &mut HeapCellValue {
        let scan = self.scan;
        &mut self.target[scan]
    }

    fn reinstantiate_var(&mut self, addr: Addr, threshold: usize)
    {
        match addr {
            Addr::HeapCell(h) => {
                self.target[threshold] = HeapCellValue::Addr(Addr::HeapCell(threshold));
                self.target[h] = HeapCellValue::Addr(Addr::HeapCell(threshold));
                self.trail.push((Ref::HeapCell(h), HeapCellValue::Addr(Addr::HeapCell(h))));
            },
            Addr::StackCell(fr, sc) => {
                self.target[threshold] = HeapCellValue::Addr(Addr::HeapCell(threshold));
                self.target.stack()[fr][sc] = Addr::HeapCell(threshold);
                self.trail.push((Ref::StackCell(fr, sc), HeapCellValue::Addr(Addr::StackCell(fr, sc))));
            },
            Addr::AttrVar(h) => {
                self.target[threshold] = HeapCellValue::Addr(Addr::AttrVar(threshold));
                self.target[h] = HeapCellValue::Addr(Addr::AttrVar(threshold));
                self.trail.push((Ref::AttrVar(h), HeapCellValue::Addr(Addr::AttrVar(h))));
            },
            _ => {}
        }
    }

    fn copied_list(&mut self, addr: usize) -> bool {
        if let HeapCellValue::Addr(Addr::Lis(addr)) = self.target[addr].clone() {
            if addr >= self.old_h {
                *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(addr));
                self.scan += 1;
                return true;
            }
        }

        false
    }

    fn copy_list(&mut self, addr: usize) {
        if self.copied_list(addr) {
            return;
        }

        let threshold = self.target.threshold();
        *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(threshold));

        let hcv = self.target[addr].clone();
        self.target.push(hcv.clone());

        let ra = hcv.as_addr(threshold);
        let rd = self.target.store(self.target.deref(ra));

        match rd.clone() {
            Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h =>
                self.target[threshold] = HeapCellValue::Addr(rd),
            ra @ Addr::AttrVar(_) | ra @ Addr::HeapCell(..) | ra @ Addr::StackCell(..) =>
                if ra == rd {
                    self.reinstantiate_var(ra, threshold);
                } else {
                    self.target[threshold] = HeapCellValue::Addr(ra);
                },
            _ => {
                self.trail.push((Ref::HeapCell(addr), self.target[addr].clone()));
                self.target[addr] = HeapCellValue::Addr(Addr::Lis(threshold))
            }
        };

        let hcv = self.target[addr + 1].clone();
        self.target.push(hcv);

        self.scan += 1;
    }

    fn copy_var(&mut self, addr: Addr) {
        let rd = self.target.store(self.target.deref(addr.clone()));

        match rd.clone() {
            Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h => {
                *self.value_at_scan() = HeapCellValue::Addr(rd);
                self.scan += 1;
            },
            Addr::AttrVar(h) if addr == rd => {
                let threshold = self.target.threshold();
                self.target.push(HeapCellValue::Addr(Addr::AttrVar(threshold)));

                let list_val = self.target[h + 1].clone();
                self.target.push(list_val);

                self.reinstantiate_var(addr, threshold);
                *self.value_at_scan() = HeapCellValue::Addr(Addr::AttrVar(threshold));
            },
            _ if addr == rd => {
                let scan = self.scan;
                self.reinstantiate_var(addr, scan);
                self.scan += 1;
            },
            _ => *self.value_at_scan() = HeapCellValue::Addr(rd)
        }
    }

    fn copy_structure(&mut self, addr: usize) {
        match self.target[addr].clone() {
            HeapCellValue::NamedStr(arity, name, fixity) => {
                let threshold = self.target.threshold();

                *self.value_at_scan() = HeapCellValue::Addr(Addr::Str(threshold));
                self.target[addr] = HeapCellValue::Addr(Addr::Str(threshold));

                self.trail.push((Ref::HeapCell(addr),
                                 HeapCellValue::NamedStr(arity, name.clone(), fixity)));

                self.target.push(HeapCellValue::NamedStr(arity, name, fixity));

                for i in 0 .. arity {
                    let hcv = self.target[addr + 1 + i].clone();
                    self.target.push(hcv);
                }
            },
            HeapCellValue::Addr(Addr::Str(addr)) =>
                *self.value_at_scan() = HeapCellValue::Addr(Addr::Str(addr)),
            _ => {}
        }

        self.scan += 1;
    }

    fn copy_term_impl(&mut self, addr: Addr) {
        self.target.push(HeapCellValue::Addr(addr));

        while self.scan < self.target.threshold() {
            match self.value_at_scan().clone() {
                HeapCellValue::NamedStr(..) =>
                    self.scan += 1,
                HeapCellValue::Addr(addr) =>
                    match addr.clone() {
                        Addr::Lis(addr) =>
                            self.copy_list(addr),
                        Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) =>
                            self.copy_var(addr),
                        Addr::Str(addr) =>
                            self.copy_structure(addr),
                        Addr::Con(_) =>
                            self.scan += 1
                    }
            }
        }

        self.unwind_trail();
    }

    fn unwind_trail(&mut self) {
        for (r, value) in self.trail.drain(0 ..) {
            match r {
                Ref::AttrVar(h) | Ref::HeapCell(h) =>
                    self.target[h] = value,
                Ref::StackCell(fr, sc) =>
                    self.target.stack()[fr][sc] = value.as_addr(0)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::{fmt, ptr, slice, mem};
use std::ffi::{CStr, CString};

use openvr_sys as sys;

use {RenderModels, ControllerState, get_string};

impl<'a> RenderModels<'a> {
    /// Loads and returns a render model for use in the application. `name` should be a render model name from the
    /// `RenderModelName_String` property or an absolute path name to a render model on disk.
    ///
    /// The method returns `Ok(None)` while the render model is still being loaded. Call it at regular intervals until
    /// it returns `Ok(Some(model))`.
    pub fn load_render_model(&self, name: &CStr) -> Result<Option<Model>> {
        let mut ptr = ptr::null_mut();
        let r = unsafe {
            self.0.LoadRenderModel_Async.unwrap()(name.as_ptr() as *mut _, &mut ptr)
        };
        match Error(r) {
            error::NONE => Ok(Some(Model { ptr: ptr, sys: self.0 })),
            error::LOADING => Ok(None),
            x => Err(x),
        }
    }

    /// Returns the number of components of the specified render model.
    ///
    /// Components are useful when client application wish to draw, label, or otherwise interact with components of tracked objects.
    /// Examples controller components:
    ///  renderable things such as triggers, buttons
    ///  non-renderable things which include coordinate systems such as 'tip', 'base', a neutral controller agnostic hand-pose
    ///  If all controller components are enumerated and rendered, it will be equivalent to drawing the traditional render model
    ///  Returns 0 if components not supported, >0 otherwise
    pub fn component_count(&self, model: &CStr) -> u32 {
        unsafe { self.0.GetComponentCount.unwrap()(model.as_ptr() as *mut _) }
    }

    /// Get the names of available components.
    ///
    /// `component` does not correlate to a tracked device index, but is only used for iterating over all available
    /// components.  If it's out of range, this function will return None.  Otherwise, it will return the size of the
    /// buffer required for the name.
    pub fn component_name(&self, model: &CStr, component: u32) -> Option<CString> {
        unsafe { get_string(|ptr, n| self.0.GetComponentName.unwrap()(model.as_ptr() as *mut _, component, ptr, n)) }
    }

    /// Gets all component names of a given model
    pub fn component_names(&self, model: &CStr) -> ::std::vec::IntoIter<CString> { // FIXME: impl Iterator rather than allocating
        let n = self.component_count(model);
        (0..n).map(|i| self.component_name(model, i).expect("inconsistent component presence reported by OpenVR")).collect::<Vec<_>>().into_iter()
    }

    /// Use this to get the render model name for the specified rendermode/component combination, to be passed to
    /// `load_render_model`.
    ///
    /// If the component name is out of range, this function will return None.
    /// Otherwise, it will return the size of the buffer required for the name.
    pub fn component_render_model_name(&self, model: &CStr, component: &CStr) -> Option<CString> {
        unsafe {
            get_string(|ptr, n| self.0.GetComponentRenderModelName.unwrap()(
                model.as_ptr() as *mut _, component.as_ptr() as *mut _, ptr, n))
        }
    }

    /// Use this to query information about the component, as a function of the controller state.
    ///
    /// Returns None if the component is invalid or should not be rendered in the current state.
    ///
    /// For dynamic controller components (ex: trigger) values will reflect component motions
    /// For static components this will return a consistent value independent of the VRControllerState_t
    pub fn component_state(&self, model: &CStr, component: &CStr, state: &ControllerState, mode: &ControllerMode) -> Option<ComponentState> {
        unsafe {
            let mut out = mem::uninitialized();
            if self.0.GetComponentState.unwrap()(model.as_ptr() as *mut _, component.as_ptr() as *mut _,
                                                 state as *const _ as *mut _, mode as *const _ as *mut _,
                                                 &mut out as *mut _ as *mut _) {
                Some(out)
            } else {
                None
            }
        }
    }

    /// Loads and returns a texture for use in the application. Texture IDs can be obtained from
    /// `Model::diffuse_texture_id()`.
    ///
    /// The method returns `Ok(None)` while the texture is still being loaded. Call it at regular intervals until it
    /// returns `Ok(Some(texture))`.
    pub fn load_texture(&self, id: TextureId) -> Result<Option<Texture>> {
        let mut ptr = ptr::null_mut();
        let r = unsafe {
            self.0.LoadTexture_Async.unwrap()(id, &mut ptr)
        };
        match Error(r) {
            error::NONE => Ok(Some(Texture { ptr: ptr, sys: self.0 })),
            error::LOADING => Ok(None),
            x => Err(x),
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(sys::EVRRenderModelError);

pub mod error {
    use super::{sys, Error};

    pub const NONE: Error = Error(sys::EVRRenderModelError_VRRenderModelError_None);
    pub const LOADING: Error = Error(sys::EVRRenderModelError_VRRenderModelError_Loading);
    pub const NOT_SUPPORTED: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NotSupported);
    pub const INVALID_ARG: Error = Error(sys::EVRRenderModelError_VRRenderModelError_InvalidArg);
    pub const INVALID_MODEL: Error = Error(sys::EVRRenderModelError_VRRenderModelError_InvalidModel);
    pub const NO_SHAPES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NoShapes);
    pub const MULTIPLE_SHAPES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_MultipleShapes);
    pub const TOO_MANY_VERTICES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_TooManyVertices);
    pub const MULTIPLE_TEXTURES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_MultipleTextures);
    pub const BUFFER_TOO_SMALL: Error = Error(sys::EVRRenderModelError_VRRenderModelError_BufferTooSmall);
    pub const NOT_ENOUGH_NORMALS: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NotEnoughNormals);
    pub const NOT_ENOUGH_TEX_COORDS: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NotEnoughTexCoords);
    pub const INVALID_TEXTURE: Error = Error(sys::EVRRenderModelError_VRRenderModelError_InvalidTexture);
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad(::std::error::Error::description(self))
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        use self::error::*;
        match *self {
            NONE => "NONE",
            LOADING => "LOADING",
            NOT_SUPPORTED => "NOT_SUPPORTED",
            INVALID_ARG => "INVALID_ARG",
            INVALID_MODEL => "INVALID_MODEL",
            NO_SHAPES => "NO_SHAPES",
            MULTIPLE_SHAPES => "MULTIPLE_SHAPES",
            TOO_MANY_VERTICES => "TOO_MANY_VERTICES",
            MULTIPLE_TEXTURES => "MULTIPLE_TEXTURES",
            BUFFER_TOO_SMALL => "BUFFER_TOO_SMALL",
            NOT_ENOUGH_NORMALS => "NOT_ENOUGH_NORMALS",
            NOT_ENOUGH_TEX_COORDS => "NOT_ENOUGH_TEX_COORDS",
            INVALID_TEXTURE => "INVALID_TEXTURE",
            _ => "UNKNOWN",
        }
    }
}

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

pub type Result<T> = ::std::result::Result<T, Error>;

/// 3D geometry for rendering as an indexed triangle list
pub struct Model<'a> {
    ptr: *mut sys::RenderModel_t,
    sys: &'a sys::VR_IVRRenderModels_FnTable,
}

impl<'a> Model<'a> {
    pub fn vertices(&self) -> &[Vertex] {
        unsafe {
            let model = &*self.ptr;
            slice::from_raw_parts(model.rVertexData as *mut Vertex, model.unVertexCount as usize)
        }
    }

    pub fn indices(&self) -> &[u16] {
        unsafe {
            let model = &*self.ptr;
            slice::from_raw_parts(model.rIndexData, 3 * model.unTriangleCount as usize)
        }
    }

    pub fn diffuse_texture_id(&self) -> Option<TextureId> {
        let id = unsafe { (&*self.ptr).diffuseTextureId };
        if id < 0 { None } else { Some(id) }
    }
}

impl<'a> Drop for Model<'a> {
    fn drop(&mut self) { unsafe { self.sys.FreeRenderModel.unwrap()(self.ptr) } }
}

pub struct Texture<'a> {
    ptr: *mut sys::RenderModel_TextureMap_t,
    sys: &'a sys::VR_IVRRenderModels_FnTable,
}

impl<'a> Texture<'a> {
    pub fn dimensions(&self) -> (u16, u16) {
        let tex = unsafe { &*self.ptr };
        (tex.unWidth, tex.unHeight)
    }

    /// R8G8B8A8
    pub fn data(&self) -> &[u8] {
        unsafe {
            let tex = &*self.ptr;
            slice::from_raw_parts(tex.rubTextureMapData, tex.unWidth as usize * tex.unHeight as usize * 4)
        }
    }
}

impl<'a> Drop for Texture<'a> {
    fn drop(&mut self) { unsafe { self.sys.FreeTexture.unwrap()(self.ptr) } }
}

pub type TextureId = sys::TextureID_t;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
    pub texture_coord: [f32; 2],
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ControllerMode {
    pub scroll_wheel_visible: bool,
}

impl Default for ControllerMode {
    fn default() -> Self { ControllerMode { scroll_wheel_visible: false } }
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ComponentState {
    pub tracking_to_component_render_model: [[f32; 4]; 3],
    pub tracking_to_component_local: [[f32; 4]; 3],
    pub properties: ComponentProperties,
}

type ComponentProperties = sys::VRComponentProperties;

pub mod component_properties {
    use super::{sys, ComponentProperties};

    pub const IS_STATIC: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsStatic;
    pub const IS_VISIBLE: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsVisible;
    pub const IS_TOUCHED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsTouched;
    pub const IS_PRESSED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsPressed;
    pub const IS_SCROLLED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsScrolled;
}
<commit_msg>Render model component state helpers<commit_after>use std::{fmt, ptr, slice, mem};
use std::ffi::{CStr, CString};

use openvr_sys as sys;

use {RenderModels, ControllerState, get_string};

impl<'a> RenderModels<'a> {
    /// Loads and returns a render model for use in the application. `name` should be a render model name from the
    /// `RenderModelName_String` property or an absolute path name to a render model on disk.
    ///
    /// The method returns `Ok(None)` while the render model is still being loaded. Call it at regular intervals until
    /// it returns `Ok(Some(model))`.
    pub fn load_render_model(&self, name: &CStr) -> Result<Option<Model>> {
        let mut ptr = ptr::null_mut();
        let r = unsafe {
            self.0.LoadRenderModel_Async.unwrap()(name.as_ptr() as *mut _, &mut ptr)
        };
        match Error(r) {
            error::NONE => Ok(Some(Model { ptr: ptr, sys: self.0 })),
            error::LOADING => Ok(None),
            x => Err(x),
        }
    }

    /// Returns the number of components of the specified render model.
    ///
    /// Components are useful when client application wish to draw, label, or otherwise interact with components of tracked objects.
    /// Examples controller components:
    ///  renderable things such as triggers, buttons
    ///  non-renderable things which include coordinate systems such as 'tip', 'base', a neutral controller agnostic hand-pose
    ///  If all controller components are enumerated and rendered, it will be equivalent to drawing the traditional render model
    ///  Returns 0 if components not supported, >0 otherwise
    pub fn component_count(&self, model: &CStr) -> u32 {
        unsafe { self.0.GetComponentCount.unwrap()(model.as_ptr() as *mut _) }
    }

    /// Get the names of available components.
    ///
    /// `component` does not correlate to a tracked device index, but is only used for iterating over all available
    /// components.  If it's out of range, this function will return None.  Otherwise, it will return the size of the
    /// buffer required for the name.
    pub fn component_name(&self, model: &CStr, component: u32) -> Option<CString> {
        unsafe { get_string(|ptr, n| self.0.GetComponentName.unwrap()(model.as_ptr() as *mut _, component, ptr, n)) }
    }

    /// Gets all component names of a given model
    pub fn component_names(&self, model: &CStr) -> ::std::vec::IntoIter<CString> { // FIXME: impl Iterator rather than allocating
        let n = self.component_count(model);
        (0..n).map(|i| self.component_name(model, i).expect("inconsistent component presence reported by OpenVR")).collect::<Vec<_>>().into_iter()
    }

    /// Use this to get the render model name for the specified rendermode/component combination, to be passed to
    /// `load_render_model`.
    ///
    /// If the component name is out of range, this function will return None.
    /// Otherwise, it will return the size of the buffer required for the name.
    pub fn component_render_model_name(&self, model: &CStr, component: &CStr) -> Option<CString> {
        unsafe {
            get_string(|ptr, n| self.0.GetComponentRenderModelName.unwrap()(
                model.as_ptr() as *mut _, component.as_ptr() as *mut _, ptr, n))
        }
    }

    /// Use this to query information about the component, as a function of the controller state.
    ///
    /// Returns None if the component is invalid or should not be rendered in the current state.
    ///
    /// For dynamic controller components (ex: trigger) values will reflect component motions
    /// For static components this will return a consistent value independent of the VRControllerState_t
    pub fn component_state(&self, model: &CStr, component: &CStr, state: &ControllerState, mode: &ControllerMode) -> Option<ComponentState> {
        unsafe {
            let mut out = mem::uninitialized();
            if self.0.GetComponentState.unwrap()(model.as_ptr() as *mut _, component.as_ptr() as *mut _,
                                                 state as *const _ as *mut _, mode as *const _ as *mut _,
                                                 &mut out as *mut _ as *mut _) {
                Some(out)
            } else {
                None
            }
        }
    }

    /// Loads and returns a texture for use in the application. Texture IDs can be obtained from
    /// `Model::diffuse_texture_id()`.
    ///
    /// The method returns `Ok(None)` while the texture is still being loaded. Call it at regular intervals until it
    /// returns `Ok(Some(texture))`.
    pub fn load_texture(&self, id: TextureId) -> Result<Option<Texture>> {
        let mut ptr = ptr::null_mut();
        let r = unsafe {
            self.0.LoadTexture_Async.unwrap()(id, &mut ptr)
        };
        match Error(r) {
            error::NONE => Ok(Some(Texture { ptr: ptr, sys: self.0 })),
            error::LOADING => Ok(None),
            x => Err(x),
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(sys::EVRRenderModelError);

pub mod error {
    use super::{sys, Error};

    pub const NONE: Error = Error(sys::EVRRenderModelError_VRRenderModelError_None);
    pub const LOADING: Error = Error(sys::EVRRenderModelError_VRRenderModelError_Loading);
    pub const NOT_SUPPORTED: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NotSupported);
    pub const INVALID_ARG: Error = Error(sys::EVRRenderModelError_VRRenderModelError_InvalidArg);
    pub const INVALID_MODEL: Error = Error(sys::EVRRenderModelError_VRRenderModelError_InvalidModel);
    pub const NO_SHAPES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NoShapes);
    pub const MULTIPLE_SHAPES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_MultipleShapes);
    pub const TOO_MANY_VERTICES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_TooManyVertices);
    pub const MULTIPLE_TEXTURES: Error = Error(sys::EVRRenderModelError_VRRenderModelError_MultipleTextures);
    pub const BUFFER_TOO_SMALL: Error = Error(sys::EVRRenderModelError_VRRenderModelError_BufferTooSmall);
    pub const NOT_ENOUGH_NORMALS: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NotEnoughNormals);
    pub const NOT_ENOUGH_TEX_COORDS: Error = Error(sys::EVRRenderModelError_VRRenderModelError_NotEnoughTexCoords);
    pub const INVALID_TEXTURE: Error = Error(sys::EVRRenderModelError_VRRenderModelError_InvalidTexture);
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad(::std::error::Error::description(self))
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        use self::error::*;
        match *self {
            NONE => "NONE",
            LOADING => "LOADING",
            NOT_SUPPORTED => "NOT_SUPPORTED",
            INVALID_ARG => "INVALID_ARG",
            INVALID_MODEL => "INVALID_MODEL",
            NO_SHAPES => "NO_SHAPES",
            MULTIPLE_SHAPES => "MULTIPLE_SHAPES",
            TOO_MANY_VERTICES => "TOO_MANY_VERTICES",
            MULTIPLE_TEXTURES => "MULTIPLE_TEXTURES",
            BUFFER_TOO_SMALL => "BUFFER_TOO_SMALL",
            NOT_ENOUGH_NORMALS => "NOT_ENOUGH_NORMALS",
            NOT_ENOUGH_TEX_COORDS => "NOT_ENOUGH_TEX_COORDS",
            INVALID_TEXTURE => "INVALID_TEXTURE",
            _ => "UNKNOWN",
        }
    }
}

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

pub type Result<T> = ::std::result::Result<T, Error>;

/// 3D geometry for rendering as an indexed triangle list
pub struct Model<'a> {
    ptr: *mut sys::RenderModel_t,
    sys: &'a sys::VR_IVRRenderModels_FnTable,
}

impl<'a> Model<'a> {
    pub fn vertices(&self) -> &[Vertex] {
        unsafe {
            let model = &*self.ptr;
            slice::from_raw_parts(model.rVertexData as *mut Vertex, model.unVertexCount as usize)
        }
    }

    pub fn indices(&self) -> &[u16] {
        unsafe {
            let model = &*self.ptr;
            slice::from_raw_parts(model.rIndexData, 3 * model.unTriangleCount as usize)
        }
    }

    pub fn diffuse_texture_id(&self) -> Option<TextureId> {
        let id = unsafe { (&*self.ptr).diffuseTextureId };
        if id < 0 { None } else { Some(id) }
    }
}

impl<'a> Drop for Model<'a> {
    fn drop(&mut self) { unsafe { self.sys.FreeRenderModel.unwrap()(self.ptr) } }
}

pub struct Texture<'a> {
    ptr: *mut sys::RenderModel_TextureMap_t,
    sys: &'a sys::VR_IVRRenderModels_FnTable,
}

impl<'a> Texture<'a> {
    pub fn dimensions(&self) -> (u16, u16) {
        let tex = unsafe { &*self.ptr };
        (tex.unWidth, tex.unHeight)
    }

    /// R8G8B8A8
    pub fn data(&self) -> &[u8] {
        unsafe {
            let tex = &*self.ptr;
            slice::from_raw_parts(tex.rubTextureMapData, tex.unWidth as usize * tex.unHeight as usize * 4)
        }
    }
}

impl<'a> Drop for Texture<'a> {
    fn drop(&mut self) { unsafe { self.sys.FreeTexture.unwrap()(self.ptr) } }
}

pub type TextureId = sys::TextureID_t;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
    pub texture_coord: [f32; 2],
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ControllerMode {
    pub scroll_wheel_visible: bool,
}

impl Default for ControllerMode {
    fn default() -> Self { ControllerMode { scroll_wheel_visible: false } }
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ComponentState {
    pub tracking_to_component_render_model: [[f32; 4]; 3],
    pub tracking_to_component_local: [[f32; 4]; 3],
    pub properties: ComponentProperties,
}

impl ComponentState {
    pub fn is_static(&self) -> bool { self.properties & component_properties::IS_STATIC != 0 }
    pub fn is_visible(&self) -> bool { self.properties & component_properties::IS_VISIBLE != 0 }
    pub fn is_touched(&self) -> bool { self.properties & component_properties::IS_TOUCHED != 0 }
    pub fn is_pressed(&self) -> bool { self.properties & component_properties::IS_PRESSED != 0 }
    pub fn is_scrolled(&self) -> bool { self.properties & component_properties::IS_SCROLLED != 0 }
}

type ComponentProperties = sys::VRComponentProperties;

pub mod component_properties {
    use super::{sys, ComponentProperties};

    pub const IS_STATIC: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsStatic;
    pub const IS_VISIBLE: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsVisible;
    pub const IS_TOUCHED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsTouched;
    pub const IS_PRESSED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsPressed;
    pub const IS_SCROLLED: ComponentProperties = sys::EVRComponentProperty_VRComponentProperty_IsScrolled;
}
<|endoftext|>


<commit_before>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::uint32_t;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}
<commit_msg>Fix typo<commit_after>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::uint32_t;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}
<|endoftext|>


<commit_before>use screen::*;
use context::Context;
use resource_manager::Textures;
use graphics::Texture;
use piston_window::{self, PistonWindow, ImageSize, Transformed};

pub struct Battle {
    empty_tile: Texture,
}

impl Battle {
    pub fn new(window: &PistonWindow, context: &mut Context) -> Self {
        Battle { empty_tile: context.resource_manager.load_texture(window, Textures::EmptyTile) }
    }
}

impl Screen for Battle {
    fn on_input(&mut self, input: &piston_window::Input, window: &PistonWindow, context: &mut Context) -> InputResults {
        use piston_window::{Input, Button, Key};

        let mut result = Vec::new();

        match input {
            &Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Escape) => {
                        result.push(InputResult::PushScreen(Box::new(MainMenu::new(&window, context))));
                    }
                    _ => {}
                }
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, _args: &piston_window::RenderArgs, window: &PistonWindow) {
        window.draw_2d(|context, graphics| {
            piston_window::clear([0.0, 0.0, 0.0, 1.0], graphics);

            // Draw grid.
            let tiles_count = 4;
            let offset = 5;
            let (height, width) = self.empty_tile.get_size();
            for x in 0..tiles_count {
                for y in 0..tiles_count {
                    // TODO: draw_many?
                    //piston_window::image(&self.empty_tile, context.transform.trans((x * height + (5 * if x == 0 { 1 } else { 2 })) as f64, (y * width + (5 * if y == 0 { 1 } else { 2 })) as f64), graphics);
                    piston_window::image(&self.empty_tile, context.transform.trans((x * (height + offset)) as f64, (y * (width + offset)) as  f64), graphics);
                }
            }
        });
    }

    fn on_update(&mut self, _args: &piston_window::UpdateArgs) {}
}
<commit_msg>Format<commit_after>use screen::*;
use context::Context;
use resource_manager::Textures;
use graphics::Texture;
use piston_window::{self, PistonWindow, ImageSize, Transformed};

pub struct Battle {
    empty_tile: Texture,
}

impl Battle {
    pub fn new(window: &PistonWindow, context: &mut Context) -> Self {
        Battle { empty_tile: context.resource_manager.load_texture(window, Textures::EmptyTile) }
    }
}

impl Screen for Battle {
    fn on_input(&mut self, input: &piston_window::Input, window: &PistonWindow, context: &mut Context) -> InputResults {
        use piston_window::{Input, Button, Key};

        let mut result = Vec::new();

        match input {
            &Input::Press(button) => {
                match button {
                    Button::Keyboard(Key::Escape) => {
                        result.push(InputResult::PushScreen(Box::new(MainMenu::new(&window, context))));
                    }
                    _ => {}
                }
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, _args: &piston_window::RenderArgs, window: &PistonWindow) {
        window.draw_2d(|context, graphics| {
            piston_window::clear([0.0, 0.0, 0.0, 1.0], graphics);

            // Draw grid.
            let tiles_count = 4;
            let offset = 5;
            let (height, width) = self.empty_tile.get_size();
            for x in 0..tiles_count {
                for y in 0..tiles_count {
                    // TODO: draw_many?
                    piston_window::image(&self.empty_tile,
                                         context.transform.trans((x * (height + offset)) as f64,
                                                                 (y * (width + offset)) as f64),
                                         graphics);
                }
            }
        });
    }

    fn on_update(&mut self, _args: &piston_window::UpdateArgs) {}
}
<|endoftext|>


<commit_before>use chrono;
use chrono::TimeZone;
use futures_cpupool::CpuPool;
use futures::{BoxFuture, Future};
use r2d2;
use r2d2_sqlite::SqliteConnectionManager;
use rusqlite;
use rand::{Rng, thread_rng};
use serde;
use linear_map::LinearMap;

use std::sync::Arc;
use std::path::Path;

#[derive(Clone, Debug, Serialize)]
pub struct Transaction {
    pub shafter: String,
    pub shaftee: String,
    pub amount: i64,
    #[serde(serialize_with = "serialize_time")]
    pub datetime: chrono::DateTime<chrono::Utc>,
    pub reason: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct User {
    pub user_id: String,
    pub display_name: String,
    pub balance: i64,
}

pub trait Database: Send + Sync {
    fn get_user_by_github_id(&self, github_user_id: String)
        -> BoxFuture<Option<String>, DatabaseError>;

    fn add_user_by_github_id(&self, github_user_id: String, display_name: String)
        -> BoxFuture<String, DatabaseError>;

    fn create_token_for_user(&self, user_id: String)
        -> BoxFuture<String, DatabaseError>;

    fn delete_token(&self, token: String)
        -> BoxFuture<(), DatabaseError>;

    fn get_user_from_token(&self, token: String)
        -> BoxFuture<Option<User>, DatabaseError>;

    fn get_balance_for_user(&self, user: String)
        -> BoxFuture<i64, DatabaseError>;

    fn get_all_users(&self)
        -> BoxFuture<LinearMap<String, User>, DatabaseError>;

    fn shaft_user(&self, transaction: Transaction)
        -> BoxFuture<(), ShaftUserError>;

    fn get_last_transactions(&self, limit: u32)
        -> BoxFuture<Vec<Transaction>, DatabaseError>;
}

#[derive(Clone)]
pub struct SqliteDatabase {
    cpu_pool: CpuPool,
    db_pool: Arc<r2d2::Pool<SqliteConnectionManager>>,
}

impl SqliteDatabase {
    pub fn with_path<P: AsRef<Path>>(path: P) -> SqliteDatabase {
        let config = r2d2::Config::default();
        let manager = SqliteConnectionManager::new(path);
        let pool = r2d2::Pool::new(config, manager).unwrap();

        SqliteDatabase {
            cpu_pool: CpuPool::new_num_cpus(),
            db_pool: Arc::new(pool),
        }
    }
}

impl Database for SqliteDatabase {
    fn get_user_by_github_id(&self, github_user_id: String)
        -> BoxFuture<Option<String>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let row = conn.query_row(
                "SELECT user_id FROM github_users WHERE github_id = $1",
                &[&github_user_id],
                |row| -> String {
                    row.get(0)
                }
            )
            .map(|row| Some(row))
            .or_else(|err| {
                if let rusqlite::Error::QueryReturnedNoRows = err {
                    Ok(None)
                } else {
                    Err(err)
                }
            })?;

            Ok(row)
        }).boxed()
    }

    fn add_user_by_github_id(&self, github_user_id: String, display_name: String)
        -> BoxFuture<String, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            conn.execute(
                "INSERT INTO github_users (user_id, github_id, display_name)
                VALUES ($1, $1, $2)",
                &[&github_user_id, &display_name],
            )?;

            // TODO: Handle clashing user IDs

            Ok(github_user_id)
        }).boxed()
    }

    fn create_token_for_user(&self, user_id: String)
        -> BoxFuture<String, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let token: String = thread_rng().gen_ascii_chars().take(32).collect();

            conn.execute(
                "INSERT INTO tokens (user_id, token) VALUES ($1, $2)",
                &[&user_id, &token],
            )?;

            Ok(token)
        }).boxed()
    }

    fn delete_token(&self, token: String)
        -> BoxFuture<(), DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            conn.execute(
                "DELETE FROM tokens WHERE token = $1",
                &[&token],
            )?;

            Ok(())
        }).boxed()
    }

    fn get_user_from_token(&self, token: String)
        -> BoxFuture<Option<User>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let row = conn.query_row(
                r#"
                SELECT user_id, display_name, COALESCE(balance, 0)
                FROM tokens
                INNER JOIN users USING (user_id)
                LEFT JOIN (
                    SELECT user_id, SUM(amount) as balance
                    FROM (
                        SELECT shafter AS user_id, SUM(amount) AS amount
                        FROM transactions GROUP BY shafter
                        UNION ALL
                        SELECT shaftee AS user_id, -SUM(amount) AS amount
                        FROM transactions GROUP BY shaftee
                    ) t GROUP BY user_id
                )
                USING (user_id)
                WHERE token = $1
                "#,
                &[&token],
                |row| -> User {
                    User {
                        user_id: row.get(0),
                        display_name: row.get(1),
                        balance: row.get(2),
                    }
                }
            )
            .map(|row| Some(row))
            .or_else(|err| {
                if let rusqlite::Error::QueryReturnedNoRows = err {
                    Ok(None)
                } else {
                    Err(err)
                }
            })?;

            Ok(row)
        }).boxed()
    }

    fn get_balance_for_user(&self, user: String)
        -> BoxFuture<i64, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let row = conn.query_row(
                r#"SELECT (
                    SELECT COALESCE(SUM(amount), 0)
                    FROM transactions
                    WHERE shafter = $1
                ) - (
                    SELECT COALESCE(SUM(amount), 0)
                    FROM transactions
                    WHERE shaftee = $1
                )"#,
                &[&user],
                |row| -> i64 {
                    row.get(0)
                }
            )?;

            Ok(row)
        }).boxed()
    }

    fn get_all_users(&self)
        -> BoxFuture<LinearMap<String, User>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let mut stmt = conn.prepare(
                r#"
                SELECT user_id, display_name, COALESCE(balance, 0) AS balance
                FROM users
                LEFT JOIN (
                    SELECT user_id, SUM(amount) as balance
                    FROM (
                        SELECT shafter AS user_id, SUM(amount) AS amount
                        FROM transactions GROUP BY shafter
                        UNION ALL
                        SELECT shaftee AS user_id, -SUM(amount) AS amount
                        FROM transactions GROUP BY shaftee
                    ) t GROUP BY user_id
                )
                USING (user_id)
                ORDER BY balance ASC
                "#
            )?;

            let rows: Result<LinearMap<String, User>, _> = stmt.query_map(
                &[],
                |row| -> (String, User) {
                    (row.get(0), User {
                        user_id: row.get(0),
                        display_name: row.get(1),
                        balance: row.get(2),
                    })
                }
            )?.collect();

            Ok(rows?)
        }).boxed()
    }

    fn shaft_user(&self, transaction: Transaction)
        -> BoxFuture<(), ShaftUserError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, ShaftUserError>  {
            let conn = db_pool.get()?;

            match conn.query_row(
                "SELECT user_id FROM users WHERE user_id = $1",
                &[&transaction.shaftee],
                |_row| {}
            ) {
                Ok(_) => (),
                Err(rusqlite::Error::QueryReturnedNoRows) => {
                    return Err(ShaftUserError::UnknownUser(transaction.shaftee))
                }
                Err(err) => {
                    return Err(ShaftUserError::Database(err.into()))
                }
            }

            let mut stmt = conn.prepare(
                "INSERT INTO transactions (shafter, shaftee, amount, time_sec, reason)\
                VALUES ($1, $2, $3, $4, $5)"
            )?;

            stmt.execute(&[
                &transaction.shafter,
                &transaction.shaftee,
                &transaction.amount,
                &transaction.datetime.timestamp(),
                &transaction.reason,
            ])?;

            Ok(())
        }).boxed()
    }

    fn get_last_transactions(&self, limit: u32)
        -> BoxFuture<Vec<Transaction>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let mut stmt = conn.prepare(
                r#"SELECT shafter, shaftee, amount, time_sec, reason
                FROM transactions
                ORDER BY id DESC
                LIMIT $1
                "#
            )?;

            let rows: Result<Vec<_>, _> = stmt.query_map(
                &[&limit],
                |row| {
                    Transaction {
                        shafter: row.get(0),
                        shaftee: row.get(1),
                        amount: row.get(2),
                        datetime: chrono::Utc.timestamp(row.get(3), 0),
                        reason: row.get(4),
                    }
                }
            )?.collect();

            Ok(rows?)
        }).boxed()
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum DatabaseError {
        ConnectionPool(err: r2d2::GetTimeout) {
            from()
            display("DB Pool error: {}", err)
        }
        SqliteError(err: rusqlite::Error) {
            from()
            display("Sqlite Pool error: {}", err)
        }
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum ShaftUserError {
        Database(err: DatabaseError) {
            from()
            from(e: r2d2::GetTimeout) -> (DatabaseError::from(e))
            from(e: rusqlite::Error) -> (DatabaseError::from(e))
            display("{}", err)
        }
        UnknownUser(user_id: String) {
            from()
            display("Unknown user: {}", user_id)
        }
    }
}

fn serialize_time<S>(date: &chrono::DateTime<chrono::Utc>, serializer: S)
    -> Result<S::Ok, S::Error>
where S: serde::Serializer
{
    serializer.serialize_i64(date.timestamp())
}<commit_msg>Insert display name in correct place<commit_after>use chrono;
use chrono::TimeZone;
use futures_cpupool::CpuPool;
use futures::{BoxFuture, Future};
use r2d2;
use r2d2_sqlite::SqliteConnectionManager;
use rusqlite;
use rand::{Rng, thread_rng};
use serde;
use linear_map::LinearMap;

use std::sync::Arc;
use std::path::Path;

#[derive(Clone, Debug, Serialize)]
pub struct Transaction {
    pub shafter: String,
    pub shaftee: String,
    pub amount: i64,
    #[serde(serialize_with = "serialize_time")]
    pub datetime: chrono::DateTime<chrono::Utc>,
    pub reason: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct User {
    pub user_id: String,
    pub display_name: String,
    pub balance: i64,
}

pub trait Database: Send + Sync {
    fn get_user_by_github_id(&self, github_user_id: String)
        -> BoxFuture<Option<String>, DatabaseError>;

    fn add_user_by_github_id(&self, github_user_id: String, display_name: String)
        -> BoxFuture<String, DatabaseError>;

    fn create_token_for_user(&self, user_id: String)
        -> BoxFuture<String, DatabaseError>;

    fn delete_token(&self, token: String)
        -> BoxFuture<(), DatabaseError>;

    fn get_user_from_token(&self, token: String)
        -> BoxFuture<Option<User>, DatabaseError>;

    fn get_balance_for_user(&self, user: String)
        -> BoxFuture<i64, DatabaseError>;

    fn get_all_users(&self)
        -> BoxFuture<LinearMap<String, User>, DatabaseError>;

    fn shaft_user(&self, transaction: Transaction)
        -> BoxFuture<(), ShaftUserError>;

    fn get_last_transactions(&self, limit: u32)
        -> BoxFuture<Vec<Transaction>, DatabaseError>;
}

#[derive(Clone)]
pub struct SqliteDatabase {
    cpu_pool: CpuPool,
    db_pool: Arc<r2d2::Pool<SqliteConnectionManager>>,
}

impl SqliteDatabase {
    pub fn with_path<P: AsRef<Path>>(path: P) -> SqliteDatabase {
        let config = r2d2::Config::default();
        let manager = SqliteConnectionManager::new(path);
        let pool = r2d2::Pool::new(config, manager).unwrap();

        SqliteDatabase {
            cpu_pool: CpuPool::new_num_cpus(),
            db_pool: Arc::new(pool),
        }
    }
}

impl Database for SqliteDatabase {
    fn get_user_by_github_id(&self, github_user_id: String)
        -> BoxFuture<Option<String>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let row = conn.query_row(
                "SELECT user_id FROM github_users WHERE github_id = $1",
                &[&github_user_id],
                |row| -> String {
                    row.get(0)
                }
            )
            .map(|row| Some(row))
            .or_else(|err| {
                if let rusqlite::Error::QueryReturnedNoRows = err {
                    Ok(None)
                } else {
                    Err(err)
                }
            })?;

            Ok(row)
        }).boxed()
    }

    fn add_user_by_github_id(&self, github_user_id: String, display_name: String)
        -> BoxFuture<String, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            conn.execute(
                "INSERT INTO github_users (user_id, github_id)
                VALUES ($1, $1, $2)",
                &[&github_user_id],
            )?;

             conn.execute(
                "INSERT INTO users (user_id, display_name)
                VALUES ($1, $1, $2)",
                &[&github_user_id, &display_name],
            )?;

            Ok(github_user_id)
        }).boxed()
    }

    fn create_token_for_user(&self, user_id: String)
        -> BoxFuture<String, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let token: String = thread_rng().gen_ascii_chars().take(32).collect();

            conn.execute(
                "INSERT INTO tokens (user_id, token) VALUES ($1, $2)",
                &[&user_id, &token],
            )?;

            Ok(token)
        }).boxed()
    }

    fn delete_token(&self, token: String)
        -> BoxFuture<(), DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            conn.execute(
                "DELETE FROM tokens WHERE token = $1",
                &[&token],
            )?;

            Ok(())
        }).boxed()
    }

    fn get_user_from_token(&self, token: String)
        -> BoxFuture<Option<User>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let row = conn.query_row(
                r#"
                SELECT user_id, display_name, COALESCE(balance, 0)
                FROM tokens
                INNER JOIN users USING (user_id)
                LEFT JOIN (
                    SELECT user_id, SUM(amount) as balance
                    FROM (
                        SELECT shafter AS user_id, SUM(amount) AS amount
                        FROM transactions GROUP BY shafter
                        UNION ALL
                        SELECT shaftee AS user_id, -SUM(amount) AS amount
                        FROM transactions GROUP BY shaftee
                    ) t GROUP BY user_id
                )
                USING (user_id)
                WHERE token = $1
                "#,
                &[&token],
                |row| -> User {
                    User {
                        user_id: row.get(0),
                        display_name: row.get(1),
                        balance: row.get(2),
                    }
                }
            )
            .map(|row| Some(row))
            .or_else(|err| {
                if let rusqlite::Error::QueryReturnedNoRows = err {
                    Ok(None)
                } else {
                    Err(err)
                }
            })?;

            Ok(row)
        }).boxed()
    }

    fn get_balance_for_user(&self, user: String)
        -> BoxFuture<i64, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let row = conn.query_row(
                r#"SELECT (
                    SELECT COALESCE(SUM(amount), 0)
                    FROM transactions
                    WHERE shafter = $1
                ) - (
                    SELECT COALESCE(SUM(amount), 0)
                    FROM transactions
                    WHERE shaftee = $1
                )"#,
                &[&user],
                |row| -> i64 {
                    row.get(0)
                }
            )?;

            Ok(row)
        }).boxed()
    }

    fn get_all_users(&self)
        -> BoxFuture<LinearMap<String, User>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let mut stmt = conn.prepare(
                r#"
                SELECT user_id, display_name, COALESCE(balance, 0) AS balance
                FROM users
                LEFT JOIN (
                    SELECT user_id, SUM(amount) as balance
                    FROM (
                        SELECT shafter AS user_id, SUM(amount) AS amount
                        FROM transactions GROUP BY shafter
                        UNION ALL
                        SELECT shaftee AS user_id, -SUM(amount) AS amount
                        FROM transactions GROUP BY shaftee
                    ) t GROUP BY user_id
                )
                USING (user_id)
                ORDER BY balance ASC
                "#
            )?;

            let rows: Result<LinearMap<String, User>, _> = stmt.query_map(
                &[],
                |row| -> (String, User) {
                    (row.get(0), User {
                        user_id: row.get(0),
                        display_name: row.get(1),
                        balance: row.get(2),
                    })
                }
            )?.collect();

            Ok(rows?)
        }).boxed()
    }

    fn shaft_user(&self, transaction: Transaction)
        -> BoxFuture<(), ShaftUserError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, ShaftUserError>  {
            let conn = db_pool.get()?;

            match conn.query_row(
                "SELECT user_id FROM users WHERE user_id = $1",
                &[&transaction.shaftee],
                |_row| {}
            ) {
                Ok(_) => (),
                Err(rusqlite::Error::QueryReturnedNoRows) => {
                    return Err(ShaftUserError::UnknownUser(transaction.shaftee))
                }
                Err(err) => {
                    return Err(ShaftUserError::Database(err.into()))
                }
            }

            let mut stmt = conn.prepare(
                "INSERT INTO transactions (shafter, shaftee, amount, time_sec, reason)\
                VALUES ($1, $2, $3, $4, $5)"
            )?;

            stmt.execute(&[
                &transaction.shafter,
                &transaction.shaftee,
                &transaction.amount,
                &transaction.datetime.timestamp(),
                &transaction.reason,
            ])?;

            Ok(())
        }).boxed()
    }

    fn get_last_transactions(&self, limit: u32)
        -> BoxFuture<Vec<Transaction>, DatabaseError>
    {
        let db_pool = self.db_pool.clone();

        self.cpu_pool.spawn_fn(move || -> Result<_, DatabaseError>  {
            let conn = db_pool.get()?;

            let mut stmt = conn.prepare(
                r#"SELECT shafter, shaftee, amount, time_sec, reason
                FROM transactions
                ORDER BY id DESC
                LIMIT $1
                "#
            )?;

            let rows: Result<Vec<_>, _> = stmt.query_map(
                &[&limit],
                |row| {
                    Transaction {
                        shafter: row.get(0),
                        shaftee: row.get(1),
                        amount: row.get(2),
                        datetime: chrono::Utc.timestamp(row.get(3), 0),
                        reason: row.get(4),
                    }
                }
            )?.collect();

            Ok(rows?)
        }).boxed()
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum DatabaseError {
        ConnectionPool(err: r2d2::GetTimeout) {
            from()
            display("DB Pool error: {}", err)
        }
        SqliteError(err: rusqlite::Error) {
            from()
            display("Sqlite Pool error: {}", err)
        }
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum ShaftUserError {
        Database(err: DatabaseError) {
            from()
            from(e: r2d2::GetTimeout) -> (DatabaseError::from(e))
            from(e: rusqlite::Error) -> (DatabaseError::from(e))
            display("{}", err)
        }
        UnknownUser(user_id: String) {
            from()
            display("Unknown user: {}", user_id)
        }
    }
}

fn serialize_time<S>(date: &chrono::DateTime<chrono::Utc>, serializer: S)
    -> Result<S::Ok, S::Error>
where S: serde::Serializer
{
    serializer.serialize_i64(date.timestamp())
}<|endoftext|>