<commit_before>extern mod extra;
use std::vec::from_elem;
use grid::{Row, Column, alive, dead, Cell, Grid};
mod grid;

/****************************************************************************
 * Something to say Ben??
 ****************************************************************************/

/*
 * Constructs an immutable grid from the contents of an array of strings.
 * The grid is declared mutable, and mutated only inside this fn.
*/
pub fn build_from_file_contents(file_contents: ~[~str]) -> Grid {
  let height = file_contents.len();
  assert!(height > 0u);
  let width = file_contents[0].len();
  let cells = from_elem(width, Cell { value: alive } );

  let mut result = Grid {
      inner: from_elem(height, cells.clone())
  };
  for row in range(0, height) {
    for column in range(0, width) {
      let file_value: char = file_contents[row][column] as char;
      let cell_value = match file_value {
        'o' => alive,
        '.' => dead,
         _  => fail!("Unexpected cell value found in file.")
      };
      result.inner[row][column] = Cell { value: cell_value };
   }
  }
  return result;
} // fn build_from_file_contents

/*
 Returns a count for how many neighbors of a cell in the grid
 are alive.
 Starts to the cell to the left of the cell/row and sums up cell_alive
 working in clockwise order.
*/
fn count_neighbors(Row(row): Row, Column(col): Column, grid: &Grid) -> uint {
  let left_column  = Column(col - 1);
  let right_column = Column(col + 1);
  let above_row    = Row(row - 1);
  let below_row    = Row(row + 1);

  return grid.cell_alive(Row(row), left_column) + // left
      grid.cell_alive(above_row, left_column)   + // left-above
      grid.cell_alive(above_row, Column(col))   + // above
      grid.cell_alive(above_row, right_column)  + // above-right
      grid.cell_alive(Row(row), right_column)   + // right
      grid.cell_alive(below_row, right_column)  + // below-right
      grid.cell_alive(below_row, Column(col))   + // below
      grid.cell_alive(below_row, left_column);    // below-left
}  // fn count_neighbors

/*pub fn print_neighbor_count(grid: &Grid) {
  let x = grid.inner.len();
  for row in range(0, x) {
    for column in range(0, x) {
      let r = Row(row);
      let c = Column(column);
      let n = count_neighbors(r, c, grid);
      print!("{:u}", n);
    }
    println!("");
  }
}  // fn print_neighbor_count
*/

// 1)Any live cell with fewer than two live neighbours dies, as if caused by
// under-population.
// 2) Any live cell with two or three live neighbours lives on to the next
// generation.
// 3) Any live cell with more than three live neighbours dies, as if by
// overcrowding.
// 4) Any dead cell with exactly three live neighbours becomes a live cell, as
// if by reproduction.
pub fn build_from_grid(prevg: &Grid) -> Grid {
  let cell_value = |row: uint, column: uint| {
    let ncount = count_neighbors(Row(row), Column(column), prevg);
    let cv = match (prevg.inner[row][column].value, ncount) {
      (dead, 3)       => alive,
      (alive, 2..3)   => alive,
      _               => dead
    };
    return Cell { value: cv };
  };
  let mut result = prevg.clone();
  for row in range(0, prevg.height()) {
    for column in range(0, prevg.width()) {
      result.inner[row][column] = cell_value(row, column);
    }
  }
  return result;
}  // fn build_from_grid
<commit_msg>Made grid_builder more functional!!<commit_after>extern mod extra;
extern mod std;
use grid::{Row, Column, alive, dead, Cell, Grid};
mod grid;

/****************************************************************************
 * Something to say Ben??
 ****************************************************************************/

/*
 * Constructs an immutable grid from the contents of an array of strings.
 * The grid is declared mutable, and mutated only inside this fn.
*/
pub fn build_from_file_contents(file_contents: ~[~str]) -> Grid {
  let height = file_contents.len();
  assert!(height > 0u);
  let width = file_contents[0].len();
  let cells = std::vec::from_elem(width, Cell { value: alive } );

  let mut result = Grid {
      inner: std::vec::from_elem(height, cells.clone())
  };
  for row in range(0, height) {
    for column in range(0, width) {
      let file_value: char = file_contents[row][column] as char;
      let cell_value = match file_value {
        'o' => alive,
        '.' => dead,
         _  => fail!("Unexpected cell value found in file.")
      };
      result.inner[row][column] = Cell { value: cell_value };
   }
  }
  return result;
} // fn build_from_file_contents

/*
 Returns a count for how many neighbors of a cell in the grid
 are alive.
 Starts to the cell to the left of the cell/row and sums up cell_alive
 working in clockwise order.
*/
fn count_neighbors(Row(row): Row, Column(col): Column, grid: &Grid) -> uint {
  let left_column  = Column(col - 1);
  let right_column = Column(col + 1);
  let above_row    = Row(row - 1);
  let below_row    = Row(row + 1);

  return grid.cell_alive(Row(row), left_column) + // left
      grid.cell_alive(above_row, left_column)   + // left-above
      grid.cell_alive(above_row, Column(col))   + // above
      grid.cell_alive(above_row, right_column)  + // above-right
      grid.cell_alive(Row(row), right_column)   + // right
      grid.cell_alive(below_row, right_column)  + // below-right
      grid.cell_alive(below_row, Column(col))   + // below
      grid.cell_alive(below_row, left_column);    // below-left
}  // fn count_neighbors

/*pub fn print_neighbor_count(grid: &Grid) {
  let x = grid.inner.len();
  for row in range(0, x) {
    for column in range(0, x) {
      let r = Row(row);
      let c = Column(column);
      let n = count_neighbors(r, c, grid);
      print!("{:u}", n);
    }
    println!("");
  }
}  // fn print_neighbor_count
*/

// 1)Any live cell with fewer than two live neighbours dies, as if caused by
// under-population.
// 2) Any live cell with two or three live neighbours lives on to the next
// generation.
// 3) Any live cell with more than three live neighbours dies, as if by
// overcrowding.
// 4) Any dead cell with exactly three live neighbours becomes a live cell, as
// if by reproduction.
pub fn build_from_grid(prevg: &Grid) -> Grid {
  let cell_value = |row: uint, column: uint| {
    let ncount = count_neighbors(Row(row), Column(column), prevg);
    let cv = match (prevg.inner[row][column].value, ncount) {
      (dead, 3)       => alive,
      (alive, 2..3)   => alive,
      _               => dead
    };
    return Cell { value: cv };
  };
  return Grid {
    inner: std::vec::from_fn(prevg.height(), |row| {
      std::vec::from_fn(prevg.width(), |column| {
        cell_value(row, column)
      })
    })
  };
}  // fn build_from_grid
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Interface between Mempool and Network layers.

use crate::{
    error::NetworkError, interface::NetworkRequest, proto::MempoolSyncMsg,
    protocols::direct_send::Message, utils::MessageExt, validator_network::NetworkEvents,
    ProtocolId,
};
use channel;
use futures::SinkExt;
use libra_types::PeerId;

/// Protocol id for mempool direct-send calls
pub const MEMPOOL_DIRECT_SEND_PROTOCOL: &[u8] = b"/libra/mempool/direct-send/0.1.0";

/// The interface from Network to Mempool layer.
///
/// `MempoolNetworkEvents` is a `Stream` of `NetworkNotification` where the
/// raw `Bytes` direct-send and rpc messages are deserialized into
/// `MempoolMessage` types. `MempoolNetworkEvents` is a thin wrapper around an
/// `channel::Receiver<NetworkNotification>`.
pub type MempoolNetworkEvents = NetworkEvents<MempoolSyncMsg>;

/// The interface from Mempool to Networking layer.
///
/// This is a thin wrapper around an `channel::Sender<NetworkRequest>`, so it is
/// easy to clone and send off to a separate task. For example, the rpc requests
/// return Futures that encapsulate the whole flow, from sending the request to
/// remote, to finally receiving the response and deserializing. It therefore
/// makes the most sense to make the rpc call on a separate async task, which
/// requires the `MempoolNetworkSender` to be `Clone` and `Send`.
#[derive(Clone)]
pub struct MempoolNetworkSender {
    // TODO(philiphayes): remove pub
    pub inner: channel::Sender<NetworkRequest>,
}

impl MempoolNetworkSender {
    pub fn new(inner: channel::Sender<NetworkRequest>) -> Self {
        Self { inner }
    }

    /// Send a fire-and-forget "direct-send" message to remote peer `recipient`.
    ///
    /// Currently, the returned Future simply resolves when the message has been
    /// enqueued on the network actor's event queue. It therefore makes no
    /// reliable delivery guarantees.
    pub async fn send_to(
        &mut self,
        recipient: PeerId,
        message: MempoolSyncMsg,
    ) -> Result<(), NetworkError> {
        self.inner
            .send(NetworkRequest::SendMessage(
                recipient,
                Message {
                    protocol: ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL),
                    mdata: message.to_bytes().unwrap(),
                },
            ))
            .await?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{interface::NetworkNotification, validator_network::Event};
    use futures::{executor::block_on, stream::StreamExt};

    fn new_test_sync_msg(peer_id: PeerId) -> MempoolSyncMsg {
        let mut mempool_msg = MempoolSyncMsg::default();
        mempool_msg.peer_id = peer_id.into();
        mempool_msg
    }

    // Direct send messages should get deserialized through the
    // `MempoolNetworkEvents` stream.
    #[test]
    fn test_mempool_network_events() {
        let (mut mempool_tx, mempool_rx) = channel::new_test(8);
        let mut stream = MempoolNetworkEvents::new(mempool_rx);

        let peer_id = PeerId::random();
        let mempool_msg = new_test_sync_msg(peer_id);
        let network_msg = Message {
            protocol: ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL),
            mdata: mempool_msg.clone().to_bytes().unwrap(),
        };

        block_on(mempool_tx.send(NetworkNotification::RecvMessage(peer_id, network_msg))).unwrap();
        let event = block_on(stream.next()).unwrap().unwrap();
        assert_eq!(event, Event::Message((peer_id, mempool_msg)));

        block_on(mempool_tx.send(NetworkNotification::NewPeer(peer_id))).unwrap();
        let event = block_on(stream.next()).unwrap().unwrap();
        assert_eq!(event, Event::NewPeer(peer_id));
    }

    // `MempoolNetworkSender` should serialize outbound messages
    #[test]
    fn test_mempool_network_sender() {
        let (network_reqs_tx, mut network_reqs_rx) = channel::new_test(8);
        let mut sender = MempoolNetworkSender::new(network_reqs_tx);

        let peer_id = PeerId::random();
        let mempool_msg = new_test_sync_msg(peer_id);
        let expected_network_msg = Message {
            protocol: ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL),
            mdata: mempool_msg.clone().to_bytes().unwrap(),
        };

        // Send the message to network layer
        block_on(sender.send_to(peer_id, mempool_msg)).unwrap();

        // Network layer should receive serialized message to send out
        let event = block_on(network_reqs_rx.next()).unwrap();
        match event {
            NetworkRequest::SendMessage(recv_peer_id, network_msg) => {
                assert_eq!(recv_peer_id, peer_id);
                assert_eq!(network_msg, expected_network_msg);
            }
            event => panic!("Unexpected event: {:?}", event),
        }
    }
}
<commit_msg>[network][mempool] `MempoolNetworkSender` now wraps `NetworkSender`<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Interface between Mempool and Network layers.

use crate::{
    error::NetworkError,
    interface::NetworkRequest,
    proto::MempoolSyncMsg,
    validator_network::{NetworkEvents, NetworkSender},
    ProtocolId,
};
use channel;
use libra_types::PeerId;

/// Protocol id for mempool direct-send calls
pub const MEMPOOL_DIRECT_SEND_PROTOCOL: &[u8] = b"/libra/mempool/direct-send/0.1.0";

/// The interface from Network to Mempool layer.
///
/// `MempoolNetworkEvents` is a `Stream` of `NetworkNotification` where the
/// raw `Bytes` direct-send and rpc messages are deserialized into
/// `MempoolMessage` types. `MempoolNetworkEvents` is a thin wrapper around an
/// `channel::Receiver<NetworkNotification>`.
pub type MempoolNetworkEvents = NetworkEvents<MempoolSyncMsg>;

/// The interface from Mempool to Networking layer.
///
/// This is a thin wrapper around a `NetworkSender<MempoolSyncMsg>`, which is in
/// turn a thin wrapper around a `channel::Sender<NetworkRequest>`, so it is
/// easy to clone and send off to a separate task. For example, the rpc requests
/// return Futures that encapsulate the whole flow, from sending the request to
/// remote, to finally receiving the response and deserializing. It therefore
/// makes the most sense to make the rpc call on a separate async task, which
/// requires the `MempoolNetworkSender` to be `Clone` and `Send`.
#[derive(Clone)]
pub struct MempoolNetworkSender {
    inner: NetworkSender<MempoolSyncMsg>,
}

impl MempoolNetworkSender {
    pub fn new(inner: channel::Sender<NetworkRequest>) -> Self {
        Self {
            inner: NetworkSender::new(inner),
        }
    }

    pub async fn send_to(
        &mut self,
        recipient: PeerId,
        message: MempoolSyncMsg,
    ) -> Result<(), NetworkError> {
        let protocol = ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL);
        self.inner.send_to(recipient, protocol, message).await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        interface::NetworkNotification, protocols::direct_send::Message, utils::MessageExt,
        validator_network::Event,
    };
    use futures::{executor::block_on, sink::SinkExt, stream::StreamExt};

    fn new_test_sync_msg(peer_id: PeerId) -> MempoolSyncMsg {
        let mut mempool_msg = MempoolSyncMsg::default();
        mempool_msg.peer_id = peer_id.into();
        mempool_msg
    }

    // Direct send messages should get deserialized through the
    // `MempoolNetworkEvents` stream.
    #[test]
    fn test_mempool_network_events() {
        let (mut mempool_tx, mempool_rx) = channel::new_test(8);
        let mut stream = MempoolNetworkEvents::new(mempool_rx);

        let peer_id = PeerId::random();
        let mempool_msg = new_test_sync_msg(peer_id);
        let network_msg = Message {
            protocol: ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL),
            mdata: mempool_msg.clone().to_bytes().unwrap(),
        };

        block_on(mempool_tx.send(NetworkNotification::RecvMessage(peer_id, network_msg))).unwrap();
        let event = block_on(stream.next()).unwrap().unwrap();
        assert_eq!(event, Event::Message((peer_id, mempool_msg)));

        block_on(mempool_tx.send(NetworkNotification::NewPeer(peer_id))).unwrap();
        let event = block_on(stream.next()).unwrap().unwrap();
        assert_eq!(event, Event::NewPeer(peer_id));
    }

    // `MempoolNetworkSender` should serialize outbound messages
    #[test]
    fn test_mempool_network_sender() {
        let (network_reqs_tx, mut network_reqs_rx) = channel::new_test(8);
        let mut sender = MempoolNetworkSender::new(network_reqs_tx);

        let peer_id = PeerId::random();
        let mempool_msg = new_test_sync_msg(peer_id);
        let expected_network_msg = Message {
            protocol: ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL),
            mdata: mempool_msg.clone().to_bytes().unwrap(),
        };

        // Send the message to network layer
        block_on(sender.send_to(peer_id, mempool_msg)).unwrap();

        // Network layer should receive serialized message to send out
        let event = block_on(network_reqs_rx.next()).unwrap();
        match event {
            NetworkRequest::SendMessage(recv_peer_id, network_msg) => {
                assert_eq!(recv_peer_id, peer_id);
                assert_eq!(network_msg, expected_network_msg);
            }
            event => panic!("Unexpected event: {:?}", event),
        }
    }
}
<|endoftext|>


<commit_before>//! Tools to define Rust components compatible with the COM protocol.
//!
//! Intercom provides attributes to automatically derive `extern` compatible
//! functions for Rust methods. These functions are compatible with COM binary
//! interface standard, which allows them to be used from any language that
//! supports COM.
//!
//! # Examples
//!
//! A basic example of a calculator type exposed as a COM object.
//!
//! ```
//! #![feature(proc_macro)]
//!
//! use intercom::{com_library, com_class, com_interface, com_impl, ComResult};
//!
//! // Define COM classes to expose from this library.
//! #[com_library(AUTO_GUID, Calculator)]
//!
//! // Define the COM class and the interfaces it implements.
//! #[com_class(AUTO_GUID, Calculator)]
//! struct Calculator;
//!
//! // Define the implementation for the class. The COM interface is defined
//! // implicitly by the `impl`.
//! #[com_interface(AUTO_GUID)]
//! #[com_impl]
//! impl Calculator {
//!
//!     // Intercom requires a `new` method with no parameters for all classes.
//! #   // NOTE: This should be replaced with Default::default implementation.
//!     fn new() -> Calculator { Calculator }
//!
//!     fn add(&self, a: i32, b: i32) -> ComResult<i32> { Ok(a + b) }
//!     fn sub(&self, a: i32, b: i32) -> ComResult<i32> { Ok(a - b) }
//! }
//! # fn main() {}
//! ```
//!
//! The above library can be used for example from C# in the following manner.
//!
//! ```csharp
//! void Main()
//! {
//!     var calculator = new CalculatorLib.Calculator();
//!     Console.WriteLine( calculator.Add( 1, 2 ) );
//! }
//! ```

#![crate_type="dylib"]
#![feature(proc_macro, try_from, fundamental, specialization, non_exhaustive)]

#[cfg(feature = "generators")]
extern crate intercom_common;
#[cfg(feature = "generators")]
extern crate serde;
#[cfg(feature = "generators")]
#[macro_use] extern crate serde_derive;
#[cfg(feature = "generators")]
extern crate handlebars;
#[cfg(feature = "generators")]
pub mod generators;

// The crate doesn't really need the macros. However Rust will complain that
// the import does nothing if we don't define #[macro_use]. Once we define
// #[macro_use] to get rid of that warning, Rust will complain that the
// #[macro_use] does nothing. Fortunately THAT warning comes with a named
// warning option so we can allow that explicitly.
//
// Unfortunately clippy disagrees on the macro_use being unused and claims that
// the unused_imports attribute is useless. So now we also need to tell clippy
// to ignore useless attributes in this scenario! \:D/
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
#[allow(unused_imports)]
extern crate intercom_attributes;
pub use intercom_attributes::*;

#[macro_use] extern crate failure;

mod classfactory; pub use classfactory::*;
mod combox; pub use combox::*;
mod comrc; pub use comrc::*;
mod comitf; pub use comitf::*;
mod bstr; pub use bstr::*;
mod guid; pub use guid::GUID;
mod error; pub use error::{return_hresult, get_last_error, ComError, ErrorInfo};
pub mod runtime;

// intercom_attributes use "intercom::" to qualify things in this crate.
// Declare such module here and import everything we have in it to make those
// references valid.
mod intercom {
    pub use ::*;
}

pub trait IidOf {
    fn iid() -> &'static IID;
}

/// Raw COM pointer type.
pub type RawComPtr = *mut std::os::raw::c_void;

/// Interface ID GUID.
pub type IID = GUID;

/// A reference to an interface ID.
pub type REFIID = *const IID;

/// Class ID GUID.
pub type CLSID = GUID;

/// A reference to a class ID.
pub type REFCLSID = *const IID;

/// COM method status code.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
#[repr(C)]
pub struct HRESULT {

    /// The numerical HRESULT code.
    pub hr : i32
}

impl HRESULT {

    /// Constructs a new `HRESULT` with the given numerical code.
    pub fn new( hr : u32 ) -> HRESULT {
        #[allow(overflowing_literals)]
        HRESULT { hr : hr as i32 }
    }
}

macro_rules! make_hr {
    ( $hr_name: ident, $hr_value: expr ) => {
        #[allow(overflowing_literals)]
        pub const $hr_name : HRESULT = HRESULT { hr: $hr_value as i32 };
    }
}

/// `HRESULT` indicating the operation completed successfully.
make_hr!( S_OK, 0 );

/// `HRESULT` indicating the operation completed successfully and returned
/// `false`.
make_hr!( S_FALSE, 1 );

/// `HRESULT` for unimplemented functionality.
make_hr!( E_NOTIMPL, 0x8000_4001 );

/// `HRESULT` indicating the type does not support the requested interface.
make_hr!( E_NOINTERFACE, 0x8000_4002 );

/// `HRESULT` indicating a pointer parameter was invalid.
make_hr!( E_POINTER, 0x8000_4003 );

/// `HRESULT` for aborted operation.
make_hr!( E_ABORT, 0x8000_4004 );

/// `HRESULT` for unspecified failure.
make_hr!( E_FAIL, 0x8000_4005 );

/// `HRESULT` for invalid argument.
make_hr!( E_INVALIDARG, 0x8007_0057 );

make_hr!( E_ACCESSDENIED, 0x8007_0005 );

make_hr!( STG_E_FILENOTFOUND, 0x8003_0002 );

make_hr!( RPC_E_DISCONNECTED, 0x8001_0108 );

make_hr!( RPC_E_CALL_REJECTED, 0x8001_0001 );

make_hr!( RPC_E_CALL_CANCELED, 0x8001_0002 );

make_hr!( RPC_E_TIMEOUT, 0x8001_011F );

/// `IClassFactory` interface ID.
#[allow(non_upper_case_globals)]
pub const IID_IClassFactory : GUID = GUID {
    data1: 0x0000_0001, data2: 0x0000, data3: 0x0000,
    data4: [ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 ]
};

/// `IErrorInfo` interface ID.
#[allow(non_upper_case_globals)]
pub const IID_IErrorInfo : GUID = GUID {
    data1: 0x1CF2_B120, data2: 0x547D, data3: 0x101B,
    data4: [ 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19 ]
};

mod interfaces {

    /// The `IUnknown` COM interface.
    ///
    /// All COM interfaces must inherit from `IUnknown` interface directly or
    /// indirectly. The interface provides the basis of COM reference counting
    /// and interface discovery.
    ///
    /// For Rust code, Intercom implements the interface automatically.
    #[::com_interface( "00000000-0000-0000-C000-000000000046", NO_BASE )]
    pub trait IUnknown {

        /// Tries to get a different COM interface for the current object.
        ///
        /// COM objects may (and do) implement multiple interfaces. COM defines
        /// `QueryInterface` as the mechanism for acquiring an interface pointer
        /// to a different interface the object implements.
        ///
        /// * `riid` - The `IID` of the interface to query.
        ///
        /// Returns `Ok( interface_ptr )` if the object supports the specified
        /// interface or `Err( E_NOINTERFACE )` if it doesn't.
        fn query_interface( &self, riid : ::REFIID ) -> ::ComResult< ::RawComPtr >;

        /// Increments the reference count of the object.
        ///
        /// Returns the reference count after the incrementation.
        fn add_ref( &self ) -> u32;

        /// Decreases the reference count of the object.
        ///
        /// Returns the reference count after the decrement.
        ///
        /// If the reference count reaches zero, the object will deallocate
        /// itself. As the call might deallocate the object, the caller must
        /// ensure that the released reference is not used afterwards.
        fn release( &self ) -> u32;
    }

    /// The `ISupportErrorInfo` COM interface.
    ///
    /// The `ISupportErrorInfo` is part of COM error handling concept. As the
    /// methods are traditionally limited to `HRESULT` return values, they may
    /// make more detailed `IErrorInfo` data available through the error info
    /// APIs.
    ///
    /// The `ISupportErrorInfo` interface communicates which interfaces that an
    /// object implements support detailed error info. When a COM client
    /// receives an error-HRESULT, it may query for error info support through
    /// this interface. If the interface returns an `S_OK` as opposed to
    /// `S_FALSE` return value, the client can then use separate error info
    /// APIs to retrieve a detailed `IErrorInfo` object that contains more
    /// details about the error, such as the error message.
    ///
    /// Intercom COM classes support the detailed error info for all user
    /// specified interfaces automatically. Only methods that return a
    /// two-parameter `Result<S,E>` value will store the detailed `IErrorInfo`.
    /// Other methods will set a null `IErrorInfo` value.
    #[::com_interface( "DF0B3D60-548F-101B-8E65-08002B2BD119" )]
    pub trait ISupportErrorInfo {

        /// Informs the current COM class supports `IErrorInfo` for a specific
        /// interface.
        ///
        /// * `riid` - The `IID` of the interface to query.
        ///
        /// Returns `S_OK` if the object supports `IErrorInfo` for the
        /// interface specified by the `riid` parameter. Otherwise returns
        /// `S_FALSE` - even in the case the object doesn't implement `riid`
        /// at all.
        ///
        /// # Description
        ///
        /// If the object returns `S_OK` for an interface, then any methods
        /// the object implements for that interface must store the
        /// `IErrorInfo` on failure.
        ///
        /// Intercom will implement the support for `IErrorInfo` automatically
        /// for all custom interfaces the user defines. This includes returning
        /// `S_OK` from this method.
        ///
        fn interface_supports_error_info( &self, riid : ::REFIID ) -> ::HRESULT;
    }
}

pub use interfaces::__IUnknownVtbl as IUnknownVtbl;
pub use interfaces::IID_IUnknown;
pub use interfaces::IUnknown;

pub use interfaces::__ISupportErrorInfoVtbl as ISupportErrorInfoVtbl;
pub use interfaces::IID_ISupportErrorInfo;
pub use interfaces::ISupportErrorInfo;

// Do we need this? Would rather not export this through an extern crate
// for another dll.
//
// com_library should have dllmain!() macro or similar that implements this
// together with the COM registration.
#[no_mangle]
#[allow(non_camel_case_types)]
#[deprecated]
#[doc(hidden)]
pub extern "stdcall" fn DllMain(
    _dll_instance : *mut std::os::raw::c_void,
    _reason : u32,
    _reserved : *mut std::os::raw::c_void ) -> bool
{
    true
}

/// Basic COM result type.
///
/// The `ComResult` maps the Rust concept of `Ok` and `Err` values to COM
/// `[out, retval]` parameter and `HRESULT` return value.
pub type ComResult<A> = Result<A, HRESULT>;

<commit_msg>"Fix" lints<commit_after>//! Tools to define Rust components compatible with the COM protocol.
//!
//! Intercom provides attributes to automatically derive `extern` compatible
//! functions for Rust methods. These functions are compatible with COM binary
//! interface standard, which allows them to be used from any language that
//! supports COM.
//!
//! # Examples
//!
//! A basic example of a calculator type exposed as a COM object.
//!
//! ```
//! #![feature(proc_macro)]
//!
//! use intercom::{com_library, com_class, com_interface, com_impl, ComResult};
//!
//! // Define COM classes to expose from this library.
//! #[com_library(AUTO_GUID, Calculator)]
//!
//! // Define the COM class and the interfaces it implements.
//! #[com_class(AUTO_GUID, Calculator)]
//! struct Calculator;
//!
//! // Define the implementation for the class. The COM interface is defined
//! // implicitly by the `impl`.
//! #[com_interface(AUTO_GUID)]
//! #[com_impl]
//! impl Calculator {
//!
//!     // Intercom requires a `new` method with no parameters for all classes.
//! #   // NOTE: This should be replaced with Default::default implementation.
//!     fn new() -> Calculator { Calculator }
//!
//!     fn add(&self, a: i32, b: i32) -> ComResult<i32> { Ok(a + b) }
//!     fn sub(&self, a: i32, b: i32) -> ComResult<i32> { Ok(a - b) }
//! }
//! # fn main() {}
//! ```
//!
//! The above library can be used for example from C# in the following manner.
//!
//! ```csharp
//! void Main()
//! {
//!     var calculator = new CalculatorLib.Calculator();
//!     Console.WriteLine( calculator.Add( 1, 2 ) );
//! }
//! ```

#![crate_type="dylib"]
#![feature(proc_macro, try_from, fundamental, specialization, non_exhaustive)]

#[cfg(feature = "generators")]
extern crate intercom_common;
#[cfg(feature = "generators")]
extern crate serde;
#[cfg(feature = "generators")]
#[macro_use] extern crate serde_derive;
#[cfg(feature = "generators")]
extern crate handlebars;
#[cfg(feature = "generators")]
pub mod generators;

// The crate doesn't really need the macros. However Rust will complain that
// the import does nothing if we don't define #[macro_use]. Once we define
// #[macro_use] to get rid of that warning, Rust will complain that the
// #[macro_use] does nothing. Fortunately THAT warning comes with a named
// warning option so we can allow that explicitly.
//
// Unfortunately clippy disagrees on the macro_use being unused and claims that
// the unused_imports attribute is useless. So now we also need to tell clippy
// to ignore useless attributes in this scenario! \:D/
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
#[allow(unused_imports)]
extern crate intercom_attributes;
pub use intercom_attributes::*;

#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
#[allow(unused_imports)]
#[macro_use] extern crate failure;

mod classfactory; pub use classfactory::*;
mod combox; pub use combox::*;
mod comrc; pub use comrc::*;
mod comitf; pub use comitf::*;
mod bstr; pub use bstr::*;
mod guid; pub use guid::GUID;
mod error; pub use error::{return_hresult, get_last_error, ComError, ErrorInfo};
pub mod runtime;

// intercom_attributes use "intercom::" to qualify things in this crate.
// Declare such module here and import everything we have in it to make those
// references valid.
mod intercom {
    pub use ::*;
}

pub trait IidOf {
    fn iid() -> &'static IID;
}

/// Raw COM pointer type.
pub type RawComPtr = *mut std::os::raw::c_void;

/// Interface ID GUID.
pub type IID = GUID;

/// A reference to an interface ID.
pub type REFIID = *const IID;

/// Class ID GUID.
pub type CLSID = GUID;

/// A reference to a class ID.
pub type REFCLSID = *const IID;

/// COM method status code.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
#[repr(C)]
pub struct HRESULT {

    /// The numerical HRESULT code.
    pub hr : i32
}

impl HRESULT {

    /// Constructs a new `HRESULT` with the given numerical code.
    pub fn new( hr : u32 ) -> HRESULT {
        #[allow(overflowing_literals)]
        HRESULT { hr : hr as i32 }
    }
}

macro_rules! make_hr {
    ( $hr_name: ident, $hr_value: expr ) => {
        #[allow(overflowing_literals)]
        pub const $hr_name : HRESULT = HRESULT { hr: $hr_value as i32 };
    }
}

/// `HRESULT` indicating the operation completed successfully.
make_hr!( S_OK, 0 );

/// `HRESULT` indicating the operation completed successfully and returned
/// `false`.
make_hr!( S_FALSE, 1 );

/// `HRESULT` for unimplemented functionality.
make_hr!( E_NOTIMPL, 0x8000_4001 );

/// `HRESULT` indicating the type does not support the requested interface.
make_hr!( E_NOINTERFACE, 0x8000_4002 );

/// `HRESULT` indicating a pointer parameter was invalid.
make_hr!( E_POINTER, 0x8000_4003 );

/// `HRESULT` for aborted operation.
make_hr!( E_ABORT, 0x8000_4004 );

/// `HRESULT` for unspecified failure.
make_hr!( E_FAIL, 0x8000_4005 );

/// `HRESULT` for invalid argument.
make_hr!( E_INVALIDARG, 0x8007_0057 );

make_hr!( E_ACCESSDENIED, 0x8007_0005 );

make_hr!( STG_E_FILENOTFOUND, 0x8003_0002 );

make_hr!( RPC_E_DISCONNECTED, 0x8001_0108 );

make_hr!( RPC_E_CALL_REJECTED, 0x8001_0001 );

make_hr!( RPC_E_CALL_CANCELED, 0x8001_0002 );

make_hr!( RPC_E_TIMEOUT, 0x8001_011F );

/// `IClassFactory` interface ID.
#[allow(non_upper_case_globals)]
pub const IID_IClassFactory : GUID = GUID {
    data1: 0x0000_0001, data2: 0x0000, data3: 0x0000,
    data4: [ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 ]
};

/// `IErrorInfo` interface ID.
#[allow(non_upper_case_globals)]
pub const IID_IErrorInfo : GUID = GUID {
    data1: 0x1CF2_B120, data2: 0x547D, data3: 0x101B,
    data4: [ 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19 ]
};

mod interfaces {

    /// The `IUnknown` COM interface.
    ///
    /// All COM interfaces must inherit from `IUnknown` interface directly or
    /// indirectly. The interface provides the basis of COM reference counting
    /// and interface discovery.
    ///
    /// For Rust code, Intercom implements the interface automatically.
    #[::com_interface( "00000000-0000-0000-C000-000000000046", NO_BASE )]
    pub trait IUnknown {

        /// Tries to get a different COM interface for the current object.
        ///
        /// COM objects may (and do) implement multiple interfaces. COM defines
        /// `QueryInterface` as the mechanism for acquiring an interface pointer
        /// to a different interface the object implements.
        ///
        /// * `riid` - The `IID` of the interface to query.
        ///
        /// Returns `Ok( interface_ptr )` if the object supports the specified
        /// interface or `Err( E_NOINTERFACE )` if it doesn't.
        fn query_interface( &self, riid : ::REFIID ) -> ::ComResult< ::RawComPtr >;

        /// Increments the reference count of the object.
        ///
        /// Returns the reference count after the incrementation.
        fn add_ref( &self ) -> u32;

        /// Decreases the reference count of the object.
        ///
        /// Returns the reference count after the decrement.
        ///
        /// If the reference count reaches zero, the object will deallocate
        /// itself. As the call might deallocate the object, the caller must
        /// ensure that the released reference is not used afterwards.
        fn release( &self ) -> u32;
    }

    /// The `ISupportErrorInfo` COM interface.
    ///
    /// The `ISupportErrorInfo` is part of COM error handling concept. As the
    /// methods are traditionally limited to `HRESULT` return values, they may
    /// make more detailed `IErrorInfo` data available through the error info
    /// APIs.
    ///
    /// The `ISupportErrorInfo` interface communicates which interfaces that an
    /// object implements support detailed error info. When a COM client
    /// receives an error-HRESULT, it may query for error info support through
    /// this interface. If the interface returns an `S_OK` as opposed to
    /// `S_FALSE` return value, the client can then use separate error info
    /// APIs to retrieve a detailed `IErrorInfo` object that contains more
    /// details about the error, such as the error message.
    ///
    /// Intercom COM classes support the detailed error info for all user
    /// specified interfaces automatically. Only methods that return a
    /// two-parameter `Result<S,E>` value will store the detailed `IErrorInfo`.
    /// Other methods will set a null `IErrorInfo` value.
    #[::com_interface( "DF0B3D60-548F-101B-8E65-08002B2BD119" )]
    pub trait ISupportErrorInfo {

        /// Informs the current COM class supports `IErrorInfo` for a specific
        /// interface.
        ///
        /// * `riid` - The `IID` of the interface to query.
        ///
        /// Returns `S_OK` if the object supports `IErrorInfo` for the
        /// interface specified by the `riid` parameter. Otherwise returns
        /// `S_FALSE` - even in the case the object doesn't implement `riid`
        /// at all.
        ///
        /// # Description
        ///
        /// If the object returns `S_OK` for an interface, then any methods
        /// the object implements for that interface must store the
        /// `IErrorInfo` on failure.
        ///
        /// Intercom will implement the support for `IErrorInfo` automatically
        /// for all custom interfaces the user defines. This includes returning
        /// `S_OK` from this method.
        ///
        fn interface_supports_error_info( &self, riid : ::REFIID ) -> ::HRESULT;
    }
}

pub use interfaces::__IUnknownVtbl as IUnknownVtbl;
pub use interfaces::IID_IUnknown;
pub use interfaces::IUnknown;

pub use interfaces::__ISupportErrorInfoVtbl as ISupportErrorInfoVtbl;
pub use interfaces::IID_ISupportErrorInfo;
pub use interfaces::ISupportErrorInfo;

// Do we need this? Would rather not export this through an extern crate
// for another dll.
//
// com_library should have dllmain!() macro or similar that implements this
// together with the COM registration.
#[no_mangle]
#[allow(non_camel_case_types)]
#[deprecated]
#[doc(hidden)]
pub extern "stdcall" fn DllMain(
    _dll_instance : *mut std::os::raw::c_void,
    _reason : u32,
    _reserved : *mut std::os::raw::c_void ) -> bool
{
    true
}

/// Basic COM result type.
///
/// The `ComResult` maps the Rust concept of `Ok` and `Err` values to COM
/// `[out, retval]` parameter and `HRESULT` return value.
pub type ComResult<A> = Result<A, HRESULT>;

<|endoftext|>


<commit_before>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/casile.mk"
    )));
    let rules = status::get_rules()?;
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if status::is_gha()? {
        targets.push(String::from("_gha"));
        if targets.len() == 1 {
            targets.push(String::from("default"));
        }
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!(errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if targets.contains(&"debug".into()) {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<commit_msg>chore(rules): Show make output in build plan dump is requested<commit_after>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/casile.mk"
    )));
    let rules = status::get_rules()?;
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if status::is_gha()? {
        targets.push(String::from("_gha"));
        if targets.len() == 1 {
            targets.push(String::from("default"));
        }
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!(errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if CONF.get_bool("debug")?
                        || targets.contains(&"debug".into())
                        || targets.contains(&"-p".into())
                    {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<|endoftext|>


<commit_before>use rustc::ty;
use rustc::ty::layout::Primitive;
use rustc::mir;

use super::*;

use helpers::EvalContextExt as HelperEvalContextExt;

pub trait EvalContextExt<'tcx> {
    fn ptr_op(
        &self,
        bin_op: mir::BinOp,
        left: Scalar,
        left_ty: ty::Ty<'tcx>,
        right: Scalar,
        right_ty: ty::Ty<'tcx>,
    ) -> EvalResult<'tcx, Option<(Scalar, bool)>>;

    fn ptr_int_arithmetic(
        &self,
        bin_op: mir::BinOp,
        left: Pointer,
        right: i128,
        signed: bool,
    ) -> EvalResult<'tcx, (Scalar, bool)>;
}

impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> {
    fn ptr_op(
        &self,
        bin_op: mir::BinOp,
        left: Scalar,
        left_ty: ty::Ty<'tcx>,
        right: Scalar,
        right_ty: ty::Ty<'tcx>,
    ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
        trace!("ptr_op: {:?} {:?} {:?}", left, bin_op, right);

        use rustc::mir::BinOp::*;
        use rustc::ty::layout::Integer::*;
        let usize = Primitive::Int(match self.memory.pointer_size().bytes() {
            1 => I8,
            2 => I16,
            4 => I32,
            8 => I64,
            16 => I128,
            _ => unreachable!(),
        }, false);
        let isize = Primitive::Int(match self.memory.pointer_size().bytes() {
            1 => I8,
            2 => I16,
            4 => I32,
            8 => I64,
            16 => I128,
            _ => unreachable!(),
        }, true);
        let left_layout = self.layout_of(left_ty)?;
        let left_kind = match left_layout.abi {
            ty::layout::Abi::Scalar(ref scalar) => scalar.value,
            _ => Err(EvalErrorKind::TypeNotPrimitive(left_ty))?,
        };
        let right_layout = self.layout_of(right_ty)?;
        let right_kind = match right_layout.abi {
            ty::layout::Abi::Scalar(ref scalar) => scalar.value,
            _ => Err(EvalErrorKind::TypeNotPrimitive(right_ty))?,
        };
        match bin_op {
            Offset if left_kind == Primitive::Pointer && right_kind == usize => {
                let pointee_ty = left_ty
                    .builtin_deref(true)
                    .expect("Offset called on non-ptr type")
                    .ty;
                let ptr = self.pointer_offset(
                    left,
                    pointee_ty,
                    right.to_bits(self.memory.pointer_size())? as i64,
                )?;
                Ok(Some((ptr, false)))
            }
            // These work on anything
            Eq if left_kind == right_kind => {
                let result = match (left, right) {
                    (Scalar::Bits { .. }, Scalar::Bits { .. }) => {
                        left.to_bits(left_layout.size)? == right.to_bits(right_layout.size)?
                    },
                    // FIXME: Test if both allocations are still live *or* if they are in the same allocation? (same for Ne below)
                    (Scalar::Ptr(left), Scalar::Ptr(right)) => left == right,
                    // FIXME: We should probably error out when comparing anything but NULL with a pointer (same for Ne below)
                    _ => false,
                };
                Ok(Some((Scalar::from_bool(result), false)))
            }
            Ne if left_kind == right_kind => {
                let result = match (left, right) {
                    (Scalar::Bits { .. }, Scalar::Bits { .. }) => {
                        left.to_bits(left_layout.size)? != right.to_bits(right_layout.size)?
                    },
                    (Scalar::Ptr(left), Scalar::Ptr(right)) => left != right,
                    _ => true,
                };
                Ok(Some((Scalar::from_bool(result), false)))
            }
            // These need both pointers to be in the same allocation
            Lt | Le | Gt | Ge | Sub
                if left_kind == right_kind &&
                       (left_kind == Primitive::Pointer || left_kind == usize || left_kind == isize) &&
                       left.is_ptr() && right.is_ptr() => {
                let left = left.to_ptr()?;
                let right = right.to_ptr()?;
                if left.alloc_id == right.alloc_id {
                    let res = match bin_op {
                        Lt => left.offset < right.offset,
                        Le => left.offset <= right.offset,
                        Gt => left.offset > right.offset,
                        Ge => left.offset >= right.offset,
                        Sub => {
                            return self.binary_op(
                                Sub,
                                Scalar::Bits { bits: left.offset.bytes() as u128, size: self.memory.pointer_size().bytes() as u8 },
                                self.tcx.types.usize,
                                Scalar::Bits { bits: right.offset.bytes() as u128, size: self.memory.pointer_size().bytes() as u8 },
                                self.tcx.types.usize,
                            ).map(Some)
                        }
                        _ => bug!("We already established it has to be one of these operators."),
                    };
                    Ok(Some((Scalar::from_bool(res), false)))
                } else {
                    // Both are pointers, but from different allocations.
                    err!(InvalidPointerMath)
                }
            }
            // These work if the left operand is a pointer, the right an integer
            Add | BitAnd | Sub | Rem
                if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
                       left.is_ptr() && right.is_bits() => {
                // Cast to i128 is fine as we checked the kind to be ptr-sized
                self.ptr_int_arithmetic(
                    bin_op,
                    left.to_ptr()?,
                    right.to_bits(self.memory.pointer_size())? as i128,
                    left_kind == isize,
                ).map(Some)
            }
            // Commutative operators also work if the integer is on the left
            Add | BitAnd
                if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
                       left.is_bits() && right.is_ptr() => {
                // This is a commutative operation, just swap the operands
                self.ptr_int_arithmetic(
                    bin_op,
                    right.to_ptr()?,
                    left.to_bits(self.memory.pointer_size())? as i128,
                    left_kind == isize,
                ).map(Some)
            }
            _ => Ok(None),
        }
    }

    fn ptr_int_arithmetic(
        &self,
        bin_op: mir::BinOp,
        left: Pointer,
        right: i128,
        signed: bool,
    ) -> EvalResult<'tcx, (Scalar, bool)> {
        use rustc::mir::BinOp::*;

        fn map_to_primval((res, over): (Pointer, bool)) -> (Scalar, bool) {
            (Scalar::Ptr(res), over)
        }

        Ok(match bin_op {
            Sub =>
                // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
                map_to_primval(left.overflowing_signed_offset(-right, self)),
            Add if signed =>
                map_to_primval(left.overflowing_signed_offset(right, self)),
            Add if !signed =>
                map_to_primval(left.overflowing_offset(Size::from_bytes(right as u64), self)),

            BitAnd if !signed => {
                let ptr_base_align = self.memory.get(left.alloc_id)?.align.abi();
                let base_mask : u64 = !(ptr_base_align - 1);
                let right = right as u64;
                let ptr_size = self.memory.pointer_size().bytes() as u8;
                if right & base_mask == base_mask {
                    // Case 1: The base address bits are all preserved, i.e., right is all-1 there
                    (Scalar::Ptr(Pointer::new(left.alloc_id, Size::from_bytes(left.offset.bytes() & right))), false)
                } else if right & base_mask == 0 {
                    // Case 2: The base address bits are all taken away, i.e., right is all-0 there
                    (Scalar::Bits { bits: (left.offset.bytes() & right) as u128, size: ptr_size }, false)
                } else {
                    return err!(ReadPointerAsBytes);
                }
            }

            Rem if !signed => {
                // Doing modulo a divisor of the alignment is allowed.
                // (Intuition: Modulo a divisor leaks less information.)
                let ptr_base_align = self.memory.get(left.alloc_id)?.align.abi();
                let right = right as u64;
                let ptr_size = self.memory.pointer_size().bytes() as u8;
                if right == 1 {
                    // modulo 1 is always 0
                    (Scalar::Bits { bits: 0, size: ptr_size }, false)
                } else if ptr_base_align % right == 0 {
                    // the base address would be cancelled out by the modulo operation, so we can
                    // just take the modulo of the offset
                    (Scalar::Bits { bits: (left.offset.bytes() % right) as u128, size: ptr_size }, false)
                } else {
                    return err!(ReadPointerAsBytes);
                }
            }

            _ => {
                let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
                return err!(Unimplemented(msg));
            }
        })
    }
}
<commit_msg>fix int ptr ops on 32bit<commit_after>use rustc::ty;
use rustc::ty::layout::Primitive;
use rustc::mir;

use super::*;

use helpers::EvalContextExt as HelperEvalContextExt;

pub trait EvalContextExt<'tcx> {
    fn ptr_op(
        &self,
        bin_op: mir::BinOp,
        left: Scalar,
        left_ty: ty::Ty<'tcx>,
        right: Scalar,
        right_ty: ty::Ty<'tcx>,
    ) -> EvalResult<'tcx, Option<(Scalar, bool)>>;

    fn ptr_int_arithmetic(
        &self,
        bin_op: mir::BinOp,
        left: Pointer,
        right: u128,
        signed: bool,
    ) -> EvalResult<'tcx, (Scalar, bool)>;
}

impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> {
    fn ptr_op(
        &self,
        bin_op: mir::BinOp,
        left: Scalar,
        left_ty: ty::Ty<'tcx>,
        right: Scalar,
        right_ty: ty::Ty<'tcx>,
    ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
        trace!("ptr_op: {:?} {:?} {:?}", left, bin_op, right);

        use rustc::mir::BinOp::*;
        use rustc::ty::layout::Integer::*;
        let usize = Primitive::Int(match self.memory.pointer_size().bytes() {
            1 => I8,
            2 => I16,
            4 => I32,
            8 => I64,
            16 => I128,
            _ => unreachable!(),
        }, false);
        let isize = Primitive::Int(match self.memory.pointer_size().bytes() {
            1 => I8,
            2 => I16,
            4 => I32,
            8 => I64,
            16 => I128,
            _ => unreachable!(),
        }, true);
        let left_layout = self.layout_of(left_ty)?;
        let left_kind = match left_layout.abi {
            ty::layout::Abi::Scalar(ref scalar) => scalar.value,
            _ => Err(EvalErrorKind::TypeNotPrimitive(left_ty))?,
        };
        let right_layout = self.layout_of(right_ty)?;
        let right_kind = match right_layout.abi {
            ty::layout::Abi::Scalar(ref scalar) => scalar.value,
            _ => Err(EvalErrorKind::TypeNotPrimitive(right_ty))?,
        };
        match bin_op {
            Offset if left_kind == Primitive::Pointer && right_kind == usize => {
                let pointee_ty = left_ty
                    .builtin_deref(true)
                    .expect("Offset called on non-ptr type")
                    .ty;
                let ptr = self.pointer_offset(
                    left,
                    pointee_ty,
                    right.to_bits(self.memory.pointer_size())? as i64,
                )?;
                Ok(Some((ptr, false)))
            }
            // These work on anything
            Eq if left_kind == right_kind => {
                let result = match (left, right) {
                    (Scalar::Bits { .. }, Scalar::Bits { .. }) => {
                        left.to_bits(left_layout.size)? == right.to_bits(right_layout.size)?
                    },
                    // FIXME: Test if both allocations are still live *or* if they are in the same allocation? (same for Ne below)
                    (Scalar::Ptr(left), Scalar::Ptr(right)) => left == right,
                    // FIXME: We should probably error out when comparing anything but NULL with a pointer (same for Ne below)
                    _ => false,
                };
                Ok(Some((Scalar::from_bool(result), false)))
            }
            Ne if left_kind == right_kind => {
                let result = match (left, right) {
                    (Scalar::Bits { .. }, Scalar::Bits { .. }) => {
                        left.to_bits(left_layout.size)? != right.to_bits(right_layout.size)?
                    },
                    (Scalar::Ptr(left), Scalar::Ptr(right)) => left != right,
                    _ => true,
                };
                Ok(Some((Scalar::from_bool(result), false)))
            }
            // These need both pointers to be in the same allocation
            Lt | Le | Gt | Ge | Sub
                if left_kind == right_kind &&
                       (left_kind == Primitive::Pointer || left_kind == usize || left_kind == isize) &&
                       left.is_ptr() && right.is_ptr() => {
                let left = left.to_ptr()?;
                let right = right.to_ptr()?;
                if left.alloc_id == right.alloc_id {
                    let res = match bin_op {
                        Lt => left.offset < right.offset,
                        Le => left.offset <= right.offset,
                        Gt => left.offset > right.offset,
                        Ge => left.offset >= right.offset,
                        Sub => {
                            return self.binary_op(
                                Sub,
                                Scalar::Bits { bits: left.offset.bytes() as u128, size: self.memory.pointer_size().bytes() as u8 },
                                self.tcx.types.usize,
                                Scalar::Bits { bits: right.offset.bytes() as u128, size: self.memory.pointer_size().bytes() as u8 },
                                self.tcx.types.usize,
                            ).map(Some)
                        }
                        _ => bug!("We already established it has to be one of these operators."),
                    };
                    Ok(Some((Scalar::from_bool(res), false)))
                } else {
                    // Both are pointers, but from different allocations.
                    err!(InvalidPointerMath)
                }
            }
            // These work if the left operand is a pointer, the right an integer
            Add | BitAnd | Sub | Rem
                if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
                       left.is_ptr() && right.is_bits() => {
                // Cast to i128 is fine as we checked the kind to be ptr-sized
                self.ptr_int_arithmetic(
                    bin_op,
                    left.to_ptr()?,
                    right.to_bits(self.memory.pointer_size())?,
                    left_kind == isize,
                ).map(Some)
            }
            // Commutative operators also work if the integer is on the left
            Add | BitAnd
                if left_kind == right_kind && (left_kind == usize || left_kind == isize) &&
                       left.is_bits() && right.is_ptr() => {
                // This is a commutative operation, just swap the operands
                self.ptr_int_arithmetic(
                    bin_op,
                    right.to_ptr()?,
                    left.to_bits(self.memory.pointer_size())?,
                    left_kind == isize,
                ).map(Some)
            }
            _ => Ok(None),
        }
    }

    fn ptr_int_arithmetic(
        &self,
        bin_op: mir::BinOp,
        left: Pointer,
        right: u128,
        signed: bool,
    ) -> EvalResult<'tcx, (Scalar, bool)> {
        use rustc::mir::BinOp::*;

        fn map_to_primval((res, over): (Pointer, bool)) -> (Scalar, bool) {
            (Scalar::Ptr(res), over)
        }

        Ok(match bin_op {
            Sub =>
                // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
                map_to_primval(left.overflowing_signed_offset(-(right as i128), self)),
            Add if signed =>
                map_to_primval(left.overflowing_signed_offset(right as i128, self)),
            Add if !signed =>
                map_to_primval(left.overflowing_offset(Size::from_bytes(right as u64), self)),

            BitAnd if !signed => {
                let ptr_base_align = self.memory.get(left.alloc_id)?.align.abi();
                let base_mask = {
                    // FIXME: Use interpret::truncate, once that takes a Size instead of a Layout
                    let shift = 128 - self.memory.pointer_size().bits();
                    let value = !(ptr_base_align as u128 - 1);
                    // truncate (shift left to drop out leftover values, shift right to fill with zeroes)
                    (value << shift) >> shift
                };
                let ptr_size = self.memory.pointer_size().bytes() as u8;
                trace!("Ptr BitAnd, align {}, operand {:#010x}, base_mask {:#010x}",
                    ptr_base_align, right, base_mask);
                if right & base_mask == base_mask {
                    // Case 1: The base address bits are all preserved, i.e., right is all-1 there
                    let offset = (left.offset.bytes() as u128 & right) as u64;
                    (Scalar::Ptr(Pointer::new(left.alloc_id, Size::from_bytes(offset))), false)
                } else if right & base_mask == 0 {
                    // Case 2: The base address bits are all taken away, i.e., right is all-0 there
                    (Scalar::Bits { bits: (left.offset.bytes() as u128) & right, size: ptr_size }, false)
                } else {
                    return err!(ReadPointerAsBytes);
                }
            }

            Rem if !signed => {
                // Doing modulo a divisor of the alignment is allowed.
                // (Intuition: Modulo a divisor leaks less information.)
                let ptr_base_align = self.memory.get(left.alloc_id)?.align.abi();
                let right = right as u64;
                let ptr_size = self.memory.pointer_size().bytes() as u8;
                if right == 1 {
                    // modulo 1 is always 0
                    (Scalar::Bits { bits: 0, size: ptr_size }, false)
                } else if ptr_base_align % right == 0 {
                    // the base address would be cancelled out by the modulo operation, so we can
                    // just take the modulo of the offset
                    (Scalar::Bits { bits: (left.offset.bytes() % right) as u128, size: ptr_size }, false)
                } else {
                    return err!(ReadPointerAsBytes);
                }
            }

            _ => {
                let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
                return err!(Unimplemented(msg));
            }
        })
    }
}
<|endoftext|>


<commit_before>/*
Copyright ⓒ 2015-2017 cargo-script contributors.

Licensed under the MIT license (see LICENSE or <http://opensource.org
/licenses/MIT>) or the Apache License, Version 2.0 (see LICENSE of
<http://www.apache.org/licenses/LICENSE-2.0>), at your option. All
files in the project carrying such notice may not be copied, modified,
or distributed except according to those terms.
*/
/*!
This module is for platform-specific stuff.
*/

pub use self::inner::{
    current_time, file_last_modified, get_cache_dir,
    migrate_old_data,
};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MigrationKind {
    DryRun,
    ForReal,
}

impl MigrationKind {
    pub fn for_real(&self) -> bool {
        *self == MigrationKind::ForReal
    }
}

#[cfg(any(unix, windows))]
mod inner_unix_or_windows {
    extern crate time;

    /**
    Gets the current system time, in milliseconds since the UNIX epoch.
    */
    pub fn current_time() -> u64 {
        /*
        This is kinda dicey, since *ideally* both this function and `file_last_modified` would be using the same underlying APIs.  They are not, insofar as I know.

        At least, not when targetting Windows.

        That said, so long as everything is in the same units and uses the same epoch, it should be fine.
        */
        let now_1970_utc = time::now_utc().to_timespec();
        if now_1970_utc.sec < 0 || now_1970_utc.nsec < 0 {
            // Fuck it.
            return 0
        }
        let now_ms_1970_utc = (now_1970_utc.sec as u64 * 1000)
            + (now_1970_utc.nsec as u64 / 1_000_000);
        now_ms_1970_utc
    }
}

#[cfg(unix)]
mod inner {
    pub use super::inner_unix_or_windows::current_time;

    use std::path::{Path, PathBuf};
    use std::{cmp, env, fs};
    use std::os::unix::fs::MetadataExt;
    use error::{MainError, Blame};
    use super::MigrationKind;

    /**
    Gets the last-modified time of a file, in milliseconds since the UNIX epoch.
    */
    pub fn file_last_modified(file: &fs::File) -> u64 {
        let mtime_s_1970_utc = file.metadata()
            .map(|md| md.mtime())
            .unwrap_or(0);

        let mtime_s_1970_utc = cmp::max(0, mtime_s_1970_utc);
        mtime_s_1970_utc as u64 * 1000
    }

    /**
    Get a directory suitable for storing user- and machine-specific data which may or may not be persisted across sessions.

    This is chosen to match the location where Cargo places its cache data.
    */
    pub fn get_cache_dir() -> Result<PathBuf, MainError> {
        // try $CARGO_HOME then fall back to $HOME
        if let Some(home) = env::var_os("CARGO_HOME") {
            let home = Path::new(&home);
            let old_home = home.join(".cargo");
            if old_home.exists() {
                // Keep using the old directory in preference to the new one, but only if it still contains `script-cache` and/or `binary-cache`.
                if old_home.join("script-cache").exists() || old_home.join("binary-cache").exists() {
                    // Yup; use this one.
                    return Ok(old_home);
                }
            }

            // Just use `$CARGO_HOME` directly.
            return Ok(home.into());
        }

        if let Some(home) = env::var_os("HOME") {
            return Ok(Path::new(&home).join(".cargo"));
        }

        Err((Blame::Human, "neither $CARGO_HOME nor $HOME is defined").into())
    }

    pub fn migrate_old_data(kind: MigrationKind) -> (Vec<String>, Result<(), MainError>) {
        let mut log = vec![];
        match migrate_0_2_0(kind, &mut log) {
            Ok(()) => (),
            Err(e) => return (log, Err(e)),
        }
        (log, Ok(()))
    }

    fn migrate_0_2_0(kind: MigrationKind, log: &mut Vec<String>) -> Result<(), MainError> {
        /*
        Previously, when `CARGO_HOME` was defined on !Windows, the cache would be at `$CARGO_HOME/.cargo`.  If it exists, its contents (`script-cache` and `binary-cache`) need to moved into `$CARGO_HOME` directly.
        */
        if let Some(home) = env::var_os("CARGO_HOME") {
            let home = Path::new(&home);
            let old_base = home.join(".cargo");
            if old_base.exists() {
                info!("<0.2.0 cache directory ({:?}) exists; attempting migration", old_base);

                /*
                Why both `info!` and `log`?  One for *before* we try (to help debug any issues) that only appears in the "real" log, and one for the user to let them know what we did/didn't do.
                */

                let old_script_cache = old_base.join("script-cache");
                let new_script_cache = home.join("script-cache");
                match (old_script_cache.exists(), new_script_cache.exists()) {
                    (true, true) => {
                        info!("not migrating {:?}; already exists at new location", old_script_cache);
                        log.push(format!("Did not move {:?}: new location {:?} already exists.", old_script_cache, new_script_cache));
                    },
                    (true, false) => {
                        info!("migrating {:?} -> {:?}", old_script_cache, new_script_cache);
                        if kind.for_real() {
                            try!(fs::rename(&old_script_cache, &new_script_cache));
                        }
                        log.push(format!("Moved {:?} to {:?}.", old_script_cache, new_script_cache));
                    },
                    (false, _) => {
                        info!("not migrating {:?}; does not exist", old_script_cache);
                    },
                }

                let old_binary_cache = old_base.join("binary-cache");
                let new_binary_cache = home.join("binary-cache");
                match (old_binary_cache.exists(), new_binary_cache.exists()) {
                    (true, true) => {
                        info!("not migrating {:?}; already exists at new location", old_binary_cache);
                        log.push(format!("Did not move {:?}: new location {:?} already exists.", old_binary_cache, new_binary_cache));
                    },
                    (true, false) => {
                        info!("migrating {:?} -> {:?}", old_binary_cache, new_binary_cache);
                        if kind.for_real() {
                            try!(fs::rename(&old_binary_cache, &new_binary_cache));
                        }
                        log.push(format!("Moved {:?} to {:?}.", old_script_cache, new_script_cache));
                    },
                    (false, _) => {
                        info!("not migrating {:?}; does not exist", old_binary_cache);
                    },
                }

                // If `$CARGO_HOME/.cargo` is empty, remove it.
                if try!(fs::read_dir(&old_base)).next().is_none() {
                    info!("{:?} is empty; removing", old_base);
                    if kind.for_real() {
                        try!(fs::remove_dir(&old_base));
                    }
                    log.push(format!("Removed empty directory {:?}", old_base));
                } else {
                    info!("not removing {:?}; not empty", old_base);
                    log.push(format!("Not removing {:?}: not empty.", old_base));
                }

                info!("done with migration");
            }
        }

        Ok(())
    }
}

#[cfg(windows)]
pub mod inner {
    #![allow(non_snake_case)]

    extern crate ole32;
    extern crate shell32;
    extern crate winapi;

    pub use super::inner_unix_or_windows::current_time;

    use std::ffi::OsString;
    use std::fmt;
    use std::fs;
    use std::path::{Path, PathBuf};
    use std::mem;
    use std::os::windows::ffi::OsStringExt;
    use error::MainError;
    use super::MigrationKind;

    #[cfg(old_rustc_windows_linking_behaviour)]
    mod uuid {
        // This *is* in `uuid-sys` ≤ 0.1.2, but it doesn't work in Rust < 1.15.
        #[link(name="uuid")]
        extern { static FOLDERID_LocalAppData: super::winapi::KNOWNFOLDERID; }

        pub unsafe fn local_app_data() -> &'static super::winapi::KNOWNFOLDERID {
            &FOLDERID_LocalAppData
        }
    }

    #[cfg(not(old_rustc_windows_linking_behaviour))]
    mod uuid {
        // WARNING: do not use with rustc < 1.15; it will cause linking errors.
        extern crate uuid;

        pub unsafe fn local_app_data() -> &'static super::winapi::KNOWNFOLDERID {
            &uuid::FOLDERID_LocalAppData
        }
    }

    /**
    Gets the last-modified time of a file, in milliseconds since the UNIX epoch.
    */
    pub fn file_last_modified(file: &fs::File) -> u64 {
        use ::std::os::windows::fs::MetadataExt;

        const MS_BETWEEN_1601_1970: u64 = 11_644_473_600_000;

        let mtime_100ns_1601_utc = file.metadata()
            .map(|md| md.last_write_time())
            .unwrap_or(0);
        let mtime_ms_1601_utc = mtime_100ns_1601_utc / (1000*10);

        // This can obviously underflow... but since files created prior to 1970 are going to be *somewhat rare*, I'm just going to saturate to zero.
        let mtime_ms_1970_utc = mtime_ms_1601_utc.saturating_sub(MS_BETWEEN_1601_1970);
        mtime_ms_1970_utc
    }

    /**
    Get a directory suitable for storing user- and machine-specific data which may or may not be persisted across sessions.

    This is *not* chosen to match the location where Cargo places its cache data, because Cargo is *wrong*.  This is at least *less wrong*.

    On Windows, LocalAppData is where user- and machine- specific data should go, but it *might* be more appropriate to use whatever the official name for "Program Data" is, though.
    */
    pub fn get_cache_dir() -> Result<PathBuf, MainError>
    where P: AsRef<Path> {
        let rfid = unsafe { uuid::local_app_data() };
        let dir = try!(SHGetKnownFolderPath(rfid, 0, ::std::ptr::null_mut())
            .map_err(|e| e.to_string()));
        Ok(Path::new(&dir).to_path_buf().join("Cargo"))
    }

    type WinResult<T> = Result<T, WinError>;

    struct WinError(winapi::HRESULT);

    impl fmt::Display for WinError {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            write!(fmt, "HRESULT({})", self.0)
        }
    }

    fn SHGetKnownFolderPath(rfid: &winapi::KNOWNFOLDERID, dwFlags: winapi::DWORD, hToken: winapi::HANDLE) -> WinResult<OsString> {
        use self::winapi::PWSTR;
        let mut psz_path: PWSTR = unsafe { mem::uninitialized() };
        let hresult = unsafe {
            shell32::SHGetKnownFolderPath(
                rfid,
                dwFlags,
                hToken,
                mem::transmute(&mut psz_path as &mut PWSTR as *mut PWSTR)
            )
        };

        if hresult == winapi::S_OK {
            let r = unsafe { pwstr_to_os_string(psz_path) };
            unsafe { ole32::CoTaskMemFree(psz_path as *mut _) };
            Ok(r)
        } else {
            Err(WinError(hresult))
        }
    }

    unsafe fn pwstr_to_os_string(ptr: winapi::PWSTR) -> OsString {
        OsStringExt::from_wide(::std::slice::from_raw_parts(ptr, pwstr_len(ptr)))
    }

    unsafe fn pwstr_len(mut ptr: winapi::PWSTR) -> usize {
        let mut len = 0;
        while *ptr != 0 {
            len += 1;
            ptr = ptr.offset(1);
        }
        len
    }

    pub fn migrate_old_data(kind: MigrationKind) -> (Vec<String>, Result<(), MainError>) {
        // Avoid unused code/variable warnings.
        let _ = kind.for_real();
        (vec![], Ok(()))
    }
}
<commit_msg>Fixed get_cache_dir on Windows.<commit_after>/*
Copyright ⓒ 2015-2017 cargo-script contributors.

Licensed under the MIT license (see LICENSE or <http://opensource.org
/licenses/MIT>) or the Apache License, Version 2.0 (see LICENSE of
<http://www.apache.org/licenses/LICENSE-2.0>), at your option. All
files in the project carrying such notice may not be copied, modified,
or distributed except according to those terms.
*/
/*!
This module is for platform-specific stuff.
*/

pub use self::inner::{
    current_time, file_last_modified, get_cache_dir,
    migrate_old_data,
};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MigrationKind {
    DryRun,
    ForReal,
}

impl MigrationKind {
    pub fn for_real(&self) -> bool {
        *self == MigrationKind::ForReal
    }
}

#[cfg(any(unix, windows))]
mod inner_unix_or_windows {
    extern crate time;

    /**
    Gets the current system time, in milliseconds since the UNIX epoch.
    */
    pub fn current_time() -> u64 {
        /*
        This is kinda dicey, since *ideally* both this function and `file_last_modified` would be using the same underlying APIs.  They are not, insofar as I know.

        At least, not when targetting Windows.

        That said, so long as everything is in the same units and uses the same epoch, it should be fine.
        */
        let now_1970_utc = time::now_utc().to_timespec();
        if now_1970_utc.sec < 0 || now_1970_utc.nsec < 0 {
            // Fuck it.
            return 0
        }
        let now_ms_1970_utc = (now_1970_utc.sec as u64 * 1000)
            + (now_1970_utc.nsec as u64 / 1_000_000);
        now_ms_1970_utc
    }
}

#[cfg(unix)]
mod inner {
    pub use super::inner_unix_or_windows::current_time;

    use std::path::{Path, PathBuf};
    use std::{cmp, env, fs};
    use std::os::unix::fs::MetadataExt;
    use error::{MainError, Blame};
    use super::MigrationKind;

    /**
    Gets the last-modified time of a file, in milliseconds since the UNIX epoch.
    */
    pub fn file_last_modified(file: &fs::File) -> u64 {
        let mtime_s_1970_utc = file.metadata()
            .map(|md| md.mtime())
            .unwrap_or(0);

        let mtime_s_1970_utc = cmp::max(0, mtime_s_1970_utc);
        mtime_s_1970_utc as u64 * 1000
    }

    /**
    Get a directory suitable for storing user- and machine-specific data which may or may not be persisted across sessions.

    This is chosen to match the location where Cargo places its cache data.
    */
    pub fn get_cache_dir() -> Result<PathBuf, MainError> {
        // try $CARGO_HOME then fall back to $HOME
        if let Some(home) = env::var_os("CARGO_HOME") {
            let home = Path::new(&home);
            let old_home = home.join(".cargo");
            if old_home.exists() {
                // Keep using the old directory in preference to the new one, but only if it still contains `script-cache` and/or `binary-cache`.
                if old_home.join("script-cache").exists() || old_home.join("binary-cache").exists() {
                    // Yup; use this one.
                    return Ok(old_home);
                }
            }

            // Just use `$CARGO_HOME` directly.
            return Ok(home.into());
        }

        if let Some(home) = env::var_os("HOME") {
            return Ok(Path::new(&home).join(".cargo"));
        }

        Err((Blame::Human, "neither $CARGO_HOME nor $HOME is defined").into())
    }

    pub fn migrate_old_data(kind: MigrationKind) -> (Vec<String>, Result<(), MainError>) {
        let mut log = vec![];
        match migrate_0_2_0(kind, &mut log) {
            Ok(()) => (),
            Err(e) => return (log, Err(e)),
        }
        (log, Ok(()))
    }

    fn migrate_0_2_0(kind: MigrationKind, log: &mut Vec<String>) -> Result<(), MainError> {
        /*
        Previously, when `CARGO_HOME` was defined on !Windows, the cache would be at `$CARGO_HOME/.cargo`.  If it exists, its contents (`script-cache` and `binary-cache`) need to moved into `$CARGO_HOME` directly.
        */
        if let Some(home) = env::var_os("CARGO_HOME") {
            let home = Path::new(&home);
            let old_base = home.join(".cargo");
            if old_base.exists() {
                info!("<0.2.0 cache directory ({:?}) exists; attempting migration", old_base);

                /*
                Why both `info!` and `log`?  One for *before* we try (to help debug any issues) that only appears in the "real" log, and one for the user to let them know what we did/didn't do.
                */

                let old_script_cache = old_base.join("script-cache");
                let new_script_cache = home.join("script-cache");
                match (old_script_cache.exists(), new_script_cache.exists()) {
                    (true, true) => {
                        info!("not migrating {:?}; already exists at new location", old_script_cache);
                        log.push(format!("Did not move {:?}: new location {:?} already exists.", old_script_cache, new_script_cache));
                    },
                    (true, false) => {
                        info!("migrating {:?} -> {:?}", old_script_cache, new_script_cache);
                        if kind.for_real() {
                            try!(fs::rename(&old_script_cache, &new_script_cache));
                        }
                        log.push(format!("Moved {:?} to {:?}.", old_script_cache, new_script_cache));
                    },
                    (false, _) => {
                        info!("not migrating {:?}; does not exist", old_script_cache);
                    },
                }

                let old_binary_cache = old_base.join("binary-cache");
                let new_binary_cache = home.join("binary-cache");
                match (old_binary_cache.exists(), new_binary_cache.exists()) {
                    (true, true) => {
                        info!("not migrating {:?}; already exists at new location", old_binary_cache);
                        log.push(format!("Did not move {:?}: new location {:?} already exists.", old_binary_cache, new_binary_cache));
                    },
                    (true, false) => {
                        info!("migrating {:?} -> {:?}", old_binary_cache, new_binary_cache);
                        if kind.for_real() {
                            try!(fs::rename(&old_binary_cache, &new_binary_cache));
                        }
                        log.push(format!("Moved {:?} to {:?}.", old_script_cache, new_script_cache));
                    },
                    (false, _) => {
                        info!("not migrating {:?}; does not exist", old_binary_cache);
                    },
                }

                // If `$CARGO_HOME/.cargo` is empty, remove it.
                if try!(fs::read_dir(&old_base)).next().is_none() {
                    info!("{:?} is empty; removing", old_base);
                    if kind.for_real() {
                        try!(fs::remove_dir(&old_base));
                    }
                    log.push(format!("Removed empty directory {:?}", old_base));
                } else {
                    info!("not removing {:?}; not empty", old_base);
                    log.push(format!("Not removing {:?}: not empty.", old_base));
                }

                info!("done with migration");
            }
        }

        Ok(())
    }
}

#[cfg(windows)]
pub mod inner {
    #![allow(non_snake_case)]

    extern crate ole32;
    extern crate shell32;
    extern crate winapi;

    pub use super::inner_unix_or_windows::current_time;

    use std::ffi::OsString;
    use std::fmt;
    use std::fs;
    use std::path::{Path, PathBuf};
    use std::mem;
    use std::os::windows::ffi::OsStringExt;
    use error::MainError;
    use super::MigrationKind;

    #[cfg(old_rustc_windows_linking_behaviour)]
    mod uuid {
        // This *is* in `uuid-sys` ≤ 0.1.2, but it doesn't work in Rust < 1.15.
        #[link(name="uuid")]
        extern { static FOLDERID_LocalAppData: super::winapi::KNOWNFOLDERID; }

        pub unsafe fn local_app_data() -> &'static super::winapi::KNOWNFOLDERID {
            &FOLDERID_LocalAppData
        }
    }

    #[cfg(not(old_rustc_windows_linking_behaviour))]
    mod uuid {
        // WARNING: do not use with rustc < 1.15; it will cause linking errors.
        extern crate uuid;

        pub unsafe fn local_app_data() -> &'static super::winapi::KNOWNFOLDERID {
            &uuid::FOLDERID_LocalAppData
        }
    }

    /**
    Gets the last-modified time of a file, in milliseconds since the UNIX epoch.
    */
    pub fn file_last_modified(file: &fs::File) -> u64 {
        use ::std::os::windows::fs::MetadataExt;

        const MS_BETWEEN_1601_1970: u64 = 11_644_473_600_000;

        let mtime_100ns_1601_utc = file.metadata()
            .map(|md| md.last_write_time())
            .unwrap_or(0);
        let mtime_ms_1601_utc = mtime_100ns_1601_utc / (1000*10);

        // This can obviously underflow... but since files created prior to 1970 are going to be *somewhat rare*, I'm just going to saturate to zero.
        let mtime_ms_1970_utc = mtime_ms_1601_utc.saturating_sub(MS_BETWEEN_1601_1970);
        mtime_ms_1970_utc
    }

    /**
    Get a directory suitable for storing user- and machine-specific data which may or may not be persisted across sessions.

    This is *not* chosen to match the location where Cargo places its cache data, because Cargo is *wrong*.  This is at least *less wrong*.

    On Windows, LocalAppData is where user- and machine- specific data should go, but it *might* be more appropriate to use whatever the official name for "Program Data" is, though.
    */
    pub fn get_cache_dir() -> Result<PathBuf, MainError> {
        let rfid = unsafe { uuid::local_app_data() };
        let dir = try!(SHGetKnownFolderPath(rfid, 0, ::std::ptr::null_mut())
            .map_err(|e| e.to_string()));
        Ok(Path::new(&dir).to_path_buf().join("Cargo"))
    }

    type WinResult<T> = Result<T, WinError>;

    struct WinError(winapi::HRESULT);

    impl fmt::Display for WinError {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            write!(fmt, "HRESULT({})", self.0)
        }
    }

    fn SHGetKnownFolderPath(rfid: &winapi::KNOWNFOLDERID, dwFlags: winapi::DWORD, hToken: winapi::HANDLE) -> WinResult<OsString> {
        use self::winapi::PWSTR;
        let mut psz_path: PWSTR = unsafe { mem::uninitialized() };
        let hresult = unsafe {
            shell32::SHGetKnownFolderPath(
                rfid,
                dwFlags,
                hToken,
                mem::transmute(&mut psz_path as &mut PWSTR as *mut PWSTR)
            )
        };

        if hresult == winapi::S_OK {
            let r = unsafe { pwstr_to_os_string(psz_path) };
            unsafe { ole32::CoTaskMemFree(psz_path as *mut _) };
            Ok(r)
        } else {
            Err(WinError(hresult))
        }
    }

    unsafe fn pwstr_to_os_string(ptr: winapi::PWSTR) -> OsString {
        OsStringExt::from_wide(::std::slice::from_raw_parts(ptr, pwstr_len(ptr)))
    }

    unsafe fn pwstr_len(mut ptr: winapi::PWSTR) -> usize {
        let mut len = 0;
        while *ptr != 0 {
            len += 1;
            ptr = ptr.offset(1);
        }
        len
    }

    pub fn migrate_old_data(kind: MigrationKind) -> (Vec<String>, Result<(), MainError>) {
        // Avoid unused code/variable warnings.
        let _ = kind.for_real();
        (vec![], Ok(()))
    }
}
<|endoftext|>