<commit_before>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
extern crate mio;
extern crate mio_uds;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
#[macro_use] extern crate sozu_lib as sozu;
extern crate sozu_command_lib as sozu_command;

mod command;
mod worker;
mod logging;
mod upgrade;

use std::net::{UdpSocket,ToSocketAddrs};
use std::collections::HashMap;
use std::env;
use sozu::network::metrics::{METRICS,ProxyMetrics};
use sozu_command::config::Config;
use clap::{App,Arg,SubCommand};

use command::Worker;
use worker::{begin_worker_process,start_workers};
use upgrade::begin_new_master_process;

fn main() {
  let matches = App::new("sozu")
                        .version(crate_version!())
                        .about("hot reconfigurable proxy")
                        .subcommand(SubCommand::with_name("start")
                                    .about("launch the master process")
                                    .arg(Arg::with_name("config")
                                        .short("c")
                                        .long("config")
                                        .value_name("FILE")
                                        .help("Sets a custom config file")
                                        .takes_value(true)
                                        .required(true)))
                        .subcommand(SubCommand::with_name("worker")
                                    .about("start a worker (internal command, should not be used directly)")
                                    .arg(Arg::with_name("tag").long("tag")
                                         .takes_value(true).required(true).help("worker configuration tag"))
                                    .arg(Arg::with_name("id").long("id")
                                         .takes_value(true).required(true).help("worker identifier"))
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .subcommand(SubCommand::with_name("upgrade")
                                    .about("start a new master process (internal command, should not be used directly)")
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .get_matches();

  if let Some(matches) = matches.subcommand_matches("worker") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let id  = matches.value_of("id").expect("needs a worker id");
    let tag = matches.value_of("tag").expect("needs a configuration tag");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_worker_process(fd, id, tag, buffer_size);
    return;
  }

  if let Some(matches) = matches.subcommand_matches("upgrade") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_new_master_process(fd, buffer_size);
    return;
  }

  let submatches = matches.subcommand_matches("start").expect("unknown subcommand");
  let config_file = submatches.value_of("config").expect("required config file");

  if let Ok(config) = Config::load_from_path(config_file) {
    //FIXME: should have an id for the master too
    logging::setup("MASTER".to_string(), &config.log_level, &config.log_target);
    info!("starting up");

    let metrics_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
    let metrics_host   = (&config.metrics.address[..], config.metrics.port).to_socket_addrs().unwrap().next().unwrap();
    METRICS.lock().unwrap().set_up_remote(metrics_socket, metrics_host);
    let metrics_guard = ProxyMetrics::run();

    let mut proxies:HashMap<String,Vec<Worker>> = HashMap::new();

    for (ref tag, ref ls) in &config.proxies {
      if let Some(workers) = start_workers(&tag, ls) {
        proxies.insert(tag.to_string(), workers);
      }
    };
    info!("created proxies: {:?}", proxies);

    command::start(config, proxies);
    info!("master process stopped");
  } else {
    error!("could not load configuration file at '{}', stopping", config_file);
  }
}

<commit_msg>Main: check system's file descriptors limits before launching workers<commit_after>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
extern crate mio;
extern crate mio_uds;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
#[macro_use] extern crate sozu_lib as sozu;
extern crate sozu_command_lib as sozu_command;
extern crate procinfo;

mod command;
mod worker;
mod logging;
mod upgrade;
mod limits;

use std::net::{UdpSocket,ToSocketAddrs};
use std::collections::HashMap;
use std::env;
use sozu::network::metrics::{METRICS,ProxyMetrics};
use sozu_command::config::Config;
use clap::{App,Arg,SubCommand};

use command::Worker;
use worker::{begin_worker_process,start_workers};
use upgrade::begin_new_master_process;

fn main() {
  let matches = App::new("sozu")
                        .version(crate_version!())
                        .about("hot reconfigurable proxy")
                        .subcommand(SubCommand::with_name("start")
                                    .about("launch the master process")
                                    .arg(Arg::with_name("config")
                                        .short("c")
                                        .long("config")
                                        .value_name("FILE")
                                        .help("Sets a custom config file")
                                        .takes_value(true)
                                        .required(true)))
                        .subcommand(SubCommand::with_name("worker")
                                    .about("start a worker (internal command, should not be used directly)")
                                    .arg(Arg::with_name("tag").long("tag")
                                         .takes_value(true).required(true).help("worker configuration tag"))
                                    .arg(Arg::with_name("id").long("id")
                                         .takes_value(true).required(true).help("worker identifier"))
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .subcommand(SubCommand::with_name("upgrade")
                                    .about("start a new master process (internal command, should not be used directly)")
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .get_matches();

  if let Some(matches) = matches.subcommand_matches("worker") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let id  = matches.value_of("id").expect("needs a worker id");
    let tag = matches.value_of("tag").expect("needs a configuration tag");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_worker_process(fd, id, tag, buffer_size);
    return;
  }

  if let Some(matches) = matches.subcommand_matches("upgrade") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_new_master_process(fd, buffer_size);
    return;
  }

  let submatches = matches.subcommand_matches("start").expect("unknown subcommand");
  let config_file = submatches.value_of("config").expect("required config file");

  if let Ok(config) = Config::load_from_path(config_file) {
    //FIXME: should have an id for the master too
    logging::setup("MASTER".to_string(), &config.log_level, &config.log_target);
    info!("starting up");

    let metrics_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
    let metrics_host   = (&config.metrics.address[..], config.metrics.port).to_socket_addrs().unwrap().next().unwrap();
    METRICS.lock().unwrap().set_up_remote(metrics_socket, metrics_host);
    let metrics_guard = ProxyMetrics::run();

    let mut proxies:HashMap<String,Vec<Worker>> = HashMap::new();

    if check_process_limits(&config) {
      for (ref tag, ref ls) in &config.proxies {
        if let Some(workers) = start_workers(&tag, ls) {
          proxies.insert(tag.to_string(), workers);
        }
      };
      info!("created proxies: {:?}", proxies);
      command::start(config, proxies);
    }

    info!("master process stopped");
  } else {
    error!("could not load configuration file at '{}', stopping", config_file);
  }
}

fn check_process_limits(config: &Config) -> bool{
  let process_limits = procinfo::pid::limits_self().expect("Couldn't read /proc/self/limits to determine max open file descriptors limit");
  // We check the hard_limit. The soft_limit can be changed at runtime
  // by the process. hard_limit can only be changed by root
  let hard_limit = process_limits.max_open_files.hard_limit;

  // If limit is "unlimited"
  if hard_limit == -1 {
    return true;
  }

  let max_limit_per_proxy = config.proxies.values().all(|proxy| (proxy.max_connections as isize) <= hard_limit);
  if !max_limit_per_proxy {
    error!("At least one proxy can't have that much of connections. Current max file descriptor hard limit is: {}", hard_limit);
    return false;
  }

  let total_proxies_connections = config.proxies.values().fold(0, |acc, ref proxy| acc + proxy.max_connections);
  let system_max_fd = limits::limits_file_max().expect("Couldn't read /proc/sys/fs/file-max");

  if total_proxies_connections > system_max_fd {
    error!("Proxies total max_connections can't be higher than system's file-max limit. Current limit: {}, current value: {}",
           system_max_fd, total_proxies_connections);
    return false;
  }

  true
}
<|endoftext|>


<commit_before>//! Helper module to enable serializing bytes more efficiently

use core::{ops, fmt, char, iter, slice};
use core::fmt::Write;

use ser;

#[cfg(any(feature = "std", feature = "collections"))]
pub use self::bytebuf::{ByteBuf, ByteBufVisitor};

#[cfg(feature = "collections")]
use collections::Vec;

///////////////////////////////////////////////////////////////////////////////

/// `Bytes` wraps a `&[u8]` in order to serialize into a byte array.
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Bytes<'a> {
    bytes: &'a [u8],
}

impl<'a> fmt::Debug for Bytes<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(f.write_str("b\""));
        for c in escape_bytestring(self.bytes) {
            try!(f.write_char(c));
        }
        f.write_char('"')
    }
}

impl<'a> From<&'a [u8]> for Bytes<'a> {
    fn from(bytes: &'a [u8]) -> Self {
        Bytes {
            bytes: bytes,
        }
    }
}

#[cfg(any(feature = "std", feature = "collections"))]
impl<'a> From<&'a Vec<u8>> for Bytes<'a> {
    fn from(bytes: &'a Vec<u8>) -> Self {
        Bytes {
            bytes: bytes,
        }
    }
}

impl<'a> Into<&'a [u8]> for Bytes<'a> {
    fn into(self) -> &'a [u8] {
        self.bytes
    }
}

impl<'a> ops::Deref for Bytes<'a> {
    type Target = [u8];

    fn deref(&self) -> &[u8] { self.bytes }
}

impl<'a> ser::Serialize for Bytes<'a> {
    #[inline]
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: ser::Serializer
    {
        serializer.serialize_bytes(self.bytes)
    }
}

///////////////////////////////////////////////////////////////////////////////

#[cfg(any(feature = "std", feature = "collections"))]
mod bytebuf {
    use core::ops;
    use core::fmt;
    use core::fmt::Write;

    use ser;
    use de;

    #[cfg(feature = "collections")]
    use collections::Vec;

    /// `ByteBuf` wraps a `Vec<u8>` and serializes as a byte array.
    #[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
    pub struct ByteBuf {
        bytes: Vec<u8>,
    }

    impl ByteBuf {
        /// Construct a new, empty `ByteBuf`.
        pub fn new() -> Self {
            ByteBuf {
                bytes: Vec::new(),
            }
        }

        /// Construct a new, empty `ByteBuf` with the specified capacity.
        pub fn with_capacity(cap: usize) -> Self {
            ByteBuf {
                bytes: Vec::with_capacity(cap)
            }
        }
    }

    impl fmt::Debug for ByteBuf {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            try!(f.write_str("b\""));
            for c in super::escape_bytestring(self.bytes.as_ref()) {
                try!(f.write_char(c));
            }
            f.write_char('"')
        }
    }

    impl Into<Vec<u8>> for ByteBuf {
        fn into(self) -> Vec<u8> {
            self.bytes
        }
    }

    impl From<Vec<u8>> for ByteBuf {
        fn from(bytes: Vec<u8>) -> Self {
            ByteBuf {
                bytes: bytes,
            }
        }
    }

    impl AsRef<Vec<u8>> for ByteBuf {
        fn as_ref(&self) -> &Vec<u8> {
            &self.bytes
        }
    }

    impl AsRef<[u8]> for ByteBuf {
        fn as_ref(&self) -> &[u8] {
            &self.bytes
        }
    }

    impl AsMut<Vec<u8>> for ByteBuf {
        fn as_mut(&mut self) -> &mut Vec<u8> {
            &mut self.bytes
        }
    }

    impl AsMut<[u8]> for ByteBuf {
        fn as_mut(&mut self) -> &mut [u8] {
            &mut self.bytes
        }
    }

    impl ops::Deref for ByteBuf {
        type Target = [u8];

        fn deref(&self) -> &[u8] { &self.bytes[..] }
    }

    impl ops::DerefMut for ByteBuf {
        fn deref_mut(&mut self) -> &mut [u8] { &mut self.bytes[..] }
    }

    impl ser::Serialize for ByteBuf {
        fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
            where S: ser::Serializer
        {
            serializer.serialize_bytes(self)
        }
    }

    /// This type implements the `serde::de::Visitor` trait for a `ByteBuf`.
    pub struct ByteBufVisitor;

    impl de::Visitor for ByteBufVisitor {
        type Value = ByteBuf;

        #[inline]
        fn visit_unit<E>(&mut self) -> Result<ByteBuf, E>
            where E: de::Error,
        {
            Ok(ByteBuf {
                bytes: Vec::new(),
            })
        }

        #[inline]
        fn visit_seq<V>(&mut self, mut visitor: V) -> Result<ByteBuf, V::Error>
            where V: de::SeqVisitor,
        {
            let (len, _) = visitor.size_hint();
            let mut values = Vec::with_capacity(len);

            while let Some(value) = try!(visitor.visit()) {
                values.push(value);
            }

            try!(visitor.end());

            Ok(ByteBuf {
                bytes: values,
            })
        }

        #[inline]
        fn visit_bytes<E>(&mut self, v: &[u8]) -> Result<ByteBuf, E>
            where E: de::Error,
        {
            self.visit_byte_buf(v.to_vec())
        }

        #[inline]
        fn visit_byte_buf<E>(&mut self, v: Vec<u8>) -> Result<ByteBuf, E>
            where E: de::Error,
        {
            Ok(ByteBuf {
                bytes: v,
            })
        }
    }

    impl de::Deserialize for ByteBuf {
        #[inline]
        fn deserialize<D>(deserializer: &mut D) -> Result<ByteBuf, D::Error>
            where D: de::Deserializer
        {
            deserializer.deserialize_bytes(ByteBufVisitor)
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

#[inline]
fn escape_bytestring<'a>(bytes: &'a [u8]) -> iter::FlatMap<slice::Iter<'a, u8>, char::EscapeDefault, fn(&u8) -> char::EscapeDefault> {
    fn f(b: &u8) -> char::EscapeDefault {
        char::from_u32(*b as u32).unwrap().escape_default()
    }
    bytes.iter().flat_map(f as fn(&u8) -> char::EscapeDefault)
}
<commit_msg>Add constructors for Bytes and ByteBuf<commit_after>//! Helper module to enable serializing bytes more efficiently

use core::{ops, fmt, char, iter, slice};
use core::fmt::Write;

use ser;

#[cfg(any(feature = "std", feature = "collections"))]
pub use self::bytebuf::{ByteBuf, ByteBufVisitor};

#[cfg(feature = "collections")]
use collections::Vec;

///////////////////////////////////////////////////////////////////////////////

/// `Bytes` wraps a `&[u8]` in order to serialize into a byte array.
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Bytes<'a> {
    bytes: &'a [u8],
}

impl<'a> Bytes<'a> {
    /// Wrap an existing `&[u8]`.
    pub fn new(bytes: &'a [u8]) -> Self {
        Bytes {
            bytes: bytes,
        }
    }
}

impl<'a> fmt::Debug for Bytes<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(f.write_str("b\""));
        for c in escape_bytestring(self.bytes) {
            try!(f.write_char(c));
        }
        f.write_char('"')
    }
}

impl<'a> From<&'a [u8]> for Bytes<'a> {
    fn from(bytes: &'a [u8]) -> Self {
        Bytes {
            bytes: bytes,
        }
    }
}

#[cfg(any(feature = "std", feature = "collections"))]
impl<'a> From<&'a Vec<u8>> for Bytes<'a> {
    fn from(bytes: &'a Vec<u8>) -> Self {
        Bytes {
            bytes: bytes,
        }
    }
}

impl<'a> Into<&'a [u8]> for Bytes<'a> {
    fn into(self) -> &'a [u8] {
        self.bytes
    }
}

impl<'a> ops::Deref for Bytes<'a> {
    type Target = [u8];

    fn deref(&self) -> &[u8] { self.bytes }
}

impl<'a> ser::Serialize for Bytes<'a> {
    #[inline]
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: ser::Serializer
    {
        serializer.serialize_bytes(self.bytes)
    }
}

///////////////////////////////////////////////////////////////////////////////

#[cfg(any(feature = "std", feature = "collections"))]
mod bytebuf {
    use core::ops;
    use core::fmt;
    use core::fmt::Write;

    use ser;
    use de;

    #[cfg(feature = "collections")]
    use collections::Vec;

    /// `ByteBuf` wraps a `Vec<u8>` and serializes as a byte array.
    #[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
    pub struct ByteBuf {
        bytes: Vec<u8>,
    }

    impl ByteBuf {
        /// Construct a new, empty `ByteBuf`.
        pub fn new() -> Self {
            ByteBuf {
                bytes: Vec::new(),
            }
        }

        /// Construct a new, empty `ByteBuf` with the specified capacity.
        pub fn with_capacity(cap: usize) -> Self {
            ByteBuf {
                bytes: Vec::with_capacity(cap)
            }
        }

        /// Wrap existing bytes in a `ByteBuf`.
        pub fn from<T: Into<Vec<u8>>>(bytes: T) -> Self {
            ByteBuf {
                bytes: bytes.into(),
            }
        }
    }

    impl fmt::Debug for ByteBuf {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            try!(f.write_str("b\""));
            for c in super::escape_bytestring(self.bytes.as_ref()) {
                try!(f.write_char(c));
            }
            f.write_char('"')
        }
    }

    impl Into<Vec<u8>> for ByteBuf {
        fn into(self) -> Vec<u8> {
            self.bytes
        }
    }

    impl From<Vec<u8>> for ByteBuf {
        fn from(bytes: Vec<u8>) -> Self {
            ByteBuf {
                bytes: bytes,
            }
        }
    }

    impl AsRef<Vec<u8>> for ByteBuf {
        fn as_ref(&self) -> &Vec<u8> {
            &self.bytes
        }
    }

    impl AsRef<[u8]> for ByteBuf {
        fn as_ref(&self) -> &[u8] {
            &self.bytes
        }
    }

    impl AsMut<Vec<u8>> for ByteBuf {
        fn as_mut(&mut self) -> &mut Vec<u8> {
            &mut self.bytes
        }
    }

    impl AsMut<[u8]> for ByteBuf {
        fn as_mut(&mut self) -> &mut [u8] {
            &mut self.bytes
        }
    }

    impl ops::Deref for ByteBuf {
        type Target = [u8];

        fn deref(&self) -> &[u8] { &self.bytes[..] }
    }

    impl ops::DerefMut for ByteBuf {
        fn deref_mut(&mut self) -> &mut [u8] { &mut self.bytes[..] }
    }

    impl ser::Serialize for ByteBuf {
        fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
            where S: ser::Serializer
        {
            serializer.serialize_bytes(self)
        }
    }

    /// This type implements the `serde::de::Visitor` trait for a `ByteBuf`.
    pub struct ByteBufVisitor;

    impl de::Visitor for ByteBufVisitor {
        type Value = ByteBuf;

        #[inline]
        fn visit_unit<E>(&mut self) -> Result<ByteBuf, E>
            where E: de::Error,
        {
            Ok(ByteBuf {
                bytes: Vec::new(),
            })
        }

        #[inline]
        fn visit_seq<V>(&mut self, mut visitor: V) -> Result<ByteBuf, V::Error>
            where V: de::SeqVisitor,
        {
            let (len, _) = visitor.size_hint();
            let mut values = Vec::with_capacity(len);

            while let Some(value) = try!(visitor.visit()) {
                values.push(value);
            }

            try!(visitor.end());

            Ok(ByteBuf {
                bytes: values,
            })
        }

        #[inline]
        fn visit_bytes<E>(&mut self, v: &[u8]) -> Result<ByteBuf, E>
            where E: de::Error,
        {
            self.visit_byte_buf(v.to_vec())
        }

        #[inline]
        fn visit_byte_buf<E>(&mut self, v: Vec<u8>) -> Result<ByteBuf, E>
            where E: de::Error,
        {
            Ok(ByteBuf {
                bytes: v,
            })
        }
    }

    impl de::Deserialize for ByteBuf {
        #[inline]
        fn deserialize<D>(deserializer: &mut D) -> Result<ByteBuf, D::Error>
            where D: de::Deserializer
        {
            deserializer.deserialize_bytes(ByteBufVisitor)
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

#[inline]
fn escape_bytestring<'a>(bytes: &'a [u8]) -> iter::FlatMap<slice::Iter<'a, u8>, char::EscapeDefault, fn(&u8) -> char::EscapeDefault> {
    fn f(b: &u8) -> char::EscapeDefault {
        char::from_u32(*b as u32).unwrap().escape_default()
    }
    bytes.iter().flat_map(f as fn(&u8) -> char::EscapeDefault)
}
<|endoftext|>


<commit_before>use std::{
    env::current_exe,
    path::PathBuf,
};
use piston_window::*;
use crate::{
    direction::Direction,
    game::*,
    settings::Settings,
};

pub struct App<'a> {
    window: PistonWindow,
    window_size: [u32; 2],
    game: Game<'a>,
    turn_active: bool,
    turn_end_timer: f64,
    cpu_turn_timer: f64,
    mouse_cursor: [f64; 2],
    grid_area: [u32; 4],
}

impl<'a> App<'a> {
    pub fn new(settings: &Settings) -> App {
        let grid_area = [
            settings.space_size,
            settings.space_size * 3,
            settings.spaces[0] as u32 * settings.space_size,
            settings.spaces[1] as u32 * settings.space_size
        ];

        let window_size = [
            grid_area[2] + settings.space_size * 2,
            grid_area[3] + settings.space_size * 4
        ];

        let window_title = "Battleship";
        let window: PistonWindow = WindowSettings::new(
            window_title,
            window_size,
            )
            .exit_on_esc(true)
            .resizable(false)
            .build()
            .unwrap();

        App {
            window: window,
            window_size: window_size,
            game: Game::new(&settings),
            turn_active: true,
            turn_end_timer: 0.0,
            cpu_turn_timer: 0.0,
            mouse_cursor: [0.0; 2],
            grid_area: grid_area,
        }
    }

    pub fn init(&mut self) {
        self.window.set_ups(60);
        self.window.set_max_fps(60);

        // TODO set textures in a not terrible way
        let assets_dir = Self::get_assets_dir(current_exe().unwrap()).unwrap();
        let images_dir: PathBuf = assets_dir.join("images");
        let mut space_textures = vec![];

        for state in 0..3 {
            let image_file = format!("gridspace-{}.png", state);
            space_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        space_textures.push(self.get_texture(images_dir.join("shipspace.png")));

        let grid_cursor_texture = self.get_texture(images_dir.join("grid-cursor.png"));

        let mut ship_textures = vec![];
        for ship_size in 2..6 {
            let image_file = format!("ship-{}.png", ship_size);
            ship_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        let player_text = [
            self.get_texture(images_dir.join("player-1.png")),
            self.get_texture(images_dir.join("player-2.png")),
        ];

        let game_over_text = [
            self.get_texture(images_dir.join("game-over.png")),
            self.get_texture(images_dir.join("wins.png")),
        ];

        while let Some(e) = self.window.next() {
            if let Some(p) = e.press_args() {
                match p {
                    Button::Mouse(mouse::MouseButton::Left) => self.mouse_left_click(),
                    Button::Mouse(mouse::MouseButton::Right) => self.button_secondary(),
                    Button::Keyboard(keyboard::Key::Left) => self.button_left(),
                    Button::Keyboard(keyboard::Key::Right) => self.button_right(),
                    Button::Keyboard(keyboard::Key::Up) => self.button_up(),
                    Button::Keyboard(keyboard::Key::Down) => self.button_down(),
                    Button::Keyboard(keyboard::Key::Return) => self.button_primary(),
                    Button::Keyboard(keyboard::Key::Space) => self.button_secondary(),
                    _ => {}
                }
            }

            if let Some(c) = e.mouse_cursor_args() {
                self.mouse_cursor_movement(&c);
            }

            if let Some(u) = e.update_args() {
                self.update(&u);
            }

            if e.render_args().is_some() {
                let current_player = self.game.active_player();
                let game_state_placement = self.game.is_state_placement();
                let game_state_active = self.game.is_state_active();
                let game_state_complete = self.game.is_state_complete();
                let shown_player = match game_state_placement {
                    true => current_player,
                    false => self.game.inactive_player()
                };

                let space_size_u32 = self.game.settings.space_size as u32;
                let grid_area = self.grid_area;
                let window_size = self.window_size;
                let turn_end_timer = self.turn_end_timer;
                let game_winner = self.game.get_winner();
                let game_turn = self.game.turn();
                let turn_active = self.turn_active;

                self.window.draw_2d(&e, |c, g| {
                    clear([0.6, 0.6, 1.0, 1.0], g);

                    // Ship icons above grid
                    for (i, ship) in shown_player.ships().iter().enumerate() {
                        if ship.is_active() {
                            let transform = c.transform.trans(
                                (space_size_u32 * 2 * i as u32 + grid_area[0] * 2) as f64,
                                30.0 as f64,
                            );
                            image(&ship_textures[i], transform, g);
                        }
                    }

                    // Grid spaces
                    for space in shown_player.spaces() {
                        let space_pos = space.pos();
                        let transform = c.transform.trans(
                            (space_size_u32 * space_pos[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * space_pos[1] as u32 + grid_area[1]) as f64,
                        );

                        // Only show ship locations during ship placement or if the
                        // current player is computer-controlled.
                        if shown_player.ship_is_in_space(space_pos) && (game_state_placement || (space.is_unchecked() && current_player.is_cpu)) {
                            image(&space_textures[3], transform, g);
                        } else {
                            let space_state = if space.is_unchecked() {
                                0
                            } else if space.is_empty() {
                                1
                            } else {
                                2
                            };
                            image(&space_textures[space_state], transform, g);
                        }
                    }

                    // During ship placement, show the temporary position of the
                    // next ship to be placed.
                    if game_state_placement {
                        for pos in &shown_player.temp_ship_pos {
                            let transform = c.transform.trans(
                                (space_size_u32 * pos[0] as u32 + grid_area[0]) as f64,
                                (space_size_u32 * pos[1] as u32 + grid_area[1]) as f64,
                            );
                            image(&space_textures[3], transform, g);
                        }
                    }

                    // During the game, show the player's grid cursor.
                    if game_state_active && turn_end_timer == 0.0 && !current_player.is_cpu {
                        let grid_cursor = current_player.get_grid_cursor();
                        let transform = c.transform.trans(
                            (space_size_u32 * grid_cursor[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * grid_cursor[1] as u32 + grid_area[1]) as f64,
                        );
                        image(&grid_cursor_texture, transform, g);
                    }

                    // Current player text image
                    if game_winner.is_none() {
                        let turn = game_turn;
                        let player_text_size = player_text[turn].get_size();
                        let transform = c.transform.trans(
                            ((window_size[0] - player_text_size.0) / 2) as f64,
                            2.0
                        );
                        image(&player_text[turn], transform, g);
                    }

                    // During turn transitions / game over, cover the window with
                    // a black rectangle of increasing opacity.
                    if !turn_active && turn_end_timer >= 0.75 {
                        let alpha = match game_state_complete {
                            true => (turn_end_timer as f32 - 0.75) / 1.125,
                            false => (turn_end_timer as f32 - 0.75) / 0.75,
                        };
                        rectangle(
                            [0.0, 0.0, 0.0, alpha],
                            [0.0, 0.0, window_size[0] as f64, window_size[1] as f64],
                            c.transform,
                            g
                        );
                    }

                    // Game over content, to appear over the black rectangle.
                    if turn_end_timer >= 1.5 && game_winner.is_some() {
                        let winner = game_winner.unwrap();
                        let game_over_text_size = game_over_text[0].get_size();
                        let wins_text_size = game_over_text[1].get_size();
                        let player_text_size = player_text[winner].get_size();
                        image(&game_over_text[0], c.transform.trans(
                            ((window_size[0] - game_over_text_size.0) / 2) as f64,
                            2.0
                        ), g);
                        image(&player_text[winner], c.transform.trans(
                            ((window_size[0] - player_text_size.0 - wins_text_size.0 - 2) / 2) as f64,
                            22.0
                        ), g);
                        image(&game_over_text[1], c.transform.trans(
                            ((window_size[0] + player_text_size.0 - wins_text_size.0 + 2) / 2) as f64,
                            22.0
                        ), g);
                    }
                });
            }
        }
    }

    fn update(&mut self, u: &UpdateArgs) {
        let active_player_is_cpu = self.game.is_active_player_cpu();

        if self.game.is_state_placement() {
            if active_player_is_cpu {
                self.game.active_player_mut().cpu_place_ships();
            }

            if self.game.active_player().ships().len() == 4 {
                self.game.switch_turn();
            }

            // All ships have been placed; start the game.
            if self.game.active_player().ships().len() == 4 && self.game.inactive_player().ships().len() == 4 {
                self.game.start();
            }
        } else {
            if !self.turn_active {
                // Continue/end the end-of-turn delay.
                if self.turn_end_timer < 1.5 {
                    self.turn_end_timer += u.dt;
                } else if self.game.is_state_active() {
                    self.game.switch_turn();
                    self.turn_end_timer = 0.0;
                    self.turn_active = true;
                }
            }

            // Continue/end the delay when CPU players take their turn.
            if self.turn_active && active_player_is_cpu {
                self.cpu_turn_timer += u.dt;

                if self.cpu_turn_timer >= 1.0 {
                    let cpu_space = self.game.inactive_player().cpu_select_space();
                    self.game.select_opponent_space(&cpu_space);
                    self.cpu_turn_timer = 0.0;
                    self.turn_active = false;
                }
            }
        }
    }

    fn primary_action(&mut self, grid_pos: &[u8; 2]) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().place_temp_ship();
        }

        if self.game.is_player_selecting_space() && self.game.select_opponent_space(grid_pos) {
            self.turn_active = false;
        }
    }

    /// Processes left button presses according to the current program state.
    fn button_left(&mut self) {
        self.movement(Direction::West);
    }

    /// Processes right button presses according to the current program state.
    fn button_right(&mut self) {
        self.movement(Direction::East);
    }

    /// Processes up button presses according to the current program state.
    fn button_up(&mut self) {
        self.movement(Direction::North);
    }

    /// Processes down button presses according to the current program state.
    fn button_down(&mut self) {
        self.movement(Direction::South);
    }

    /// Processes primary button presses according to the current program state.
    fn button_primary(&mut self) {
        let grid_pos = self.game.active_player().get_grid_cursor();
        self.primary_action(&grid_pos);
    }

    /// Processes secondary button presses according to the current program state.
    fn button_secondary(&mut self) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().rotate_temp_ship();
        }
    }

    /// Processes left mouse clicks according to the current program state.
    fn mouse_left_click(&mut self) {
        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            self.primary_action(&grid_pos);
        }
    }

    /// Performs grid movement according to the current program state.
    fn movement(&mut self, direction: Direction) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().move_temp_ship(direction);
        }

        if self.game.is_player_selecting_space() && self.turn_active {
            self.game.active_player_mut().move_grid_cursor(direction);
        }
    }

    /// Records the last known mouse cursor position.
    pub fn mouse_cursor_movement(&mut self, c: &[f64; 2]) {
        self.mouse_cursor = *c;

        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            let is_player_turn = self.game.is_player_turn();
            let game_state_placement = self.game.is_state_placement();
            let game_state_active = self.game.is_state_active();
            let ref mut player = self.game.active_player_mut();

            if game_state_placement {
                if let Some(ship) = player.get_ship_position(
                    grid_pos,
                    player.temp_ship_dir,
                    player.ships().len() as u8 + 2
                ) {
                    player.temp_ship_pos = ship;
                }
            } else if game_state_active && is_player_turn {
                player.set_grid_cursor(&grid_pos);
            }
        }
    }

    /// Returns the grid coordinates of the mouse cursor position.
    fn mouse_cursor_grid_position(&self) -> Option<[u8; 2]> {
        match self.mouse_over_grid() {
            true => Some([
                ((self.mouse_cursor[0] - self.grid_area[0] as f64) / self.grid_area[0] as f64) as u8,
                ((self.mouse_cursor[1] - self.grid_area[1] as f64) / self.grid_area[0] as f64) as u8,
            ]),
            false => None,
        }
    }

    fn mouse_over_grid(&self) -> bool {
        self.mouse_cursor[0] >= self.grid_area[0] as f64
        && self.mouse_cursor[1] >= self.grid_area[1] as f64
        && self.mouse_cursor[0] < (self.grid_area[0] + self.grid_area[2]) as f64
        && self.mouse_cursor[1] < (self.grid_area[1] + self.grid_area[3]) as f64
    }

    /// Returns the texture from the file at the given path.
    fn get_texture(&mut self, path: PathBuf) -> G2dTexture {
        Texture::from_path(
            &mut self.window.factory,
            path,
            Flip::None,
            &TextureSettings::new(),
        ).unwrap()
    }

    /// Returns the assets directory, if it could be found.
    fn get_assets_dir(mut dir: PathBuf) -> Result<PathBuf, &'static str> {
        let mut result = None;

        while dir.pop() {
            if dir.join("assets").exists() {
                result = Some(dir.join("assets"));
                break;
            }
        }

        result.ok_or("could not find assets directory")
    }
}

<commit_msg>App: use game::Game, not game::*<commit_after>use std::{
    env::current_exe,
    path::PathBuf,
};
use piston_window::*;
use crate::{
    direction::Direction,
    game::Game,
    settings::Settings,
};

pub struct App<'a> {
    window: PistonWindow,
    window_size: [u32; 2],
    game: Game<'a>,
    turn_active: bool,
    turn_end_timer: f64,
    cpu_turn_timer: f64,
    mouse_cursor: [f64; 2],
    grid_area: [u32; 4],
}

impl<'a> App<'a> {
    pub fn new(settings: &Settings) -> App {
        let grid_area = [
            settings.space_size,
            settings.space_size * 3,
            settings.spaces[0] as u32 * settings.space_size,
            settings.spaces[1] as u32 * settings.space_size
        ];

        let window_size = [
            grid_area[2] + settings.space_size * 2,
            grid_area[3] + settings.space_size * 4
        ];

        let window_title = "Battleship";
        let window: PistonWindow = WindowSettings::new(
            window_title,
            window_size,
            )
            .exit_on_esc(true)
            .resizable(false)
            .build()
            .unwrap();

        App {
            window: window,
            window_size: window_size,
            game: Game::new(&settings),
            turn_active: true,
            turn_end_timer: 0.0,
            cpu_turn_timer: 0.0,
            mouse_cursor: [0.0; 2],
            grid_area: grid_area,
        }
    }

    pub fn init(&mut self) {
        self.window.set_ups(60);
        self.window.set_max_fps(60);

        // TODO set textures in a not terrible way
        let assets_dir = Self::get_assets_dir(current_exe().unwrap()).unwrap();
        let images_dir: PathBuf = assets_dir.join("images");
        let mut space_textures = vec![];

        for state in 0..3 {
            let image_file = format!("gridspace-{}.png", state);
            space_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        space_textures.push(self.get_texture(images_dir.join("shipspace.png")));

        let grid_cursor_texture = self.get_texture(images_dir.join("grid-cursor.png"));

        let mut ship_textures = vec![];
        for ship_size in 2..6 {
            let image_file = format!("ship-{}.png", ship_size);
            ship_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        let player_text = [
            self.get_texture(images_dir.join("player-1.png")),
            self.get_texture(images_dir.join("player-2.png")),
        ];

        let game_over_text = [
            self.get_texture(images_dir.join("game-over.png")),
            self.get_texture(images_dir.join("wins.png")),
        ];

        while let Some(e) = self.window.next() {
            if let Some(p) = e.press_args() {
                match p {
                    Button::Mouse(mouse::MouseButton::Left) => self.mouse_left_click(),
                    Button::Mouse(mouse::MouseButton::Right) => self.button_secondary(),
                    Button::Keyboard(keyboard::Key::Left) => self.button_left(),
                    Button::Keyboard(keyboard::Key::Right) => self.button_right(),
                    Button::Keyboard(keyboard::Key::Up) => self.button_up(),
                    Button::Keyboard(keyboard::Key::Down) => self.button_down(),
                    Button::Keyboard(keyboard::Key::Return) => self.button_primary(),
                    Button::Keyboard(keyboard::Key::Space) => self.button_secondary(),
                    _ => {}
                }
            }

            if let Some(c) = e.mouse_cursor_args() {
                self.mouse_cursor_movement(&c);
            }

            if let Some(u) = e.update_args() {
                self.update(&u);
            }

            if e.render_args().is_some() {
                let current_player = self.game.active_player();
                let game_state_placement = self.game.is_state_placement();
                let game_state_active = self.game.is_state_active();
                let game_state_complete = self.game.is_state_complete();
                let shown_player = match game_state_placement {
                    true => current_player,
                    false => self.game.inactive_player()
                };

                let space_size_u32 = self.game.settings.space_size as u32;
                let grid_area = self.grid_area;
                let window_size = self.window_size;
                let turn_end_timer = self.turn_end_timer;
                let game_winner = self.game.get_winner();
                let game_turn = self.game.turn();
                let turn_active = self.turn_active;

                self.window.draw_2d(&e, |c, g| {
                    clear([0.6, 0.6, 1.0, 1.0], g);

                    // Ship icons above grid
                    for (i, ship) in shown_player.ships().iter().enumerate() {
                        if ship.is_active() {
                            let transform = c.transform.trans(
                                (space_size_u32 * 2 * i as u32 + grid_area[0] * 2) as f64,
                                30.0 as f64,
                            );
                            image(&ship_textures[i], transform, g);
                        }
                    }

                    // Grid spaces
                    for space in shown_player.spaces() {
                        let space_pos = space.pos();
                        let transform = c.transform.trans(
                            (space_size_u32 * space_pos[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * space_pos[1] as u32 + grid_area[1]) as f64,
                        );

                        // Only show ship locations during ship placement or if the
                        // current player is computer-controlled.
                        if shown_player.ship_is_in_space(space_pos) && (game_state_placement || (space.is_unchecked() && current_player.is_cpu)) {
                            image(&space_textures[3], transform, g);
                        } else {
                            let space_state = if space.is_unchecked() {
                                0
                            } else if space.is_empty() {
                                1
                            } else {
                                2
                            };
                            image(&space_textures[space_state], transform, g);
                        }
                    }

                    // During ship placement, show the temporary position of the
                    // next ship to be placed.
                    if game_state_placement {
                        for pos in &shown_player.temp_ship_pos {
                            let transform = c.transform.trans(
                                (space_size_u32 * pos[0] as u32 + grid_area[0]) as f64,
                                (space_size_u32 * pos[1] as u32 + grid_area[1]) as f64,
                            );
                            image(&space_textures[3], transform, g);
                        }
                    }

                    // During the game, show the player's grid cursor.
                    if game_state_active && turn_end_timer == 0.0 && !current_player.is_cpu {
                        let grid_cursor = current_player.get_grid_cursor();
                        let transform = c.transform.trans(
                            (space_size_u32 * grid_cursor[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * grid_cursor[1] as u32 + grid_area[1]) as f64,
                        );
                        image(&grid_cursor_texture, transform, g);
                    }

                    // Current player text image
                    if game_winner.is_none() {
                        let turn = game_turn;
                        let player_text_size = player_text[turn].get_size();
                        let transform = c.transform.trans(
                            ((window_size[0] - player_text_size.0) / 2) as f64,
                            2.0
                        );
                        image(&player_text[turn], transform, g);
                    }

                    // During turn transitions / game over, cover the window with
                    // a black rectangle of increasing opacity.
                    if !turn_active && turn_end_timer >= 0.75 {
                        let alpha = match game_state_complete {
                            true => (turn_end_timer as f32 - 0.75) / 1.125,
                            false => (turn_end_timer as f32 - 0.75) / 0.75,
                        };
                        rectangle(
                            [0.0, 0.0, 0.0, alpha],
                            [0.0, 0.0, window_size[0] as f64, window_size[1] as f64],
                            c.transform,
                            g
                        );
                    }

                    // Game over content, to appear over the black rectangle.
                    if turn_end_timer >= 1.5 && game_winner.is_some() {
                        let winner = game_winner.unwrap();
                        let game_over_text_size = game_over_text[0].get_size();
                        let wins_text_size = game_over_text[1].get_size();
                        let player_text_size = player_text[winner].get_size();
                        image(&game_over_text[0], c.transform.trans(
                            ((window_size[0] - game_over_text_size.0) / 2) as f64,
                            2.0
                        ), g);
                        image(&player_text[winner], c.transform.trans(
                            ((window_size[0] - player_text_size.0 - wins_text_size.0 - 2) / 2) as f64,
                            22.0
                        ), g);
                        image(&game_over_text[1], c.transform.trans(
                            ((window_size[0] + player_text_size.0 - wins_text_size.0 + 2) / 2) as f64,
                            22.0
                        ), g);
                    }
                });
            }
        }
    }

    fn update(&mut self, u: &UpdateArgs) {
        let active_player_is_cpu = self.game.is_active_player_cpu();

        if self.game.is_state_placement() {
            if active_player_is_cpu {
                self.game.active_player_mut().cpu_place_ships();
            }

            if self.game.active_player().ships().len() == 4 {
                self.game.switch_turn();
            }

            // All ships have been placed; start the game.
            if self.game.active_player().ships().len() == 4 && self.game.inactive_player().ships().len() == 4 {
                self.game.start();
            }
        } else {
            if !self.turn_active {
                // Continue/end the end-of-turn delay.
                if self.turn_end_timer < 1.5 {
                    self.turn_end_timer += u.dt;
                } else if self.game.is_state_active() {
                    self.game.switch_turn();
                    self.turn_end_timer = 0.0;
                    self.turn_active = true;
                }
            }

            // Continue/end the delay when CPU players take their turn.
            if self.turn_active && active_player_is_cpu {
                self.cpu_turn_timer += u.dt;

                if self.cpu_turn_timer >= 1.0 {
                    let cpu_space = self.game.inactive_player().cpu_select_space();
                    self.game.select_opponent_space(&cpu_space);
                    self.cpu_turn_timer = 0.0;
                    self.turn_active = false;
                }
            }
        }
    }

    fn primary_action(&mut self, grid_pos: &[u8; 2]) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().place_temp_ship();
        }

        if self.game.is_player_selecting_space() && self.game.select_opponent_space(grid_pos) {
            self.turn_active = false;
        }
    }

    /// Processes left button presses according to the current program state.
    fn button_left(&mut self) {
        self.movement(Direction::West);
    }

    /// Processes right button presses according to the current program state.
    fn button_right(&mut self) {
        self.movement(Direction::East);
    }

    /// Processes up button presses according to the current program state.
    fn button_up(&mut self) {
        self.movement(Direction::North);
    }

    /// Processes down button presses according to the current program state.
    fn button_down(&mut self) {
        self.movement(Direction::South);
    }

    /// Processes primary button presses according to the current program state.
    fn button_primary(&mut self) {
        let grid_pos = self.game.active_player().get_grid_cursor();
        self.primary_action(&grid_pos);
    }

    /// Processes secondary button presses according to the current program state.
    fn button_secondary(&mut self) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().rotate_temp_ship();
        }
    }

    /// Processes left mouse clicks according to the current program state.
    fn mouse_left_click(&mut self) {
        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            self.primary_action(&grid_pos);
        }
    }

    /// Performs grid movement according to the current program state.
    fn movement(&mut self, direction: Direction) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().move_temp_ship(direction);
        }

        if self.game.is_player_selecting_space() && self.turn_active {
            self.game.active_player_mut().move_grid_cursor(direction);
        }
    }

    /// Records the last known mouse cursor position.
    pub fn mouse_cursor_movement(&mut self, c: &[f64; 2]) {
        self.mouse_cursor = *c;

        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            let is_player_turn = self.game.is_player_turn();
            let game_state_placement = self.game.is_state_placement();
            let game_state_active = self.game.is_state_active();
            let ref mut player = self.game.active_player_mut();

            if game_state_placement {
                if let Some(ship) = player.get_ship_position(
                    grid_pos,
                    player.temp_ship_dir,
                    player.ships().len() as u8 + 2
                ) {
                    player.temp_ship_pos = ship;
                }
            } else if game_state_active && is_player_turn {
                player.set_grid_cursor(&grid_pos);
            }
        }
    }

    /// Returns the grid coordinates of the mouse cursor position.
    fn mouse_cursor_grid_position(&self) -> Option<[u8; 2]> {
        match self.mouse_over_grid() {
            true => Some([
                ((self.mouse_cursor[0] - self.grid_area[0] as f64) / self.grid_area[0] as f64) as u8,
                ((self.mouse_cursor[1] - self.grid_area[1] as f64) / self.grid_area[0] as f64) as u8,
            ]),
            false => None,
        }
    }

    fn mouse_over_grid(&self) -> bool {
        self.mouse_cursor[0] >= self.grid_area[0] as f64
        && self.mouse_cursor[1] >= self.grid_area[1] as f64
        && self.mouse_cursor[0] < (self.grid_area[0] + self.grid_area[2]) as f64
        && self.mouse_cursor[1] < (self.grid_area[1] + self.grid_area[3]) as f64
    }

    /// Returns the texture from the file at the given path.
    fn get_texture(&mut self, path: PathBuf) -> G2dTexture {
        Texture::from_path(
            &mut self.window.factory,
            path,
            Flip::None,
            &TextureSettings::new(),
        ).unwrap()
    }

    /// Returns the assets directory, if it could be found.
    fn get_assets_dir(mut dir: PathBuf) -> Result<PathBuf, &'static str> {
        let mut result = None;

        while dir.pop() {
            if dir.join("assets").exists() {
                result = Some(dir.join("assets"));
                break;
            }
        }

        result.ok_or("could not find assets directory")
    }
}

<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Just check if we don't get an ICE for the _S type.

#![feature(const_size_of)]

use std::cell::Cell;
use std::mem;

pub struct S {
    s: Cell<usize>
}

pub type _S = [usize; 0 - (mem::size_of::<S>() != 4) as usize];
<commit_msg>Fix rustdoc test<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Just check if we don't get an ICE for the _S type.

use std::cell::Cell;
use std::mem;

pub struct S {
    s: Cell<usize>
}

pub type _S = [usize; 0 - (mem::size_of::<S>() != 4) as usize];
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// aux-build:pub-use-extern-macros.rs

#![feature(use_extern_macros)]

extern crate macros;

// @has pub_use_extern_macros/macro.bar.html
// @!has pub_use_extern_macros/index.html '//code' 'pub use macros::bar;'
pub use macros::bar;

// @has pub_use_extern_macros/macro.baz.html
// @!has pub_use_extern_macros/index.html '//code' 'pub use macros::baz;'
#[doc(inline)]
pub use macros::baz;

// @has pub_use_extern_macros/macro.quux.html
// @!has pub_use_extern_macros/index.html '//code' 'pub use macros::quux;'
#[doc(hidden)]
pub use macros::quux;
<commit_msg>fix test pub-use-extern-macros<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// aux-build:pub-use-extern-macros.rs

#![feature(use_extern_macros)]

extern crate macros;

// @has pub_use_extern_macros/macro.bar.html
// @!has pub_use_extern_macros/index.html '//code' 'pub use macros::bar;'
pub use macros::bar;

// @has pub_use_extern_macros/macro.baz.html
// @!has pub_use_extern_macros/index.html '//code' 'pub use macros::baz;'
#[doc(inline)]
pub use macros::baz;

// @!has pub_use_extern_macros/macro.quux.html
// @!has pub_use_extern_macros/index.html '//code' 'pub use macros::quux;'
#[doc(hidden)]
pub use macros::quux;
<|endoftext|>


<commit_before>#![allow(unused_variables)]

#[derive(Debug)]
struct File {
    name: String,
    data: Vec<u8>,
}

impl File {
    fn new(name: &str) -> File {
        File {
            name: String::from(name),
            data: Vec::new(),
        }
    }

    fn new_with_data(name: &str, data: &Vec<u8>) -> File {
        let mut f = File::new(name);
        f.data = data.clone();
        f
    }

    fn read(self: &File, save_to: &mut Vec<u8>) -> usize {
        let mut tmp = self.data.clone();
        let read_length = tmp.len();
        save_to.reserve(read_length);
        save_to.append(&mut tmp);
        read_length
    }
}

fn open(f: &mut File) -> bool {
    true
}

fn close(f: &mut File) -> bool {
    true
}

fn main() {
    let data: Vec<u8> = vec![114, 117, 115, 116, 33];
    let mut file = File::new_with_data("file.txt", &data);

    let mut buffer: Vec<u8> = vec![];

    open(&mut file);
    let length = &file.read(&mut buffer);
    close(&mut file);

    let text = String::from_utf8_lossy(&buffer);

    println!("{:?}", file);
    println!("{} is {} bytes long", &file.name, length);
    println!("{}", text);
}
<commit_msg>trait having to implement Display<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]

use std::fmt;
use std::fmt::{Display};

#[derive(Debug,PartialEq)]
enum FileState {
    Open,
    Closed,
}
#[derive(Debug)]
struct File {
    name: String,
    data: Vec<u8>,
    state: FileState,
}

impl Display for FileState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FileState::Open => write!(f, "OPEN"),
            FileState::Closed => write!(f, "CLOSED"),
        }
    }
}

impl Display for File {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<{} ({})>", self.name, self.state)
    }
}

impl File {
    fn new(name: &str) -> File {
        File {
            name: String::from(name),
            data: Vec::new(),
            state: FileState::Closed,
        }
    }

    fn new_with_data(name: &str, data: &Vec<u8>) -> File {
        let mut f = File::new(name);
        f.data = data.clone();
        f
    }

    fn read(self: &File, save_to: &mut Vec<u8>) -> usize {
        let mut tmp = self.data.clone();
        let read_length = tmp.len();
        save_to.reserve(read_length);
        save_to.append(&mut tmp);
        read_length
    }
}

fn open(f: &mut File) -> bool {
    true
}

fn close(f: &mut File) -> bool {
    true
}

fn main() {
    let data: Vec<u8> = vec![114, 117, 115, 116, 33];
    let mut file = File::new_with_data("file.txt", &data);

    let mut buffer: Vec<u8> = vec![];

    open(&mut file);
    let length = &file.read(&mut buffer);
    close(&mut file);

    let text = String::from_utf8_lossy(&buffer);

    println!("{:?}", file);
    println!("{} is {} bytes long", &file.name, length);
    println!("{}", text);
}
<|endoftext|>


<commit_before>//! Write benchmark information to the output repository

use std::fs::{self, read_dir, File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;
use std::process::Command;
use std::str;
use std::collections::HashSet;

use serde_json;
use collector::CommitData;
use chrono::{Duration, Utc};
use rust_sysroot::git::Commit as GitCommit;
use execute::Benchmark;
use failure::{Error, ResultExt};

pub struct Repo {
    path: PathBuf,
    use_remote: bool,
    retries: Vec<String>,
}

impl Repo {
    fn git(&self, args: &[&str]) -> Result<(), Error> {
        let mut command = Command::new("git");
        command.current_dir(&self.path);
        info!("git {:?}", args);
        command.args(args);
        let status = command
            .status()
            .with_context(|_| format!("could not spawn git {:?}", args))?;
        if !status.success() {
            bail!(
                "command `git {:?}` failed in `{}`",
                args,
                self.path.display()
            );
        }
        Ok(())
    }

    pub fn open(path: PathBuf, allow_new_dir: bool, use_remote: bool) -> Result<Self, Error> {
        let mut result = Repo {
            path: path,
            use_remote,
            retries: vec![],
        };

        // Don't nuke random repositories, unless specifically requested.
        if !allow_new_dir && !result.retries_file().exists() {
            bail!("`{}` file not present", result.retries_file().display());
        }

        if result.use_remote {
            result.git(&["fetch"])?;
            result.git(&["reset", "--hard", "@{upstream}"])?;
        }

        fs::create_dir_all(result.times()).context("can't create `times/`")?;
        result.load_retries()?;

        Ok(result)
    }

    pub fn success(&self, data: &CommitData) -> Result<(), Error> {
        self.add_commit_data(data)?;
        self.commit_and_push(&format!("{} - success", data.commit.sha))?;
        Ok(())
    }

    pub fn find_missing_commits<'a>(
        &self,
        commits: &'a [GitCommit],
        benchmarks: &[Benchmark],
        triple: &str,
    ) -> Result<Vec<&'a GitCommit>, Error> {
        let mut have = HashSet::new();
        let path = self.times();
        for entry in read_dir(path)? {
            let entry = entry?;
            let filename = entry.file_name().to_string_lossy().to_string();
            let sha =
                &filename[filename.find("00:00").unwrap() + 6..filename.find("-x86").unwrap()];
            have.insert(sha.to_string());
        }

        if let Ok(file) = File::open(self.broken_commits_file()) {
            let file = BufReader::new(file);
            for line in file.lines() {
                let line = line?;
                let sha = &line[..line.find(":").unwrap()];
                have.insert(sha.to_string());
            }
        }

        let missing = commits
            .iter()
            .filter(|c| Utc::now().signed_duration_since(c.date) < Duration::days(29))
            .filter(|c| {
                !have.contains(&c.sha) || {
                    self.load_commit_data(c, triple)
                        .ok()
                        .map(|data| {
                            benchmarks
                                .iter()
                                .any(|b| data.benchmarks.keys().find(|k| **k == b.name).is_none())
                        })
                        .unwrap_or(true)
                }
            })
            .collect::<Vec<_>>();

        Ok(missing)
    }

    fn commit_and_push(&self, message: &str) -> Result<(), Error> {
        self.write_retries()?;
        self.git(&["add", "retries", "times"])?;

        // dirty index
        if let Err(_) = self.git(&["diff-index", "--quiet", "--cached", "HEAD"]) {
            self.git(&["commit", "-m", message])?;
            if self.use_remote {
                self.git(&["push"])?;
            }
        } else {
            println!("nothing to commit...");
        }
        Ok(())
    }

    pub fn load_commit_data(&self, commit: &GitCommit, triple: &str) -> Result<CommitData, Error> {
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date.to_rfc3339(),
            commit.sha,
            triple
        ));
        trace!("loading file {}", filepath.display());
        let mut file = File::open(&filepath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let data = serde_json::from_str(&contents)
            .with_context(|_| format!("failed to read JSON from {:?}", filepath))?;
        Ok(data)
    }

    pub fn add_commit_data(&self, data: &CommitData) -> Result<(), Error> {
        let commit = &data.commit;
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date, commit.sha, data.triple
        ));
        info!("creating file {}", filepath.display());
        let mut file = File::create(&filepath)?;
        serde_json::to_writer(&mut file, &data)?;
        Ok(())
    }

    fn load_retries(&mut self) -> Result<(), Error> {
        let mut retries = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(self.retries_file())
            .with_context(|_| format!("can't create `{}`", self.retries_file().display()))?;
        let mut retries_s = String::new();
        retries.read_to_string(&mut retries_s)?;
        self.retries = retries_s
            .split('\n')
            .map(|line| line.trim())
            .filter(|line| !line.is_empty())
            .map(|line| {
                if line.len() == 40 {
                    Ok(line.to_owned())
                } else {
                    bail!("bad retry hash `{}`", line)
                }
            })
            .collect::<Result<_, _>>()?;
        info!("loaded retries: {:?}", self.retries);
        Ok(())
    }

    fn write_retries(&self) -> Result<(), Error> {
        info!("writing retries");
        let mut retries = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(self.retries_file())
            .context("can't create `retries`")?;
        for retry in self.retries.iter() {
            writeln!(retries, "{}", retry)?;
        }
        Ok(())
    }

    pub fn write_broken_commit(&self, commit: &GitCommit, err: Error) -> Result<(), Error> {
        info!("writing broken commit {:?}: {:?}", commit, err);
        let mut broken = OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(self.broken_commits_file())
            .context("can't create `broken-commits-log`")?;
        writeln!(broken, "{}: \"{:?}\"", commit.sha, err)?;
        Ok(())
    }

    pub fn next_retry(&mut self) -> Option<String> {
        if self.retries.len() == 0 {
            None
        } else {
            Some(self.retries.remove(0))
        }
    }

    fn broken_commits_file(&self) -> PathBuf {
        self.path.join("broken-commits-log")
    }

    fn retries_file(&self) -> PathBuf {
        self.path.join("retries")
    }

    fn times(&self) -> PathBuf {
        self.path.join("times")
    }
}
<commit_msg>Timeout git commands and retry 5 times<commit_after>//! Write benchmark information to the output repository

use std::fs::{self, read_dir, File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;
use std::process::Command;
use std::str;
use std::collections::HashSet;

use std::thread;
use std::time::{self, Instant};
use serde_json;
use collector::CommitData;
use chrono::{Duration, Utc};
use rust_sysroot::git::Commit as GitCommit;
use execute::Benchmark;
use failure::{Error, ResultExt};

pub struct Repo {
    path: PathBuf,
    use_remote: bool,
    retries: Vec<String>,
}

impl Repo {
    fn git(&self, args: &[&str]) -> Result<(), Error> {
        for iteration in 0..5 {
            let mut command = Command::new("git");
            command.current_dir(&self.path);
            info!("[{}/5]: git {:?}", iteration, args);
            command.args(args);
            let mut child = command
                .spawn()
                .with_context(|_| format!("could not spawn git {:?}", args))?;
            let start_time = Instant::now();
            loop {
                if start_time.elapsed().as_secs() > 3 {
                    warn!("killing git command -- timed out");
                    child.kill()?;
                    break;
                }
                match child.try_wait() {
                    Ok(Some(status)) => {
                        if status.success() {
                            return Ok(());
                        } else {
                            bail!(
                                "command `git {:?}` failed in `{}`",
                                args,
                                self.path.display()
                            );
                        }
                    }
                    Ok(None) => thread::sleep(time::Duration::from_millis(250)),
                    Err(err) => bail!("command `git {:?}` failed to try_wait in {:?}: {:?}",
                        args, self.path.display(), err),

                }
            }
        }
        bail!("failed to run git command, timed out too many times")
    }

    pub fn open(path: PathBuf, allow_new_dir: bool, use_remote: bool) -> Result<Self, Error> {
        let mut result = Repo {
            path: path,
            use_remote,
            retries: vec![],
        };

        // Don't nuke random repositories, unless specifically requested.
        if !allow_new_dir && !result.retries_file().exists() {
            bail!("`{}` file not present", result.retries_file().display());
        }

        if result.use_remote {
            result.git(&["fetch"])?;
            result.git(&["reset", "--hard", "@{upstream}"])?;
        }

        fs::create_dir_all(result.times()).context("can't create `times/`")?;
        result.load_retries()?;

        Ok(result)
    }

    pub fn success(&self, data: &CommitData) -> Result<(), Error> {
        self.add_commit_data(data)?;
        self.commit_and_push(&format!("{} - success", data.commit.sha))?;
        Ok(())
    }

    pub fn find_missing_commits<'a>(
        &self,
        commits: &'a [GitCommit],
        benchmarks: &[Benchmark],
        triple: &str,
    ) -> Result<Vec<&'a GitCommit>, Error> {
        let mut have = HashSet::new();
        let path = self.times();
        for entry in read_dir(path)? {
            let entry = entry?;
            let filename = entry.file_name().to_string_lossy().to_string();
            let sha =
                &filename[filename.find("00:00").unwrap() + 6..filename.find("-x86").unwrap()];
            have.insert(sha.to_string());
        }

        if let Ok(file) = File::open(self.broken_commits_file()) {
            let file = BufReader::new(file);
            for line in file.lines() {
                let line = line?;
                let sha = &line[..line.find(":").unwrap()];
                have.insert(sha.to_string());
            }
        }

        let missing = commits
            .iter()
            .filter(|c| Utc::now().signed_duration_since(c.date) < Duration::days(29))
            .filter(|c| {
                !have.contains(&c.sha) || {
                    self.load_commit_data(c, triple)
                        .ok()
                        .map(|data| {
                            benchmarks
                                .iter()
                                .any(|b| data.benchmarks.keys().find(|k| **k == b.name).is_none())
                        })
                        .unwrap_or(true)
                }
            })
            .collect::<Vec<_>>();

        Ok(missing)
    }

    fn commit_and_push(&self, message: &str) -> Result<(), Error> {
        self.write_retries()?;
        self.git(&["add", "retries", "times"])?;

        // dirty index
        if let Err(_) = self.git(&["diff-index", "--quiet", "--cached", "HEAD"]) {
            self.git(&["commit", "-m", message])?;
            if self.use_remote {
                self.git(&["push"])?;
            }
        } else {
            println!("nothing to commit...");
        }
        Ok(())
    }

    pub fn load_commit_data(&self, commit: &GitCommit, triple: &str) -> Result<CommitData, Error> {
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date.to_rfc3339(),
            commit.sha,
            triple
        ));
        trace!("loading file {}", filepath.display());
        let mut file = File::open(&filepath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let data = serde_json::from_str(&contents)
            .with_context(|_| format!("failed to read JSON from {:?}", filepath))?;
        Ok(data)
    }

    pub fn add_commit_data(&self, data: &CommitData) -> Result<(), Error> {
        let commit = &data.commit;
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date, commit.sha, data.triple
        ));
        info!("creating file {}", filepath.display());
        let mut file = File::create(&filepath)?;
        serde_json::to_writer(&mut file, &data)?;
        Ok(())
    }

    fn load_retries(&mut self) -> Result<(), Error> {
        let mut retries = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(self.retries_file())
            .with_context(|_| format!("can't create `{}`", self.retries_file().display()))?;
        let mut retries_s = String::new();
        retries.read_to_string(&mut retries_s)?;
        self.retries = retries_s
            .split('\n')
            .map(|line| line.trim())
            .filter(|line| !line.is_empty())
            .map(|line| {
                if line.len() == 40 {
                    Ok(line.to_owned())
                } else {
                    bail!("bad retry hash `{}`", line)
                }
            })
            .collect::<Result<_, _>>()?;
        info!("loaded retries: {:?}", self.retries);
        Ok(())
    }

    fn write_retries(&self) -> Result<(), Error> {
        info!("writing retries");
        let mut retries = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(self.retries_file())
            .context("can't create `retries`")?;
        for retry in self.retries.iter() {
            writeln!(retries, "{}", retry)?;
        }
        Ok(())
    }

    pub fn write_broken_commit(&self, commit: &GitCommit, err: Error) -> Result<(), Error> {
        info!("writing broken commit {:?}: {:?}", commit, err);
        let mut broken = OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(self.broken_commits_file())
            .context("can't create `broken-commits-log`")?;
        writeln!(broken, "{}: \"{:?}\"", commit.sha, err)?;
        Ok(())
    }

    pub fn next_retry(&mut self) -> Option<String> {
        if self.retries.len() == 0 {
            None
        } else {
            Some(self.retries.remove(0))
        }
    }

    fn broken_commits_file(&self) -> PathBuf {
        self.path.join("broken-commits-log")
    }

    fn retries_file(&self) -> PathBuf {
        self.path.join("retries")
    }

    fn times(&self) -> PathBuf {
        self.path.join("times")
    }
}
<|endoftext|>


<commit_before>use chrono::Utc;
use conduit_cookie::RequestSession;

use super::prelude::*;

use crate::middleware::log_request;
use crate::models::{ApiToken, User};
use crate::util::errors::{
    account_locked, forbidden, internal, AppError, AppResult, ChainError,
    InsecurelyGeneratedTokenRevoked,
};

#[derive(Debug)]
pub struct AuthenticatedUser {
    user: User,
    token_id: Option<i32>,
}

impl AuthenticatedUser {
    pub fn user_id(&self) -> i32 {
        self.user.id
    }

    pub fn api_token_id(&self) -> Option<i32> {
        self.token_id
    }

    pub fn user(self) -> User {
        self.user
    }
}

/// The Origin header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin)
/// is sent with CORS requests and POST requests, and indicates where the request comes from.
/// We don't want to accept authenticated requests that originated from other sites, so this
/// function returns an error if the Origin header doesn't match what we expect "this site" to
/// be: https://crates.io in production, or http://localhost:port/ in development.
fn verify_origin(req: &dyn RequestExt) -> AppResult<()> {
    let headers = req.headers();
    let allowed_origins = &req.app().config.allowed_origins;

    let bad_origin = headers
        .get_all(header::ORIGIN)
        .iter()
        .filter_map(|value| value.to_str().ok())
        .find(|value| !allowed_origins.iter().any(|it| it == value));

    if let Some(bad_origin) = bad_origin {
        let error_message = format!(
            "only same-origin requests can be authenticated. got {:?}",
            bad_origin
        );
        return Err(internal(&error_message))
            .chain_error(|| Box::new(forbidden()) as Box<dyn AppError>);
    }
    Ok(())
}

fn authenticate_user(req: &dyn RequestExt) -> AppResult<AuthenticatedUser> {
    let conn = req.db_conn()?;

    let session = req.session();
    let user_id_from_session = session.get("user_id").and_then(|s| s.parse::<i32>().ok());

    let (user, token_id) = if let Some(id) = user_id_from_session {
        let user = User::find(&conn, id)
            .chain_error(|| internal("user_id from cookie not found in database"))?;

        (user, None)
    } else {
        // Otherwise, look for an `Authorization` header on the request
        let maybe_authorization = req
            .headers()
            .get(header::AUTHORIZATION)
            .and_then(|h| h.to_str().ok());

        if let Some(header_value) = maybe_authorization {
            let token = ApiToken::find_by_api_token(&conn, header_value).map_err(|e| {
                if e.is::<InsecurelyGeneratedTokenRevoked>() {
                    e
                } else {
                    e.chain(internal("invalid token")).chain(forbidden())
                }
            })?;

            let user = User::find(&conn, token.user_id)
                .chain_error(|| internal("user_id from token not found in database"))?;

            (user, Some(token.id))
        } else {
            // Unable to authenticate the user
            return Err(internal("no cookie session or auth header found")).chain_error(forbidden);
        }
    };

    Ok(AuthenticatedUser { user, token_id })
}

impl<'a> UserAuthenticationExt for dyn RequestExt + 'a {
    /// Obtain `AuthenticatedUser` for the request or return an `Forbidden` error
    fn authenticate(&mut self) -> AppResult<AuthenticatedUser> {
        verify_origin(self)?;

        let authenticated_user = authenticate_user(self)?;

        if let Some(reason) = &authenticated_user.user.account_lock_reason {
            let still_locked = if let Some(until) = authenticated_user.user.account_lock_until {
                until > Utc::now().naive_utc()
            } else {
                true
            };
            if still_locked {
                return Err(account_locked(
                    &reason,
                    authenticated_user.user.account_lock_until,
                ));
            }
        }

        log_request::add_custom_metadata(self, "uid", authenticated_user.user_id());
        if let Some(id) = authenticated_user.api_token_id() {
            log_request::add_custom_metadata(self, "tokenid", id);
        }

        Ok(authenticated_user)
    }
}
<commit_msg>controllers::util: Move `return` statements into the condition branches<commit_after>use chrono::Utc;
use conduit_cookie::RequestSession;

use super::prelude::*;

use crate::middleware::log_request;
use crate::models::{ApiToken, User};
use crate::util::errors::{
    account_locked, forbidden, internal, AppError, AppResult, ChainError,
    InsecurelyGeneratedTokenRevoked,
};

#[derive(Debug)]
pub struct AuthenticatedUser {
    user: User,
    token_id: Option<i32>,
}

impl AuthenticatedUser {
    pub fn user_id(&self) -> i32 {
        self.user.id
    }

    pub fn api_token_id(&self) -> Option<i32> {
        self.token_id
    }

    pub fn user(self) -> User {
        self.user
    }
}

/// The Origin header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin)
/// is sent with CORS requests and POST requests, and indicates where the request comes from.
/// We don't want to accept authenticated requests that originated from other sites, so this
/// function returns an error if the Origin header doesn't match what we expect "this site" to
/// be: https://crates.io in production, or http://localhost:port/ in development.
fn verify_origin(req: &dyn RequestExt) -> AppResult<()> {
    let headers = req.headers();
    let allowed_origins = &req.app().config.allowed_origins;

    let bad_origin = headers
        .get_all(header::ORIGIN)
        .iter()
        .filter_map(|value| value.to_str().ok())
        .find(|value| !allowed_origins.iter().any(|it| it == value));

    if let Some(bad_origin) = bad_origin {
        let error_message = format!(
            "only same-origin requests can be authenticated. got {:?}",
            bad_origin
        );
        return Err(internal(&error_message))
            .chain_error(|| Box::new(forbidden()) as Box<dyn AppError>);
    }
    Ok(())
}

fn authenticate_user(req: &dyn RequestExt) -> AppResult<AuthenticatedUser> {
    let conn = req.db_conn()?;

    let session = req.session();
    let user_id_from_session = session.get("user_id").and_then(|s| s.parse::<i32>().ok());

    if let Some(id) = user_id_from_session {
        let user = User::find(&conn, id)
            .chain_error(|| internal("user_id from cookie not found in database"))?;

        return Ok(AuthenticatedUser {
            user,
            token_id: None,
        });
    } else {
        // Otherwise, look for an `Authorization` header on the request
        let maybe_authorization = req
            .headers()
            .get(header::AUTHORIZATION)
            .and_then(|h| h.to_str().ok());

        if let Some(header_value) = maybe_authorization {
            let token = ApiToken::find_by_api_token(&conn, header_value).map_err(|e| {
                if e.is::<InsecurelyGeneratedTokenRevoked>() {
                    e
                } else {
                    e.chain(internal("invalid token")).chain(forbidden())
                }
            })?;

            let user = User::find(&conn, token.user_id)
                .chain_error(|| internal("user_id from token not found in database"))?;

            return Ok(AuthenticatedUser {
                user,
                token_id: Some(token.id),
            });
        } else {
            // Unable to authenticate the user
            return Err(internal("no cookie session or auth header found")).chain_error(forbidden);
        }
    }
}

impl<'a> UserAuthenticationExt for dyn RequestExt + 'a {
    /// Obtain `AuthenticatedUser` for the request or return an `Forbidden` error
    fn authenticate(&mut self) -> AppResult<AuthenticatedUser> {
        verify_origin(self)?;

        let authenticated_user = authenticate_user(self)?;

        if let Some(reason) = &authenticated_user.user.account_lock_reason {
            let still_locked = if let Some(until) = authenticated_user.user.account_lock_until {
                until > Utc::now().naive_utc()
            } else {
                true
            };
            if still_locked {
                return Err(account_locked(
                    &reason,
                    authenticated_user.user.account_lock_until,
                ));
            }
        }

        log_request::add_custom_metadata(self, "uid", authenticated_user.user_id());
        if let Some(id) = authenticated_user.api_token_id() {
            log_request::add_custom_metadata(self, "tokenid", id);
        }

        Ok(authenticated_user)
    }
}
<|endoftext|>