<commit_before>use std::str;
use std::ascii::AsciiExt;
use futures::{Stream, Future, Async};
use futures::future::{ok};
use futures::sync::mpsc::unbounded;
use tokio_core::reactor::Handle;
use tokio_io::{AsyncRead, AsyncWrite};
use tk_http::Status;
use tk_http::server::{Dispatcher, Head, Error, Codec, RecvMode, Encoder};
use tk_http::websocket::{Accept, Loop, ServerCodec, Config as WsConfig};
use tk_bufstream::{WriteBuf, ReadBuf};

use incoming::{Request, Reply, Transport};
use runtime::ServerId;
use super::spawn::Handler;
use super::{IncomingChannel, ReplAction};

/// Incoming requests dispatcher.
pub struct Incoming {
    sender: IncomingChannel,
    handle: Handle,
    server_id: ServerId,
}

struct WebsocketCodec {
    sender: IncomingChannel,
    handle: Handle,
    accept: Accept,
    server_id: ServerId,
    remote_id: ServerId,
}

impl Incoming {

    pub fn new(sender: IncomingChannel, server_id: ServerId, handle: &Handle)
        -> Incoming
    {
        Incoming {
            sender: sender,
            handle: handle.clone(),
            server_id: server_id,
        }
    }

    fn parse_remote_id(&self, headers: &Head) -> Option<ServerId>
    {
        headers.all_headers().iter()
        .find(|h| h.name.eq_ignore_ascii_case("X-Swindon-Node-Id"))
        .and_then(|h| str::from_utf8(h.value).ok())
        .and_then(|s| s.parse().ok())
    }
}

impl<S: Transport> Dispatcher<S> for Incoming {
    type Codec = Request<S>;

    fn headers_received(&mut self, headers: &Head)
        -> Result<Self::Codec, Error>
    {
        if let Some("/v1/swindon-chat") = headers.path() {
            if let Ok(Some(ws)) = headers.get_websocket_upgrade() {
                if let Some(remote_id) = self.parse_remote_id(headers)
                {
                    Ok(Box::new(WebsocketCodec {
                        sender: self.sender.clone(),
                        accept: ws.accept,
                        server_id: self.server_id,
                        remote_id: remote_id,
                        handle: self.handle.clone(),
                    }))
                } else {
                    Ok(error_reply(Status::BadRequest))
                }
            } else {
                Ok(error_reply(Status::BadRequest))
            }
        } else {
            Ok(error_reply(Status::NotFound))
        }
    }
}

impl<S: AsyncRead + AsyncWrite + 'static> Codec<S> for WebsocketCodec {
    type ResponseFuture = Reply<S>;

    fn recv_mode(&mut self) -> RecvMode {
        RecvMode::hijack()
    }

    fn data_received(&mut self, _data: &[u8], _end: bool)
        -> Result<Async<usize>, Error>
    {
        unreachable!()
    }

    fn start_response(&mut self, mut e: Encoder<S>) -> Reply<S> {
        e.status(Status::SwitchingProtocol);
        e.add_header("Connection", "upgrade").unwrap();
        e.add_header("Upgrade", "websocket").unwrap();
        e.format_header("Sec-Websocket-Accept", &self.accept).unwrap();
        e.format_header("X-Swindon-Node-Id", &self.server_id).unwrap();
        e.done_headers().unwrap();
        Box::new(ok(e.done()))
    }

    fn hijack(&mut self, write_buf: WriteBuf<S>, read_buf: ReadBuf<S>) {
        let out = write_buf.framed(ServerCodec);
        let inp = read_buf.framed(ServerCodec);
        let wcfg = WsConfig::new().done();

        let (tx, rx) = unbounded();
        let rx = rx.map_err(|e| format!("receive error: {:?}", e));
        self.sender.send(ReplAction::Attach {
            tx: tx,
            server_id: self.remote_id,
            peer: None,
        }).ok();

        self.handle.spawn(
            Loop::server(out, inp, rx, Handler(self.sender.clone()), &wcfg)
            .map_err(|e| error!("Websocket loop error: {:?}", e))
        );
    }
}

// Shortcut for error replies

fn error_reply<S: 'static>(status: Status) -> Request<S> {
    Box::new(QuickReply(Some(status)))
}

struct QuickReply(Option<Status>);

impl<S: 'static> Codec<S> for QuickReply {
    type ResponseFuture = Reply<S>;
    fn recv_mode(&mut self) -> RecvMode {
        RecvMode::buffered_upfront(0)
    }
    fn data_received(&mut self, data: &[u8], end: bool)
        -> Result<Async<usize>, Error>
    {
        assert!(end);
        assert!(data.len() == 0);
        Ok(Async::Ready(0))
    }
    fn start_response(&mut self, mut e: Encoder<S>) -> Reply<S> {
        e.status(self.0.take().expect("start response called once"));
        e.add_length(0).unwrap();
        e.done_headers().unwrap();
        Box::new(ok(e.done()))
    }
}
<commit_msg>chat replication: do not allow connections from self<commit_after>use std::str;
use std::ascii::AsciiExt;
use futures::{Stream, Future, Async};
use futures::future::{ok};
use futures::sync::mpsc::unbounded;
use tokio_core::reactor::Handle;
use tokio_io::{AsyncRead, AsyncWrite};
use tk_http::Status;
use tk_http::server::{Dispatcher, Head, Error, Codec, RecvMode, Encoder};
use tk_http::websocket::{Accept, Loop, ServerCodec, Config as WsConfig};
use tk_bufstream::{WriteBuf, ReadBuf};

use incoming::{Request, Reply, Transport};
use runtime::ServerId;
use super::spawn::Handler;
use super::{IncomingChannel, ReplAction};

/// Incoming requests dispatcher.
pub struct Incoming {
    sender: IncomingChannel,
    handle: Handle,
    server_id: ServerId,
}

struct WebsocketCodec {
    sender: IncomingChannel,
    handle: Handle,
    accept: Accept,
    server_id: ServerId,
    remote_id: ServerId,
}

impl Incoming {

    pub fn new(sender: IncomingChannel, server_id: ServerId, handle: &Handle)
        -> Incoming
    {
        Incoming {
            sender: sender,
            handle: handle.clone(),
            server_id: server_id,
        }
    }

    fn parse_remote_id(&self, headers: &Head) -> Option<ServerId>
    {
        headers.all_headers().iter()
        .find(|h| h.name.eq_ignore_ascii_case("X-Swindon-Node-Id"))
        .and_then(|h| str::from_utf8(h.value).ok())
        .and_then(|s| s.parse().ok())
    }
}

impl<S: Transport> Dispatcher<S> for Incoming {
    type Codec = Request<S>;

    fn headers_received(&mut self, headers: &Head)
        -> Result<Self::Codec, Error>
    {
        if let Some("/v1/swindon-chat") = headers.path() {
            if let Ok(Some(ws)) = headers.get_websocket_upgrade() {
                if let Some(remote_id) = self.parse_remote_id(headers)
                {
                    if remote_id == self.server_id {
                        error!(concat!("Configuration error:",
                            " got connection from ourself: {:?}"),
                            self.server_id);
                        return Ok(error_reply(Status::BadRequest));
                    }
                    Ok(Box::new(WebsocketCodec {
                        sender: self.sender.clone(),
                        accept: ws.accept,
                        server_id: self.server_id,
                        remote_id: remote_id,
                        handle: self.handle.clone(),
                    }))
                } else {
                    Ok(error_reply(Status::BadRequest))
                }
            } else {
                Ok(error_reply(Status::BadRequest))
            }
        } else {
            Ok(error_reply(Status::NotFound))
        }
    }
}

impl<S: AsyncRead + AsyncWrite + 'static> Codec<S> for WebsocketCodec {
    type ResponseFuture = Reply<S>;

    fn recv_mode(&mut self) -> RecvMode {
        RecvMode::hijack()
    }

    fn data_received(&mut self, _data: &[u8], _end: bool)
        -> Result<Async<usize>, Error>
    {
        unreachable!()
    }

    fn start_response(&mut self, mut e: Encoder<S>) -> Reply<S> {
        e.status(Status::SwitchingProtocol);
        e.add_header("Connection", "upgrade").unwrap();
        e.add_header("Upgrade", "websocket").unwrap();
        e.format_header("Sec-Websocket-Accept", &self.accept).unwrap();
        e.format_header("X-Swindon-Node-Id", &self.server_id).unwrap();
        e.done_headers().unwrap();
        Box::new(ok(e.done()))
    }

    fn hijack(&mut self, write_buf: WriteBuf<S>, read_buf: ReadBuf<S>) {
        let out = write_buf.framed(ServerCodec);
        let inp = read_buf.framed(ServerCodec);
        let wcfg = WsConfig::new().done();

        let (tx, rx) = unbounded();
        let rx = rx.map_err(|e| format!("receive error: {:?}", e));
        self.sender.send(ReplAction::Attach {
            tx: tx,
            server_id: self.remote_id,
            peer: None,
        }).ok();

        self.handle.spawn(
            Loop::server(out, inp, rx, Handler(self.sender.clone()), &wcfg)
            .map_err(|e| error!("Websocket loop error: {:?}", e))
        );
    }
}

// Shortcut for error replies

fn error_reply<S: 'static>(status: Status) -> Request<S> {
    Box::new(QuickReply(Some(status)))
}

struct QuickReply(Option<Status>);

impl<S: 'static> Codec<S> for QuickReply {
    type ResponseFuture = Reply<S>;
    fn recv_mode(&mut self) -> RecvMode {
        RecvMode::buffered_upfront(0)
    }
    fn data_received(&mut self, data: &[u8], end: bool)
        -> Result<Async<usize>, Error>
    {
        assert!(end);
        assert!(data.len() == 0);
        Ok(Async::Ready(0))
    }
    fn start_response(&mut self, mut e: Encoder<S>) -> Reply<S> {
        e.status(self.0.take().expect("start response called once"));
        e.add_length(0).unwrap();
        e.done_headers().unwrap();
        Box::new(ok(e.done()))
    }
}
<|endoftext|>


<commit_before>fn process_rate_per_hour(speed: u8, expected_rate: f64) {
    assert!((assembly_line::production_rate_per_hour(speed) - expected_rate).abs() < f64::EPSILON);
}

fn process_rate_per_minute(speed: u8, expected_rate: u32) {
    assert_eq!(
        assembly_line::working_items_per_minute(speed),
        expected_rate
    );
}

#[test]
fn production_rate_per_hour_at_speed_zero() {
    process_rate_per_hour(0, 0.0);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_one() {
    process_rate_per_hour(1, 221.0);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_four() {
    process_rate_per_hour(4, 884.0);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_seven() {
    process_rate_per_hour(7, 1392.3);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_nine() {
    process_rate_per_hour(9, 1531.53);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_zero() {
    process_rate_per_minute(0, 0);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_one() {
    process_rate_per_minute(1, 3);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_five() {
    process_rate_per_minute(5, 16);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_eight() {
    process_rate_per_minute(8, 26);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_ten() {
    process_rate_per_minute(10, 28);
}
<commit_msg>Relax precision for assembly-line tests (#1328)<commit_after>fn process_rate_per_hour(speed: u8, expected_rate: f64) {
    let actual_rate = assembly_line::production_rate_per_hour(speed);
    let actual_rate = (actual_rate * 100.0).round() / 100.0;
    assert!((actual_rate - expected_rate).abs() < f64::EPSILON);
}

fn process_rate_per_minute(speed: u8, expected_rate: u32) {
    assert_eq!(
        assembly_line::working_items_per_minute(speed),
        expected_rate
    );
}

#[test]
fn production_rate_per_hour_at_speed_zero() {
    process_rate_per_hour(0, 0.0);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_one() {
    process_rate_per_hour(1, 221.0);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_four() {
    process_rate_per_hour(4, 884.0);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_seven() {
    process_rate_per_hour(7, 1392.3);
}

#[ignore]
#[test]
fn production_rate_per_hour_at_speed_nine() {
    process_rate_per_hour(9, 1531.53);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_zero() {
    process_rate_per_minute(0, 0);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_one() {
    process_rate_per_minute(1, 3);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_five() {
    process_rate_per_minute(5, 16);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_eight() {
    process_rate_per_minute(8, 26);
}

#[ignore]
#[test]
fn production_rate_per_minute_at_speed_ten() {
    process_rate_per_minute(10, 28);
}
<|endoftext|>


<commit_before>// ignore-windows: Concurrency on Windows is not supported yet.
// compile-flags: -Zmiri-disable-isolation

use std::thread::{spawn, sleep};
use std::ptr::null_mut;
use std::sync::atomic::{Ordering, AtomicPtr};
use std::time::Duration;

#[derive(Copy, Clone)]
struct EvilSend<T>(pub T);

unsafe impl<T> Send for EvilSend<T> {}
unsafe impl<T> Sync for EvilSend<T> {}

pub fn main() {
    // Shared atomic pointer
    let pointer = AtomicPtr::new(null_mut::<usize>());
    let ptr = EvilSend(&pointer as *const AtomicPtr<usize>);

    // Note: this is scheduler-dependent
    // the operations need to occur in
    // order, otherwise the allocation is
    // not visible to the other-thread to
    // detect the race:
    //  1. stack-allocate
    //  2. atomic_store
    //  3. atomic_load
    //  4. write-value
    //  5. read-value
    unsafe {
        let j1 = spawn(move || {
            // Concurrent allocate the memory.
            // Uses relaxed semantics to not generate
            // a release sequence.
            let pointer = &*ptr.0;

            let mut stack_var = 0usize;

            pointer.store(&mut stack_var as *mut _, Ordering::Release);

            sleep(Duration::from_millis(1000));

            //read
            stack_var //~ ERROR Data race detected between Read on Thread(id = 1) and Write on Thread(id = 2)
        });

        let j2 = spawn(move || {
            let pointer = &*ptr.0;
            *pointer.load(Ordering::Acquire) = 3;
        });

        j1.join().unwrap();
        j2.join().unwrap();
    }
}
<commit_msg>Defeat the mir-opt=3 optimizer.<commit_after>// ignore-windows: Concurrency on Windows is not supported yet.
// compile-flags: -Zmiri-disable-isolation

use std::thread::{spawn, sleep};
use std::ptr::null_mut;
use std::sync::atomic::{Ordering, AtomicPtr};
use std::time::Duration;

#[derive(Copy, Clone)]
struct EvilSend<T>(pub T);

unsafe impl<T> Send for EvilSend<T> {}
unsafe impl<T> Sync for EvilSend<T> {}

pub fn main() {
    // Shared atomic pointer
    let pointer = AtomicPtr::new(null_mut::<usize>());
    let ptr = EvilSend(&pointer as *const AtomicPtr<usize>);

    // Note: this is scheduler-dependent
    // the operations need to occur in
    // order, otherwise the allocation is
    // not visible to the other-thread to
    // detect the race:
    //  1. stack-allocate
    //  2. atomic_store
    //  3. atomic_load
    //  4. write-value
    //  5. read-value
    unsafe {
        let j1 = spawn(move || {
            // Concurrent allocate the memory.
            // Uses relaxed semantics to not generate
            // a release sequence.
            let pointer = &*ptr.0;

            let mut stack_var = 0usize;

            pointer.store(&mut stack_var as *mut _, Ordering::Release);

            sleep(Duration::from_millis(1000));

            // Read, the add 1 fixes -Z mir-opt-level=3 from removing the read via dest-prop and breaking
            // the test.
            stack_var + 1 //~ ERROR Data race detected between Read on Thread(id = 1) and Write on Thread(id = 2)
        });

        let j2 = spawn(move || {
            let pointer = &*ptr.0;
            *pointer.load(Ordering::Acquire) = 3;
        });

        j1.join().unwrap();
        j2.join().unwrap();
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2016 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for reading & extracting data from Multiboot 2 boot information.
//!
//! Consult the [Multiboot Specification](http://nongnu.askapache.com/grub/phcoder/multiboot.pdf)
//! for more information.
use memory::PAddr;
use memory::paging::{PhysicalPage, FrameRange};
use elf::section::{Sections, HeaderRepr};

const END_TAG_LEN: u32 = 8;

const HEADER_LEN: u32 = 24;

pub const MAGIC: u32 = 0xe85250d6;

#[repr(u32)]
pub enum HeaderArch {
      I386 = 0
    , Mips = 4
}

#[repr(C)]
pub struct Header {
      pub magic: u32
    , pub arch: HeaderArch
    , pub header_length: u32
    , pub checksum: u32
    , pub end_tag: Tag
}

// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[linkage = "external"]
#[link_section = ".multiboot_header"]
pub static HEADER: Header = Header {
    magic: MAGIC
  , arch: HeaderArch::I386
  , header_length: HEADER_LEN
  , checksum: -((MAGIC + 0 + HEADER_LEN) as i32) as u32
  , end_tag: Tag { ty: TagType::End
                 , length: END_TAG_LEN
                 }
};

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    #[inline]
    pub fn start_addr(&self) -> PAddr {
        PAddr::from(self as *const _ as u64)
    }

    pub fn end_addr(&self) -> PAddr {
        PAddr::from(self.start_addr() + self.length as u64)
    }

    pub unsafe fn from(addr: PAddr) -> Result<&'static Self, &'static str> {
        let info: &Info = &*(addr.into(): u64 as *const Info);
        if info.has_end() { Ok(info) }
        else { Err("Multiboot info structure had no end tag!") }
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    ///
    /// # Returns
    ///  - `Some(tag)` if a tag of the given type could be found.
    ///  - `None` if no tag of the given type could be found.
    pub fn get_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    /// Finds the memory map tag.
    ///
    ///  # Returns
    ///  - `Some(MemMapTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn mem_map(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*((tag as *const Tag) as *const MemMapTag) })
    }

    /// Finds the ELF sections tag.
    ///
    ///  # Returns
    ///  - `Some(ElfSectionsTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn elf_sections(&self) -> Option<&'static ElfSectionsTag> {
        self.get_tag(TagType::ELFSections)
            .map(|tag| unsafe {
                &*((tag as *const Tag) as *const ElfSectionsTag)
            })
    }

    /// Returns an iterator over all Multiboot tags.
    #[inline]
    fn tags(&self) -> Tags { Tags(&self.tag_start as *const Tag) }

    /// Returns true if the multiboot structure has a valid end tag.
    fn has_end(&self) -> bool {
        let end_tag_addr
            = self as *const _ as usize +
              (self.length as usize - END_TAG_LEN as usize);
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }

    /// Returns the kernel frame range from the Multiboot 2 ELF Sections
    pub fn kernel_frames(&self) -> Result<FrameRange, &'static str> {
        let sections_tag = self.elf_sections()
                               .ok_or("ELF sections tag required!")?;

        let kernel_start = sections_tag.sections()
                              .map(|s| s.addr())
                              .min()
                              .ok_or("Couldn't find kernel start section!")?;
        let kernel_end = sections_tag.sections()
                              .map(|s| s.addr())
                              .max()
                              .ok_or("Couldn't find kernel end section!")?;

        Ok(PhysicalPage::from(kernel_start) .. PhysicalPage::from(kernel_end))
    }
}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
///
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
pub struct Tag { /// the type of this tag.
                 pub ty: TagType
               , length: u32
               }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType { /// Tag that indicates the end of multiboot tags
                   End              = 0
                 , /// Command line passed to the bootloader
                   CommandLine      = 1
                 , BootloaderName   = 2
                 , Modules          = 3
                 , BasicMemInfo     = 4
                 , BIOSBootDev      = 5
                 , MemoryMap        = 6
                 , VBEInfo          = 7
                 , FramebufferInfo  = 8
                 , ELFSections      = 9
                 , APMTable         = 10
                 }

/// An iterator over Multiboot 2 tags.
struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let next_addr = self.0 as usize + size as usize;
        self.0 = (((next_addr-1) & !0x7) + 0x8) as *const _;
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag{ ty: TagType::End, length: END_TAG_LEN } => None
          , tag => { self.advance(tag.length); Some(tag) }
        }
    }
}

/// A Memory Map tag
#[repr(C)]
pub struct MemMapTag { tag: Tag
                     , pub entry_size: u32
                     , pub entry_version: u32
                     , first_entry: MemArea
                     }

impl MemMapTag {

    /// Returns an iterator over all the memory areas in this tag.
    pub fn areas(&self) -> MemAreas {
        MemAreas { curr: (&self.first_entry) as *const MemArea
                 , last: ((self as *const MemMapTag as u32) +
                         self.tag.length - self.entry_size)
                         as *const MemArea
                 , size: self.entry_size
                 }
    }
}

/// A tag that stores the boot command line.
#[repr(C)]
pub struct CommandLineTag { tag: Tag
                          , /// The boot command line.
                            ///
                            /// The command line is a normal C-style zero-
                            /// terminated UTF-8 string.
                            pub command_line: [u8]
                          }

#[repr(C)]
pub struct ModulesTag { tag: Tag
                      , /// The address at which the module begins.
                        pub mod_begin: PAddr
                      , /// The address at which the module ends.
                        pub mod_end: PAddr
                      , /// A string (typically a command line)
                        pub string: [u8]
                      }

#[repr(u32)]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum MemAreaType { Available = 1
                     , Acpi      = 3
                     , Preserve  = 4
                     }

/// A multiboot 2 memory area
#[repr(C)]
pub struct MemArea { /// the starting address of the memory area
                     pub base: PAddr
                   , /// the length of the memory area
                     pub length: PAddr
                   , /// the type of the memory area
                     pub ty: MemAreaType
                   , _pad: u32
                   }

impl MemArea {
    #[inline] pub fn address(&self) -> PAddr {
        self.base + self.length - 1
    }
}

/// An iterator over memory areas
#[derive(Clone)]
pub struct MemAreas { curr: *const MemArea
                    , last: *const MemArea
                    , size: u32
                    }

impl Iterator for MemAreas {
    type Item = &'static MemArea;

    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = unsafe { &*self.curr };
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            if current.ty == MemAreaType::Available {
                Some(current)
            } else {
                self.next()
            }
        }
    }
}

#[cfg(target_pointer_width = "32")]
pub type Word = u32;
#[cfg(target_pointer_width = "64")]
pub type Word = u64;

/// A Multiboot 2 ELF sections tag
#[derive(Debug)]
#[repr(packed)]
pub struct ElfSectionsTag { tag: Tag
                          , /// the number of sections pointed to by this tag
                            pub n_sections: u32
                          , /// the size of each ELF section
                            pub section_size: u32
                          , stringtable_idx: u32
                          , first_section: HeaderRepr<Word>
                          }

impl ElfSectionsTag {
    /// Returns an iterator over the ELF sections pointed to by this tag.
    pub fn sections(&'static self) -> Sections<'static, Word> {
        Sections::new( &self.first_section
                     , self.n_sections - 1
                     , self.section_size
                     )
    }
}
<commit_msg>[kernel] fix imports in x86 multiboot2 module<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2016 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for reading & extracting data from Multiboot 2 boot information.
//!
//! Consult the [Multiboot Specification](http://nongnu.askapache.com/grub/phcoder/multiboot.pdf)
//! for more information.
use memory::{PAddr, PhysicalPage, FrameRange};
use elf::section::{Sections, HeaderRepr};

const END_TAG_LEN: u32 = 8;

const HEADER_LEN: u32 = 24;

pub const MAGIC: u32 = 0xe85250d6;

#[repr(u32)]
pub enum HeaderArch {
      I386 = 0
    , Mips = 4
}

#[repr(C)]
pub struct Header {
      pub magic: u32
    , pub arch: HeaderArch
    , pub header_length: u32
    , pub checksum: u32
    , pub end_tag: Tag
}

// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[linkage = "external"]
#[link_section = ".multiboot_header"]
pub static HEADER: Header = Header {
    magic: MAGIC
  , arch: HeaderArch::I386
  , header_length: HEADER_LEN
  , checksum: -((MAGIC + 0 + HEADER_LEN) as i32) as u32
  , end_tag: Tag { ty: TagType::End
                 , length: END_TAG_LEN
                 }
};

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    #[inline]
    pub fn start_addr(&self) -> PAddr {
        PAddr::from(self as *const _ as u64)
    }

    pub fn end_addr(&self) -> PAddr {
        PAddr::from(self.start_addr() + self.length as u64)
    }

    pub unsafe fn from(addr: PAddr) -> Result<&'static Self, &'static str> {
        let info: &Info = &*(addr.into(): u64 as *const Info);
        if info.has_end() { Ok(info) }
        else { Err("Multiboot info structure had no end tag!") }
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    ///
    /// # Returns
    ///  - `Some(tag)` if a tag of the given type could be found.
    ///  - `None` if no tag of the given type could be found.
    pub fn get_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    /// Finds the memory map tag.
    ///
    ///  # Returns
    ///  - `Some(MemMapTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn mem_map(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*((tag as *const Tag) as *const MemMapTag) })
    }

    /// Finds the ELF sections tag.
    ///
    ///  # Returns
    ///  - `Some(ElfSectionsTag)` if a memory map tag could be found
    ///  - `None` if no tag of the given type could be found.
    #[inline]
    pub fn elf_sections(&self) -> Option<&'static ElfSectionsTag> {
        self.get_tag(TagType::ELFSections)
            .map(|tag| unsafe {
                &*((tag as *const Tag) as *const ElfSectionsTag)
            })
    }

    /// Returns an iterator over all Multiboot tags.
    #[inline]
    fn tags(&self) -> Tags { Tags(&self.tag_start as *const Tag) }

    /// Returns true if the multiboot structure has a valid end tag.
    fn has_end(&self) -> bool {
        let end_tag_addr
            = self as *const _ as usize +
              (self.length as usize - END_TAG_LEN as usize);
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }

    /// Returns the kernel frame range from the Multiboot 2 ELF Sections
    pub fn kernel_frames(&self) -> Result<FrameRange, &'static str> {
        let sections_tag = self.elf_sections()
                               .ok_or("ELF sections tag required!")?;

        let kernel_start = sections_tag.sections()
                              .map(|s| s.addr())
                              .min()
                              .ok_or("Couldn't find kernel start section!")?;
        let kernel_end = sections_tag.sections()
                              .map(|s| s.addr())
                              .max()
                              .ok_or("Couldn't find kernel end section!")?;

        Ok(PhysicalPage::from(kernel_start) .. PhysicalPage::from(kernel_end))
    }
}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
///
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
pub struct Tag { /// the type of this tag.
                 pub ty: TagType
               , length: u32
               }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
pub enum TagType { /// Tag that indicates the end of multiboot tags
                   End              = 0
                 , /// Command line passed to the bootloader
                   CommandLine      = 1
                 , BootloaderName   = 2
                 , Modules          = 3
                 , BasicMemInfo     = 4
                 , BIOSBootDev      = 5
                 , MemoryMap        = 6
                 , VBEInfo          = 7
                 , FramebufferInfo  = 8
                 , ELFSections      = 9
                 , APMTable         = 10
                 }

/// An iterator over Multiboot 2 tags.
struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let next_addr = self.0 as usize + size as usize;
        self.0 = (((next_addr-1) & !0x7) + 0x8) as *const _;
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag{ ty: TagType::End, length: END_TAG_LEN } => None
          , tag => { self.advance(tag.length); Some(tag) }
        }
    }
}

/// A Memory Map tag
#[repr(C)]
pub struct MemMapTag { tag: Tag
                     , pub entry_size: u32
                     , pub entry_version: u32
                     , first_entry: MemArea
                     }

impl MemMapTag {

    /// Returns an iterator over all the memory areas in this tag.
    pub fn areas(&self) -> MemAreas {
        MemAreas { curr: (&self.first_entry) as *const MemArea
                 , last: ((self as *const MemMapTag as u32) +
                         self.tag.length - self.entry_size)
                         as *const MemArea
                 , size: self.entry_size
                 }
    }
}

/// A tag that stores the boot command line.
#[repr(C)]
pub struct CommandLineTag { tag: Tag
                          , /// The boot command line.
                            ///
                            /// The command line is a normal C-style zero-
                            /// terminated UTF-8 string.
                            pub command_line: [u8]
                          }

#[repr(C)]
pub struct ModulesTag { tag: Tag
                      , /// The address at which the module begins.
                        pub mod_begin: PAddr
                      , /// The address at which the module ends.
                        pub mod_end: PAddr
                      , /// A string (typically a command line)
                        pub string: [u8]
                      }

#[repr(u32)]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum MemAreaType { Available = 1
                     , Acpi      = 3
                     , Preserve  = 4
                     }

/// A multiboot 2 memory area
#[repr(C)]
pub struct MemArea { /// the starting address of the memory area
                     pub base: PAddr
                   , /// the length of the memory area
                     pub length: PAddr
                   , /// the type of the memory area
                     pub ty: MemAreaType
                   , _pad: u32
                   }

impl MemArea {
    #[inline] pub fn address(&self) -> PAddr {
        self.base + self.length - 1
    }
}

/// An iterator over memory areas
#[derive(Clone)]
pub struct MemAreas { curr: *const MemArea
                    , last: *const MemArea
                    , size: u32
                    }

impl Iterator for MemAreas {
    type Item = &'static MemArea;

    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = unsafe { &*self.curr };
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            if current.ty == MemAreaType::Available {
                Some(current)
            } else {
                self.next()
            }
        }
    }
}

#[cfg(target_pointer_width = "32")]
pub type Word = u32;
#[cfg(target_pointer_width = "64")]
pub type Word = u64;

/// A Multiboot 2 ELF sections tag
#[derive(Debug)]
#[repr(packed)]
pub struct ElfSectionsTag { tag: Tag
                          , /// the number of sections pointed to by this tag
                            pub n_sections: u32
                          , /// the size of each ELF section
                            pub section_size: u32
                          , stringtable_idx: u32
                          , first_section: HeaderRepr<Word>
                          }

impl ElfSectionsTag {
    /// Returns an iterator over the ELF sections pointed to by this tag.
    pub fn sections(&'static self) -> Sections<'static, Word> {
        Sections::new( &self.first_section
                     , self.n_sections - 1
                     , self.section_size
                     )
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::mem;
use std::collections::HashSet;

use clap::{App, Arg, ArgMatches};
use hyper::method::Method;
use mime;
use multipart::client::Multipart;
use serde_json;
use walkdir::{WalkDir, Iter as WalkDirIter};
use zip;

use CliResult;
use utils::{TempFile, get_org_and_project, get_sha1_checksum};
use macho::is_macho_file;
use commands::Config;

const BATCH_SIZE : usize = 15;

#[derive(Debug, Deserialize)]
struct DSymFile {
    uuid: String,
    #[serde(rename="objectName")]
    object_name: String,
    #[serde(rename="cpuName")]
    cpu_name: String,
}

#[derive(Deserialize)]
struct MissingChecksumsResponse {
    missing: HashSet<String>,
}

struct LocalFile {
    path: PathBuf,
    arc_name: String,
    checksum: String,
}

struct BatchIter {
    path: PathBuf,
    wd_iter: WalkDirIter,
    batch: Vec<LocalFile>,
}

impl BatchIter {
    pub fn new<P: AsRef<Path>>(path: P) -> BatchIter {
        BatchIter {
            path: path.as_ref().to_path_buf(),
            wd_iter: WalkDir::new(&path).into_iter(),
            batch: vec![],
        }
    }
}

impl Iterator for BatchIter {
    type Item = CliResult<Vec<LocalFile>>;

    fn next(&mut self) -> Option<CliResult<Vec<LocalFile>>> {
        loop {
            if let Some(dent_res) = self.wd_iter.next() {
                let dent = iter_try!(dent_res);
                let md = iter_try!(dent.metadata());
                if md.is_file() && is_macho_file(dent.path()) {
                    let name = Path::new("DebugSymbols")
                        .join(dent.path().strip_prefix(&self.path).unwrap());
                    println!("  {}", name.display());
                    self.batch.push(LocalFile {
                        path: dent.path().to_path_buf(),
                        arc_name: name.to_string_lossy().into_owned(),
                        checksum: iter_try!(get_sha1_checksum(dent.path())),
                    });
                    if self.batch.len() > BATCH_SIZE {
                        break;
                    }
                }
            } else {
                break;
            }
        }
        if self.batch.len() == 0 {
            None
        } else {
            Some(Ok(mem::replace(&mut self.batch, vec![])))
        }
    }
}

fn find_missing_files(config: &Config, files: Vec<LocalFile>, api_path: &str)
    -> CliResult<Vec<LocalFile>>
{
    let mut url = format!("{}unknown/?", api_path);
    for (idx, ref file) in files.iter().enumerate() {
        if idx > 0 {
            url.push('&');
        }
        url.push_str("checksums=");
        url.push_str(&file.checksum);
    }
    let mut resp = config.api_request(Method::Get, &url)?;

    // This happens if the sentry installation we're connecting to does not
    // have that endpoint.  In that case just continue.  Any other HTTP
    // failure is ignored here too which is okay since we will try to upload
    // next step anyways.
    if !resp.status.is_success() {
        return Ok(files);
    }

    let state : MissingChecksumsResponse = serde_json::from_reader(&mut resp)?;
    let mut rv = vec![];
    for file in files.into_iter() {
        if state.missing.contains(&file.checksum) {
            rv.push(file)
        }
    }
    Ok(rv)
}

fn zip_up(files: &[LocalFile]) -> CliResult<TempFile> {
    println!("  Uploading a batch of missing files ...");
    let tf = TempFile::new()?;
    let mut zip = zip::ZipWriter::new(tf.open());
    for ref file in files {
        println!("    {}", file.arc_name);
        zip.start_file(file.arc_name.clone(),
            zip::CompressionMethod::Deflated)?;
        io::copy(&mut File::open(file.path.clone())?, &mut zip)?;
    }
    Ok(tf)
}

fn upload_dsyms(files: &[LocalFile], config: &Config,
                api_path: &str) -> CliResult<Vec<DSymFile>> {
    let tf = zip_up(files)?;
    let req = config.prepare_api_request(Method::Post, api_path)?;
    let mut mp = Multipart::from_request_sized(req)?;
    mp.write_stream("file", &mut tf.open(), Some("archive.zip"),
        "application/zip".parse::<mime::Mime>().ok());
    let mut resp = mp.send()?;
    if !resp.status.is_success() {
        fail!(resp);
    }
    Ok(serde_json::from_reader(&mut resp)?)
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("uploads debug symbols to a project")
        .arg(Arg::with_name("org")
             .value_name("ORG")
             .long("org")
             .short("o")
             .help("The organization slug"))
        .arg(Arg::with_name("project")
             .value_name("PROJECT")
             .long("project")
             .short("p")
             .help("The project slug"))
        .arg(Arg::with_name("global")
             .long("global")
             .short("g")
             .help("Uploads the dsyms globally. This can only be done \
                    with super admin access for the Sentry installation"))
        .arg(Arg::with_name("path")
             .value_name("PATH")
             .help("The path to the debug symbols")
             .required(true)
             .index(1))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> CliResult<()> {
    let path = matches.value_of("path").unwrap();
    let api_path = if matches.is_present("global") {
        "/system/global-dsyms/".to_owned()
    } else {
        let (org, project) = get_org_and_project(matches)?;
        format!("/projects/{}/{}/files/dsyms/", org, project)
    };

    println!("Uploading symbols from {}...", path);

    for batch_res in BatchIter::new(path) {
        let batch = batch_res?;
        let missing = find_missing_files(config, batch, &api_path)?;
        if missing.len() == 0 {
            continue;
        }
        println!("Detected missing files");
        let rv = upload_dsyms(&missing, config, &api_path)?;
        if rv.len() > 0 {
            println!("  Accepted debug symbols:");
            for df in rv {
                println!("    {} ({}; {})", df.uuid, df.object_name, df.cpu_name);
            }
        }
    }

    Ok(())
}
<commit_msg>Simplification<commit_after>use std::io;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::mem;
use std::collections::HashSet;

use clap::{App, Arg, ArgMatches};
use hyper::method::Method;
use mime;
use multipart::client::Multipart;
use serde_json;
use walkdir::{WalkDir, Iter as WalkDirIter};
use zip;

use CliResult;
use utils::{TempFile, get_org_and_project, get_sha1_checksum};
use macho::is_macho_file;
use commands::Config;

const BATCH_SIZE : usize = 15;

#[derive(Debug, Deserialize)]
struct DSymFile {
    uuid: String,
    #[serde(rename="objectName")]
    object_name: String,
    #[serde(rename="cpuName")]
    cpu_name: String,
}

#[derive(Deserialize)]
struct MissingChecksumsResponse {
    missing: HashSet<String>,
}

struct LocalFile {
    path: PathBuf,
    arc_name: String,
    checksum: String,
}

struct BatchIter {
    path: PathBuf,
    wd_iter: WalkDirIter,
    batch: Vec<LocalFile>,
}

impl BatchIter {
    pub fn new<P: AsRef<Path>>(path: P) -> BatchIter {
        BatchIter {
            path: path.as_ref().to_path_buf(),
            wd_iter: WalkDir::new(&path).into_iter(),
            batch: vec![],
        }
    }
}

impl Iterator for BatchIter {
    type Item = CliResult<Vec<LocalFile>>;

    fn next(&mut self) -> Option<CliResult<Vec<LocalFile>>> {
        loop {
            if let Some(dent_res) = self.wd_iter.next() {
                let dent = iter_try!(dent_res);
                let md = iter_try!(dent.metadata());
                if md.is_file() && is_macho_file(dent.path()) {
                    let name = Path::new("DebugSymbols")
                        .join(dent.path().strip_prefix(&self.path).unwrap());
                    println!("  {}", name.display());
                    self.batch.push(LocalFile {
                        path: dent.path().to_path_buf(),
                        arc_name: name.to_string_lossy().into_owned(),
                        checksum: iter_try!(get_sha1_checksum(dent.path())),
                    });
                    if self.batch.len() > BATCH_SIZE {
                        break;
                    }
                }
            } else {
                break;
            }
        }
        if self.batch.len() == 0 {
            None
        } else {
            Some(Ok(mem::replace(&mut self.batch, vec![])))
        }
    }
}

fn find_missing_files(config: &Config, files: Vec<LocalFile>, api_path: &str)
    -> CliResult<Vec<LocalFile>>
{
    let mut url = format!("{}unknown/?", api_path);
    for (idx, ref file) in files.iter().enumerate() {
        if idx > 0 {
            url.push('&');
        }
        url.push_str("checksums=");
        url.push_str(&file.checksum);
    }
    let mut resp = config.api_request(Method::Get, &url)?;

    // This happens if the sentry installation we're connecting to does not
    // have that endpoint.  In that case just continue.  Any other HTTP
    // failure is ignored here too which is okay since we will try to upload
    // next step anyways.
    if !resp.status.is_success() {
        return Ok(files);
    }

    let state : MissingChecksumsResponse = serde_json::from_reader(&mut resp)?;
    let mut rv = vec![];
    for file in files.into_iter() {
        if state.missing.contains(&file.checksum) {
            rv.push(file)
        }
    }
    Ok(rv)
}

fn zip_up(files: &[LocalFile]) -> CliResult<TempFile> {
    println!("  Uploading a batch of missing files ...");
    let tf = TempFile::new()?;
    let mut zip = zip::ZipWriter::new(tf.open());
    for ref file in files {
        println!("    {}", file.arc_name);
        zip.start_file(file.arc_name.clone(),
            zip::CompressionMethod::Deflated)?;
        io::copy(&mut File::open(file.path.clone())?, &mut zip)?;
    }
    Ok(tf)
}

fn upload_dsyms(files: &[LocalFile], config: &Config,
                api_path: &str) -> CliResult<Vec<DSymFile>> {
    let tf = zip_up(files)?;
    let req = config.prepare_api_request(Method::Post, api_path)?;
    let mut mp = Multipart::from_request_sized(req)?;
    mp.write_stream("file", &mut tf.open(), Some("archive.zip"),
        "application/zip".parse::<mime::Mime>().ok());
    let mut resp = mp.send()?;
    if !resp.status.is_success() {
        fail!(resp);
    }
    Ok(serde_json::from_reader(&mut resp)?)
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("uploads debug symbols to a project")
        .arg(Arg::with_name("org")
             .value_name("ORG")
             .long("org")
             .short("o")
             .help("The organization slug"))
        .arg(Arg::with_name("project")
             .value_name("PROJECT")
             .long("project")
             .short("p")
             .help("The project slug"))
        .arg(Arg::with_name("global")
             .long("global")
             .short("g")
             .help("Uploads the dsyms globally. This can only be done \
                    with super admin access for the Sentry installation"))
        .arg(Arg::with_name("path")
             .value_name("PATH")
             .help("The path to the debug symbols")
             .required(true)
             .index(1))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> CliResult<()> {
    let path = matches.value_of("path").unwrap();
    let api_path = if matches.is_present("global") {
        "/system/global-dsyms/".to_owned()
    } else {
        let (org, project) = get_org_and_project(matches)?;
        format!("/projects/{}/{}/files/dsyms/", org, project)
    };

    println!("Uploading symbols from {}...", path);

    for batch_res in BatchIter::new(path) {
        let missing = find_missing_files(config, batch_res?, &api_path)?;
        if missing.len() == 0 {
            continue;
        }
        println!("Detected missing files");
        let rv = upload_dsyms(&missing, config, &api_path)?;
        if rv.len() > 0 {
            println!("  Accepted debug symbols:");
            for df in rv {
                println!("    {} ({}; {})", df.uuid, df.object_name, df.cpu_name);
            }
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use crate::command_prelude::*;
use anyhow::Error;
use cargo::ops::{self, CompileFilter, FilterRule, LibRule};

pub fn cli() -> App {
    subcommand("test")
        // Subcommand aliases are handled in `aliased_command()`.
        // .alias("t")
        .setting(AppSettings::TrailingVarArg)
        .about("Execute all unit and integration tests and build examples of a local package")
        .arg(
            Arg::with_name("TESTNAME")
                .help("If specified, only run tests containing this string in their names"),
        )
        .arg(
            Arg::with_name("args")
                .help("Arguments for the test binary")
                .multiple(true)
                .last(true),
        )
        .arg(
            opt(
                "quiet",
                "Display one character per test instead of one line",
            )
            .short("q"),
        )
        .arg_targets_all(
            "Test only this package's library unit tests",
            "Test only the specified binary",
            "Test all binaries",
            "Test only the specified example",
            "Test all examples",
            "Test only the specified test target",
            "Test all tests",
            "Test only the specified bench target",
            "Test all benches",
            "Test all targets",
        )
        .arg(opt("doc", "Test only this library's documentation"))
        .arg(opt("no-run", "Compile, but don't run tests"))
        .arg(opt("no-fail-fast", "Run all tests regardless of failure"))
        .arg_package_spec(
            "Package to run tests for",
            "Test all packages in the workspace",
            "Exclude packages from the test",
        )
        .arg_jobs()
        .arg_release("Build artifacts in release mode, with optimizations")
        .arg_profile("Build artifacts with the specified profile")
        .arg_features()
        .arg_target_triple("Build for the target triple")
        .arg_target_dir()
        .arg_manifest_path()
        .arg_ignore_rust_version()
        .arg_message_format()
        .arg_unit_graph()
        .arg_future_incompat_report()
        .after_help("Run `cargo help test` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    let ws = args.workspace(config)?;

    let mut compile_opts = args.compile_options(
        config,
        CompileMode::Test,
        Some(&ws),
        ProfileChecking::Custom,
    )?;

    compile_opts.build_config.requested_profile =
        args.get_profile_name(config, "test", ProfileChecking::Custom)?;

    // `TESTNAME` is actually an argument of the test binary, but it's
    // important, so we explicitly mention it and reconfigure.
    let test_name: Option<&str> = args.value_of("TESTNAME");
    let test_args = args.value_of("TESTNAME").into_iter();
    let test_args = test_args.chain(args.values_of("args").unwrap_or_default());
    let test_args = test_args.collect::<Vec<_>>();

    let no_run = args.is_present("no-run");
    let doc = args.is_present("doc");
    if doc {
        if let CompileFilter::Only { .. } = compile_opts.filter {
            return Err(CliError::new(
                anyhow::format_err!("Can't mix --doc with other target selecting options"),
                101,
            ));
        }
        if no_run {
            return Err(CliError::new(
                anyhow::format_err!("Can't skip running doc tests with --no-run"),
                101,
            ));
        }
        compile_opts.build_config.mode = CompileMode::Doctest;
        compile_opts.filter = ops::CompileFilter::new(
            LibRule::True,
            FilterRule::none(),
            FilterRule::none(),
            FilterRule::none(),
            FilterRule::none(),
        );
    } else if test_name.is_some() {
        if let CompileFilter::Default { .. } = compile_opts.filter {
            compile_opts.filter = ops::CompileFilter::new(
                LibRule::Default,   // compile the library, so the unit tests can be run filtered
                FilterRule::All, // compile the binaries, so the unit tests in binaries can be run filtered
                FilterRule::All, // compile the tests, so the integration tests can be run filtered
                FilterRule::none(), // specify --examples to unit test binaries filtered
                FilterRule::none(), // specify --benches to unit test benchmarks filtered
            ); // also, specify --doc to run doc tests filtered
        }
    }

    let ops = ops::TestOptions {
        no_run,
        no_fail_fast: args.is_present("no-fail-fast"),
        compile_opts,
    };

    let err = ops::run_tests(&ws, &ops, &test_args)?;
    match err {
        None => Ok(()),
        Some(err) => {
            let context = anyhow::format_err!("{}", err.hint(&ws, &ops.compile_opts));
            let e = match err.code {
                // Don't show "process didn't exit successfully" for simple errors.
                Some(i) if cargo_util::is_simple_exit_code(i) => CliError::new(context, i),
                Some(i) => CliError::new(Error::from(err).context(context), i),
                None => CliError::new(Error::from(err).context(context), 101),
            };
            Err(e)
        }
    }
}
<commit_msg>Auto merge of #10210 - nyurik:test-help, r=ehuss<commit_after>use crate::command_prelude::*;
use anyhow::Error;
use cargo::ops::{self, CompileFilter, FilterRule, LibRule};

pub fn cli() -> App {
    subcommand("test")
        // Subcommand aliases are handled in `aliased_command()`.
        // .alias("t")
        .setting(AppSettings::TrailingVarArg)
        .about("Execute all unit and integration tests and build examples of a local package")
        .arg(
            Arg::with_name("TESTNAME")
                .help("If specified, only run tests containing this string in their names"),
        )
        .arg(
            Arg::with_name("args")
                .help("Arguments for the test binary")
                .multiple(true)
                .last(true),
        )
        .arg(
            opt(
                "quiet",
                "Display one character per test instead of one line",
            )
            .short("q"),
        )
        .arg_targets_all(
            "Test only this package's library unit tests",
            "Test only the specified binary",
            "Test all binaries",
            "Test only the specified example",
            "Test all examples",
            "Test only the specified test target",
            "Test all tests",
            "Test only the specified bench target",
            "Test all benches",
            "Test all targets",
        )
        .arg(opt("doc", "Test only this library's documentation"))
        .arg(opt("no-run", "Compile, but don't run tests"))
        .arg(opt("no-fail-fast", "Run all tests regardless of failure"))
        .arg_package_spec(
            "Package to run tests for",
            "Test all packages in the workspace",
            "Exclude packages from the test",
        )
        .arg_jobs()
        .arg_release("Build artifacts in release mode, with optimizations")
        .arg_profile("Build artifacts with the specified profile")
        .arg_features()
        .arg_target_triple("Build for the target triple")
        .arg_target_dir()
        .arg_manifest_path()
        .arg_ignore_rust_version()
        .arg_message_format()
        .arg_unit_graph()
        .arg_future_incompat_report()
        .after_help(
            "Run `cargo help test` for more detailed information.\n\
             Run `cargo test -- --help` for test binary options.\n",
        )
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    let ws = args.workspace(config)?;

    let mut compile_opts = args.compile_options(
        config,
        CompileMode::Test,
        Some(&ws),
        ProfileChecking::Custom,
    )?;

    compile_opts.build_config.requested_profile =
        args.get_profile_name(config, "test", ProfileChecking::Custom)?;

    // `TESTNAME` is actually an argument of the test binary, but it's
    // important, so we explicitly mention it and reconfigure.
    let test_name: Option<&str> = args.value_of("TESTNAME");
    let test_args = args.value_of("TESTNAME").into_iter();
    let test_args = test_args.chain(args.values_of("args").unwrap_or_default());
    let test_args = test_args.collect::<Vec<_>>();

    let no_run = args.is_present("no-run");
    let doc = args.is_present("doc");
    if doc {
        if let CompileFilter::Only { .. } = compile_opts.filter {
            return Err(CliError::new(
                anyhow::format_err!("Can't mix --doc with other target selecting options"),
                101,
            ));
        }
        if no_run {
            return Err(CliError::new(
                anyhow::format_err!("Can't skip running doc tests with --no-run"),
                101,
            ));
        }
        compile_opts.build_config.mode = CompileMode::Doctest;
        compile_opts.filter = ops::CompileFilter::new(
            LibRule::True,
            FilterRule::none(),
            FilterRule::none(),
            FilterRule::none(),
            FilterRule::none(),
        );
    } else if test_name.is_some() {
        if let CompileFilter::Default { .. } = compile_opts.filter {
            compile_opts.filter = ops::CompileFilter::new(
                LibRule::Default,   // compile the library, so the unit tests can be run filtered
                FilterRule::All, // compile the binaries, so the unit tests in binaries can be run filtered
                FilterRule::All, // compile the tests, so the integration tests can be run filtered
                FilterRule::none(), // specify --examples to unit test binaries filtered
                FilterRule::none(), // specify --benches to unit test benchmarks filtered
            ); // also, specify --doc to run doc tests filtered
        }
    }

    let ops = ops::TestOptions {
        no_run,
        no_fail_fast: args.is_present("no-fail-fast"),
        compile_opts,
    };

    let err = ops::run_tests(&ws, &ops, &test_args)?;
    match err {
        None => Ok(()),
        Some(err) => {
            let context = anyhow::format_err!("{}", err.hint(&ws, &ops.compile_opts));
            let e = match err.code {
                // Don't show "process didn't exit successfully" for simple errors.
                Some(i) if cargo_util::is_simple_exit_code(i) => CliError::new(context, i),
                Some(i) => CliError::new(Error::from(err).context(context), i),
                None => CliError::new(Error::from(err).context(context), 101),
            };
            Err(e)
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt::{self, Show, Formatter};
use std::io::{USER_DIR};
use std::io::fs::{mkdir_recursive, rmdir_recursive, PathExtensions};
use rustc_serialize::{Encodable, Encoder};
use url::Url;
use git2;

use core::GitReference;
use util::{CargoResult, ChainError, human, ToUrl, internal};

#[derive(PartialEq, Clone)]
#[allow(missing_copy_implementations)]
pub struct GitRevision(git2::Oid);

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[derive(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[derive(PartialEq,Clone,RustcEncodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
    repo: git2::Repository,
}

#[derive(RustcEncodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout<'a> {
    database: &'a GitDatabase,
    location: Path,
    revision: GitRevision,
    repo: git2::Repository,
}

#[derive(RustcEncodable)]
pub struct EncodableGitCheckout {
    database: EncodableGitDatabase,
    location: String,
    revision: String,
}

impl<'a, E, S: Encoder<E>> Encodable<S, E> for GitCheckout<'a> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            location: self.location.display().to_string(),
            revision: self.revision.to_string(),
            database: EncodableGitDatabase {
                remote: self.database.remote.clone(),
                path: self.database.path.display().to_string(),
            },
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for(&self, path: &Path, reference: &GitReference)
                   -> CargoResult<GitRevision> {
        let db = try!(self.db_at(path));
        db.rev_for(reference)
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        let repo = match git2::Repository::open(into) {
            Ok(repo) => {
                try!(self.fetch_into(&repo).chain_error(|| {
                    internal(format!("failed to fetch into {}", into.display()))
                }));
                repo
            }
            Err(..) => {
                try!(self.clone_into(into).chain_error(|| {
                    internal(format!("failed to clone into: {}", into.display()))
                }))
            }
        };

        Ok(GitDatabase { remote: self.clone(), path: into.clone(), repo: repo })
    }

    pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> {
        let repo = try!(git2::Repository::open(db_path));
        Ok(GitDatabase {
            remote: self.clone(),
            path: db_path.clone(),
            repo: repo,
        })
    }

    fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
        // Create a local anonymous remote in the repository to fetch the url
        let url = self.url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        fetch(dst, url.as_slice(), refspec)
    }

    fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
        let url = self.url.to_string();
        if dst.exists() {
            try!(rmdir_recursive(dst));
        }
        try!(mkdir_recursive(dst, USER_DIR));
        let repo = try!(git2::Repository::init_bare(dst));
        try!(fetch(&repo, url.as_slice(), "refs/heads/*:refs/heads/*"));
        Ok(repo)
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        let checkout = match git2::Repository::open(dest) {
            Ok(repo) => {
                let checkout = GitCheckout::new(dest, self, rev, repo);
                if !checkout.is_fresh() {
                    try!(checkout.fetch());
                    try!(checkout.reset());
                    assert!(checkout.is_fresh());
                }
                checkout
            }
            Err(..) => try!(GitCheckout::clone_into(dest, self, rev)),
        };
        try!(checkout.update_submodules().chain_error(|| {
            internal("failed to update submodules")
        }));
        Ok(checkout)
    }

    pub fn rev_for(&self, reference: &GitReference) -> CargoResult<GitRevision> {
        let id = match *reference {
            GitReference::Tag(ref s) => {
                try!((|:| {
                    let refname = format!("refs/tags/{}", s);
                    let id = try!(self.repo.refname_to_id(refname.as_slice()));
                    let tag = try!(self.repo.find_tag(id));
                    let obj = try!(tag.peel());
                    Ok(obj.id())
                }).chain_error(|| {
                    human(format!("failed to find tag `{}`", s))
                }))
            }
            GitReference::Branch(ref s) => {
                try!((|:| {
                    let b = try!(self.repo.find_branch(s.as_slice(),
                                                       git2::BranchType::Local));
                    b.get().target().chain_error(|| {
                        human(format!("branch `{}` did not have a target", s))
                    })
                }).chain_error(|| {
                    human(format!("failed to find branch `{}`", s))
                }))
            }
            GitReference::Rev(ref s) => {
                let obj = try!(self.repo.revparse_single(s.as_slice()));
                obj.id()
            }
        };
        Ok(GitRevision(id))
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        try!(self.repo.revparse_single(reference.as_slice()));
        Ok(())
    }
}

impl<'a> GitCheckout<'a> {
    fn new(path: &Path, database: &'a GitDatabase, revision: GitRevision,
           repo: git2::Repository)
           -> GitCheckout<'a>
    {
        GitCheckout {
            location: path.clone(),
            database: database,
            revision: revision,
            repo: repo,
        }
    }

    fn clone_into(into: &Path, database: &'a GitDatabase,
                  revision: GitRevision)
                  -> CargoResult<GitCheckout<'a>>
    {
        let repo = try!(GitCheckout::clone_repo(database.get_path(), into));
        let checkout = GitCheckout::new(into, database, revision, repo);
        try!(checkout.reset());
        Ok(checkout)
    }

    fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
        let dirname = into.dir_path();

        try!(mkdir_recursive(&dirname, USER_DIR).chain_error(|| {
            human(format!("Couldn't mkdir {}", dirname.display()))
        }));

        if into.exists() {
            try!(rmdir_recursive(into).chain_error(|| {
                human(format!("Couldn't rmdir {}", into.display()))
            }));
        }

        let url = try!(source.to_url().map_err(human));
        let url = url.to_string();
        let repo = try!(git2::Repository::clone(url.as_slice(),
                                                into).chain_error(|| {
            internal(format!("failed to clone {} into {}", source.display(),
                             into.display()))
        }));
        Ok(repo)
    }

    fn is_fresh(&self) -> bool {
        match self.repo.revparse_single("HEAD") {
            Ok(head) => head.id().to_string() == self.revision.to_string(),
            _ => false,
        }
    }

    fn fetch(&self) -> CargoResult<()> {
        info!("fetch {}", self.repo.path().display());
        let url = try!(self.database.path.to_url().map_err(human));
        let url = url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        try!(fetch(&self.repo, url.as_slice(), refspec));
        Ok(())
    }

    fn reset(&self) -> CargoResult<()> {
        info!("reset {} to {}", self.repo.path().display(), self.revision);
        let object = try!(self.repo.find_object(self.revision.0, None));
        try!(self.repo.reset(&object, git2::ResetType::Hard, None, None));
        Ok(())
    }

    fn update_submodules(&self) -> CargoResult<()> {
        return update_submodules(&self.repo);

        fn update_submodules(repo: &git2::Repository) -> CargoResult<()> {
            info!("update submodules for: {}", repo.path().display());

            for mut child in try!(repo.submodules()).into_iter() {
                try!(child.init(false));
                let url = try!(child.url().chain_error(|| {
                    internal("non-utf8 url for submodule")
                }));

                // A submodule which is listed in .gitmodules but not actually
                // checked out will not have a head id, so we should ignore it.
                let head = match child.head_id() {
                    Some(head) => head,
                    None => continue,
                };

                // If the submodule hasn't been checked out yet, we need to
                // clone it. If it has been checked out and the head is the same
                // as the submodule's head, then we can bail out and go to the
                // next submodule.
                let head_and_repo = child.open().and_then(|repo| {
                    Ok((try!(repo.head()).target(), repo))
                });
                let repo = match head_and_repo {
                    Ok((head, repo)) => {
                        if child.head_id() == head {
                            continue
                        }
                        repo
                    }
                    Err(..) => {
                        let path = repo.path().dir_path().join(child.path());
                        try!(git2::Repository::clone(url, &path))
                    }
                };

                // Fetch data from origin and reset to the head commit
                let refspec = "refs/heads/*:refs/heads/*";
                try!(fetch(&repo, url, refspec).chain_error(|| {
                    internal(format!("failed to fetch submodule `{}` from {}",
                                     child.name().unwrap_or(""), url))
                }));

                let obj = try!(repo.find_object(head, None));
                try!(repo.reset(&obj, git2::ResetType::Hard, None, None));
                try!(update_submodules(&repo));
            }
            Ok(())
        }
    }
}

fn with_authentication<T>(url: &str,
                          cfg: &git2::Config,
                          f: |&mut git2::Credentials| -> CargoResult<T>)
                          -> CargoResult<T> {
    // Prepare the authentication callbacks.
    //
    // We check the `allowed` types of credentials, and we try to do as much as
    // possible based on that:
    //
    // * Prioritize SSH keys from the local ssh agent as they're likely the most
    //   reliable. The username here is prioritized from the credential
    //   callback, then from whatever is configured in git itself, and finally
    //   we fall back to the generic user of `git`.
    //
    // * If a username/password is allowed, then we fallback to git2-rs's
    //   implementation of the credential helper. This is what is configured
    //   with `credential.helper` in git, and is the interface for the OSX
    //   keychain, for example.
    //
    // * After the above two have failed, we just kinda grapple attempting to
    //   return *something*.
    let mut cred_helper = git2::CredentialHelper::new(url);
    cred_helper.config(cfg);
    let mut cred_error = false;
    let ret = f(&mut |&mut: url, username, allowed| {
        let username = if username.is_empty() {None} else {Some(username)};
        let creds = if allowed.contains(git2::SSH_KEY) {
            let user = username.map(|s| s.to_string())
                               .or_else(|| cred_helper.username.clone())
                               .unwrap_or("git".to_string());
            git2::Cred::ssh_key_from_agent(user.as_slice())
        } else if allowed.contains(git2::USER_PASS_PLAINTEXT) {
            git2::Cred::credential_helper(cfg, url, username)
        } else if allowed.contains(git2::DEFAULT) {
            git2::Cred::default()
        } else {
            Err(git2::Error::from_str("no authentication available"))
        };
        cred_error = creds.is_err();
        creds
    });
    if cred_error {
        ret.chain_error(|| {
            human("Failed to authenticate when downloading repository")
        })
    } else {
        ret
    }
}

pub fn fetch(repo: &git2::Repository, url: &str,
             refspec: &str) -> CargoResult<()> {
    // Create a local anonymous remote in the repository to fetch the url

    with_authentication(url, &try!(repo.config()), |f| {
        let mut cb = git2::RemoteCallbacks::new();
        cb.credentials(|a, b, c| f.call_mut((a, b, c)));
        let mut remote = try!(repo.remote_anonymous(url.as_slice(),
                                                    Some(refspec)));
        try!(remote.add_fetch("refs/tags/*:refs/tags/*"));
        remote.set_callbacks(&mut cb);
        try!(remote.fetch(&["refs/tags/*:refs/tags/*", refspec], None, None));
        Ok(())
    })
}
<commit_msg>Auto merge of #1122 - alexcrichton:issue-1119, r=brson<commit_after>use std::fmt::{self, Show, Formatter};
use std::io::{USER_DIR};
use std::io::fs::{mkdir_recursive, rmdir_recursive, PathExtensions};
use rustc_serialize::{Encodable, Encoder};
use url::Url;
use git2::{mod, ObjectType};

use core::GitReference;
use util::{CargoResult, ChainError, human, ToUrl, internal};

#[derive(PartialEq, Clone)]
#[allow(missing_copy_implementations)]
pub struct GitRevision(git2::Oid);

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[derive(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[derive(PartialEq,Clone,RustcEncodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
    repo: git2::Repository,
}

#[derive(RustcEncodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout<'a> {
    database: &'a GitDatabase,
    location: Path,
    revision: GitRevision,
    repo: git2::Repository,
}

#[derive(RustcEncodable)]
pub struct EncodableGitCheckout {
    database: EncodableGitDatabase,
    location: String,
    revision: String,
}

impl<'a, E, S: Encoder<E>> Encodable<S, E> for GitCheckout<'a> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            location: self.location.display().to_string(),
            revision: self.revision.to_string(),
            database: EncodableGitDatabase {
                remote: self.database.remote.clone(),
                path: self.database.path.display().to_string(),
            },
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for(&self, path: &Path, reference: &GitReference)
                   -> CargoResult<GitRevision> {
        let db = try!(self.db_at(path));
        db.rev_for(reference)
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        let repo = match git2::Repository::open(into) {
            Ok(repo) => {
                try!(self.fetch_into(&repo).chain_error(|| {
                    internal(format!("failed to fetch into {}", into.display()))
                }));
                repo
            }
            Err(..) => {
                try!(self.clone_into(into).chain_error(|| {
                    internal(format!("failed to clone into: {}", into.display()))
                }))
            }
        };

        Ok(GitDatabase { remote: self.clone(), path: into.clone(), repo: repo })
    }

    pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> {
        let repo = try!(git2::Repository::open(db_path));
        Ok(GitDatabase {
            remote: self.clone(),
            path: db_path.clone(),
            repo: repo,
        })
    }

    fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
        // Create a local anonymous remote in the repository to fetch the url
        let url = self.url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        fetch(dst, url.as_slice(), refspec)
    }

    fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
        let url = self.url.to_string();
        if dst.exists() {
            try!(rmdir_recursive(dst));
        }
        try!(mkdir_recursive(dst, USER_DIR));
        let repo = try!(git2::Repository::init_bare(dst));
        try!(fetch(&repo, url.as_slice(), "refs/heads/*:refs/heads/*"));
        Ok(repo)
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        let checkout = match git2::Repository::open(dest) {
            Ok(repo) => {
                let checkout = GitCheckout::new(dest, self, rev, repo);
                if !checkout.is_fresh() {
                    try!(checkout.fetch());
                    try!(checkout.reset());
                    assert!(checkout.is_fresh());
                }
                checkout
            }
            Err(..) => try!(GitCheckout::clone_into(dest, self, rev)),
        };
        try!(checkout.update_submodules().chain_error(|| {
            internal("failed to update submodules")
        }));
        Ok(checkout)
    }

    pub fn rev_for(&self, reference: &GitReference) -> CargoResult<GitRevision> {
        let id = match *reference {
            GitReference::Tag(ref s) => {
                try!((|:| {
                    let refname = format!("refs/tags/{}", s);
                    let id = try!(self.repo.refname_to_id(refname.as_slice()));
                    let obj = try!(self.repo.find_object(id, None));
                    let obj = try!(obj.peel(ObjectType::Commit));
                    Ok(obj.id())
                }).chain_error(|| {
                    human(format!("failed to find tag `{}`", s))
                }))
            }
            GitReference::Branch(ref s) => {
                try!((|:| {
                    let b = try!(self.repo.find_branch(s.as_slice(),
                                                       git2::BranchType::Local));
                    b.get().target().chain_error(|| {
                        human(format!("branch `{}` did not have a target", s))
                    })
                }).chain_error(|| {
                    human(format!("failed to find branch `{}`", s))
                }))
            }
            GitReference::Rev(ref s) => {
                let obj = try!(self.repo.revparse_single(s.as_slice()));
                obj.id()
            }
        };
        Ok(GitRevision(id))
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        try!(self.repo.revparse_single(reference.as_slice()));
        Ok(())
    }
}

impl<'a> GitCheckout<'a> {
    fn new(path: &Path, database: &'a GitDatabase, revision: GitRevision,
           repo: git2::Repository)
           -> GitCheckout<'a>
    {
        GitCheckout {
            location: path.clone(),
            database: database,
            revision: revision,
            repo: repo,
        }
    }

    fn clone_into(into: &Path, database: &'a GitDatabase,
                  revision: GitRevision)
                  -> CargoResult<GitCheckout<'a>>
    {
        let repo = try!(GitCheckout::clone_repo(database.get_path(), into));
        let checkout = GitCheckout::new(into, database, revision, repo);
        try!(checkout.reset());
        Ok(checkout)
    }

    fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
        let dirname = into.dir_path();

        try!(mkdir_recursive(&dirname, USER_DIR).chain_error(|| {
            human(format!("Couldn't mkdir {}", dirname.display()))
        }));

        if into.exists() {
            try!(rmdir_recursive(into).chain_error(|| {
                human(format!("Couldn't rmdir {}", into.display()))
            }));
        }

        let url = try!(source.to_url().map_err(human));
        let url = url.to_string();
        let repo = try!(git2::Repository::clone(url.as_slice(),
                                                into).chain_error(|| {
            internal(format!("failed to clone {} into {}", source.display(),
                             into.display()))
        }));
        Ok(repo)
    }

    fn is_fresh(&self) -> bool {
        match self.repo.revparse_single("HEAD") {
            Ok(head) => head.id().to_string() == self.revision.to_string(),
            _ => false,
        }
    }

    fn fetch(&self) -> CargoResult<()> {
        info!("fetch {}", self.repo.path().display());
        let url = try!(self.database.path.to_url().map_err(human));
        let url = url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        try!(fetch(&self.repo, url.as_slice(), refspec));
        Ok(())
    }

    fn reset(&self) -> CargoResult<()> {
        info!("reset {} to {}", self.repo.path().display(), self.revision);
        let object = try!(self.repo.find_object(self.revision.0, None));
        try!(self.repo.reset(&object, git2::ResetType::Hard, None, None));
        Ok(())
    }

    fn update_submodules(&self) -> CargoResult<()> {
        return update_submodules(&self.repo);

        fn update_submodules(repo: &git2::Repository) -> CargoResult<()> {
            info!("update submodules for: {}", repo.path().display());

            for mut child in try!(repo.submodules()).into_iter() {
                try!(child.init(false));
                let url = try!(child.url().chain_error(|| {
                    internal("non-utf8 url for submodule")
                }));

                // A submodule which is listed in .gitmodules but not actually
                // checked out will not have a head id, so we should ignore it.
                let head = match child.head_id() {
                    Some(head) => head,
                    None => continue,
                };

                // If the submodule hasn't been checked out yet, we need to
                // clone it. If it has been checked out and the head is the same
                // as the submodule's head, then we can bail out and go to the
                // next submodule.
                let head_and_repo = child.open().and_then(|repo| {
                    Ok((try!(repo.head()).target(), repo))
                });
                let repo = match head_and_repo {
                    Ok((head, repo)) => {
                        if child.head_id() == head {
                            continue
                        }
                        repo
                    }
                    Err(..) => {
                        let path = repo.path().dir_path().join(child.path());
                        try!(git2::Repository::clone(url, &path))
                    }
                };

                // Fetch data from origin and reset to the head commit
                let refspec = "refs/heads/*:refs/heads/*";
                try!(fetch(&repo, url, refspec).chain_error(|| {
                    internal(format!("failed to fetch submodule `{}` from {}",
                                     child.name().unwrap_or(""), url))
                }));

                let obj = try!(repo.find_object(head, None));
                try!(repo.reset(&obj, git2::ResetType::Hard, None, None));
                try!(update_submodules(&repo));
            }
            Ok(())
        }
    }
}

fn with_authentication<T>(url: &str,
                          cfg: &git2::Config,
                          f: |&mut git2::Credentials| -> CargoResult<T>)
                          -> CargoResult<T> {
    // Prepare the authentication callbacks.
    //
    // We check the `allowed` types of credentials, and we try to do as much as
    // possible based on that:
    //
    // * Prioritize SSH keys from the local ssh agent as they're likely the most
    //   reliable. The username here is prioritized from the credential
    //   callback, then from whatever is configured in git itself, and finally
    //   we fall back to the generic user of `git`.
    //
    // * If a username/password is allowed, then we fallback to git2-rs's
    //   implementation of the credential helper. This is what is configured
    //   with `credential.helper` in git, and is the interface for the OSX
    //   keychain, for example.
    //
    // * After the above two have failed, we just kinda grapple attempting to
    //   return *something*.
    let mut cred_helper = git2::CredentialHelper::new(url);
    cred_helper.config(cfg);
    let mut cred_error = false;
    let ret = f(&mut |&mut: url, username, allowed| {
        let username = if username.is_empty() {None} else {Some(username)};
        let creds = if allowed.contains(git2::SSH_KEY) {
            let user = username.map(|s| s.to_string())
                               .or_else(|| cred_helper.username.clone())
                               .unwrap_or("git".to_string());
            git2::Cred::ssh_key_from_agent(user.as_slice())
        } else if allowed.contains(git2::USER_PASS_PLAINTEXT) {
            git2::Cred::credential_helper(cfg, url, username)
        } else if allowed.contains(git2::DEFAULT) {
            git2::Cred::default()
        } else {
            Err(git2::Error::from_str("no authentication available"))
        };
        cred_error = creds.is_err();
        creds
    });
    if cred_error {
        ret.chain_error(|| {
            human("Failed to authenticate when downloading repository")
        })
    } else {
        ret
    }
}

pub fn fetch(repo: &git2::Repository, url: &str,
             refspec: &str) -> CargoResult<()> {
    // Create a local anonymous remote in the repository to fetch the url

    with_authentication(url, &try!(repo.config()), |f| {
        let mut cb = git2::RemoteCallbacks::new();
        cb.credentials(|a, b, c| f.call_mut((a, b, c)));
        let mut remote = try!(repo.remote_anonymous(url.as_slice(),
                                                    Some(refspec)));
        try!(remote.add_fetch("refs/tags/*:refs/tags/*"));
        remote.set_callbacks(&mut cb);
        try!(remote.fetch(&["refs/tags/*:refs/tags/*", refspec], None, None));
        Ok(())
    })
}
<|endoftext|>


<commit_before>use Prefab;
use field::Field;
use form::Form;
use location::{Location, Portal};
use mapfile;
use serde;
use std::collections::HashMap;
use std::io::Cursor;
use std::slice;
use terrain::Terrain;
use world::Loadout;

/// Static generated world.
pub struct Worldgen {
    seed: u32,
    terrain: Field<Terrain>,
    portals: HashMap<Location, Portal>,
    spawns: Vec<(Location, Loadout)>,
    player_entry: Location,
}

impl Worldgen {
    pub fn new(seed: u32) -> Worldgen {
        let mut ret = Worldgen {
            seed: seed,
            terrain: Field::new(),
            portals: HashMap::new(),
            spawns: Vec::new(),
            player_entry: Location::new(0, 0, 0),
        };

        ret.sprint_map();

        ret
    }

    fn sprint_map(&mut self) {
        const SPRINT_PREFAB: &'static str = include_str!("../../sprint.ron");

        let sprint_prefab = mapfile::load_prefab(&mut Cursor::new(SPRINT_PREFAB))
            .expect("Corrupt sprint file");

        // Top-left of the default on-screen area.
        let origin = Location::new(-21, -22, 0);
        self.load_prefab(origin, &sprint_prefab);
    }

    fn load_prefab(&mut self, origin: Location, prefab: &Prefab) {
        for (p, &(ref terrain, ref entities)) in prefab.iter() {
            let loc = origin + p;

            self.terrain.set(loc, *terrain);

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.player_entry = loc;
                } else {
                    let form = Form::named(spawn).expect(&format!(
                        "Bad prefab: Form '{}' not found!",
                        spawn
                    ));
                    self.spawns.push((loc, form.loadout.clone()));
                }
            }
        }
    }

    pub fn seed(&self) -> u32 { self.seed }

    pub fn get_terrain(&self, loc: Location) -> Terrain { self.terrain.get(loc) }

    pub fn get_portal(&self, loc: Location) -> Option<Location> {
        self.portals.get(&loc).map(|&p| loc + p)
    }

    pub fn spawns(&self) -> slice::Iter<(Location, Loadout)> { self.spawns.iter() }

    pub fn player_entry(&self) -> Location { self.player_entry }
}

impl serde::Serialize for Worldgen {
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        self.seed.serialize(s)
    }
}

impl<'a> serde::Deserialize<'a> for Worldgen {
    fn deserialize<D: serde::Deserializer<'a>>(d: D) -> Result<Self, D::Error> {
        Ok(Worldgen::new(serde::Deserialize::deserialize(d)?))
    }
}
<commit_msg>Worldgen makes caves<commit_after>use Prefab;
use Rng;
use calx_grid::Dir6;
use field::Field;
use form::Form;
use location::{Location, Portal};
use mapfile;
use rand::{self, Rand, SeedableRng};
use serde;
use std::cmp;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::io::Cursor;
use std::slice;
use terrain::Terrain;
use world::Loadout;

/// Static generated world.
pub struct Worldgen {
    seed: u32,
    terrain: Field<Terrain>,
    portals: HashMap<Location, Portal>,
    spawns: Vec<(Location, Loadout)>,
    player_entry: Location,
}

impl Worldgen {
    pub fn new(seed: u32) -> Worldgen {
        let mut ret = Worldgen {
            seed: seed,
            terrain: Field::new(),
            portals: HashMap::new(),
            spawns: Vec::new(),
            player_entry: Location::new(0, 0, 0),
        };

        let mut rng: Rng = SeedableRng::from_seed([seed, seed, seed, seed]);
        //ret.sprint_map();
        ret.gen_caves(&mut rng);

        ret
    }

    fn sprint_map(&mut self) {
        const SPRINT_PREFAB: &'static str = include_str!("../../sprint.ron");

        let sprint_prefab = mapfile::load_prefab(&mut Cursor::new(SPRINT_PREFAB))
            .expect("Corrupt sprint file");

        // Top-left of the default on-screen area.
        let origin = Location::new(-21, -22, 0);
        self.load_prefab(origin, &sprint_prefab);
    }

    fn load_prefab(&mut self, origin: Location, prefab: &Prefab) {
        for (p, &(ref terrain, ref entities)) in prefab.iter() {
            let loc = origin + p;

            self.terrain.set(loc, *terrain);

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.player_entry = loc;
                } else {
                    let form = Form::named(spawn).expect(&format!(
                        "Bad prefab: Form '{}' not found!",
                        spawn
                    ));
                    self.spawns.push((loc, form.loadout.clone()));
                }
            }
        }
    }

    pub fn seed(&self) -> u32 { self.seed }

    pub fn get_terrain(&self, loc: Location) -> Terrain { self.terrain.get(loc) }

    pub fn get_portal(&self, loc: Location) -> Option<Location> {
        self.portals.get(&loc).map(|&p| loc + p)
    }

    pub fn spawns(&self) -> slice::Iter<(Location, Loadout)> { self.spawns.iter() }

    pub fn player_entry(&self) -> Location { self.player_entry }

    fn gen_caves<R: rand::Rng>(&mut self, rng: &mut R) {
        use self::Prototerrain::*;

        let mut cells_to_dig = 500;

        let mut map = screen_map(Location::new(0, 0, 0));

        let start = *rand::sample(rng, map.iter().filter(|x| *x.1 == Unused), 1)[0].0;
        map.set(start, Floor);
        let mut edge: BTreeSet<Location> = Dir6::iter()
            .map(|&d| start + d)
            .filter(|&loc| map.get(loc) == Unused)
            .collect();

        // Arbitrary long iteration, should break after digging a sufficient number of cells before
        // this.
        for _ in 0..10000 {
            if edge.is_empty() {
                break;
            }

            let dig_loc = *rand::sample(rng, edge.iter(), 1)[0];

            // Prefer digging narrow corridors, there's an increasing chance to abort the dig when the
            // selected location is in a very open space.
            let adjacent_floors = Dir6::iter()
                .filter(|d| map.get(dig_loc + **d) == Floor)
                .count();
            if rng.gen_range(0, cmp::max(1, adjacent_floors * 2)) != 0 {
                continue;
            }

            map.set(dig_loc, Floor);
            edge.extend(Dir6::iter().map(|&d| dig_loc + d).filter(|&loc| {
                map.get(loc) == Unused
            }));

            cells_to_dig -= 1;
            if cells_to_dig == 0 {
                break;
            }
        }

        self.terrain.extend(map.iter().map(|(&loc, &t)| {
            let t = match t {
                Outside => Terrain::Empty,
                Unused => Terrain::Rock,
                Border => Terrain::Rock,
                Floor => Terrain::Ground,
                Wall => Terrain::Rock,
                Door => Terrain::Door,
            };
            (loc, t)
        }));

    }
}

impl serde::Serialize for Worldgen {
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        self.seed.serialize(s)
    }
}

impl<'a> serde::Deserialize<'a> for Worldgen {
    fn deserialize<D: serde::Deserializer<'a>>(d: D) -> Result<Self, D::Error> {
        Ok(Worldgen::new(serde::Deserialize::deserialize(d)?))
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum Prototerrain {
    /// Area completely outside the map to be generated.
    Outside,
    /// Area that should be filled with map content but hasn't been yet.
    Unused,
    /// Area that the player cannot enter but that may be visible on screen.
    Border,
    Floor,
    Wall,
    Door,
}

impl Default for Prototerrain {
    fn default() -> Self { Prototerrain::Outside }
}

fn screen_map(origin: Location) -> Field<Prototerrain> {
    let mut ret = Field::new();

    for &v in ::onscreen_locations() {
        let loc = origin + v;
        for &d in Dir6::iter() {
            if ret.get(loc + d) == Default::default() {
                ret.set(loc + d, Prototerrain::Border);
            }
            ret.set(loc, Prototerrain::Unused);
        }
    }

    ret
}
<|endoftext|>