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

//! Messages used in the Exonum consensus algorithm.
//!
//! Every message, unless stated otherwise, is checked by the same set of rules. The message is
//! ignored if it
//!     * is sent from a lower height than the current one
//!     * contains incorrect validator id
//!     * is signed with incorrect signature
//!
//! Specific nuances are described in each message documentation and typically consist of three
//! parts:
//!     * validation - additional checks before processing
//!     * processing - how message is processed and result of the processing
//!     * generation - in which cases message is generated

use std::net::SocketAddr;
use std::time::SystemTime;

use crypto::{Hash, PublicKey};
use blockchain;
use helpers::{Height, Round, ValidatorId};
use super::{RawMessage, BitVec};

/// Consensus message type.
pub const CONSENSUS: u16 = 0;

/// `Connect` message id.
pub const CONNECT_MESSAGE_ID: u16 = 0;
/// `Status` message id.
pub const STATUS_MESSAGE_ID: u16 = 1;

/// `Propose` message id.
pub const PROPOSE_MESSAGE_ID: u16 = 2;
/// `Prevote` message id.
pub const PREVOTE_MESSAGE_ID: u16 = 3;
/// `Precommit` message id.
pub const PRECOMMIT_MESSAGE_ID: u16 = 4;
/// `BlockResponse` message id.
pub const BLOCK_RESPONSE_MESSAGE_ID: u16 = 5;

/// `RequestPropose` message id.
pub const PROPOSE_REQUEST_MESSAGE_ID: u16 = 6;
/// `RequestTransactions` message id.
pub const TRANSACTIONS_REQUEST_MESSAGE_ID: u16 = 7;
/// `RequestPrevotes` message id.
pub const PREVOTES_REQUEST_MESSAGE_ID: u16 = 8;
/// `RequestPeers` message id.
pub const PEERS_REQUEST_MESSAGE_ID: u16 = 9;
/// `RequestBlock` message id.
pub const BLOCK_REQUEST_MESSAGE_ID: u16 = 10;

message! {
/// Connect to a node.
///
/// ### Validation
/// The message is ignored if its time is earlier than in the previous `Connect` message received
/// from the same peer.
///
/// ### Processing
/// Connect to the peer.
///
/// ### Generation
/// A node sends `Connect` message to all known addresses during initialization. Additionally,
/// the node responds by its own `Connect` message after receiving `node::Event::Connected`.
    struct Connect {
        const TYPE = CONSENSUS;
        const ID = CONNECT_MESSAGE_ID;
        const SIZE = 50;

        /// The sender's public key.
        field pub_key:        &PublicKey  [00 => 32]
        /// The node's address.
        field addr:           SocketAddr  [32 => 38]
        /// Time when the message was created.
        field time:           SystemTime  [38 => 50]
    }
}

message! {
/// Proposal for a new block.
///
/// ### Validation
/// The message is ignored if it
///     * contains incorrect `prev_hash`
///     * is sent by non-leader
///     * contains already committed transactions
///     * is already known
///
/// ### Processing
/// If the message contains unknown transactions, then `TransactionsRequest` is sent in reply.
/// Otherwise `Prevote` is broadcast.
///
/// ### Generation
/// A node broadcasts `Propose` if it is a leader and is not locked for a different proposal. Also
/// `Propose` can be sent as response to `ProposeRequest`.
    struct Propose {
        const TYPE = CONSENSUS;
        const ID = PROPOSE_MESSAGE_ID;
        const SIZE = 54;

        /// The validator id.
        field validator:      ValidatorId [00 => 02]
        /// The height to which the message is related.
        field height:         Height      [02 => 10]
        /// The round to which the message is related.
        field round:          Round       [10 => 14]
        /// Hash of the previous block.
        field prev_hash:      &Hash       [14 => 46]
        /// The list of transactions to include in the next block.
        field transactions:   &[Hash]     [46 => 54]
    }
}

message! {
/// Pre-vote for a new block.
///
/// ### Validation
/// A node panics if it has already sent a different `Prevote` for the same round.
///
/// ### Processing
/// Pre-vote is added to the list of known votes for the same proposal.
/// If `locked_round` number from the message is bigger than in a node state, then a node replies
/// with `PrevotesRequest`.
/// If there are unknown transactions in the propose specified by `propose_hash`,
/// `TransactionsRequest` is sent in reply.
/// Otherwise if all transactions are known and there are +2/3 pre-votes, then a node is locked
/// to that proposal and `Precommit` is broadcast.
///
/// ### Generation
/// A node broadcasts `Prevote` in response to `Propose` when it has received all the transactions.
    struct Prevote {
        const TYPE = CONSENSUS;
        const ID = PREVOTE_MESSAGE_ID;
        const SIZE = 50;

        /// The validator id.
        field validator:      ValidatorId [00 => 02]
        /// The height to which the message is related.
        field height:         Height      [02 => 10]
        /// The round to which the message is related.
        field round:          Round       [10 => 14]
        /// Hash of the corresponding `Propose`.
        field propose_hash:   &Hash       [14 => 46]
        /// Locked round.
        field locked_round:   Round       [46 => 50]
    }
}

message! {
/// Pre-commit for a proposal.
///
/// ### Validation
/// A node panics if it  has already sent a different `Precommit` for the same round.
///
/// ### Processing
/// Pre-commit is added to the list of known pre-commits.
/// If a proposal is unknown to the node, `ProposeRequest` is sent in reply.
/// If `round` number from the message is bigger than a node's "locked round", then a node replies
/// with `PrevotesRequest`.
/// If there are unknown transactions, then `TransactionsRequest` is sent in reply.
/// If a validator receives +2/3 precommits for the same proposal with the same block_hash, then
/// block is executed and `Status` is broadcast.
///
/// ### Generation
/// A node broadcasts `Precommit` in response to `Prevote` if there are +2/3 pre-votes and no
/// unknown transactions.
    struct Precommit {
        const TYPE = CONSENSUS;
        const ID = PRECOMMIT_MESSAGE_ID;
        const SIZE = 90;

        /// The validator id.
        field validator:      ValidatorId [00 => 02]
        /// The height to which the message is related.
        field height:         Height      [02 => 10]
        /// The round to which the message is related.
        field round:          Round       [10 => 14]
        /// Hash of the corresponding `Propose`.
        field propose_hash:   &Hash       [14 => 46]
        /// Hash of the new block.
        field block_hash:     &Hash       [46 => 78]
        /// Time of the `Precommit`.
        field time:           SystemTime  [78 => 90]
    }
}

message! {
/// Current node status.
///
/// ### Validation
/// The message is ignored if its signature is incorrect or its `height` is lower than a node's
/// height.
///
/// ### Processing
/// If the message's `height` number is bigger than a node's one, then `BlockRequest` with current
/// node's height is sent in reply.
///
/// ### Generation
/// `Status` message is broadcast regularly with the timeout controlled by
/// `blockchain::ConsensusConfig::status_timeout`. Also, it is broadcast after accepting a new
/// block.
    struct Status {
        const TYPE = CONSENSUS;
        const ID = STATUS_MESSAGE_ID;
        const SIZE = 72;

        /// The sender's public key.
        field from:           &PublicKey          [00 => 32]
        /// The height to which the message is related.
        field height:         Height              [32 => 40]
        /// Hash of the last committed block.
        field last_hash:      &Hash               [40 => 72]
    }
}

message! {
/// Information about a block.
///
/// ### Validation
/// The message is ignored if
///     * its `to` field corresponds to a different node
///     * the `block`, `transaction` and `precommits` fields cannot be parsed or verified
///
/// ### Processing
/// The block is added to the blockchain.
///
/// ### Generation
/// The message is sent as response to `BlockRequest`.
    struct BlockResponse {
        const TYPE = CONSENSUS;
        const ID = BLOCK_RESPONSE_MESSAGE_ID;
        const SIZE = 88;

        /// The sender's public key.
        field from:           &PublicKey          [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey          [32 => 64]
        /// Block header.
        field block:          blockchain::Block   [64 => 72]
        /// List of pre-commits.
        field precommits:     Vec<Precommit>      [72 => 80]
        /// List of the transactions.
        field transactions:   Vec<RawMessage>     [80 => 88]
    }
}

message! {
/// Request for the `Propose`.
///
/// ### Validation
/// The message is ignored if its `height` is not equal to the node's height.
///
/// ### Processing
/// `Propose` is sent as the response.
///
/// ### Generation
/// A node can send `ProposeRequest` during `Precommit` and `Prevote` handling.
    struct ProposeRequest {
        const TYPE = CONSENSUS;
        const ID = PROPOSE_REQUEST_MESSAGE_ID;
        const SIZE = 104;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The height to which the message is related.
        field height:         Height      [64 => 72]
        /// Hash of the `Propose`.
        field propose_hash:   &Hash       [72 => 104]
    }
}

message! {
/// Request for transactions by hash.
///
/// ### Processing
/// Requested transactions are sent to the recipient.
///
/// ### Generation
/// This message can be sent during `Propose`, `Prevote` and `Precommit` handling.
    struct TransactionsRequest {
        const TYPE = CONSENSUS;
        const ID = TRANSACTIONS_REQUEST_MESSAGE_ID;
        const SIZE = 72;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The list of the transaction hashes.
        field txs:            &[Hash]     [64 => 72]
    }
}

message! {
/// Request for pre-votes.
///
/// ### Validation
/// The message is ignored if its `height` is not equal to the node's height.
///
/// ### Processing
/// The requested pre-votes are sent to the recipient.
///
/// ### Generation
/// This message can be sent during `Prevote` and `Precommit` handling.
    struct PrevotesRequest {
        const TYPE = CONSENSUS;
        const ID = PREVOTES_REQUEST_MESSAGE_ID;
        const SIZE = 116;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The height to which the message is related.
        field height:         Height      [64 => 72]
        /// The round to which the message is related.
        field round:          Round       [72 => 76]
        /// Hash of the `Propose`.
        field propose_hash:   &Hash       [76 => 108]
        /// The list of validators that send pre-votes.
        field validators:     BitVec      [108 => 116]
    }
}

message! {
/// Request connected peers from a node.
///
/// ### Validation
/// Request is considered valid if the sender of the message on the network level corresponds to
/// the `from` field.
///
/// ### Processing
/// Peer `Connect` messages are sent to the recipient.
///
/// ### Generation
/// `PeersRequest` message is sent regularly with the timeout controlled by
/// `blockchain::ConsensusConfig::peers_timeout`.
    struct PeersRequest {
        const TYPE = CONSENSUS;
        const ID = PEERS_REQUEST_MESSAGE_ID;
        const SIZE = 64;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
    }
}

message! {
/// Request for the block with the given `height`.
///
/// ### Validation
/// The message is ignored if its `height` is bigger than the node's one.
///
/// ### Processing
/// `BlockResponse` message is sent as the response.
///
/// ### Generation
/// This message can be sent during `Status` processing.
    struct BlockRequest {
        const TYPE = CONSENSUS;
        const ID = BLOCK_REQUEST_MESSAGE_ID;
        const SIZE = 72;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The height to which the message is related.
        field height:         Height      [64 => 72]
    }
}
<commit_msg>Fix request messages id description<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Messages used in the Exonum consensus algorithm.
//!
//! Every message, unless stated otherwise, is checked by the same set of rules. The message is
//! ignored if it
//!     * is sent from a lower height than the current one
//!     * contains incorrect validator id
//!     * is signed with incorrect signature
//!
//! Specific nuances are described in each message documentation and typically consist of three
//! parts:
//!     * validation - additional checks before processing
//!     * processing - how message is processed and result of the processing
//!     * generation - in which cases message is generated

use std::net::SocketAddr;
use std::time::SystemTime;

use crypto::{Hash, PublicKey};
use blockchain;
use helpers::{Height, Round, ValidatorId};
use super::{RawMessage, BitVec};

/// Consensus message type.
pub const CONSENSUS: u16 = 0;

/// `Connect` message id.
pub const CONNECT_MESSAGE_ID: u16 = 0;
/// `Status` message id.
pub const STATUS_MESSAGE_ID: u16 = 1;

/// `Propose` message id.
pub const PROPOSE_MESSAGE_ID: u16 = 2;
/// `Prevote` message id.
pub const PREVOTE_MESSAGE_ID: u16 = 3;
/// `Precommit` message id.
pub const PRECOMMIT_MESSAGE_ID: u16 = 4;
/// `BlockResponse` message id.
pub const BLOCK_RESPONSE_MESSAGE_ID: u16 = 5;

/// `ProposeRequest` message id.
pub const PROPOSE_REQUEST_MESSAGE_ID: u16 = 6;
/// `TransactionsRequest` message id.
pub const TRANSACTIONS_REQUEST_MESSAGE_ID: u16 = 7;
/// `PrevotesRequest` message id.
pub const PREVOTES_REQUEST_MESSAGE_ID: u16 = 8;
/// `PeersRequest` message id.
pub const PEERS_REQUEST_MESSAGE_ID: u16 = 9;
/// `BlockRequest` message id.
pub const BLOCK_REQUEST_MESSAGE_ID: u16 = 10;

message! {
/// Connect to a node.
///
/// ### Validation
/// The message is ignored if its time is earlier than in the previous `Connect` message received
/// from the same peer.
///
/// ### Processing
/// Connect to the peer.
///
/// ### Generation
/// A node sends `Connect` message to all known addresses during initialization. Additionally,
/// the node responds by its own `Connect` message after receiving `node::Event::Connected`.
    struct Connect {
        const TYPE = CONSENSUS;
        const ID = CONNECT_MESSAGE_ID;
        const SIZE = 50;

        /// The sender's public key.
        field pub_key:        &PublicKey  [00 => 32]
        /// The node's address.
        field addr:           SocketAddr  [32 => 38]
        /// Time when the message was created.
        field time:           SystemTime  [38 => 50]
    }
}

message! {
/// Proposal for a new block.
///
/// ### Validation
/// The message is ignored if it
///     * contains incorrect `prev_hash`
///     * is sent by non-leader
///     * contains already committed transactions
///     * is already known
///
/// ### Processing
/// If the message contains unknown transactions, then `TransactionsRequest` is sent in reply.
/// Otherwise `Prevote` is broadcast.
///
/// ### Generation
/// A node broadcasts `Propose` if it is a leader and is not locked for a different proposal. Also
/// `Propose` can be sent as response to `ProposeRequest`.
    struct Propose {
        const TYPE = CONSENSUS;
        const ID = PROPOSE_MESSAGE_ID;
        const SIZE = 54;

        /// The validator id.
        field validator:      ValidatorId [00 => 02]
        /// The height to which the message is related.
        field height:         Height      [02 => 10]
        /// The round to which the message is related.
        field round:          Round       [10 => 14]
        /// Hash of the previous block.
        field prev_hash:      &Hash       [14 => 46]
        /// The list of transactions to include in the next block.
        field transactions:   &[Hash]     [46 => 54]
    }
}

message! {
/// Pre-vote for a new block.
///
/// ### Validation
/// A node panics if it has already sent a different `Prevote` for the same round.
///
/// ### Processing
/// Pre-vote is added to the list of known votes for the same proposal.
/// If `locked_round` number from the message is bigger than in a node state, then a node replies
/// with `PrevotesRequest`.
/// If there are unknown transactions in the propose specified by `propose_hash`,
/// `TransactionsRequest` is sent in reply.
/// Otherwise if all transactions are known and there are +2/3 pre-votes, then a node is locked
/// to that proposal and `Precommit` is broadcast.
///
/// ### Generation
/// A node broadcasts `Prevote` in response to `Propose` when it has received all the transactions.
    struct Prevote {
        const TYPE = CONSENSUS;
        const ID = PREVOTE_MESSAGE_ID;
        const SIZE = 50;

        /// The validator id.
        field validator:      ValidatorId [00 => 02]
        /// The height to which the message is related.
        field height:         Height      [02 => 10]
        /// The round to which the message is related.
        field round:          Round       [10 => 14]
        /// Hash of the corresponding `Propose`.
        field propose_hash:   &Hash       [14 => 46]
        /// Locked round.
        field locked_round:   Round       [46 => 50]
    }
}

message! {
/// Pre-commit for a proposal.
///
/// ### Validation
/// A node panics if it  has already sent a different `Precommit` for the same round.
///
/// ### Processing
/// Pre-commit is added to the list of known pre-commits.
/// If a proposal is unknown to the node, `ProposeRequest` is sent in reply.
/// If `round` number from the message is bigger than a node's "locked round", then a node replies
/// with `PrevotesRequest`.
/// If there are unknown transactions, then `TransactionsRequest` is sent in reply.
/// If a validator receives +2/3 precommits for the same proposal with the same block_hash, then
/// block is executed and `Status` is broadcast.
///
/// ### Generation
/// A node broadcasts `Precommit` in response to `Prevote` if there are +2/3 pre-votes and no
/// unknown transactions.
    struct Precommit {
        const TYPE = CONSENSUS;
        const ID = PRECOMMIT_MESSAGE_ID;
        const SIZE = 90;

        /// The validator id.
        field validator:      ValidatorId [00 => 02]
        /// The height to which the message is related.
        field height:         Height      [02 => 10]
        /// The round to which the message is related.
        field round:          Round       [10 => 14]
        /// Hash of the corresponding `Propose`.
        field propose_hash:   &Hash       [14 => 46]
        /// Hash of the new block.
        field block_hash:     &Hash       [46 => 78]
        /// Time of the `Precommit`.
        field time:           SystemTime  [78 => 90]
    }
}

message! {
/// Current node status.
///
/// ### Validation
/// The message is ignored if its signature is incorrect or its `height` is lower than a node's
/// height.
///
/// ### Processing
/// If the message's `height` number is bigger than a node's one, then `BlockRequest` with current
/// node's height is sent in reply.
///
/// ### Generation
/// `Status` message is broadcast regularly with the timeout controlled by
/// `blockchain::ConsensusConfig::status_timeout`. Also, it is broadcast after accepting a new
/// block.
    struct Status {
        const TYPE = CONSENSUS;
        const ID = STATUS_MESSAGE_ID;
        const SIZE = 72;

        /// The sender's public key.
        field from:           &PublicKey          [00 => 32]
        /// The height to which the message is related.
        field height:         Height              [32 => 40]
        /// Hash of the last committed block.
        field last_hash:      &Hash               [40 => 72]
    }
}

message! {
/// Information about a block.
///
/// ### Validation
/// The message is ignored if
///     * its `to` field corresponds to a different node
///     * the `block`, `transaction` and `precommits` fields cannot be parsed or verified
///
/// ### Processing
/// The block is added to the blockchain.
///
/// ### Generation
/// The message is sent as response to `BlockRequest`.
    struct BlockResponse {
        const TYPE = CONSENSUS;
        const ID = BLOCK_RESPONSE_MESSAGE_ID;
        const SIZE = 88;

        /// The sender's public key.
        field from:           &PublicKey          [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey          [32 => 64]
        /// Block header.
        field block:          blockchain::Block   [64 => 72]
        /// List of pre-commits.
        field precommits:     Vec<Precommit>      [72 => 80]
        /// List of the transactions.
        field transactions:   Vec<RawMessage>     [80 => 88]
    }
}

message! {
/// Request for the `Propose`.
///
/// ### Validation
/// The message is ignored if its `height` is not equal to the node's height.
///
/// ### Processing
/// `Propose` is sent as the response.
///
/// ### Generation
/// A node can send `ProposeRequest` during `Precommit` and `Prevote` handling.
    struct ProposeRequest {
        const TYPE = CONSENSUS;
        const ID = PROPOSE_REQUEST_MESSAGE_ID;
        const SIZE = 104;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The height to which the message is related.
        field height:         Height      [64 => 72]
        /// Hash of the `Propose`.
        field propose_hash:   &Hash       [72 => 104]
    }
}

message! {
/// Request for transactions by hash.
///
/// ### Processing
/// Requested transactions are sent to the recipient.
///
/// ### Generation
/// This message can be sent during `Propose`, `Prevote` and `Precommit` handling.
    struct TransactionsRequest {
        const TYPE = CONSENSUS;
        const ID = TRANSACTIONS_REQUEST_MESSAGE_ID;
        const SIZE = 72;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The list of the transaction hashes.
        field txs:            &[Hash]     [64 => 72]
    }
}

message! {
/// Request for pre-votes.
///
/// ### Validation
/// The message is ignored if its `height` is not equal to the node's height.
///
/// ### Processing
/// The requested pre-votes are sent to the recipient.
///
/// ### Generation
/// This message can be sent during `Prevote` and `Precommit` handling.
    struct PrevotesRequest {
        const TYPE = CONSENSUS;
        const ID = PREVOTES_REQUEST_MESSAGE_ID;
        const SIZE = 116;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The height to which the message is related.
        field height:         Height      [64 => 72]
        /// The round to which the message is related.
        field round:          Round       [72 => 76]
        /// Hash of the `Propose`.
        field propose_hash:   &Hash       [76 => 108]
        /// The list of validators that send pre-votes.
        field validators:     BitVec      [108 => 116]
    }
}

message! {
/// Request connected peers from a node.
///
/// ### Validation
/// Request is considered valid if the sender of the message on the network level corresponds to
/// the `from` field.
///
/// ### Processing
/// Peer `Connect` messages are sent to the recipient.
///
/// ### Generation
/// `PeersRequest` message is sent regularly with the timeout controlled by
/// `blockchain::ConsensusConfig::peers_timeout`.
    struct PeersRequest {
        const TYPE = CONSENSUS;
        const ID = PEERS_REQUEST_MESSAGE_ID;
        const SIZE = 64;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
    }
}

message! {
/// Request for the block with the given `height`.
///
/// ### Validation
/// The message is ignored if its `height` is bigger than the node's one.
///
/// ### Processing
/// `BlockResponse` message is sent as the response.
///
/// ### Generation
/// This message can be sent during `Status` processing.
    struct BlockRequest {
        const TYPE = CONSENSUS;
        const ID = BLOCK_REQUEST_MESSAGE_ID;
        const SIZE = 72;

        /// The sender's public key.
        field from:           &PublicKey  [00 => 32]
        /// Public key of the recipient.
        field to:             &PublicKey  [32 => 64]
        /// The height to which the message is related.
        field height:         Height      [64 => 72]
    }
}
<|endoftext|>


<commit_before>use std::ops::{Deref, DerefMut};

trait Graph {
	type R: PartialOrd<u32>;
	fn graph_fn(&self) -> Self::R ;
}
trait GraphMut: Graph {
	fn graph_mut(&mut self) -> &mut Self::R;
}
trait GraphMut2: Graph {
	fn graph_mut2(&mut self) -> &mut Self::R;
}

trait BaseConstraint: Sized {
	type Graph: Graph;
	fn get_graph(&self) -> &Self::Graph;
	fn constrain<G>(self) -> Result<G, ()>
		where G: Constraint<Base=Self>
	{
		G::constrain(self)
	}
}
trait BaseConstraintMut: BaseConstraint {
	fn get_graph_mut(&mut self) -> &mut Self::Graph;
}
trait Constraint: Sized {
	type Base:  BaseConstraint;
	type Constrained: Constraint<Base=Self::Base>;

	fn base_single(&self) -> &Self::Constrained;
	fn unconstrain_single(self) -> Self::Constrained;
	fn constrain_single(g: Self::Constrained) -> Result<Self, ()>;

	fn unconstrain(self) -> Self::Base{
		self.unconstrain_single().unconstrain()
	}
	fn constrain(g: Self::Base) -> Result<Self, ()>{
		Self::constrain_single(Self::Constrained::constrain(g)?)
	}
	fn base(&self) -> &<Self::Base as BaseConstraint>::Graph {
		self.base_single().base()
	}

}
trait ConstraintMut: Constraint<Base=<Self as ConstraintMut>::BaseMut, Constrained=<Self as ConstraintMut>::ConstrainedMut> {
	type BaseMut:  BaseConstraintMut;
	type ConstrainedMut: ConstraintMut<BaseMut=Self::BaseMut>;

	fn base_single_mut(&mut self) -> &mut Self::ConstrainedMut;
	fn base_mut(&mut self) -> &mut <Self::BaseMut as BaseConstraint>::Graph
	{
		self.base_single_mut().base_mut()
	}
}

impl<G: Graph, D: Deref<Target=G>> BaseConstraint for D {
	type Graph = G;
	fn get_graph(&self) -> &Self::Graph {&**self}
}
impl<G: GraphMut, D: DerefMut<Target=G>> BaseConstraintMut for D {
	fn get_graph_mut(&mut self) -> &mut Self::Graph {&mut **self}
}
impl<B: BaseConstraint> Constraint for B {
	type Base = Self;
	type Constrained = Self;

	fn base_single(&self) -> &Self::Constrained { &self }
	fn unconstrain_single(self) -> Self::Constrained { self }
	fn constrain_single(g: Self::Constrained) -> Result<Self, ()> { Ok(g)}

	fn unconstrain(self) -> Self::Base { self }
	fn constrain(g: Self::Base) -> Result<Self, ()>{ Ok(g) }
	fn base(&self) -> &<Self::Base as BaseConstraint>::Graph { self.get_graph() }
}
impl<B: BaseConstraintMut> ConstraintMut for B {
	type BaseMut = Self;
	type ConstrainedMut = Self;
	fn base_single_mut(&mut self) -> &mut Self::ConstrainedMut { self }
	fn base_mut(&mut self) -> &mut <Self::BaseMut as BaseConstraint>::Graph {
		self.get_graph_mut()
	}
}

struct BaseGraph(u32);
impl BaseGraph {
	fn base_graph_fn(&self) -> u32
	{
		self.0
	}
}
impl Graph for BaseGraph{
	type R = u32;
	fn graph_fn(&self) -> u32 {
		self.0
	}
}
impl GraphMut for BaseGraph {
	fn graph_mut(&mut self) -> &mut u32 { &mut self.0 }
}
impl GraphMut2 for BaseGraph {
	fn graph_mut2(&mut self) -> &mut u32 { &mut self.0 }
}
impl BaseConstraint for BaseGraph {
	type Graph = Self;
	fn get_graph(&self) -> &Self::Graph {self}
}
impl BaseConstraintMut for BaseGraph{
	fn get_graph_mut(&mut self) -> &mut Self::Graph {self}
}

struct Connected<C: Constraint>(C);
impl<C: Constraint> Constraint for Connected<C> {
	type Base = C::Base;
	type Constrained = C;

	fn base_single(&self) -> &Self::Constrained { &self.0 }
	fn unconstrain_single(self) -> Self::Constrained { self.0 }
	fn constrain_single(g: Self::Constrained) -> Result<Self, ()> {
		if g.base().graph_fn() < 32 {
			Ok(Self(g))
		} else {
			Err(())
		}

	}
}
impl<C: ConstraintMut> ConstraintMut for Connected<C> {
	type BaseMut = C::BaseMut;
	type ConstrainedMut = C;

	fn base_single_mut(&mut self) -> &mut Self::Constrained { &mut self.0 }
}

impl<C: Constraint> Connected<C> {
	fn connected_fn(&self) -> <<C::Base as BaseConstraint>::Graph as Graph>::R
	{
		self.base().graph_fn()
	}
}
impl<C: Constraint> Graph for Connected<C>{
	type R = <<C::Base as BaseConstraint>::Graph as Graph>::R;
	fn graph_fn(&self) -> Self::R {
		self.base().graph_fn()
	}
}
impl<C: ConstraintMut> GraphMut for Connected<C>
	where <C::Base as BaseConstraint>::Graph: GraphMut {
	fn graph_mut(&mut self) -> &mut <<C::Base as BaseConstraint>::Graph as Graph>::R
	{
		self.0.base_mut().graph_mut()
	}
}
impl<C: ConstraintMut> GraphMut2 for Connected<C>
	where <C::Base as BaseConstraint>::Graph: GraphMut2 {
	fn graph_mut2(&mut self) -> &mut <<C::Base as BaseConstraint>::Graph as Graph>::R { self.0.base_mut().graph_mut2() }
}

#[test]
fn constrainer_constraining_base(){
	type ConstrainedGraphRef<'a> =
	Connected<
		Connected<
			&'a BaseGraph
		>>;

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), 16);

	let c_ref = ConstrainedGraphRef::constrain(&g).unwrap();
	assert_eq!(c_ref.connected_fn(), 16);

	type ConstrainedGraphMut<'a> =
	Connected<
		Connected<
			&'a mut BaseGraph
		>>;

	let mut c_ref_mut = ConstrainedGraphMut::constrain(&mut g).unwrap();
	*c_ref_mut.graph_mut() = 30;
	*c_ref_mut.graph_mut2() = 30;
	assert_eq!(c_ref_mut.connected_fn(), 30);

	type ConstrainedGraph<'a> =
	Connected<
		Connected<
			BaseGraph
		>>;

	let c_owned = ConstrainedGraph::constrain(g).unwrap();
	assert_eq!(c_owned.connected_fn(), 30);

}

#[test]
fn inline_constrainer_constraining_base(){

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), 16);

	let c_ref = <Connected<Connected<&BaseGraph>>>::constrain(&g).unwrap();
	assert_eq!(c_ref.connected_fn(), 16);

	let mut c_ref_mut = <Connected<Connected<&mut BaseGraph>>>::constrain(&mut g).unwrap();
	*c_ref_mut.graph_mut() = 30;
	assert_eq!(c_ref_mut.connected_fn(), 30);

	let c_owned = <Connected<Connected<BaseGraph>>>::constrain(g).unwrap();
	assert_eq!(c_owned.connected_fn(), 30);

}

#[test]
fn base_constrains_self_by_constraint_inference(){
	type ConstrainedGraph<G> = Connected< Connected< Connected<G> > >;

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), 16);

	let c_ref: ConstrainedGraph<&BaseGraph> = (&g).constrain().unwrap();
	assert_eq!(c_ref.connected_fn(), 16);
	let c_ref_unc = c_ref.unconstrain_single();
	assert_eq!(c_ref_unc.connected_fn(), 16);

	let mut c_ref_mut: ConstrainedGraph<&mut BaseGraph> = (&mut g).constrain().unwrap();
	*c_ref_mut.graph_mut() = 30;
	assert_eq!(c_ref_mut.connected_fn(), 30);
	let mut c_ref_mut_unc = c_ref_mut.unconstrain_single();
	assert_eq!(c_ref_mut_unc.connected_fn(), 30);
	*c_ref_mut_unc.graph_mut() = 31;
	assert_eq!(c_ref_mut_unc.connected_fn(), 31);

	let c_owned: ConstrainedGraph<BaseGraph> = g.constrain().unwrap();
	assert_eq!(c_owned.connected_fn(), 31);
	let c_owned_unc = c_owned.unconstrain_single();
	assert_eq!(c_owned_unc.connected_fn(), 31);
	let g2 = c_owned_unc.unconstrain();
	assert_eq!(g2.base_graph_fn(), 31);
}

#[test]
fn base_constrains_self_by_inline_constraint_inference(){

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), 16);

	let c_ref: Connected<Connected<Connected<&BaseGraph>>> = (&g).constrain().unwrap();
	assert_eq!(c_ref.connected_fn(), 16);
	let c_ref_unc = c_ref.unconstrain_single();
	assert_eq!(c_ref_unc.connected_fn(), 16);

	let mut c_ref_mut: Connected<Connected<Connected<&mut BaseGraph>>> = (&mut g).constrain().unwrap();
	*c_ref_mut.graph_mut() = 30;
	assert_eq!(c_ref_mut.connected_fn(), 30);
	let mut c_ref_mut_unc = c_ref_mut.unconstrain_single();
	assert_eq!(c_ref_mut_unc.connected_fn(), 30);
	*c_ref_mut_unc.graph_mut() = 31;
	assert_eq!(c_ref_mut_unc.connected_fn(), 31);

	let c_owned: Connected<Connected<Connected<BaseGraph>>> = g.constrain().unwrap();
	assert_eq!(c_owned.connected_fn(), 31);
	let c_owned_unc = c_owned.unconstrain_single();
	assert_eq!(c_owned_unc.connected_fn(), 31);
	let g2 = c_owned_unc.unconstrain();
	assert_eq!(g2.base_graph_fn(), 31);
}<commit_msg>testing trait method now return reference for non-mutable version too.<commit_after>use std::ops::{Deref, DerefMut};

trait Graph {
	type R: PartialOrd<u32>;
	fn graph_fn(&self) -> &Self::R ;
}
trait GraphMut: Graph {
	fn graph_mut(&mut self) -> &mut Self::R;
}
trait GraphMut2: Graph {
	fn graph_mut2(&mut self) -> &mut Self::R;
}

trait BaseConstraint: Sized {
	type Graph: Graph;
	fn get_graph(&self) -> &Self::Graph;
	fn constrain<G>(self) -> Result<G, ()>
		where G: Constraint<Base=Self>
	{
		G::constrain(self)
	}
}
trait BaseConstraintMut: BaseConstraint {
	fn get_graph_mut(&mut self) -> &mut Self::Graph;
}
trait Constraint: Sized {
	type Base:  BaseConstraint;
	type Constrained: Constraint<Base=Self::Base>;

	fn base_single(&self) -> &Self::Constrained;
	fn unconstrain_single(self) -> Self::Constrained;
	fn constrain_single(g: Self::Constrained) -> Result<Self, ()>;

	fn unconstrain(self) -> Self::Base{
		self.unconstrain_single().unconstrain()
	}
	fn constrain(g: Self::Base) -> Result<Self, ()>{
		Self::constrain_single(Self::Constrained::constrain(g)?)
	}
	fn base(&self) -> &<Self::Base as BaseConstraint>::Graph {
		self.base_single().base()
	}

}
trait ConstraintMut: Constraint<Base=<Self as ConstraintMut>::BaseMut, Constrained=<Self as ConstraintMut>::ConstrainedMut> {
	type BaseMut:  BaseConstraintMut;
	type ConstrainedMut: ConstraintMut<BaseMut=Self::BaseMut>;

	fn base_single_mut(&mut self) -> &mut Self::ConstrainedMut;
	fn base_mut(&mut self) -> &mut <Self::BaseMut as BaseConstraint>::Graph
	{
		self.base_single_mut().base_mut()
	}
}

impl<G: Graph, D: Deref<Target=G>> BaseConstraint for D {
	type Graph = G;
	fn get_graph(&self) -> &Self::Graph {&**self}
}
impl<G: GraphMut, D: DerefMut<Target=G>> BaseConstraintMut for D {
	fn get_graph_mut(&mut self) -> &mut Self::Graph {&mut **self}
}
impl<B: BaseConstraint> Constraint for B {
	type Base = Self;
	type Constrained = Self;

	fn base_single(&self) -> &Self::Constrained { &self }
	fn unconstrain_single(self) -> Self::Constrained { self }
	fn constrain_single(g: Self::Constrained) -> Result<Self, ()> { Ok(g)}

	fn unconstrain(self) -> Self::Base { self }
	fn constrain(g: Self::Base) -> Result<Self, ()>{ Ok(g) }
	fn base(&self) -> &<Self::Base as BaseConstraint>::Graph { self.get_graph() }
}
impl<B: BaseConstraintMut> ConstraintMut for B {
	type BaseMut = Self;
	type ConstrainedMut = Self;
	fn base_single_mut(&mut self) -> &mut Self::ConstrainedMut { self }
	fn base_mut(&mut self) -> &mut <Self::BaseMut as BaseConstraint>::Graph {
		self.get_graph_mut()
	}
}

struct BaseGraph(u32);
impl BaseGraph {
	fn base_graph_fn(&self) -> u32
	{
		self.0
	}
}
impl Graph for BaseGraph{
	type R = u32;
	fn graph_fn(&self) -> &u32 {
		&self.0
	}
}
impl GraphMut for BaseGraph {
	fn graph_mut(&mut self) -> &mut u32 { &mut self.0 }
}
impl GraphMut2 for BaseGraph {
	fn graph_mut2(&mut self) -> &mut u32 { &mut self.0 }
}
impl BaseConstraint for BaseGraph {
	type Graph = Self;
	fn get_graph(&self) -> &Self::Graph {self}
}
impl BaseConstraintMut for BaseGraph{
	fn get_graph_mut(&mut self) -> &mut Self::Graph {self}
}

struct Connected<C: Constraint>(C);
impl<C: Constraint> Constraint for Connected<C> {
	type Base = C::Base;
	type Constrained = C;

	fn base_single(&self) -> &Self::Constrained { &self.0 }
	fn unconstrain_single(self) -> Self::Constrained { self.0 }
	fn constrain_single(g: Self::Constrained) -> Result<Self, ()> {
		if g.base().graph_fn() < &32 {
			Ok(Self(g))
		} else {
			Err(())
		}

	}
}
impl<C: ConstraintMut> ConstraintMut for Connected<C> {
	type BaseMut = C::BaseMut;
	type ConstrainedMut = C;

	fn base_single_mut(&mut self) -> &mut Self::Constrained { &mut self.0 }
}

impl<C: Constraint> Connected<C> {
	fn connected_fn(&self) -> &<<C::Base as BaseConstraint>::Graph as Graph>::R
	{
		self.base().graph_fn()
	}
}
impl<C: Constraint> Graph for Connected<C>{
	type R = <<C::Base as BaseConstraint>::Graph as Graph>::R;
	fn graph_fn(&self) -> &Self::R {
		self.base().graph_fn()
	}
}
impl<C: ConstraintMut> GraphMut for Connected<C>
	where <C::Base as BaseConstraint>::Graph: GraphMut {
	fn graph_mut(&mut self) -> &mut <<C::Base as BaseConstraint>::Graph as Graph>::R
	{
		self.0.base_mut().graph_mut()
	}
}
impl<C: ConstraintMut> GraphMut2 for Connected<C>
	where <C::Base as BaseConstraint>::Graph: GraphMut2 {
	fn graph_mut2(&mut self) -> &mut <<C::Base as BaseConstraint>::Graph as Graph>::R { self.0.base_mut().graph_mut2() }
}

#[test]
fn constrainer_constraining_base(){
	type ConstrainedGraphRef<'a> =
	Connected<
		Connected<
			&'a BaseGraph
		>>;

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), &16);

	let c_ref = ConstrainedGraphRef::constrain(&g).unwrap();
	assert_eq!(c_ref.connected_fn(), &16);

	type ConstrainedGraphMut<'a> =
	Connected<
		Connected<
			&'a mut BaseGraph
		>>;

	let mut c_ref_mut = ConstrainedGraphMut::constrain(&mut g).unwrap();
	*c_ref_mut.graph_mut() = 30;
	*c_ref_mut.graph_mut2() = 30;
	assert_eq!(c_ref_mut.connected_fn(), &30);

	type ConstrainedGraph<'a> =
	Connected<
		Connected<
			BaseGraph
		>>;

	let c_owned = ConstrainedGraph::constrain(g).unwrap();
	assert_eq!(c_owned.connected_fn(), &30);

}

#[test]
fn inline_constrainer_constraining_base(){

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), &16);

	let c_ref = <Connected<Connected<&BaseGraph>>>::constrain(&g).unwrap();
	assert_eq!(c_ref.connected_fn(), &16);

	let mut c_ref_mut = <Connected<Connected<&mut BaseGraph>>>::constrain(&mut g).unwrap();
	*c_ref_mut.graph_mut() = 30;
	assert_eq!(c_ref_mut.connected_fn(), &30);

	let c_owned = <Connected<Connected<BaseGraph>>>::constrain(g).unwrap();
	assert_eq!(c_owned.connected_fn(), &30);

}

#[test]
fn base_constrains_self_by_constraint_inference(){
	type ConstrainedGraph<G> = Connected< Connected< Connected<G> > >;

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), &16);

	let c_ref: ConstrainedGraph<&BaseGraph> = (&g).constrain().unwrap();
	assert_eq!(c_ref.connected_fn(), &16);
	let c_ref_unc = c_ref.unconstrain_single();
	assert_eq!(c_ref_unc.connected_fn(), &16);

	let mut c_ref_mut: ConstrainedGraph<&mut BaseGraph> = (&mut g).constrain().unwrap();
	*c_ref_mut.graph_mut() = 30;
	assert_eq!(c_ref_mut.connected_fn(), &30);
	let mut c_ref_mut_unc = c_ref_mut.unconstrain_single();
	assert_eq!(c_ref_mut_unc.connected_fn(), &30);
	*c_ref_mut_unc.graph_mut() = 31;
	assert_eq!(c_ref_mut_unc.connected_fn(), &31);

	let c_owned: ConstrainedGraph<BaseGraph> = g.constrain().unwrap();
	assert_eq!(c_owned.connected_fn(), &31);
	let c_owned_unc = c_owned.unconstrain_single();
	assert_eq!(c_owned_unc.connected_fn(), &31);
	let g2 = c_owned_unc.unconstrain();
	assert_eq!(g2.base_graph_fn(), 31);
}

#[test]
fn base_constrains_self_by_inline_constraint_inference(){

	let mut g = BaseGraph(16);
	assert_eq!(g.graph_fn(), &16);

	let c_ref: Connected<Connected<Connected<&BaseGraph>>> = (&g).constrain().unwrap();
	assert_eq!(c_ref.connected_fn(), &16);
	let c_ref_unc = c_ref.unconstrain_single();
	assert_eq!(c_ref_unc.connected_fn(), &16);

	let mut c_ref_mut: Connected<Connected<Connected<&mut BaseGraph>>> = (&mut g).constrain().unwrap();
	*c_ref_mut.graph_mut() = 30;
	assert_eq!(c_ref_mut.connected_fn(), &30);
	let mut c_ref_mut_unc = c_ref_mut.unconstrain_single();
	assert_eq!(c_ref_mut_unc.connected_fn(), &30);
	*c_ref_mut_unc.graph_mut() = 31;
	assert_eq!(c_ref_mut_unc.connected_fn(), &31);

	let c_owned: Connected<Connected<Connected<BaseGraph>>> = g.constrain().unwrap();
	assert_eq!(c_owned.connected_fn(), &31);
	let c_owned_unc = c_owned.unconstrain_single();
	assert_eq!(c_owned_unc.connected_fn(), &31);
	let g2 = c_owned_unc.unconstrain();
	assert_eq!(g2.base_graph_fn(), 31);
}<|endoftext|>


<commit_before>#[macro_use]
extern crate quick_error;
extern crate chrono;
use std::env;
use chrono::*;
use std::convert::AsRef;
use std::fs::{File, create_dir_all};
use std::io::{Write, Read, BufWriter};
use std::path::{Path};
use std::process::{Command, Output};
use std::collections::HashMap;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Io(err: std::io::Error) {
            from()
        }
        MissingEnvVar {
        }
        CommandFailed(err: Output){
            from()
        }
        CommandEmptyOutput(err: String){
            from()
        }
    }
}

pub enum EnvTidbit{
    Env(&'static str),
    EnvReq(&'static str),
    Cmd{key: &'static str, cmd: &'static str},
    CmdReq{key: &'static str, cmd: &'static str},
    CmdOrEnvReq{key: &'static str, cmd: &'static str},
    CmdOrEnv{key: &'static str, cmd: &'static str},
    FileContentsReq{key: &'static str, relative_to_build_rs: &'static str}
}

fn run(cmd: &str) -> std::result::Result<String,Error> {
    let mut args: Vec<&str> = cmd.split(" ").collect::<Vec<&str>>();
    if args.len() < 1 {
        panic!("");
    }
    let exe = args.remove(0);

    let output: Output = Command::new(exe)
        .args(&args)
        .output()?;
    if !output.status.success() {
        return Err(Error::CommandFailed(output));
    }
    let utf8_msg = format!("Command produced invalid UTF-8 output: {}", cmd);

    let str_out: &str = std::str::from_utf8(&output.stdout).expect(&utf8_msg);
    if str_out.split_whitespace().count() > 0 {
        Ok(str_out.to_owned())
    } else {
        Err(Error::from(str_out.to_owned()))
    }
}
fn fetch_env(key: &str, result_required: bool, empty_is_missing: bool) -> Option<String>{
    if result_required {
        match env::var(key) {
            Ok(ref v) if v.len() == 0 && empty_is_missing => {
                panic!("Required env var {} is present - but empty - in the build environment", key);
            },
            Ok(v) => Some(v),
            Err(e) => { panic!("Required env var {} missing in the build environment: {:?}", key, e); }
        }
    }else{
        env::var(key).ok().and_then(|v| if v.len() == 0 && empty_is_missing { None } else { Some(v) })
    }
}
fn command(key: &str, cmd: &str, result_required: bool, fallback_to_env: bool) -> Option<String>{

    //Panic only if non-UTF-8 output is sent
    let output = run(cmd);
    //Don't panic when fetching env var
    let env_val = match fallback_to_env { true => fetch_env(key, false, true), false => None};

    //Ensure consistency if both are present
    if let Ok(ref out_str) = output{
        if let Some(ref env_str) = env_val{
            if out_str != env_str{
                if out_str.trim() != env_str.trim(){
                    panic!("Inconsistent values for {} and {}.\nCommand output: {}\nEnv var: {}", key, cmd, out_str, env_str);
                }
            }
        }
    }

    if result_required && output.is_err() && env_val.is_none() {
        if fallback_to_env {
            panic!("Failed to acquire {} (required for build). \nCommand {} resulted in {:?}, and ENV var {} was missing or empty.",
                   key, cmd, output, key);
        } else {
            panic!("Failed to acquire {} (required for build). \nCommand {} resulted in {:?}. ENV var not consulted.",
                   key, cmd, output);
        }
    }else {
        output.ok().or(env_val)
    }
}
//
//fn get_repo_root() -> PathBuf{
//    let build_rs_path = file!();
//    Path::new(&build_rs_path).parent().expect("Rust must be stripping parent directory info from file! macro. This breaks path stuff in build.rs.").to_owned()
//}

fn collect_info(shopping_list: Vec<EnvTidbit>) -> HashMap<String, Option<String>>{

    let mut info = HashMap::new();
    for from in shopping_list {
        let (k,v) = match from {
            EnvTidbit::Env(key) => (key, fetch_env(key, false, true)),
            EnvTidbit::EnvReq(key) => (key, fetch_env(key, true, true)),
            EnvTidbit::FileContentsReq{key, relative_to_build_rs} => {

                let io_error_expect = format!("Failed to read file {:?}. This file is required to be embedded in output binaries.", relative_to_build_rs);
                let mut file = File::open(relative_to_build_rs).expect(&io_error_expect);
                let mut contents = String::new();
                file.read_to_string( &mut contents).expect(&io_error_expect);
                (key, Some(contents))
            },
            EnvTidbit::Cmd{key, cmd} => (key, command(key, cmd, false, false)),
            EnvTidbit::CmdReq{key, cmd} => (key, command(key, cmd, true, false)),
            EnvTidbit::CmdOrEnvReq{key, cmd} => (key, command(key, cmd, true, true)),
            EnvTidbit::CmdOrEnv{key, cmd} => (key, command(key, cmd, false, true)),
        };
        info.insert(k.to_owned(),v);
    }
    info
}
fn what_to_collect() -> Vec<EnvTidbit>{
    let mut c = Vec::new();
    c.push(EnvTidbit::CmdOrEnvReq {key: "GIT_COMMIT", cmd: "git rev-parse HEAD"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_COMMIT_SHORT", cmd: "git rev-parse --short HEAD"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_DESCRIBE_ALWAYS", cmd: "git describe --always --tags"});
    c.push(EnvTidbit::CmdOrEnvReq{key: "GIT_DESCRIBE_ALWAYS_LONG", cmd: "git describe --always --tags --long"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_DESCRIBE_AAL", cmd: "git describe --always --all --long"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_OPTIONAL_TAG", cmd: "git describe --exact-match --tags"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_OPTIONAL_BRANCH", cmd: "git rev-parse --abbrev-ref HEAD"});
    static ENV_VARS: [&'static str;11] = ["ESTIMATED_ARTIFACT_URL","ESTIMATED_DOCS_URL","CI_SEQUENTIAL_BUILD_NUMBER","CI_BUILD_URL","CI_JOB_URL","CI_JOB_TITLE","CI_STRING",
        "CI_PULL_REQUEST_INFO", "CI_TAG", "CI_RELATED_BRANCH", "CI"
    ];
    for name in ENV_VARS.iter(){
        c.push(EnvTidbit::Env(name));
    }
    c.push(EnvTidbit::Cmd{key: "GIT_STATUS", cmd: "git status"});
    c.push(EnvTidbit::Cmd{key: "CONAN_VERSION", cmd: "conan -V"});
    c.push(EnvTidbit::Cmd{key: "DEFAULT_GCC_VERSION", cmd: "gcc -v"});
    c.push(EnvTidbit::Cmd{key: "DEFAULT_CLANG_VERSION", cmd: "clang --version"});
    c.push(EnvTidbit::CmdReq{key: "DEFAULT_RUSTC_VERSION", cmd: "rustc -V"});
    c.push(EnvTidbit::CmdReq{key: "DEFAULT_CARGO_VERSION", cmd: "cargo -V"});
    c.push(EnvTidbit::FileContentsReq{key: "conaninfo.txt", relative_to_build_rs: "../imageflow_core/conaninfo.txt"});
    c
}

pub fn write_file(name: &str, file_contents: String) -> std::result::Result<(), Error> {
    let path = env::var_os("OUT_DIR").ok_or(Error::MissingEnvVar)?;
    let path : &Path = path.as_ref();
    create_dir_all(path)?;

    let path = path.join(name);
    let mut file = BufWriter::new(File::create(&path)?);
    write!(file, "{}", file_contents)?;
    Ok(())
}

fn main() {
    let todo = what_to_collect();
    let utcnow_val =UTC::now();

    let mut results = collect_info(todo);
    results.insert("GENERATED_DATETIME_UTC".to_owned(), Some(utcnow_val.to_rfc3339()));
    results.insert("GENERATED_DATE_UTC".to_owned(), Some(utcnow_val.format("%Y-%m-%d").to_string()));

    let mut contents = String::new();
    contents += "use std::collections::HashMap;\n";
   // contents += "#[macro_use]\nextern crate lazy_static;\n";
    contents += "fn get_build_env_info() -> HashMap<&'static str, Option<&'static str>> {\n";
    contents += "  let mut i = HashMap::new();\n";
    for (k, v) in &results{
        let line = format!("  i.insert({:?}, {:?});\n", k,v);
        contents += &line;
    }
    contents += "  i\n}\nlazy_static! {\n  pub static ref BUILD_ENV_INFO: HashMap<&'static str, Option<&'static str>> = ";
    contents += "get_build_env_info();\n}\n";

    //These vars are required for all builds
    for name in ["GIT_COMMIT", "GIT_DESCRIBE_ALWAYS"].iter(){
        let value = results.get::<str>(name).unwrap().to_owned().unwrap();
        let line = format!("pub static {}: &'static str = {:?};\n", name,&value);
        contents += &line;
    }

    let ci_value = results.get("CI").unwrap().to_owned().unwrap_or("false".to_owned()).to_lowercase();
    let line = format!("pub static BUILT_ON_CI: bool = {};\n", ci_value);
    contents += &line;

    let line = format!("pub static GENERATED_DATETIME_UTC: &'static str = {:?};\n", utcnow_val.to_rfc3339());
    contents += &line;
    let line = format!("pub static GENERATED_DATE_UTC: &'static str = {:?};\n", utcnow_val.format("%Y-%m-%d").to_string());
    contents += &line;

    let _ = write_file("build_env_info.rs", contents ).expect("Saving git version");
}<commit_msg>Add glibc/uname/winver<commit_after>#[macro_use]
extern crate quick_error;
extern crate chrono;
use std::env;
use chrono::*;
use std::convert::AsRef;
use std::fs::{File, create_dir_all};
use std::io::{Write, Read, BufWriter};
use std::path::{Path};
use std::process::{Command, Output};
use std::collections::HashMap;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Io(err: std::io::Error) {
            from()
        }
        MissingEnvVar {
        }
        CommandFailed(err: Output){
            from()
        }
        CommandEmptyOutput(err: String){
            from()
        }
    }
}

pub enum EnvTidbit{
    Env(&'static str),
    EnvReq(&'static str),
    Cmd{key: &'static str, cmd: &'static str},
    CmdReq{key: &'static str, cmd: &'static str},
    CmdOrEnvReq{key: &'static str, cmd: &'static str},
    CmdOrEnv{key: &'static str, cmd: &'static str},
    FileContentsReq{key: &'static str, relative_to_build_rs: &'static str}
}

fn run(cmd: &str) -> std::result::Result<String,Error> {
    let mut args: Vec<&str> = cmd.split(" ").collect::<Vec<&str>>();
    if args.len() < 1 {
        panic!("");
    }
    let exe = args.remove(0);

    let output: Output = Command::new(exe)
        .args(&args)
        .output()?;
    if !output.status.success() {
        return Err(Error::CommandFailed(output));
    }
    let utf8_msg = format!("Command produced invalid UTF-8 output: {}", cmd);

    let str_out: &str = std::str::from_utf8(&output.stdout).expect(&utf8_msg);
    if str_out.split_whitespace().count() > 0 {
        Ok(str_out.to_owned())
    } else {
        Err(Error::from(str_out.to_owned()))
    }
}
fn fetch_env(key: &str, result_required: bool, empty_is_missing: bool) -> Option<String>{
    if result_required {
        match env::var(key) {
            Ok(ref v) if v.len() == 0 && empty_is_missing => {
                panic!("Required env var {} is present - but empty - in the build environment", key);
            },
            Ok(v) => Some(v),
            Err(e) => { panic!("Required env var {} missing in the build environment: {:?}", key, e); }
        }
    }else{
        env::var(key).ok().and_then(|v| if v.len() == 0 && empty_is_missing { None } else { Some(v) })
    }
}
fn command(key: &str, cmd: &str, result_required: bool, fallback_to_env: bool) -> Option<String>{

    //Panic only if non-UTF-8 output is sent
    let output = run(cmd);
    //Don't panic when fetching env var
    let env_val = match fallback_to_env { true => fetch_env(key, false, true), false => None};

    //Ensure consistency if both are present
    if let Ok(ref out_str) = output{
        if let Some(ref env_str) = env_val{
            if out_str != env_str{
                if out_str.trim() != env_str.trim(){
                    panic!("Inconsistent values for {} and {}.\nCommand output: {}\nEnv var: {}", key, cmd, out_str, env_str);
                }
            }
        }
    }

    if result_required && output.is_err() && env_val.is_none() {
        if fallback_to_env {
            panic!("Failed to acquire {} (required for build). \nCommand {} resulted in {:?}, and ENV var {} was missing or empty.",
                   key, cmd, output, key);
        } else {
            panic!("Failed to acquire {} (required for build). \nCommand {} resulted in {:?}. ENV var not consulted.",
                   key, cmd, output);
        }
    }else {
        output.ok().or(env_val)
    }
}
//
//fn get_repo_root() -> PathBuf{
//    let build_rs_path = file!();
//    Path::new(&build_rs_path).parent().expect("Rust must be stripping parent directory info from file! macro. This breaks path stuff in build.rs.").to_owned()
//}

fn collect_info(shopping_list: Vec<EnvTidbit>) -> HashMap<String, Option<String>>{

    let mut info = HashMap::new();
    for from in shopping_list {
        let (k,v) = match from {
            EnvTidbit::Env(key) => (key, fetch_env(key, false, true)),
            EnvTidbit::EnvReq(key) => (key, fetch_env(key, true, true)),
            EnvTidbit::FileContentsReq{key, relative_to_build_rs} => {

                let io_error_expect = format!("Failed to read file {:?}. This file is required to be embedded in output binaries.", relative_to_build_rs);
                let mut file = File::open(relative_to_build_rs).expect(&io_error_expect);
                let mut contents = String::new();
                file.read_to_string( &mut contents).expect(&io_error_expect);
                (key, Some(contents))
            },
            EnvTidbit::Cmd{key, cmd} => (key, command(key, cmd, false, false)),
            EnvTidbit::CmdReq{key, cmd} => (key, command(key, cmd, true, false)),
            EnvTidbit::CmdOrEnvReq{key, cmd} => (key, command(key, cmd, true, true)),
            EnvTidbit::CmdOrEnv{key, cmd} => (key, command(key, cmd, false, true)),
        };
        info.insert(k.to_owned(),v);
    }
    info
}
fn what_to_collect() -> Vec<EnvTidbit>{
    let mut c = Vec::new();
    c.push(EnvTidbit::CmdOrEnvReq {key: "GIT_COMMIT", cmd: "git rev-parse HEAD"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_COMMIT_SHORT", cmd: "git rev-parse --short HEAD"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_DESCRIBE_ALWAYS", cmd: "git describe --always --tags"});
    c.push(EnvTidbit::CmdOrEnvReq{key: "GIT_DESCRIBE_ALWAYS_LONG", cmd: "git describe --always --tags --long"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_DESCRIBE_AAL", cmd: "git describe --always --all --long"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_OPTIONAL_TAG", cmd: "git describe --exact-match --tags"});
    c.push(EnvTidbit::CmdOrEnv{key: "GIT_OPTIONAL_BRANCH", cmd: "git rev-parse --abbrev-ref HEAD"});
    static ENV_VARS: [&'static str;11] = ["ESTIMATED_ARTIFACT_URL","ESTIMATED_DOCS_URL","CI_SEQUENTIAL_BUILD_NUMBER","CI_BUILD_URL","CI_JOB_URL","CI_JOB_TITLE","CI_STRING",
        "CI_PULL_REQUEST_INFO", "CI_TAG", "CI_RELATED_BRANCH", "CI"
    ];
    for name in ENV_VARS.iter(){
        c.push(EnvTidbit::Env(name));
    }
    c.push(EnvTidbit::Cmd{key: "GIT_STATUS", cmd: "git status"});
    c.push(EnvTidbit::Cmd{key: "CONAN_VERSION", cmd: "conan -V"});
    c.push(EnvTidbit::Cmd{key: "GLIBC_VERSION", cmd: "ldd --version"});
    c.push(EnvTidbit::Cmd{key: "UNAME", cmd: "uname -av"});
    c.push(EnvTidbit::Cmd{key: "WINVER", cmd: "winver"});
    c.push(EnvTidbit::Cmd{key: "DEFAULT_GCC_VERSION", cmd: "gcc -v"});
    c.push(EnvTidbit::Cmd{key: "DEFAULT_CLANG_VERSION", cmd: "clang --version"});
    c.push(EnvTidbit::CmdReq{key: "DEFAULT_RUSTC_VERSION", cmd: "rustc -V"});
    c.push(EnvTidbit::CmdReq{key: "DEFAULT_CARGO_VERSION", cmd: "cargo -V"});
    c.push(EnvTidbit::FileContentsReq{key: "conaninfo.txt", relative_to_build_rs: "../imageflow_core/conaninfo.txt"});
    c
}

pub fn write_file(name: &str, file_contents: String) -> std::result::Result<(), Error> {
    let path = env::var_os("OUT_DIR").ok_or(Error::MissingEnvVar)?;
    let path : &Path = path.as_ref();
    create_dir_all(path)?;

    let path = path.join(name);
    let mut file = BufWriter::new(File::create(&path)?);
    write!(file, "{}", file_contents)?;
    Ok(())
}

fn main() {
    let todo = what_to_collect();
    let utcnow_val =UTC::now();

    let mut results = collect_info(todo);
    results.insert("GENERATED_DATETIME_UTC".to_owned(), Some(utcnow_val.to_rfc3339()));
    results.insert("GENERATED_DATE_UTC".to_owned(), Some(utcnow_val.format("%Y-%m-%d").to_string()));

    let mut contents = String::new();
    contents += "use std::collections::HashMap;\n";
   // contents += "#[macro_use]\nextern crate lazy_static;\n";
    contents += "fn get_build_env_info() -> HashMap<&'static str, Option<&'static str>> {\n";
    contents += "  let mut i = HashMap::new();\n";
    for (k, v) in &results{
        let line = format!("  i.insert({:?}, {:?});\n", k,v);
        contents += &line;
    }
    contents += "  i\n}\nlazy_static! {\n  pub static ref BUILD_ENV_INFO: HashMap<&'static str, Option<&'static str>> = ";
    contents += "get_build_env_info();\n}\n";

    //These vars are required for all builds
    for name in ["GIT_COMMIT", "GIT_DESCRIBE_ALWAYS"].iter(){
        let value = results.get::<str>(name).unwrap().to_owned().unwrap();
        let line = format!("pub static {}: &'static str = {:?};\n", name,&value);
        contents += &line;
    }

    let ci_value = results.get("CI").unwrap().to_owned().unwrap_or("false".to_owned()).to_lowercase();
    let line = format!("pub static BUILT_ON_CI: bool = {};\n", ci_value);
    contents += &line;

    let line = format!("pub static GENERATED_DATETIME_UTC: &'static str = {:?};\n", utcnow_val.to_rfc3339());
    contents += &line;
    let line = format!("pub static GENERATED_DATE_UTC: &'static str = {:?};\n", utcnow_val.format("%Y-%m-%d").to_string());
    contents += &line;

    let _ = write_file("build_env_info.rs", contents ).expect("Saving git version");
}<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::marker::PhantomData;
use std::ops::{Index, Range};

use objc::{Id, Owned, Ownership, Shared, ShareId};
use objc::runtime::{Class, Object};

use {INSCopying, INSFastEnumeration, INSMutableCopying, INSObject, NSEnumerator};

#[repr(isize)]
#[derive(Copy)]
pub enum NSComparisonResult {
    Ascending  = -1,
    Same       = 0,
    Descending = 1,
}

impl NSComparisonResult {
    pub fn from_ordering(order: Ordering) -> NSComparisonResult {
        match order {
            Ordering::Less => NSComparisonResult::Ascending,
            Ordering::Equal => NSComparisonResult::Same,
            Ordering::Greater => NSComparisonResult::Descending,
        }
    }

    pub fn as_ordering(&self) -> Ordering {
        match *self {
            NSComparisonResult::Ascending => Ordering::Less,
            NSComparisonResult::Same => Ordering::Equal,
            NSComparisonResult::Descending => Ordering::Greater,
        }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct NSRange {
    pub location: usize,
    pub length: usize,
}

impl NSRange {
    pub fn from_range(range: Range<usize>) -> NSRange {
        assert!(range.end >= range.start);
        NSRange { location: range.start, length: range.end - range.start }
    }

    pub fn as_range(&self) -> Range<usize> {
        Range { start: self.location, end: self.location + self.length }
    }
}

pub trait INSArray : INSObject {
    type Item: INSObject;
    type Own: Ownership;

    fn count(&self) -> usize {
        unsafe {
            msg_send![self, count]
        }
    }

    fn object_at(&self, index: usize) -> &Self::Item {
        unsafe {
            let obj: *const Self::Item = msg_send![self, objectAtIndex:index];
            &*obj
        }
    }

    fn first_object(&self) -> Option<&Self::Item> {
        unsafe {
            let obj: *const Self::Item = msg_send![self, firstObject];
            obj.as_ref()
        }
    }

    fn last_object(&self) -> Option<&Self::Item> {
        unsafe {
            let obj: *const Self::Item = msg_send![self, lastObject];
            obj.as_ref()
        }
    }

    fn object_enumerator(&self) -> NSEnumerator<Self::Item> {
        unsafe {
            let result: *mut Object = msg_send![self, objectEnumerator];
            NSEnumerator::from_ptr(result)
        }
    }

    unsafe fn from_refs(refs: &[&Self::Item]) -> Id<Self> {
        let cls = Self::class();
        let obj: *mut Self = msg_send![cls, alloc];
        let obj: *mut Self = msg_send![obj, initWithObjects:refs.as_ptr()
                                                      count:refs.len()];
        Id::from_retained_ptr(obj)
    }

    fn from_vec(vec: Vec<Id<Self::Item, Self::Own>>) -> Id<Self> {
        let refs: Vec<&Self::Item> = vec.iter().map(|obj| &**obj).collect();
        unsafe {
            INSArray::from_refs(&refs)
        }
    }

    fn objects_in_range(&self, range: Range<usize>) -> Vec<&Self::Item> {
        let range = NSRange::from_range(range);
        let mut vec = Vec::with_capacity(range.length);
        unsafe {
            let _: () = msg_send![self, getObjects:vec.as_ptr() range:range];
            vec.set_len(range.length);
        }
        vec
    }

    fn to_vec(&self) -> Vec<&Self::Item> {
        self.objects_in_range(0..self.count())
    }

    fn into_vec(array: Id<Self>) -> Vec<Id<Self::Item, Self::Own>> {
        array.to_vec().into_iter().map(|obj| unsafe {
            Id::from_ptr(obj as *const Self::Item as *mut Self::Item)
        }).collect()
    }
}

pub trait INSOwnedArray : INSArray<Own=Owned> {
    fn mut_object_at(&mut self, index: usize) -> &mut Self::Item {
        unsafe {
            let result: *mut Self::Item = msg_send![self, objectAtIndex:index];
            &mut *result
        }
    }
}

pub trait INSSharedArray : INSArray<Own=Shared> {
    fn shared_object_at(&self, index: usize) -> ShareId<Self::Item> {
        let obj = self.object_at(index);
        unsafe {
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        }
    }

    fn from_slice(slice: &[ShareId<Self::Item>]) -> Id<Self> {
        let refs: Vec<&Self::Item> = slice.iter().map(|obj| &**obj).collect();
        unsafe {
            INSArray::from_refs(&refs)
        }
    }

    fn to_shared_vec(&self) -> Vec<ShareId<Self::Item>> {
        self.to_vec().into_iter().map(|obj| unsafe {
            Id::from_ptr(obj as *const Self::Item as *mut Self::Item)
        }).collect()
    }
}

pub struct NSArray<T, O = Owned> {
    item: PhantomData<Id<T, O>>,
}

object_impl!(NSArray<T, O>);

impl<T, O> INSObject for NSArray<T, O> where T: INSObject, O: Ownership {
    fn class() -> &'static Class {
        Class::get("NSArray").unwrap()
    }
}

impl<T, O> INSArray for NSArray<T, O> where T: INSObject, O: Ownership {
    type Item = T;
    type Own = O;
}

impl<T> INSOwnedArray for NSArray<T, Owned> where T: INSObject { }

impl<T> INSSharedArray for NSArray<T, Shared> where T: INSObject { }

impl<T> INSCopying for NSArray<T, Shared> where T: INSObject {
    type Output = NSSharedArray<T>;
}

impl<T> INSMutableCopying for NSArray<T, Shared> where T: INSObject {
    type Output = NSMutableSharedArray<T>;
}

impl<T, O> INSFastEnumeration for NSArray<T, O>
        where T: INSObject, O: Ownership {
    type Item = T;
}

impl<T, O> Index<usize> for NSArray<T, O> where T: INSObject, O: Ownership {
    type Output = T;

    fn index(&self, index: &usize) -> &T {
        self.object_at(*index)
    }
}

pub type NSSharedArray<T> = NSArray<T, Shared>;

pub trait INSMutableArray : INSArray {
    fn add_object(&mut self, obj: Id<Self::Item, Self::Own>) {
        unsafe {
            let _: () = msg_send![self, addObject:obj];
        }
    }

    fn insert_object_at(&mut self, index: usize, obj: Id<Self::Item, Self::Own>) {
        unsafe {
            let _: () = msg_send![self, insertObject:obj atIndex:index];
        }
    }

    fn replace_object_at(&mut self, index: usize, obj: Id<Self::Item, Self::Own>) ->
            Id<Self::Item, Self::Own> {
        let old_obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        };
        unsafe {
            let _: () = msg_send![self, replaceObjectAtIndex:index
                                                  withObject:obj];
        }
        old_obj
    }

    fn remove_object_at(&mut self, index: usize) -> Id<Self::Item, Self::Own> {
        let obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        };
        unsafe {
            let _: () = msg_send![self, removeObjectAtIndex:index];
        }
        obj
    }

    fn remove_last_object(&mut self) -> Id<Self::Item, Self::Own> {
        let obj = self.last_object().map(|obj| unsafe {
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        });
        unsafe {
            let _: () = msg_send![self, removeLastObject];
        }
        // removeLastObject would have failed if the array is empty,
        // so we know this won't be None
        obj.unwrap()
    }

    fn remove_all_objects(&mut self) {
        unsafe {
            let _: () = msg_send![self, removeAllObjects];
        }
    }

    fn sort_by<F>(&mut self, compare: F)
            where F: FnMut(&Self::Item, &Self::Item) -> Ordering {
        extern fn compare_with_closure<T, F>(obj1: &T, obj2: &T,
                compare: &mut F) -> NSComparisonResult
                where F: FnMut(&T, &T) -> Ordering {
            NSComparisonResult::from_ordering((*compare)(obj1, obj2))
        }

        let mut closure = compare;
        unsafe {
            let _: () = msg_send![self, sortUsingFunction:compare_with_closure::<Self::Item, F>
                                                  context:&mut closure];
        }
    }
}

pub struct NSMutableArray<T, O = Owned> {
    item: PhantomData<Id<T, O>>,
}

object_impl!(NSMutableArray<T, O>);

impl<T, O> INSObject for NSMutableArray<T, O> where T: INSObject, O: Ownership {
    fn class() -> &'static Class {
        Class::get("NSMutableArray").unwrap()
    }
}

impl<T, O> INSArray for NSMutableArray<T, O> where T: INSObject, O: Ownership {
    type Item = T;
    type Own = O;
}

impl<T> INSOwnedArray for NSMutableArray<T, Owned> where T: INSObject { }

impl<T> INSSharedArray for NSMutableArray<T, Shared> where T: INSObject { }

impl<T, O> INSMutableArray for NSMutableArray<T, O>
        where T: INSObject, O: Ownership { }

impl<T> INSCopying for NSMutableArray<T, Shared> where T: INSObject {
    type Output = NSSharedArray<T>;
}

impl<T> INSMutableCopying for NSMutableArray<T, Shared> where T: INSObject {
    type Output = NSMutableSharedArray<T>;
}

impl<T, O> INSFastEnumeration for NSMutableArray<T, O>
        where T: INSObject, O: Ownership {
    type Item = T;
}

impl<T, O> Index<usize> for NSMutableArray<T, O>
        where T: INSObject, O: Ownership {
    type Output = T;

    fn index(&self, index: &usize) -> &T {
        self.object_at(*index)
    }
}

pub type NSMutableSharedArray<T> = NSMutableArray<T, Shared>;

#[cfg(test)]
mod tests {
    use objc::{Id};
    use {INSObject, INSString, NSObject, NSString};
    use super::{INSArray, INSMutableArray, NSArray, NSMutableArray};

    fn sample_array(len: usize) -> Id<NSArray<NSObject>> {
        let mut vec = Vec::with_capacity(len);
        for _ in 0..len {
            vec.push(NSObject::new());
        }
        NSArray::from_vec(vec)
    }

    #[test]
    fn test_count() {
        let empty_array = NSArray::<NSObject>::new();
        assert!(empty_array.count() == 0);

        let array = sample_array(4);
        assert!(array.count() == 4);
    }

    #[test]
    fn test_object_at() {
        let array = sample_array(4);
        assert!(array.object_at(0) != array.object_at(3));
        assert!(array.first_object().unwrap() == array.object_at(0));
        assert!(array.last_object().unwrap() == array.object_at(3));

        let empty_array: Id<NSArray<NSObject>> = INSObject::new();
        assert!(empty_array.first_object().is_none());
        assert!(empty_array.last_object().is_none());
    }

    #[test]
    fn test_object_enumerator() {
        let array = sample_array(4);

        assert!(array.object_enumerator().count() == 4);
        assert!(array.object_enumerator()
                     .enumerate()
                     .all(|(i, obj)| obj == array.object_at(i)));
    }

    #[test]
    fn test_objects_in_range() {
        let array = sample_array(4);

        let middle_objs = array.objects_in_range(1..3);
        assert!(middle_objs.len() == 2);
        assert!(middle_objs[0] == array.object_at(1));
        assert!(middle_objs[1] == array.object_at(2));

        let empty_objs = array.objects_in_range(1..1);
        assert!(empty_objs.len() == 0);

        let all_objs = array.objects_in_range(0..4);
        assert!(all_objs.len() == 4);
    }

    #[test]
    fn test_into_vec() {
        let array = sample_array(4);

        let vec = INSArray::into_vec(array);
        assert!(vec.len() == 4);
    }

    #[test]
    fn test_add_object() {
        let mut array = NSMutableArray::new();
        let obj = NSObject::new();
        array.add_object(obj);

        assert!(array.count() == 1);
        assert!(array.object_at(0) == array.object_at(0));

        let obj = NSObject::new();
        array.insert_object_at(0, obj);
        assert!(array.count() == 2);
    }

    #[test]
    fn test_replace_object() {
        let mut array = NSMutableArray::new();
        let obj = NSObject::new();
        array.add_object(obj);

        let obj = NSObject::new();
        let old_obj = array.replace_object_at(0, obj);
        assert!(&*old_obj != array.object_at(0));
    }

    #[test]
    fn test_remove_object() {
        let mut array = NSMutableArray::new();
        for _ in 0..4 {
            array.add_object(NSObject::new());
        }

        array.remove_object_at(1);
        assert!(array.count() == 3);

        array.remove_last_object();
        assert!(array.count() == 2);

        array.remove_all_objects();
        assert!(array.count() == 0);
    }

    #[test]
    fn test_sort() {
        let strings = vec![
            NSString::from_str("hello"),
            NSString::from_str("hi"),
        ];
        let mut strings = NSMutableArray::from_vec(strings);

        strings.sort_by(|s1, s2| s1.as_str().len().cmp(&s2.as_str().len()));
        assert!(strings[0].as_str() == "hi");
        assert!(strings[1].as_str() == "hello");
    }
}
<commit_msg>Fixed mutable array methods broken from Id size change.<commit_after>use std::cmp::Ordering;
use std::marker::PhantomData;
use std::ops::{Index, Range};

use objc::{Id, Owned, Ownership, Shared, ShareId};
use objc::runtime::{Class, Object};

use {INSCopying, INSFastEnumeration, INSMutableCopying, INSObject, NSEnumerator};

#[repr(isize)]
#[derive(Copy)]
pub enum NSComparisonResult {
    Ascending  = -1,
    Same       = 0,
    Descending = 1,
}

impl NSComparisonResult {
    pub fn from_ordering(order: Ordering) -> NSComparisonResult {
        match order {
            Ordering::Less => NSComparisonResult::Ascending,
            Ordering::Equal => NSComparisonResult::Same,
            Ordering::Greater => NSComparisonResult::Descending,
        }
    }

    pub fn as_ordering(&self) -> Ordering {
        match *self {
            NSComparisonResult::Ascending => Ordering::Less,
            NSComparisonResult::Same => Ordering::Equal,
            NSComparisonResult::Descending => Ordering::Greater,
        }
    }
}

#[repr(C)]
#[derive(Copy)]
pub struct NSRange {
    pub location: usize,
    pub length: usize,
}

impl NSRange {
    pub fn from_range(range: Range<usize>) -> NSRange {
        assert!(range.end >= range.start);
        NSRange { location: range.start, length: range.end - range.start }
    }

    pub fn as_range(&self) -> Range<usize> {
        Range { start: self.location, end: self.location + self.length }
    }
}

pub trait INSArray : INSObject {
    type Item: INSObject;
    type Own: Ownership;

    fn count(&self) -> usize {
        unsafe {
            msg_send![self, count]
        }
    }

    fn object_at(&self, index: usize) -> &Self::Item {
        unsafe {
            let obj: *const Self::Item = msg_send![self, objectAtIndex:index];
            &*obj
        }
    }

    fn first_object(&self) -> Option<&Self::Item> {
        unsafe {
            let obj: *const Self::Item = msg_send![self, firstObject];
            obj.as_ref()
        }
    }

    fn last_object(&self) -> Option<&Self::Item> {
        unsafe {
            let obj: *const Self::Item = msg_send![self, lastObject];
            obj.as_ref()
        }
    }

    fn object_enumerator(&self) -> NSEnumerator<Self::Item> {
        unsafe {
            let result: *mut Object = msg_send![self, objectEnumerator];
            NSEnumerator::from_ptr(result)
        }
    }

    unsafe fn from_refs(refs: &[&Self::Item]) -> Id<Self> {
        let cls = Self::class();
        let obj: *mut Self = msg_send![cls, alloc];
        let obj: *mut Self = msg_send![obj, initWithObjects:refs.as_ptr()
                                                      count:refs.len()];
        Id::from_retained_ptr(obj)
    }

    fn from_vec(vec: Vec<Id<Self::Item, Self::Own>>) -> Id<Self> {
        let refs: Vec<&Self::Item> = vec.iter().map(|obj| &**obj).collect();
        unsafe {
            INSArray::from_refs(&refs)
        }
    }

    fn objects_in_range(&self, range: Range<usize>) -> Vec<&Self::Item> {
        let range = NSRange::from_range(range);
        let mut vec = Vec::with_capacity(range.length);
        unsafe {
            let _: () = msg_send![self, getObjects:vec.as_ptr() range:range];
            vec.set_len(range.length);
        }
        vec
    }

    fn to_vec(&self) -> Vec<&Self::Item> {
        self.objects_in_range(0..self.count())
    }

    fn into_vec(array: Id<Self>) -> Vec<Id<Self::Item, Self::Own>> {
        array.to_vec().into_iter().map(|obj| unsafe {
            Id::from_ptr(obj as *const Self::Item as *mut Self::Item)
        }).collect()
    }
}

pub trait INSOwnedArray : INSArray<Own=Owned> {
    fn mut_object_at(&mut self, index: usize) -> &mut Self::Item {
        unsafe {
            let result: *mut Self::Item = msg_send![self, objectAtIndex:index];
            &mut *result
        }
    }
}

pub trait INSSharedArray : INSArray<Own=Shared> {
    fn shared_object_at(&self, index: usize) -> ShareId<Self::Item> {
        let obj = self.object_at(index);
        unsafe {
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        }
    }

    fn from_slice(slice: &[ShareId<Self::Item>]) -> Id<Self> {
        let refs: Vec<&Self::Item> = slice.iter().map(|obj| &**obj).collect();
        unsafe {
            INSArray::from_refs(&refs)
        }
    }

    fn to_shared_vec(&self) -> Vec<ShareId<Self::Item>> {
        self.to_vec().into_iter().map(|obj| unsafe {
            Id::from_ptr(obj as *const Self::Item as *mut Self::Item)
        }).collect()
    }
}

pub struct NSArray<T, O = Owned> {
    item: PhantomData<Id<T, O>>,
}

object_impl!(NSArray<T, O>);

impl<T, O> INSObject for NSArray<T, O> where T: INSObject, O: Ownership {
    fn class() -> &'static Class {
        Class::get("NSArray").unwrap()
    }
}

impl<T, O> INSArray for NSArray<T, O> where T: INSObject, O: Ownership {
    type Item = T;
    type Own = O;
}

impl<T> INSOwnedArray for NSArray<T, Owned> where T: INSObject { }

impl<T> INSSharedArray for NSArray<T, Shared> where T: INSObject { }

impl<T> INSCopying for NSArray<T, Shared> where T: INSObject {
    type Output = NSSharedArray<T>;
}

impl<T> INSMutableCopying for NSArray<T, Shared> where T: INSObject {
    type Output = NSMutableSharedArray<T>;
}

impl<T, O> INSFastEnumeration for NSArray<T, O>
        where T: INSObject, O: Ownership {
    type Item = T;
}

impl<T, O> Index<usize> for NSArray<T, O> where T: INSObject, O: Ownership {
    type Output = T;

    fn index(&self, index: &usize) -> &T {
        self.object_at(*index)
    }
}

pub type NSSharedArray<T> = NSArray<T, Shared>;

pub trait INSMutableArray : INSArray {
    fn add_object(&mut self, obj: Id<Self::Item, Self::Own>) {
        unsafe {
            let _: () = msg_send![self, addObject:&*obj];
        }
    }

    fn insert_object_at(&mut self, index: usize, obj: Id<Self::Item, Self::Own>) {
        unsafe {
            let _: () = msg_send![self, insertObject:&*obj atIndex:index];
        }
    }

    fn replace_object_at(&mut self, index: usize, obj: Id<Self::Item, Self::Own>) ->
            Id<Self::Item, Self::Own> {
        let old_obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        };
        unsafe {
            let _: () = msg_send![self, replaceObjectAtIndex:index
                                                  withObject:&*obj];
        }
        old_obj
    }

    fn remove_object_at(&mut self, index: usize) -> Id<Self::Item, Self::Own> {
        let obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        };
        unsafe {
            let _: () = msg_send![self, removeObjectAtIndex:index];
        }
        obj
    }

    fn remove_last_object(&mut self) -> Id<Self::Item, Self::Own> {
        let obj = self.last_object().map(|obj| unsafe {
            Id::from_ptr(obj as *const _ as *mut Self::Item)
        });
        unsafe {
            let _: () = msg_send![self, removeLastObject];
        }
        // removeLastObject would have failed if the array is empty,
        // so we know this won't be None
        obj.unwrap()
    }

    fn remove_all_objects(&mut self) {
        unsafe {
            let _: () = msg_send![self, removeAllObjects];
        }
    }

    fn sort_by<F>(&mut self, compare: F)
            where F: FnMut(&Self::Item, &Self::Item) -> Ordering {
        extern fn compare_with_closure<T, F>(obj1: &T, obj2: &T,
                compare: &mut F) -> NSComparisonResult
                where F: FnMut(&T, &T) -> Ordering {
            NSComparisonResult::from_ordering((*compare)(obj1, obj2))
        }

        let mut closure = compare;
        unsafe {
            let _: () = msg_send![self, sortUsingFunction:compare_with_closure::<Self::Item, F>
                                                  context:&mut closure];
        }
    }
}

pub struct NSMutableArray<T, O = Owned> {
    item: PhantomData<Id<T, O>>,
}

object_impl!(NSMutableArray<T, O>);

impl<T, O> INSObject for NSMutableArray<T, O> where T: INSObject, O: Ownership {
    fn class() -> &'static Class {
        Class::get("NSMutableArray").unwrap()
    }
}

impl<T, O> INSArray for NSMutableArray<T, O> where T: INSObject, O: Ownership {
    type Item = T;
    type Own = O;
}

impl<T> INSOwnedArray for NSMutableArray<T, Owned> where T: INSObject { }

impl<T> INSSharedArray for NSMutableArray<T, Shared> where T: INSObject { }

impl<T, O> INSMutableArray for NSMutableArray<T, O>
        where T: INSObject, O: Ownership { }

impl<T> INSCopying for NSMutableArray<T, Shared> where T: INSObject {
    type Output = NSSharedArray<T>;
}

impl<T> INSMutableCopying for NSMutableArray<T, Shared> where T: INSObject {
    type Output = NSMutableSharedArray<T>;
}

impl<T, O> INSFastEnumeration for NSMutableArray<T, O>
        where T: INSObject, O: Ownership {
    type Item = T;
}

impl<T, O> Index<usize> for NSMutableArray<T, O>
        where T: INSObject, O: Ownership {
    type Output = T;

    fn index(&self, index: &usize) -> &T {
        self.object_at(*index)
    }
}

pub type NSMutableSharedArray<T> = NSMutableArray<T, Shared>;

#[cfg(test)]
mod tests {
    use objc::{Id};
    use {INSObject, INSString, NSObject, NSString};
    use super::{INSArray, INSMutableArray, NSArray, NSMutableArray};

    fn sample_array(len: usize) -> Id<NSArray<NSObject>> {
        let mut vec = Vec::with_capacity(len);
        for _ in 0..len {
            vec.push(NSObject::new());
        }
        NSArray::from_vec(vec)
    }

    #[test]
    fn test_count() {
        let empty_array = NSArray::<NSObject>::new();
        assert!(empty_array.count() == 0);

        let array = sample_array(4);
        assert!(array.count() == 4);
    }

    #[test]
    fn test_object_at() {
        let array = sample_array(4);
        assert!(array.object_at(0) != array.object_at(3));
        assert!(array.first_object().unwrap() == array.object_at(0));
        assert!(array.last_object().unwrap() == array.object_at(3));

        let empty_array: Id<NSArray<NSObject>> = INSObject::new();
        assert!(empty_array.first_object().is_none());
        assert!(empty_array.last_object().is_none());
    }

    #[test]
    fn test_object_enumerator() {
        let array = sample_array(4);

        assert!(array.object_enumerator().count() == 4);
        assert!(array.object_enumerator()
                     .enumerate()
                     .all(|(i, obj)| obj == array.object_at(i)));
    }

    #[test]
    fn test_objects_in_range() {
        let array = sample_array(4);

        let middle_objs = array.objects_in_range(1..3);
        assert!(middle_objs.len() == 2);
        assert!(middle_objs[0] == array.object_at(1));
        assert!(middle_objs[1] == array.object_at(2));

        let empty_objs = array.objects_in_range(1..1);
        assert!(empty_objs.len() == 0);

        let all_objs = array.objects_in_range(0..4);
        assert!(all_objs.len() == 4);
    }

    #[test]
    fn test_into_vec() {
        let array = sample_array(4);

        let vec = INSArray::into_vec(array);
        assert!(vec.len() == 4);
    }

    #[test]
    fn test_add_object() {
        let mut array = NSMutableArray::new();
        let obj = NSObject::new();
        array.add_object(obj);

        assert!(array.count() == 1);
        assert!(array.object_at(0) == array.object_at(0));

        let obj = NSObject::new();
        array.insert_object_at(0, obj);
        assert!(array.count() == 2);
    }

    #[test]
    fn test_replace_object() {
        let mut array = NSMutableArray::new();
        let obj = NSObject::new();
        array.add_object(obj);

        let obj = NSObject::new();
        let old_obj = array.replace_object_at(0, obj);
        assert!(&*old_obj != array.object_at(0));
    }

    #[test]
    fn test_remove_object() {
        let mut array = NSMutableArray::new();
        for _ in 0..4 {
            array.add_object(NSObject::new());
        }

        array.remove_object_at(1);
        assert!(array.count() == 3);

        array.remove_last_object();
        assert!(array.count() == 2);

        array.remove_all_objects();
        assert!(array.count() == 0);
    }

    #[test]
    fn test_sort() {
        let strings = vec![
            NSString::from_str("hello"),
            NSString::from_str("hi"),
        ];
        let mut strings = NSMutableArray::from_vec(strings);

        strings.sort_by(|s1, s2| s1.as_str().len().cmp(&s2.as_str().len()));
        assert!(strings[0].as_str() == "hi");
        assert!(strings[1].as_str() == "hello");
    }
}
<|endoftext|>


<commit_before>extern crate udt;

pub mod crypto;

use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use core::ops::DerefMut;
use std::fmt;
use std::io::Cursor;
use std::net::{UdpSocket, SocketAddr, IpAddr};
use std::str;
use std::sync::{Arc, Mutex};
use udt::{UdtSocket, UdtError, UdtOpts, SocketType, SocketFamily};

// TODO config
const UDT_BUF_SIZE: i32 = 4096000;
pub const MAX_MESSAGE_SIZE: usize = 1024000;

fn new_udt_socket() -> UdtSocket {
    udt::init();
    let sock = UdtSocket::new(SocketFamily::AFInet, SocketType::Stream).unwrap();
    sock.setsockopt(UdtOpts::UDP_RCVBUF, UDT_BUF_SIZE).unwrap();
    sock.setsockopt(UdtOpts::UDP_SNDBUF, UDT_BUF_SIZE).unwrap();
    sock
}

trait ExactIO {
    fn send_exact(&self, buf: &[u8]) -> Result<(), UdtError>;
    fn recv_exact(&self, buf: &mut [u8], len: usize) -> Result<(), UdtError>;
}

impl ExactIO for UdtSocket {
    fn send_exact(&self, buf: &[u8]) -> Result<(), UdtError> {
        let mut total: usize = 0;
        while total < buf.len() {
            total += try!(self.send(&buf[total..])) as usize;
        }
        Ok(())
    }

    fn recv_exact(&self, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
        let mut total: usize = 0;
        while total < len {
            let remaining = len - total;
            total += try!(self.recv(&mut buf[total..], remaining)) as usize;
        }
        Ok(())
    }
}

fn send(sock: &UdtSocket, crypto: &mut crypto::Handler, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
    // FIXME don't unwrap, create an Error struct that can handle everything
    if let Ok(sealed_len) = crypto.seal(buf, len) {
        assert!(sealed_len <= u32::max_value() as usize, "single chunk must be 32-bit length");

        let mut wtr = vec![];
        wtr.write_u32::<LittleEndian>(sealed_len as u32).unwrap();
        try!(sock.send_exact(&wtr));

        sock.send_exact(&buf[..sealed_len])
    } else {
        Err(UdtError {
            err_code: -1,
            err_msg: "encryption failure".into(),
        })
    }
}

fn recv(sock: &UdtSocket, crypto: &mut crypto::Handler, buf: &mut [u8]) -> Result<usize, UdtError> {
    let mut len_buf = vec![0u8; 4];
    try!(sock.recv_exact(&mut len_buf, 4)); // u32
    let mut rdr = Cursor::new(len_buf);
    let len = rdr.read_u32::<LittleEndian>().unwrap() as usize;

    try!(sock.recv_exact(buf, len));
    crypto.open(&mut buf[..len]).map_err(|_| {
        UdtError {
            err_code: -1,
            err_msg: String::from("decryption failure"),
        }
    })
}

#[derive(Copy,Clone)]
pub struct PortRange {
    start: u16,
    end: u16,
}

pub struct Server {
    pub ip_addr: IpAddr,
    pub port: u16,
    crypto: Arc<Mutex<crypto::Handler>>,
    sock: UdtSocket,
}

pub struct Client {
    addr: SocketAddr,
    sock: UdtSocket,
    crypto: crypto::Handler,
}

pub struct ServerConnection {
    crypto: Arc<Mutex<crypto::Handler>>,
    sock: UdtSocket,
}

impl Client {
    pub fn new(addr: SocketAddr, key: &[u8]) -> Client {
        let sock = new_udt_socket();
        Client {
            addr: addr,
            sock: sock,
            crypto: crypto::Handler::new(key),
        }
    }

    pub fn connect(&self) -> Result<(), UdtError> {
        self.sock.connect(self.addr)
    }
}

pub trait Transceiver {
    fn send(&mut self, buf: &mut [u8], len: usize) -> Result<(), UdtError>;
    fn recv(&mut self, buf: &mut [u8]) -> Result<usize, UdtError>;
    fn close(&self) -> Result<(), UdtError>;
}

impl Transceiver for Client {
    fn send(&mut self, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
        send(&self.sock, &mut self.crypto, buf, len)
    }

    fn recv(&mut self, buf: &mut [u8]) -> Result<usize, UdtError> {
        recv(&self.sock, &mut self.crypto, buf)
    }

    fn close(&self) -> Result<(), UdtError> {
        self.sock.close()
    }
}

impl Transceiver for ServerConnection {
    fn send(&mut self, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
        let mutex = &self.crypto;
        let mut crypto = mutex.lock().unwrap();
        send(&self.sock, crypto.deref_mut(), buf, len)
    }

    fn recv(&mut self, buf: &mut [u8]) -> Result<usize, UdtError> {
        let mutex = &self.crypto;
        let mut crypto = mutex.lock().unwrap();
        recv(&self.sock, crypto.deref_mut(), buf)
    }

    fn close(&self) -> Result<(), UdtError> {
        self.sock.close()
    }
}

impl Server {
    pub fn get_open_port(range: &PortRange) -> Result<u16, ()> {
        for p in range.start..range.end {
            if let Ok(_) = UdpSocket::bind(&format!("0.0.0.0:{}", p)[..]) {
                return Ok(p);
            }
        }
        Err(())
    }

    pub fn new(ip_addr: IpAddr, port: u16, key: &[u8]) -> Server {
        let sock = new_udt_socket();
        sock.bind(SocketAddr::new(ip_addr, port)).unwrap();
        Server {
            sock: sock,
            ip_addr: ip_addr,
            port: port,
            crypto: Arc::new(Mutex::new(crypto::Handler::new(key))),
        }
    }

    pub fn listen(&self) -> Result<(), UdtError> {
        self.sock.listen(1)
    }

    pub fn accept(&self) -> Result<ServerConnection, UdtError> {
        self.sock.accept().map(move |(sock, _)| {
            ServerConnection {
                crypto: self.crypto.clone(),
                sock: sock,
            }
        })
    }
}

impl ServerConnection {
    pub fn getpeer(&self) -> Result<SocketAddr, UdtError> {
        self.sock.getpeername()
    }
}

impl PortRange {
    fn new(start: u16, end: u16) -> Result<PortRange, String> {
        if start > end {
            Err("range end must be greater than or equal to start".into())
        } else {
            Ok(PortRange {
                start: start,
                end: end,
            })
        }
    }

    pub fn from(s: &str) -> Result<PortRange, String> {
        let sections: Vec<&str> = s.split('-').collect();
        if sections.len() != 2 {
            return Err("Range must be specified in the form of \"<start>-<end>\"".into());
        }
        let (start, end) = (sections[0].parse::<u16>(), sections[1].parse::<u16>());
        if start.is_err() || end.is_err() {
            return Err("improperly formatted port range".into());
        }
        PortRange::new(start.unwrap(), end.unwrap())
    }
}

impl fmt::Display for PortRange {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}-{}", self.start, self.end)
    }
}
<commit_msg>Reduce the amount of UDT packets send<commit_after>extern crate udt;

pub mod crypto;

use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use core::ops::DerefMut;
use std::fmt;
use std::io::Cursor;
use std::net::{UdpSocket, SocketAddr, IpAddr};
use std::str;
use std::sync::{Arc, Mutex};
use udt::{UdtSocket, UdtError, UdtOpts, SocketType, SocketFamily};

// TODO config
const UDT_BUF_SIZE: i32 = 4096000;
pub const MAX_MESSAGE_SIZE: usize = 1024000;

fn new_udt_socket() -> UdtSocket {
    udt::init();
    let sock = UdtSocket::new(SocketFamily::AFInet, SocketType::Stream).unwrap();
    sock.setsockopt(UdtOpts::UDP_RCVBUF, UDT_BUF_SIZE).unwrap();
    sock.setsockopt(UdtOpts::UDP_SNDBUF, UDT_BUF_SIZE).unwrap();
    sock
}

trait ExactIO {
    fn send_exact(&self, buf: &[u8]) -> Result<(), UdtError>;
    fn recv_exact(&self, buf: &mut [u8], len: usize) -> Result<(), UdtError>;
}

impl ExactIO for UdtSocket {
    fn send_exact(&self, buf: &[u8]) -> Result<(), UdtError> {
        let mut total: usize = 0;
        while total < buf.len() {
            total += try!(self.send(&buf[total..])) as usize;
        }
        Ok(())
    }

    fn recv_exact(&self, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
        let mut total: usize = 0;
        while total < len {
            let remaining = len - total;
            total += try!(self.recv(&mut buf[total..], remaining)) as usize;
        }
        Ok(())
    }
}

fn send(sock: &UdtSocket, crypto: &mut crypto::Handler, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
    // FIXME don't unwrap, create an Error struct that can handle everything
    if let Ok(sealed_len) = crypto.seal(buf, len) {
        assert!(sealed_len <= u32::max_value() as usize, "single chunk must be 32-bit length");

        let mut wtr = vec![];
        wtr.write_u32::<LittleEndian>(sealed_len as u32).unwrap();
        wtr.extend_from_slice(&buf[..sealed_len]);
        sock.send_exact(&wtr)
    } else {
        Err(UdtError {
            err_code: -1,
            err_msg: "encryption failure".into(),
        })
    }
}

fn recv(sock: &UdtSocket, crypto: &mut crypto::Handler, buf: &mut [u8]) -> Result<usize, UdtError> {
    let mut len_buf = vec![0u8; 4];
    try!(sock.recv_exact(&mut len_buf, 4)); // u32
    let mut rdr = Cursor::new(len_buf);
    let len = rdr.read_u32::<LittleEndian>().unwrap() as usize;

    try!(sock.recv_exact(buf, len));
    crypto.open(&mut buf[..len]).map_err(|_| {
        UdtError {
            err_code: -1,
            err_msg: String::from("decryption failure"),
        }
    })
}

#[derive(Copy,Clone)]
pub struct PortRange {
    start: u16,
    end: u16,
}

pub struct Server {
    pub ip_addr: IpAddr,
    pub port: u16,
    crypto: Arc<Mutex<crypto::Handler>>,
    sock: UdtSocket,
}

pub struct Client {
    addr: SocketAddr,
    sock: UdtSocket,
    crypto: crypto::Handler,
}

pub struct ServerConnection {
    crypto: Arc<Mutex<crypto::Handler>>,
    sock: UdtSocket,
}

impl Client {
    pub fn new(addr: SocketAddr, key: &[u8]) -> Client {
        let sock = new_udt_socket();
        Client {
            addr: addr,
            sock: sock,
            crypto: crypto::Handler::new(key),
        }
    }

    pub fn connect(&self) -> Result<(), UdtError> {
        self.sock.connect(self.addr)
    }
}

pub trait Transceiver {
    fn send(&mut self, buf: &mut [u8], len: usize) -> Result<(), UdtError>;
    fn recv(&mut self, buf: &mut [u8]) -> Result<usize, UdtError>;
    fn close(&self) -> Result<(), UdtError>;
}

impl Transceiver for Client {
    fn send(&mut self, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
        send(&self.sock, &mut self.crypto, buf, len)
    }

    fn recv(&mut self, buf: &mut [u8]) -> Result<usize, UdtError> {
        recv(&self.sock, &mut self.crypto, buf)
    }

    fn close(&self) -> Result<(), UdtError> {
        self.sock.close()
    }
}

impl Transceiver for ServerConnection {
    fn send(&mut self, buf: &mut [u8], len: usize) -> Result<(), UdtError> {
        let mutex = &self.crypto;
        let mut crypto = mutex.lock().unwrap();
        send(&self.sock, crypto.deref_mut(), buf, len)
    }

    fn recv(&mut self, buf: &mut [u8]) -> Result<usize, UdtError> {
        let mutex = &self.crypto;
        let mut crypto = mutex.lock().unwrap();
        recv(&self.sock, crypto.deref_mut(), buf)
    }

    fn close(&self) -> Result<(), UdtError> {
        self.sock.close()
    }
}

impl Server {
    pub fn get_open_port(range: &PortRange) -> Result<u16, ()> {
        for p in range.start..range.end {
            if let Ok(_) = UdpSocket::bind(&format!("0.0.0.0:{}", p)[..]) {
                return Ok(p);
            }
        }
        Err(())
    }

    pub fn new(ip_addr: IpAddr, port: u16, key: &[u8]) -> Server {
        let sock = new_udt_socket();
        sock.bind(SocketAddr::new(ip_addr, port)).unwrap();
        Server {
            sock: sock,
            ip_addr: ip_addr,
            port: port,
            crypto: Arc::new(Mutex::new(crypto::Handler::new(key))),
        }
    }

    pub fn listen(&self) -> Result<(), UdtError> {
        self.sock.listen(1)
    }

    pub fn accept(&self) -> Result<ServerConnection, UdtError> {
        self.sock.accept().map(move |(sock, _)| {
            ServerConnection {
                crypto: self.crypto.clone(),
                sock: sock,
            }
        })
    }
}

impl ServerConnection {
    pub fn getpeer(&self) -> Result<SocketAddr, UdtError> {
        self.sock.getpeername()
    }
}

impl PortRange {
    fn new(start: u16, end: u16) -> Result<PortRange, String> {
        if start > end {
            Err("range end must be greater than or equal to start".into())
        } else {
            Ok(PortRange {
                start: start,
                end: end,
            })
        }
    }

    pub fn from(s: &str) -> Result<PortRange, String> {
        let sections: Vec<&str> = s.split('-').collect();
        if sections.len() != 2 {
            return Err("Range must be specified in the form of \"<start>-<end>\"".into());
        }
        let (start, end) = (sections[0].parse::<u16>(), sections[1].parse::<u16>());
        if start.is_err() || end.is_err() {
            return Err("improperly formatted port range".into());
        }
        PortRange::new(start.unwrap(), end.unwrap())
    }
}

impl fmt::Display for PortRange {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}-{}", self.start, self.end)
    }
}
<|endoftext|>