<commit_before>use crate::behaviour::{Behaviour, BehaviourEvent, PubsubMessage};
use crate::error;
use crate::multiaddr::Protocol;
use crate::rpc::RPCEvent;
use crate::NetworkConfig;
use futures::prelude::*;
use futures::Stream;
use libp2p::core::{
    identity,
    muxing::StreamMuxerBox,
    nodes::Substream,
    transport::boxed::Boxed,
    upgrade::{InboundUpgradeExt, OutboundUpgradeExt},
};
use libp2p::identify::protocol::IdentifyInfo;
use libp2p::{core, secio, PeerId, Swarm, Transport};
use slog::{debug, info, trace, warn};
use std::io::{Error, ErrorKind};
use std::time::Duration;
use types::{TopicBuilder, TopicHash};

type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>;
type Libp2pBehaviour = Behaviour<Substream<StreamMuxerBox>>;

/// The configuration and state of the libp2p components for the beacon node.
pub struct Service {
    /// The libp2p Swarm handler.
    //TODO: Make this private
    pub swarm: Swarm<Libp2pStream, Libp2pBehaviour>,
    /// This node's PeerId.
    _local_peer_id: PeerId,
    /// The libp2p logger handle.
    pub log: slog::Logger,
}

impl Service {
    pub fn new(config: NetworkConfig, log: slog::Logger) -> error::Result<Self> {
        debug!(log, "Libp2p Service starting");

        // TODO: Currently using secp256k1 key pairs. Wire protocol specifies RSA. Waiting for this
        // PR to be merged to generate RSA keys: https://github.com/briansmith/ring/pull/733
        // TODO: Save and recover node key from disk
        let local_private_key = identity::Keypair::generate_secp256k1();

        let local_public_key = local_private_key.public();
        let local_peer_id = PeerId::from(local_private_key.public());
        info!(log, "Local peer id: {:?}", local_peer_id);

        let mut swarm = {
            // Set up the transport
            let transport = build_transport(local_private_key);
            // Set up gossipsub routing
            let behaviour = Behaviour::new(local_public_key.clone(), &config, &log);
            // Set up Topology
            let topology = local_peer_id.clone();
            Swarm::new(transport, behaviour, topology)
        };

        // listen on all addresses
        for address in config.listen_addresses().expect("invalid listen multiaddr") {
            match Swarm::listen_on(&mut swarm, address.clone()) {
                Ok(mut listen_addr) => {
                    listen_addr.append(Protocol::P2p(local_peer_id.clone().into()));
                    info!(log, "Listening on: {}", listen_addr);
                }
                Err(err) => warn!(log, "Cannot listen on: {} : {:?}", address, err),
            };
        }
        // connect to boot nodes - these are currently stored as multiaddrs
        // Once we have discovery, can set to peerId
        for bootnode in config.boot_nodes().expect("invalid boot node multiaddr") {
            match Swarm::dial_addr(&mut swarm, bootnode.clone()) {
                Ok(()) => debug!(log, "Dialing bootnode: {}", bootnode),
                Err(err) => debug!(
                    log,
                    "Could not connect to bootnode: {} error: {:?}", bootnode, err
                ),
            };
        }

        // subscribe to default gossipsub topics
        let mut subscribed_topics = vec![];
        for topic in config.topics {
            let t = TopicBuilder::new(topic.to_string()).build();
            if swarm.subscribe(t) {
                trace!(log, "Subscribed to topic: {:?}", topic);
                subscribed_topics.push(topic);
            } else {
                warn!(log, "Could not subscribe to topic: {:?}", topic)
            }
        }
        info!(log, "Subscribed to topics: {:?}", subscribed_topics);

        Ok(Service {
            _local_peer_id: local_peer_id,
            swarm,
            log,
        })
    }
}

impl Stream for Service {
    type Item = Libp2pEvent;
    type Error = crate::error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            // TODO: Currently only gossipsub events passed here.
            // Build a type for more generic events
            match self.swarm.poll() {
                //Behaviour events
                Ok(Async::Ready(Some(event))) => match event {
                    // TODO: Stub here for debugging
                    BehaviourEvent::GossipMessage {
                        source,
                        topics,
                        message,
                    } => {
                        trace!(self.log, "Pubsub message received: {:?}", message);
                        return Ok(Async::Ready(Some(Libp2pEvent::PubsubMessage {
                            source,
                            topics,
                            message,
                        })));
                    }
                    BehaviourEvent::RPC(peer_id, event) => {
                        return Ok(Async::Ready(Some(Libp2pEvent::RPC(peer_id, event))));
                    }
                    BehaviourEvent::PeerDialed(peer_id) => {
                        return Ok(Async::Ready(Some(Libp2pEvent::PeerDialed(peer_id))));
                    }
                    BehaviourEvent::Identified(peer_id, info) => {
                        return Ok(Async::Ready(Some(Libp2pEvent::Identified(peer_id, info))));
                    }
                },
                Ok(Async::Ready(None)) => unreachable!("Swarm stream shouldn't end"),
                Ok(Async::NotReady) => break,
                _ => break,
            }
        }
        Ok(Async::NotReady)
    }
}

/// The implementation supports TCP/IP, WebSockets over TCP/IP, secio as the encryption layer, and
/// mplex or yamux as the multiplexing layer.
fn build_transport(local_private_key: identity::Keypair) -> Boxed<(PeerId, StreamMuxerBox), Error> {
    // TODO: The Wire protocol currently doesn't specify encryption and this will need to be customised
    // in the future.
    let transport = libp2p::tcp::TcpConfig::new();
    let transport = libp2p::dns::DnsConfig::new(transport);
    #[cfg(feature = "libp2p-websocket")]
    let transport = {
        let trans_clone = transport.clone();
        transport.or_transport(websocket::WsConfig::new(trans_clone))
    };
    transport
        .with_upgrade(secio::SecioConfig::new(local_private_key))
        .and_then(move |out, endpoint| {
            let peer_id = out.remote_key.into_peer_id();
            let peer_id2 = peer_id.clone();
            let upgrade = core::upgrade::SelectUpgrade::new(
                libp2p::yamux::Config::default(),
                libp2p::mplex::MplexConfig::new(),
            )
            // TODO: use a single `.map` instead of two maps
            .map_inbound(move |muxer| (peer_id, muxer))
            .map_outbound(move |muxer| (peer_id2, muxer));

            core::upgrade::apply(out.stream, upgrade, endpoint)
                .map(|(id, muxer)| (id, core::muxing::StreamMuxerBox::new(muxer)))
        })
        .with_timeout(Duration::from_secs(20))
        .map_err(|err| Error::new(ErrorKind::Other, err))
        .boxed()
}

/// Events that can be obtained from polling the Libp2p Service.
pub enum Libp2pEvent {
    /// An RPC response request has been received on the swarm.
    RPC(PeerId, RPCEvent),
    /// Initiated the connection to a new peer.
    PeerDialed(PeerId),
    /// Received information about a peer on the network.
    Identified(PeerId, Box<IdentifyInfo>),
    /// Received pubsub message.
    PubsubMessage {
        source: PeerId,
        topics: Vec<TopicHash>,
        message: Box<PubsubMessage>,
    },
}
<commit_msg>Return errors instead of panic in libp2p<commit_after>use crate::behaviour::{Behaviour, BehaviourEvent, PubsubMessage};
use crate::error;
use crate::multiaddr::Protocol;
use crate::rpc::RPCEvent;
use crate::NetworkConfig;
use futures::prelude::*;
use futures::Stream;
use libp2p::core::{
    identity,
    muxing::StreamMuxerBox,
    nodes::Substream,
    transport::boxed::Boxed,
    upgrade::{InboundUpgradeExt, OutboundUpgradeExt},
};
use libp2p::identify::protocol::IdentifyInfo;
use libp2p::{core, secio, PeerId, Swarm, Transport};
use slog::{debug, info, trace, warn};
use std::io::{Error, ErrorKind};
use std::time::Duration;
use types::{TopicBuilder, TopicHash};

type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>;
type Libp2pBehaviour = Behaviour<Substream<StreamMuxerBox>>;

/// The configuration and state of the libp2p components for the beacon node.
pub struct Service {
    /// The libp2p Swarm handler.
    //TODO: Make this private
    pub swarm: Swarm<Libp2pStream, Libp2pBehaviour>,
    /// This node's PeerId.
    _local_peer_id: PeerId,
    /// The libp2p logger handle.
    pub log: slog::Logger,
}

impl Service {
    pub fn new(config: NetworkConfig, log: slog::Logger) -> error::Result<Self> {
        debug!(log, "Libp2p Service starting");

        // TODO: Currently using secp256k1 key pairs. Wire protocol specifies RSA. Waiting for this
        // PR to be merged to generate RSA keys: https://github.com/briansmith/ring/pull/733
        // TODO: Save and recover node key from disk
        let local_private_key = identity::Keypair::generate_secp256k1();

        let local_public_key = local_private_key.public();
        let local_peer_id = PeerId::from(local_private_key.public());
        info!(log, "Local peer id: {:?}", local_peer_id);

        let mut swarm = {
            // Set up the transport
            let transport = build_transport(local_private_key);
            // Set up gossipsub routing
            let behaviour = Behaviour::new(local_public_key.clone(), &config, &log);
            // Set up Topology
            let topology = local_peer_id.clone();
            Swarm::new(transport, behaviour, topology)
        };

        // listen on all addresses
        for address in config
            .listen_addresses()
            .map_err(|e| format!("Invalid listen multiaddr: {}", e))?
        {
            match Swarm::listen_on(&mut swarm, address.clone()) {
                Ok(mut listen_addr) => {
                    listen_addr.append(Protocol::P2p(local_peer_id.clone().into()));
                    info!(log, "Listening on: {}", listen_addr);
                }
                Err(err) => warn!(log, "Cannot listen on: {} : {:?}", address, err),
            };
        }
        // connect to boot nodes - these are currently stored as multiaddrs
        // Once we have discovery, can set to peerId
        for bootnode in config
            .boot_nodes()
            .map_err(|e| format!("Invalid boot node multiaddr: {:?}", e))?
        {
            match Swarm::dial_addr(&mut swarm, bootnode.clone()) {
                Ok(()) => debug!(log, "Dialing bootnode: {}", bootnode),
                Err(err) => debug!(
                    log,
                    "Could not connect to bootnode: {} error: {:?}", bootnode, err
                ),
            };
        }

        // subscribe to default gossipsub topics
        let mut subscribed_topics = vec![];
        for topic in config.topics {
            let t = TopicBuilder::new(topic.to_string()).build();
            if swarm.subscribe(t) {
                trace!(log, "Subscribed to topic: {:?}", topic);
                subscribed_topics.push(topic);
            } else {
                warn!(log, "Could not subscribe to topic: {:?}", topic)
            }
        }
        info!(log, "Subscribed to topics: {:?}", subscribed_topics);

        Ok(Service {
            _local_peer_id: local_peer_id,
            swarm,
            log,
        })
    }
}

impl Stream for Service {
    type Item = Libp2pEvent;
    type Error = crate::error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            // TODO: Currently only gossipsub events passed here.
            // Build a type for more generic events
            match self.swarm.poll() {
                //Behaviour events
                Ok(Async::Ready(Some(event))) => match event {
                    // TODO: Stub here for debugging
                    BehaviourEvent::GossipMessage {
                        source,
                        topics,
                        message,
                    } => {
                        trace!(self.log, "Pubsub message received: {:?}", message);
                        return Ok(Async::Ready(Some(Libp2pEvent::PubsubMessage {
                            source,
                            topics,
                            message,
                        })));
                    }
                    BehaviourEvent::RPC(peer_id, event) => {
                        return Ok(Async::Ready(Some(Libp2pEvent::RPC(peer_id, event))));
                    }
                    BehaviourEvent::PeerDialed(peer_id) => {
                        return Ok(Async::Ready(Some(Libp2pEvent::PeerDialed(peer_id))));
                    }
                    BehaviourEvent::Identified(peer_id, info) => {
                        return Ok(Async::Ready(Some(Libp2pEvent::Identified(peer_id, info))));
                    }
                },
                Ok(Async::Ready(None)) => unreachable!("Swarm stream shouldn't end"),
                Ok(Async::NotReady) => break,
                _ => break,
            }
        }
        Ok(Async::NotReady)
    }
}

/// The implementation supports TCP/IP, WebSockets over TCP/IP, secio as the encryption layer, and
/// mplex or yamux as the multiplexing layer.
fn build_transport(local_private_key: identity::Keypair) -> Boxed<(PeerId, StreamMuxerBox), Error> {
    // TODO: The Wire protocol currently doesn't specify encryption and this will need to be customised
    // in the future.
    let transport = libp2p::tcp::TcpConfig::new();
    let transport = libp2p::dns::DnsConfig::new(transport);
    #[cfg(feature = "libp2p-websocket")]
    let transport = {
        let trans_clone = transport.clone();
        transport.or_transport(websocket::WsConfig::new(trans_clone))
    };
    transport
        .with_upgrade(secio::SecioConfig::new(local_private_key))
        .and_then(move |out, endpoint| {
            let peer_id = out.remote_key.into_peer_id();
            let peer_id2 = peer_id.clone();
            let upgrade = core::upgrade::SelectUpgrade::new(
                libp2p::yamux::Config::default(),
                libp2p::mplex::MplexConfig::new(),
            )
            // TODO: use a single `.map` instead of two maps
            .map_inbound(move |muxer| (peer_id, muxer))
            .map_outbound(move |muxer| (peer_id2, muxer));

            core::upgrade::apply(out.stream, upgrade, endpoint)
                .map(|(id, muxer)| (id, core::muxing::StreamMuxerBox::new(muxer)))
        })
        .with_timeout(Duration::from_secs(20))
        .map_err(|err| Error::new(ErrorKind::Other, err))
        .boxed()
}

/// Events that can be obtained from polling the Libp2p Service.
pub enum Libp2pEvent {
    /// An RPC response request has been received on the swarm.
    RPC(PeerId, RPCEvent),
    /// Initiated the connection to a new peer.
    PeerDialed(PeerId),
    /// Received information about a peer on the network.
    Identified(PeerId, Box<IdentifyInfo>),
    /// Received pubsub message.
    PubsubMessage {
        source: PeerId,
        topics: Vec<TopicHash>,
        message: Box<PubsubMessage>,
    },
}
<|endoftext|>


<commit_before>use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use std::ops::Deref;

use crate::backend::allocator::{Allocator, Buffer, Format};

pub const SLOT_CAP: usize = 3;

pub struct Swapchain<A: Allocator<B>, B: Buffer> {
    allocator: A,

    width: u32,
    height: u32,
    format: Format,

    slots: [Slot<B>; SLOT_CAP],
}

pub struct Slot<B: Buffer> {
    buffer: Arc<Option<B>>,
    acquired: Arc<AtomicBool>,
}

impl<B: Buffer> Default for Slot<B> {
    fn default() -> Self {
        Slot {
            buffer: Arc::new(None),
            acquired: Arc::new(AtomicBool::new(false)),
        }
    }
}

impl<B: Buffer> Clone for Slot<B> {
    fn clone(&self) -> Self {
        Slot {
            buffer: self.buffer.clone(),
            acquired: self.acquired.clone(),
        }
    }
}

impl<B: Buffer> Deref for Slot<B> {
    type Target = B;
    fn deref(&self) -> &B {
        Option::as_ref(&*self.buffer).unwrap()
    }
}

impl<B: Buffer> Drop for Slot<B> {
    fn drop(&mut self) {
        self.acquired.store(false, Ordering::AcqRel);
    }
}

impl<A: Allocator<B>, B: Buffer> Swapchain<A, B> {
    pub fn new(allocator: A, width: u32, height: u32, format: Format) -> Swapchain<A, B> {
        Swapchain {
            allocator,
            width,
            height,
            format,
            slots: Default::default(),
        }
    }

    pub fn acquire(&mut self) -> Result<Option<Slot<B>>, A::Error> {
        if let Some(free_slot) = self.slots.iter_mut().filter(|s| !s.acquired.load(Ordering::SeqCst)).next() {
            if free_slot.buffer.is_none() {
                free_slot.buffer = Arc::new(Some(self.allocator.create_buffer(self.width, self.height, self.format)?));
            }
            assert!(!free_slot.buffer.is_some());

            if !free_slot.acquired.swap(true, Ordering::AcqRel) {
                return Ok(Some(free_slot.clone()));
            }

        }

        // no free slots
        Ok(None)
    }

    pub fn resize(&mut self, width: u32, height: u32) {
        if self.width == width && self.height == height {
            return;
        }

        self.width = width;
        self.height = height;
        self.slots = Default::default();
    }
}<commit_msg>swapchain: handle buffer conversion for the user<commit_after>use std::convert::TryInto;
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use std::ops::Deref;

use crate::backend::allocator::{Allocator, Buffer, Format};

pub const SLOT_CAP: usize = 3;

pub struct Swapchain<A: Allocator<B>, B: Buffer + TryInto<B>, D: Buffer = B> {
    allocator: A,
    _original_buffer_format: std::marker::PhantomData<B>,

    width: u32,
    height: u32,
    format: Format,

    slots: [Slot<D>; SLOT_CAP],
}

pub struct Slot<B: Buffer> {
    buffer: Arc<Option<B>>,
    acquired: Arc<AtomicBool>,
}

impl<B: Buffer> Default for Slot<B> {
    fn default() -> Self {
        Slot {
            buffer: Arc::new(None),
            acquired: Arc::new(AtomicBool::new(false)),
        }
    }
}

impl<B: Buffer> Clone for Slot<B> {
    fn clone(&self) -> Self {
        Slot {
            buffer: self.buffer.clone(),
            acquired: self.acquired.clone(),
        }
    }
}

impl<B: Buffer> Deref for Slot<B> {
    type Target = B;
    fn deref(&self) -> &B {
        Option::as_ref(&*self.buffer).unwrap()
    }
}

impl<B: Buffer> Drop for Slot<B> {
    fn drop(&mut self) {
        self.acquired.store(false, Ordering::AcqRel);
    }
}

#[derive(Debug, thiserror::Error)]
pub enum SwapchainError<E1, E2>
where
    E1: std::error::Error + 'static,
    E2: std::error::Error + 'static,
{
    #[error("Failed to allocate a new buffer: {0}")]
    AllocationError(#[source] E1),
    #[error("Failed to convert a new buffer: {0}")]
    ConversionError(#[source] E2),
}

impl<A, B, D, E1, E2> Swapchain<A, B, D>
where
    A: Allocator<B, Error=E1>,
    B: Buffer + TryInto<D, Error=E2>,
    D: Buffer,
    E1: std::error::Error + 'static,
    E2: std::error::Error + 'static,
{
    pub fn new(allocator: A, width: u32, height: u32, format: Format) -> Swapchain<A, B, D> {
        Swapchain {
            allocator,
            _original_buffer_format: std::marker::PhantomData,
            width,
            height,
            format,
            slots: Default::default(),
        }
    }

    pub fn acquire(&mut self) -> Result<Option<Slot<D>>, SwapchainError<E1, E2>> {
        if let Some(free_slot) = self.slots.iter_mut().filter(|s| !s.acquired.load(Ordering::SeqCst)).next() {
            if free_slot.buffer.is_none() {
                free_slot.buffer = Arc::new(Some(
                    self.allocator
                    .create_buffer(self.width, self.height, self.format).map_err(SwapchainError::AllocationError)?
                    .try_into().map_err(SwapchainError::ConversionError)?
                ));
            }
            assert!(!free_slot.buffer.is_some());

            if !free_slot.acquired.swap(true, Ordering::AcqRel) {
                return Ok(Some(free_slot.clone()));
            }

        }

        // no free slots
        Ok(None)
    }

    pub fn resize(&mut self, width: u32, height: u32) {
        if self.width == width && self.height == height {
            return;
        }

        self.width = width;
        self.height = height;
        self.slots = Default::default();
    }
}<|endoftext|>


<commit_before>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use clap::Clap;

#[test]
fn skip_1() {
    #[derive(Clap, Debug, PartialEq)]
    struct Opt {
        #[clap(short)]
        x: u32,
        #[clap(skip)]
        s: u32,
    }

    assert!(Opt::try_parse_from(&["test", "-x", "10", "20"]).is_err());
    assert_eq!(
        Opt::parse_from(&["test", "-x", "10"]),
        Opt {
            x: 10,
            s: 0, // default
        }
    );
}

#[test]
fn skip_2() {
    #[derive(Clap, Debug, PartialEq)]
    struct Opt {
        #[clap(short)]
        x: u32,
        #[clap(skip)]
        ss: String,
        #[clap(skip)]
        sn: u8,
        y: u32,
        #[clap(skip)]
        sz: u16,
        t: u32,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-x", "10", "20", "30"]),
        Opt {
            x: 10,
            ss: String::from(""),
            sn: 0,
            y: 20,
            sz: 0,
            t: 30,
        }
    );
}

#[test]
fn skip_enum() {
    #[derive(Debug, PartialEq)]
    #[allow(unused)]
    enum Kind {
        A,
        B,
    }

    impl Default for Kind {
        fn default() -> Self {
            Kind::B
        }
    }

    #[derive(Clap, Debug, PartialEq)]
    pub struct Opt {
        #[clap(long, short)]
        number: u32,
        #[clap(skip)]
        k: Kind,
        #[clap(skip)]
        v: Vec<u32>,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-n", "10"]),
        Opt {
            number: 10,
            k: Kind::B,
            v: vec![],
        }
    );
}

#[test]
fn skip_help_doc_comments() {
    #[derive(Clap, Debug, PartialEq)]
    pub struct Opt {
        #[clap(skip, about = "internal_stuff")]
        a: u32,

        #[clap(skip, long_about = "internal_stuff\ndo not touch")]
        b: u32,

        /// Not meant to be used by clap.
        ///
        /// I want a default here.
        #[clap(skip)]
        c: u32,

        #[clap(short, parse(try_from_str))]
        n: u32,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-n", "10"]),
        Opt {
            n: 10,
            a: 0,
            b: 0,
            c: 0,
        }
    );
}

#[test]
fn skip_val() {
    #[derive(Clap, Debug, PartialEq)]
    pub struct Opt {
        #[clap(long, short)]
        number: u32,

        #[clap(skip = "key")]
        k: String,

        #[clap(skip = vec![1, 2, 3])]
        v: Vec<u32>,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-n", "10"]),
        Opt {
            number: 10,
            k: "key".to_string(),
            v: vec![1, 2, 3]
        }
    );
}
<commit_msg>Add a skip test<commit_after>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use clap::Clap;

#[test]
fn skip_1() {
    #[derive(Clap, Debug, PartialEq)]
    struct Opt {
        #[clap(short)]
        x: u32,
        #[clap(skip)]
        s: u32,
    }

    assert!(Opt::try_parse_from(&["test", "-x", "10", "20"]).is_err());

    let mut opt = Opt::parse_from(&["test", "-x", "10"]);
    assert_eq!(
        opt,
        Opt {
            x: 10,
            s: 0, // default
        }
    );
    opt.s = 42;

    opt.update_from(&["test", "-x", "22"]);

    assert_eq!(opt, Opt { x: 22, s: 42 });
}

#[test]
fn skip_2() {
    #[derive(Clap, Debug, PartialEq)]
    struct Opt {
        #[clap(short)]
        x: u32,
        #[clap(skip)]
        ss: String,
        #[clap(skip)]
        sn: u8,
        y: u32,
        #[clap(skip)]
        sz: u16,
        t: u32,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-x", "10", "20", "30"]),
        Opt {
            x: 10,
            ss: String::from(""),
            sn: 0,
            y: 20,
            sz: 0,
            t: 30,
        }
    );
}

#[test]
fn skip_enum() {
    #[derive(Debug, PartialEq)]
    #[allow(unused)]
    enum Kind {
        A,
        B,
    }

    impl Default for Kind {
        fn default() -> Self {
            Kind::B
        }
    }

    #[derive(Clap, Debug, PartialEq)]
    pub struct Opt {
        #[clap(long, short)]
        number: u32,
        #[clap(skip)]
        k: Kind,
        #[clap(skip)]
        v: Vec<u32>,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-n", "10"]),
        Opt {
            number: 10,
            k: Kind::B,
            v: vec![],
        }
    );
}

#[test]
fn skip_help_doc_comments() {
    #[derive(Clap, Debug, PartialEq)]
    pub struct Opt {
        #[clap(skip, about = "internal_stuff")]
        a: u32,

        #[clap(skip, long_about = "internal_stuff\ndo not touch")]
        b: u32,

        /// Not meant to be used by clap.
        ///
        /// I want a default here.
        #[clap(skip)]
        c: u32,

        #[clap(short, parse(try_from_str))]
        n: u32,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-n", "10"]),
        Opt {
            n: 10,
            a: 0,
            b: 0,
            c: 0,
        }
    );
}

#[test]
fn skip_val() {
    #[derive(Clap, Debug, PartialEq)]
    pub struct Opt {
        #[clap(long, short)]
        number: u32,

        #[clap(skip = "key")]
        k: String,

        #[clap(skip = vec![1, 2, 3])]
        v: Vec<u32>,
    }

    assert_eq!(
        Opt::parse_from(&["test", "-n", "10"]),
        Opt {
            number: 10,
            k: "key".to_string(),
            v: vec![1, 2, 3]
        }
    );
}
<|endoftext|>


<commit_before>// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.

// A simple wrapper around a build_script execution to generate file to reuse
// by rust_library/rust_binary.
extern crate cargo_build_script_output_parser;

use cargo_build_script_output_parser::{BuildScriptOutput, CompileAndLinkFlags};
use std::collections::BTreeMap;
use std::env;
use std::ffi::OsString;
use std::fs::{create_dir_all, read_to_string, write};
use std::path::Path;
use std::process::Command;

fn run_buildrs() -> Result<(), String> {
    // We use exec_root.join rather than std::fs::canonicalize, to avoid resolving symlinks, as
    // some execution strategies and remote execution environments may use symlinks in ways which
    // canonicalizing them may break them, e.g. by having input files be symlinks into a /cas
    // directory - resolving these may cause tools which inspect $0, or try to resolve files
    // relative to themselves, to fail.
    let exec_root = env::current_dir().expect("Failed to get current directory");
    let manifest_dir_env = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR was not set");
    let rustc_env = env::var("RUSTC").expect("RUSTC was not set");
    let manifest_dir = exec_root.join(&manifest_dir_env);
    let rustc = exec_root.join(&rustc_env);
    let Options {
        progname,
        crate_links,
        out_dir,
        envfile,
        flagfile,
        linkflags,
        output_dep_env_path,
        stdout_path,
        stderr_path,
        input_dep_env_paths,
    } = parse_args()?;

    let out_dir_abs = exec_root.join(&out_dir);
    // For some reason Google's RBE does not create the output directory, force create it.
    create_dir_all(&out_dir_abs).expect(&format!(
        "Failed to make output directory: {:?}",
        out_dir_abs
    ));

    let target_env_vars =
        get_target_env_vars(&rustc_env).expect("Error getting target env vars from rustc");

    let mut command = Command::new(exec_root.join(&progname));
    command
        .current_dir(&manifest_dir)
        .envs(target_env_vars)
        .env("OUT_DIR", out_dir_abs)
        .env("CARGO_MANIFEST_DIR", manifest_dir)
        .env("RUSTC", rustc)
        .env("RUST_BACKTRACE", "full");

    for dep_env_path in input_dep_env_paths.iter() {
        if let Ok(contents) = read_to_string(dep_env_path) {
            for line in contents.split('\n') {
                // split on empty contents will still produce a single empty string in iterable.
                if line.is_empty() {
                    continue;
                }
                let mut key_val = line.splitn(2, '=');
                match (key_val.next(), key_val.next()) {
                    (Some(key), Some(value)) => {
                        command.env(key, value.replace("${pwd}", &exec_root.to_string_lossy()));
                    }
                    _ => {
                        return Err(
                            "error: Wrong environment file format, should not happen".to_owned()
                        )
                    }
                }
            }
        } else {
            return Err("error: Dependency environment file unreadable".to_owned());
        }
    }

    if let Some(cc_path) = env::var_os("CC") {
        let mut cc_path = absolutify(&exec_root, cc_path);
        if let Some(sysroot_path) = env::var_os("SYSROOT") {
            cc_path.push(" --sysroot=");
            cc_path.push(absolutify(&exec_root, sysroot_path));
        }
        command.env("CC", cc_path);
    }

    if let Some(ar_path) = env::var_os("AR") {
        // The default OSX toolchain uses libtool as ar_executable not ar.
        // This doesn't work when used as $AR, so simply don't set it - tools will probably fall back to
        // /usr/bin/ar which is probably good enough.
        if Path::new(&ar_path).file_name() == Some("libtool".as_ref()) {
            command.env_remove("AR");
        } else {
            command.env("AR", absolutify(&exec_root, ar_path));
        }
    }

    // replace env vars with a ${pwd} prefix with the exec_root
    for (key, value) in env::vars() {
        let exec_root_str = exec_root.to_str().expect("exec_root not in utf8");
        if value.contains("${pwd}") {
            env::set_var(key, value.replace("${pwd}", exec_root_str));
        }
    }

    let (buildrs_outputs, process_output) =
        BuildScriptOutput::from_command(&mut command).map_err(|process_output| {
            format!(
                "Build script process failed{}\n--stdout:\n{}\n--stderr:\n{}",
                if let Some(exit_code) = process_output.status.code() {
                    format!(" with exit code {}", exit_code)
                } else {
                    String::new()
                },
                String::from_utf8(process_output.stdout)
                    .expect("Failed to parse stdout of child process"),
                String::from_utf8(process_output.stderr)
                    .expect("Failed to parse stdout of child process"),
            )
        })?;

    write(
        &envfile,
        BuildScriptOutput::to_env(&buildrs_outputs, &exec_root.to_string_lossy()).as_bytes(),
    )
    .expect(&format!("Unable to write file {:?}", envfile));
    write(
        &output_dep_env_path,
        BuildScriptOutput::to_dep_env(&buildrs_outputs, &crate_links, &exec_root.to_string_lossy())
            .as_bytes(),
    )
    .expect(&format!("Unable to write file {:?}", output_dep_env_path));
    write(&stdout_path, process_output.stdout)
        .expect(&format!("Unable to write file {:?}", stdout_path));
    write(&stderr_path, process_output.stderr)
        .expect(&format!("Unable to write file {:?}", stderr_path));

    let CompileAndLinkFlags {
        compile_flags,
        link_flags,
    } = BuildScriptOutput::to_flags(&buildrs_outputs, &exec_root.to_string_lossy());

    write(&flagfile, compile_flags.as_bytes())
        .expect(&format!("Unable to write file {:?}", flagfile));
    write(&linkflags, link_flags.as_bytes())
        .expect(&format!("Unable to write file {:?}", linkflags));
    Ok(())
}

/// A representation of expected command line arguments.
struct Options {
    progname: String,
    crate_links: String,
    out_dir: String,
    envfile: String,
    flagfile: String,
    linkflags: String,
    output_dep_env_path: String,
    stdout_path: String,
    stderr_path: String,
    input_dep_env_paths: Vec<String>,
}

/// Parses positional comamnd line arguments into a well defined struct
fn parse_args() -> Result<Options, String> {
    let mut args = env::args().skip(1);

    // TODO: we should consider an alternative to positional arguments.
    match (args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next()) {
        (
            Some(progname),
            Some(crate_links),
            Some(out_dir),
            Some(envfile),
            Some(flagfile),
            Some(linkflags),
            Some(output_dep_env_path),
            Some(stdout_path),
            Some(stderr_path),
        ) => {
            Ok(Options{
                progname,
                crate_links,
                out_dir,
                envfile,
                flagfile,
                linkflags,
                output_dep_env_path,
                stdout_path,
                stderr_path,
                input_dep_env_paths: args.collect(),
            })
        }
        _ => {
            Err("Usage: $0 progname out_dir envfile flagfile linkflagfile output_dep_env_path [arg1...argn]".to_owned())
        }
    }
}

fn get_target_env_vars<P: AsRef<Path>>(rustc: &P) -> Result<BTreeMap<String, String>, String> {
    // As done by Cargo when constructing a cargo::core::compiler::build_context::target_info::TargetInfo.
    let output = Command::new(rustc.as_ref())
        .arg("--print=cfg")
        .arg(format!(
            "--target={}",
            env::var("TARGET").expect("missing TARGET")
        ))
        .output()
        .map_err(|err| format!("Error running rustc to get target information: {}", err))?;
    if !output.status.success() {
        return Err(format!(
            "Error running rustc to get target information: {:?}",
            output
        ));
    }
    let stdout = std::str::from_utf8(&output.stdout)
        .map_err(|err| format!("Non-UTF8 stdout from rustc: {:?}", err))?;

    let mut values = BTreeMap::new();

    for line in stdout.lines() {
        if line.starts_with("target_") && line.contains('=') {
            let mut parts = line.splitn(2, '=');
            // UNWRAP: Verified that line contains = and split into exactly 2 parts.
            let key = parts.next().unwrap();
            let value = parts.next().unwrap();
            if value.starts_with('"') && value.ends_with('"') && value.len() >= 2 {
                values
                    .entry(key)
                    .or_insert(vec![])
                    .push(value[1..(value.len() - 1)].to_owned());
            }
        }
    }

    Ok(values
        .into_iter()
        .map(|(key, value)| (format!("CARGO_CFG_{}", key.to_uppercase()), value.join(",")))
        .collect())
}

fn absolutify(root: &Path, maybe_relative: OsString) -> OsString {
    let path = Path::new(&maybe_relative);
    if path.is_relative() {
        root.join(path).into_os_string()
    } else {
        maybe_relative
    }
}

fn main() {
    std::process::exit(match run_buildrs() {
        Ok(_) => 0,
        Err(err) => {
            // Neatly print errors
            eprintln!("{}", err);
            1
        }
    });
}
<commit_msg>Minor cleanup of `cargo_build_script` source code. (#816)<commit_after>// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.

// A simple wrapper around a build_script execution to generate file to reuse
// by rust_library/rust_binary.
extern crate cargo_build_script_output_parser;

use cargo_build_script_output_parser::{BuildScriptOutput, CompileAndLinkFlags};
use std::collections::BTreeMap;
use std::env;
use std::ffi::OsString;
use std::fs::{create_dir_all, read_to_string, write};
use std::path::Path;
use std::process::Command;

fn run_buildrs() -> Result<(), String> {
    // We use exec_root.join rather than std::fs::canonicalize, to avoid resolving symlinks, as
    // some execution strategies and remote execution environments may use symlinks in ways which
    // canonicalizing them may break them, e.g. by having input files be symlinks into a /cas
    // directory - resolving these may cause tools which inspect $0, or try to resolve files
    // relative to themselves, to fail.
    let exec_root = env::current_dir().expect("Failed to get current directory");
    let manifest_dir_env = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR was not set");
    let rustc_env = env::var("RUSTC").expect("RUSTC was not set");
    let manifest_dir = exec_root.join(&manifest_dir_env);
    let rustc = exec_root.join(&rustc_env);
    let Options {
        progname,
        crate_links,
        out_dir,
        env_file,
        compile_flags_file,
        link_flags_file,
        output_dep_env_path,
        stdout_path,
        stderr_path,
        input_dep_env_paths,
    } = parse_args()?;

    let out_dir_abs = exec_root.join(&out_dir);
    // For some reason Google's RBE does not create the output directory, force create it.
    create_dir_all(&out_dir_abs).expect(&format!(
        "Failed to make output directory: {:?}",
        out_dir_abs
    ));

    let target_env_vars =
        get_target_env_vars(&rustc_env).expect("Error getting target env vars from rustc");

    let mut command = Command::new(exec_root.join(&progname));
    command
        .current_dir(&manifest_dir)
        .envs(target_env_vars)
        .env("OUT_DIR", out_dir_abs)
        .env("CARGO_MANIFEST_DIR", manifest_dir)
        .env("RUSTC", rustc)
        .env("RUST_BACKTRACE", "full");

    for dep_env_path in input_dep_env_paths.iter() {
        if let Ok(contents) = read_to_string(dep_env_path) {
            for line in contents.split('\n') {
                // split on empty contents will still produce a single empty string in iterable.
                if line.is_empty() {
                    continue;
                }
                let mut key_val = line.splitn(2, '=');
                match (key_val.next(), key_val.next()) {
                    (Some(key), Some(value)) => {
                        command.env(key, value.replace("${pwd}", &exec_root.to_string_lossy()));
                    }
                    _ => {
                        return Err(
                            "error: Wrong environment file format, should not happen".to_owned()
                        )
                    }
                }
            }
        } else {
            return Err("error: Dependency environment file unreadable".to_owned());
        }
    }

    if let Some(cc_path) = env::var_os("CC") {
        let mut cc_path = absolutify(&exec_root, cc_path);
        if let Some(sysroot_path) = env::var_os("SYSROOT") {
            cc_path.push(" --sysroot=");
            cc_path.push(absolutify(&exec_root, sysroot_path));
        }
        command.env("CC", cc_path);
    }

    if let Some(ar_path) = env::var_os("AR") {
        // The default OSX toolchain uses libtool as ar_executable not ar.
        // This doesn't work when used as $AR, so simply don't set it - tools will probably fall back to
        // /usr/bin/ar which is probably good enough.
        if Path::new(&ar_path).file_name() == Some("libtool".as_ref()) {
            command.env_remove("AR");
        } else {
            command.env("AR", absolutify(&exec_root, ar_path));
        }
    }

    // replace env vars with a ${pwd} prefix with the exec_root
    for (key, value) in env::vars() {
        let exec_root_str = exec_root.to_str().expect("exec_root not in utf8");
        if value.contains("${pwd}") {
            env::set_var(key, value.replace("${pwd}", exec_root_str));
        }
    }

    let (buildrs_outputs, process_output) =
        BuildScriptOutput::from_command(&mut command).map_err(|process_output| {
            format!(
                "Build script process failed{}\n--stdout:\n{}\n--stderr:\n{}",
                if let Some(exit_code) = process_output.status.code() {
                    format!(" with exit code {}", exit_code)
                } else {
                    String::new()
                },
                String::from_utf8(process_output.stdout)
                    .expect("Failed to parse stdout of child process"),
                String::from_utf8(process_output.stderr)
                    .expect("Failed to parse stdout of child process"),
            )
        })?;

    write(
        &env_file,
        BuildScriptOutput::to_env(&buildrs_outputs, &exec_root.to_string_lossy()).as_bytes(),
    )
    .expect(&format!("Unable to write file {:?}", env_file));
    write(
        &output_dep_env_path,
        BuildScriptOutput::to_dep_env(&buildrs_outputs, &crate_links, &exec_root.to_string_lossy())
            .as_bytes(),
    )
    .expect(&format!("Unable to write file {:?}", output_dep_env_path));
    write(&stdout_path, process_output.stdout)
        .expect(&format!("Unable to write file {:?}", stdout_path));
    write(&stderr_path, process_output.stderr)
        .expect(&format!("Unable to write file {:?}", stderr_path));

    let CompileAndLinkFlags {
        compile_flags,
        link_flags,
    } = BuildScriptOutput::to_flags(&buildrs_outputs, &exec_root.to_string_lossy());

    write(&compile_flags_file, compile_flags.as_bytes())
        .expect(&format!("Unable to write file {:?}", compile_flags_file));
    write(&link_flags_file, link_flags.as_bytes())
        .expect(&format!("Unable to write file {:?}", link_flags_file));
    Ok(())
}

/// A representation of expected command line arguments.
struct Options {
    progname: String,
    crate_links: String,
    out_dir: String,
    env_file: String,
    compile_flags_file: String,
    link_flags_file: String,
    output_dep_env_path: String,
    stdout_path: String,
    stderr_path: String,
    input_dep_env_paths: Vec<String>,
}

/// Parses positional comamnd line arguments into a well defined struct
fn parse_args() -> Result<Options, String> {
    let mut args = env::args().skip(1);

    // TODO: we should consider an alternative to positional arguments.
    match (args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next(), args.next()) {
        (
            Some(progname),
            Some(crate_links),
            Some(out_dir),
            Some(env_file),
            Some(compile_flags_file),
            Some(link_flags_file),
            Some(output_dep_env_path),
            Some(stdout_path),
            Some(stderr_path),
        ) => {
            Ok(Options{
                progname,
                crate_links,
                out_dir,
                env_file,
                compile_flags_file,
                link_flags_file,
                output_dep_env_path,
                stdout_path,
                stderr_path,
                input_dep_env_paths: args.collect(),
            })
        }
        _ => {
            Err(format!("Usage: $0 progname crate_links out_dir env_file compile_flags_file link_flags_file output_dep_env_path stdout_path stderr_path input_dep_env_paths[arg1...argn]\nArguments passed: {:?}", args.collect::<Vec<String>>()))
        }
    }
}

fn get_target_env_vars<P: AsRef<Path>>(rustc: &P) -> Result<BTreeMap<String, String>, String> {
    // As done by Cargo when constructing a cargo::core::compiler::build_context::target_info::TargetInfo.
    let output = Command::new(rustc.as_ref())
        .arg("--print=cfg")
        .arg(format!(
            "--target={}",
            env::var("TARGET").expect("missing TARGET")
        ))
        .output()
        .map_err(|err| format!("Error running rustc to get target information: {}", err))?;
    if !output.status.success() {
        return Err(format!(
            "Error running rustc to get target information: {:?}",
            output
        ));
    }
    let stdout = std::str::from_utf8(&output.stdout)
        .map_err(|err| format!("Non-UTF8 stdout from rustc: {:?}", err))?;

    let mut values = BTreeMap::new();

    for line in stdout.lines() {
        if line.starts_with("target_") && line.contains('=') {
            let mut parts = line.splitn(2, '=');
            // UNWRAP: Verified that line contains = and split into exactly 2 parts.
            let key = parts.next().unwrap();
            let value = parts.next().unwrap();
            if value.starts_with('"') && value.ends_with('"') && value.len() >= 2 {
                values
                    .entry(key)
                    .or_insert(vec![])
                    .push(value[1..(value.len() - 1)].to_owned());
            }
        }
    }

    Ok(values
        .into_iter()
        .map(|(key, value)| (format!("CARGO_CFG_{}", key.to_uppercase()), value.join(",")))
        .collect())
}

fn absolutify(root: &Path, maybe_relative: OsString) -> OsString {
    let path = Path::new(&maybe_relative);
    if path.is_relative() {
        root.join(path).into_os_string()
    } else {
        maybe_relative
    }
}

fn main() {
    std::process::exit(match run_buildrs() {
        Ok(_) => 0,
        Err(err) => {
            // Neatly print errors
            eprintln!("{}", err);
            1
        }
    });
}
<|endoftext|>


<commit_before>//! Implements a command for uploading react-native projects.
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use clap::{App, Arg, ArgMatches};
use serde_json;
use chrono::Duration;

use prelude::*;
use api::{Api, NewRelease};
use config::Config;
use utils::{ArgExt, TempFile, propagate_exit_status};
use sourcemaputils::SourceMapProcessor;
use xcode;

#[derive(Serialize, Deserialize, Default, Debug)]
struct SourceMapReport {
    bundle_path: Option<PathBuf>,
    sourcemap_path: Option<PathBuf>,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads react-native projects from within an xcode build step")
        .org_project_args()
        .arg(Arg::with_name("verbose")
            .long("verbose")
            .short("verbose")
            .help("Enable verbose mode"))
        .arg(Arg::with_name("force")
             .long("force")
             .short("f")
             .help("Forces the script to run, even in Debug configuration.  This rarely \
                    does what you want because the default build script does not actually \
                    produce any information that the sentry build tool could pick up on."))
        .arg(Arg::with_name("allow_fetch")
             .long("allow-fetch")
             .help("Enable sourcemap fetching from the packager.  If this is enabled \
                    the react native packager needs to run and sourcemaps are downloade \
                    from it if the simulator platform is detected."))
        .arg(Arg::with_name("fetch_from")
             .long("fetch-from")
             .value_name("URL")
             .help("When fetching is enabled this is the URL where fetches can be made \
                    from.  The default is http://127.0.0.1:8081/ where the react-native \
                    packager runs by default."))
        .arg(Arg::with_name("build_script")
             .value_name("BUILD_SCRIPT")
             .index(1)
             .help("Optional path to the build script{n}{n}\
                    This is the path to the react-native-xcode.sh script you want \
                    to use.  By default the bundled build script is used."))
        .arg(Arg::with_name("force_foreground")
             .long("force-foreground")
             .help("By default part of the build process will when triggered from Xcode \
                    detach and continue in the background.  When an error happens \
                    a dialog is shown.  If this parameter is passed Xcode will wait \
                    for the process to finish before the build finishes and output \
                    will be shown in the Xcode build output."))
}

fn find_node() -> String {
    if let Ok(path) = env::var("NODE_BINARY") {
        if path.len() > 0 {
            return path;
        }
    }
    "node".into()
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);
    let should_wrap = matches.is_present("force") || match env::var("CONFIGURATION") {
        Ok(config) => &config != "Debug",
        Err(_) => { return Err("Need to run this from Xcode".into()); }
    };
    let base = env::current_dir()?;
    let script = if let Some(path) = matches.value_of("build_script") {
        base.join(path)
    } else {
        base.join("../node_modules/react-native/packager/react-native-xcode.sh")
    }.canonicalize()?;

    // if we allow fetching and we detect a simulator run, then we need to switch
    // to simulator mode.
    let fetch_url;
    if_chain! {
        if matches.is_present("allow_fetch");
        if let Ok(val) = env::var("PLATFORM_NAME");
        if val.ends_with("simulator");
        then {
            let url = matches.value_of("fetch_from").unwrap_or("http://127.0.0.1:8081/");
            info!("Fetching sourcemaps from {}", url);
            fetch_url = Some(url);
        } else {
            info!("Using react-native build script at {}", base.display());
            fetch_url = None;
        }
    }

    // in case we are in debug mode we directly dispatch to the script
    // and exit out early.
    if !should_wrap && fetch_url.is_none() {
        info!("Running in debug mode, skipping script wrapping.");
        let rv = process::Command::new(&script).spawn()?.wait()?;
        propagate_exit_status(rv);
        return Ok(());
    }

    info!("Parsing Info.plist");
    let plist = match xcode::InfoPlist::discover_from_env(&base)? {
        Some(plist) => plist,
        None => { return Err("Could not find info.plist".into()); }
    };
    info!("Parse result from Info.plist: {:?}", &plist);
    let report_file = TempFile::new()?;
    let node = find_node();
    info!("Using node interpreter '{}'", &node);

    xcode::MayDetach::wrap("React native symbol handling", |md| {
        let bundle_path;
        let sourcemap_path;
        let bundle_url;
        let sourcemap_url;
        let bundle_file;
        let sourcemap_file;

        // If we have a fetch URL we need to fetch them from there now.  In that
        // case we do indeed fetch it right from the running packager and then
        // store it in temporary files for later consumption.
        if let Some(url) = fetch_url {
            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let url = url.trim_right_matches('/');
            bundle_file = TempFile::new()?;
            bundle_path = bundle_file.path().to_path_buf();
            bundle_url = "~/index.ios.bundle".to_string();
            sourcemap_file = TempFile::new()?;
            sourcemap_path = sourcemap_file.path().to_path_buf();
            sourcemap_url = "~/index.ios.map".to_string();

            // wait up to 10 seconds for the server to be up.
            if !api.wait_until_available(url, Duration::seconds(10))? {
                return Err("Error: react-native packager did not respond in time".into());
            }

            api.download(&format!("{}/index.ios.bundle?platform=ios&dev=true", url),
                         &mut bundle_file.open())?;
            api.download(&format!("{}/index.ios.map?platform=ios&dev=true", url),
                         &mut sourcemap_file.open())?;

        // This is the case where we need to hook into the release process to
        // collect sourcemaps when they are generated.
        //
        // this invokes via an indirection of sentry-cli our wrap_call() below.
        // What is happening behind the scenes is that we switch out NODE_BINARY
        // for ourselves which is what the react-native build script normally
        // invokes.  Because we export __SENTRY_RN_WRAP_XCODE_CALL=1, the main
        // sentry-cli script will invoke our wrap_call() function below.
        //
        // That will then attempt to figure out that a react-native bundle is
        // happening to the build script, parse out the arguments, add additional
        // arguments if needed and then report the parsed arguments to a temporary
        // JSON file we load back below.
        //
        // With that we we then have all the information we need to invoke the
        // upload process.
        } else {
            let rv = process::Command::new(&script)
                .env("NODE_BINARY", env::current_exe()?.to_str().unwrap())
                .env("SENTRY_RN_REAL_NODE_BINARY", &node)
                .env("SENTRY_RN_SOURCEMAP_REPORT", report_file.path().to_str().unwrap())
                .env("__SENTRY_RN_WRAP_XCODE_CALL", "1")
                .spawn()?
                .wait()?;
            propagate_exit_status(rv);

            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let mut f = fs::File::open(report_file.path())?;
            let report : SourceMapReport = serde_json::from_reader(&mut f)?;
            if report.bundle_path.is_none() || report.sourcemap_path.is_none() {
                println!("Warning: build produced no sourcemaps.");
                return Ok(());
            }

            bundle_path = report.bundle_path.unwrap();
            bundle_url = format!("~/{}", bundle_path.file_name()
                                 .unwrap().to_string_lossy());
            sourcemap_path = report.sourcemap_path.unwrap();
            sourcemap_url = format!("~/{}", sourcemap_path.file_name()
                                    .unwrap().to_string_lossy());
        }

        // now that we have all the data, we can now process and upload the
        // sourcemaps.
        println!("Processing react-native sourcemaps for Sentry upload.");
        info!("  bundle path: {}", bundle_path.display());
        info!("  sourcemap path: {}", sourcemap_path.display());

        let mut processor = SourceMapProcessor::new(matches.is_present("verbose"));
        processor.add(&bundle_url, &bundle_path)?;
        processor.add(&sourcemap_url, &sourcemap_path)?;
        processor.rewrite(&vec![base.parent().unwrap().to_str().unwrap()])?;
        processor.add_sourcemap_references()?;

        let release = api.new_release(&org, &project, &NewRelease {
            version: plist.release_name(),
            ..Default::default()
        })?;
        println!("Uploading sourcemaps for release {}", release.version);
        processor.upload(&api, &org, &project, &release.version)?;

        Ok(())
    })
}

pub fn wrap_call() -> Result<()> {
    let mut args : Vec<_> = env::args().skip(1).collect();
    let mut bundle_path = None;
    let mut sourcemap_path = None;

    if args.len() > 1 && args[1] == "bundle" {
        let mut iter = args.iter().fuse();
        while let Some(item) = iter.next() {
            if item == "--sourcemap-output" {
                sourcemap_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--sourcemap-output=") {
                sourcemap_path = Some(item[19..].to_string());
            } else if item == "--bundle-output" {
                bundle_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--bundle-output=") {
                bundle_path = Some(item[16..].to_string());
            }
        }
    }

    let mut sourcemap_report = SourceMapReport::default();

    if sourcemap_path.is_none() && bundle_path.is_some() {
        let path = format!("{}.map", &bundle_path.as_ref().unwrap());
        sourcemap_report.sourcemap_path = Some(PathBuf::from(&path));
        args.push("--sourcemap-output".into());
        args.push(path);
    } else if let Some(path) = sourcemap_path {
        sourcemap_report.sourcemap_path = Some(PathBuf::from(path));
    }

    sourcemap_report.bundle_path = bundle_path.map(|x| PathBuf::from(x));

    let rv = process::Command::new(env::var("SENTRY_RN_REAL_NODE_BINARY").unwrap())
        .args(&args)
        .spawn()?
        .wait()?;
    propagate_exit_status(rv);

    let mut f = fs::File::create(env::var("SENTRY_RN_SOURCEMAP_REPORT").unwrap())?;
    serde_json::to_writer(&mut f, &sourcemap_report)?;

    Ok(())
}
<commit_msg>Fixed a bad call<commit_after>//! Implements a command for uploading react-native projects.
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use clap::{App, Arg, ArgMatches};
use serde_json;
use chrono::Duration;

use prelude::*;
use api::{Api, NewRelease};
use config::Config;
use utils::{ArgExt, TempFile, propagate_exit_status};
use sourcemaputils::SourceMapProcessor;
use xcode;

#[derive(Serialize, Deserialize, Default, Debug)]
struct SourceMapReport {
    bundle_path: Option<PathBuf>,
    sourcemap_path: Option<PathBuf>,
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("uploads react-native projects from within an xcode build step")
        .org_project_args()
        .arg(Arg::with_name("verbose")
            .long("verbose")
            .short("verbose")
            .help("Enable verbose mode"))
        .arg(Arg::with_name("force")
             .long("force")
             .short("f")
             .help("Forces the script to run, even in Debug configuration.  This rarely \
                    does what you want because the default build script does not actually \
                    produce any information that the sentry build tool could pick up on."))
        .arg(Arg::with_name("allow_fetch")
             .long("allow-fetch")
             .help("Enable sourcemap fetching from the packager.  If this is enabled \
                    the react native packager needs to run and sourcemaps are downloade \
                    from it if the simulator platform is detected."))
        .arg(Arg::with_name("fetch_from")
             .long("fetch-from")
             .value_name("URL")
             .help("When fetching is enabled this is the URL where fetches can be made \
                    from.  The default is http://127.0.0.1:8081/ where the react-native \
                    packager runs by default."))
        .arg(Arg::with_name("build_script")
             .value_name("BUILD_SCRIPT")
             .index(1)
             .help("Optional path to the build script{n}{n}\
                    This is the path to the react-native-xcode.sh script you want \
                    to use.  By default the bundled build script is used."))
        .arg(Arg::with_name("force_foreground")
             .long("force-foreground")
             .help("By default part of the build process will when triggered from Xcode \
                    detach and continue in the background.  When an error happens \
                    a dialog is shown.  If this parameter is passed Xcode will wait \
                    for the process to finish before the build finishes and output \
                    will be shown in the Xcode build output."))
}

fn find_node() -> String {
    if let Ok(path) = env::var("NODE_BINARY") {
        if path.len() > 0 {
            return path;
        }
    }
    "node".into()
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::new(config);
    let should_wrap = matches.is_present("force") || match env::var("CONFIGURATION") {
        Ok(config) => &config != "Debug",
        Err(_) => { return Err("Need to run this from Xcode".into()); }
    };
    let base = env::current_dir()?;
    let script = if let Some(path) = matches.value_of("build_script") {
        base.join(path)
    } else {
        base.join("../node_modules/react-native/packager/react-native-xcode.sh")
    }.canonicalize()?;

    // if we allow fetching and we detect a simulator run, then we need to switch
    // to simulator mode.
    let fetch_url;
    if_chain! {
        if matches.is_present("allow_fetch");
        if let Ok(val) = env::var("PLATFORM_NAME");
        if val.ends_with("simulator");
        then {
            let url = matches.value_of("fetch_from").unwrap_or("http://127.0.0.1:8081/");
            info!("Fetching sourcemaps from {}", url);
            fetch_url = Some(url);
        } else {
            info!("Using react-native build script at {}", base.display());
            fetch_url = None;
        }
    }

    // in case we are in debug mode we directly dispatch to the script
    // and exit out early.
    if !should_wrap && fetch_url.is_none() {
        info!("Running in debug mode, skipping script wrapping.");
        let rv = process::Command::new(&script).spawn()?.wait()?;
        propagate_exit_status(rv);
        return Ok(());
    }

    info!("Parsing Info.plist");
    let plist = match xcode::InfoPlist::discover_from_env()? {
        Some(plist) => plist,
        None => { return Err("Could not find info.plist".into()); }
    };
    info!("Parse result from Info.plist: {:?}", &plist);
    let report_file = TempFile::new()?;
    let node = find_node();
    info!("Using node interpreter '{}'", &node);

    xcode::MayDetach::wrap("React native symbol handling", |md| {
        let bundle_path;
        let sourcemap_path;
        let bundle_url;
        let sourcemap_url;
        let bundle_file;
        let sourcemap_file;

        // If we have a fetch URL we need to fetch them from there now.  In that
        // case we do indeed fetch it right from the running packager and then
        // store it in temporary files for later consumption.
        if let Some(url) = fetch_url {
            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let url = url.trim_right_matches('/');
            bundle_file = TempFile::new()?;
            bundle_path = bundle_file.path().to_path_buf();
            bundle_url = "~/index.ios.bundle".to_string();
            sourcemap_file = TempFile::new()?;
            sourcemap_path = sourcemap_file.path().to_path_buf();
            sourcemap_url = "~/index.ios.map".to_string();

            // wait up to 10 seconds for the server to be up.
            if !api.wait_until_available(url, Duration::seconds(10))? {
                return Err("Error: react-native packager did not respond in time".into());
            }

            api.download(&format!("{}/index.ios.bundle?platform=ios&dev=true", url),
                         &mut bundle_file.open())?;
            api.download(&format!("{}/index.ios.map?platform=ios&dev=true", url),
                         &mut sourcemap_file.open())?;

        // This is the case where we need to hook into the release process to
        // collect sourcemaps when they are generated.
        //
        // this invokes via an indirection of sentry-cli our wrap_call() below.
        // What is happening behind the scenes is that we switch out NODE_BINARY
        // for ourselves which is what the react-native build script normally
        // invokes.  Because we export __SENTRY_RN_WRAP_XCODE_CALL=1, the main
        // sentry-cli script will invoke our wrap_call() function below.
        //
        // That will then attempt to figure out that a react-native bundle is
        // happening to the build script, parse out the arguments, add additional
        // arguments if needed and then report the parsed arguments to a temporary
        // JSON file we load back below.
        //
        // With that we we then have all the information we need to invoke the
        // upload process.
        } else {
            let rv = process::Command::new(&script)
                .env("NODE_BINARY", env::current_exe()?.to_str().unwrap())
                .env("SENTRY_RN_REAL_NODE_BINARY", &node)
                .env("SENTRY_RN_SOURCEMAP_REPORT", report_file.path().to_str().unwrap())
                .env("__SENTRY_RN_WRAP_XCODE_CALL", "1")
                .spawn()?
                .wait()?;
            propagate_exit_status(rv);

            if !matches.is_present("force_foreground") {
                md.may_detach()?;
            }
            let mut f = fs::File::open(report_file.path())?;
            let report : SourceMapReport = serde_json::from_reader(&mut f)?;
            if report.bundle_path.is_none() || report.sourcemap_path.is_none() {
                println!("Warning: build produced no sourcemaps.");
                return Ok(());
            }

            bundle_path = report.bundle_path.unwrap();
            bundle_url = format!("~/{}", bundle_path.file_name()
                                 .unwrap().to_string_lossy());
            sourcemap_path = report.sourcemap_path.unwrap();
            sourcemap_url = format!("~/{}", sourcemap_path.file_name()
                                    .unwrap().to_string_lossy());
        }

        // now that we have all the data, we can now process and upload the
        // sourcemaps.
        println!("Processing react-native sourcemaps for Sentry upload.");
        info!("  bundle path: {}", bundle_path.display());
        info!("  sourcemap path: {}", sourcemap_path.display());

        let mut processor = SourceMapProcessor::new(matches.is_present("verbose"));
        processor.add(&bundle_url, &bundle_path)?;
        processor.add(&sourcemap_url, &sourcemap_path)?;
        processor.rewrite(&vec![base.parent().unwrap().to_str().unwrap()])?;
        processor.add_sourcemap_references()?;

        let release = api.new_release(&org, &project, &NewRelease {
            version: plist.release_name(),
            ..Default::default()
        })?;
        println!("Uploading sourcemaps for release {}", release.version);
        processor.upload(&api, &org, &project, &release.version)?;

        Ok(())
    })
}

pub fn wrap_call() -> Result<()> {
    let mut args : Vec<_> = env::args().skip(1).collect();
    let mut bundle_path = None;
    let mut sourcemap_path = None;

    if args.len() > 1 && args[1] == "bundle" {
        let mut iter = args.iter().fuse();
        while let Some(item) = iter.next() {
            if item == "--sourcemap-output" {
                sourcemap_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--sourcemap-output=") {
                sourcemap_path = Some(item[19..].to_string());
            } else if item == "--bundle-output" {
                bundle_path = iter.next().map(|x| x.to_string());
            } else if item.starts_with("--bundle-output=") {
                bundle_path = Some(item[16..].to_string());
            }
        }
    }

    let mut sourcemap_report = SourceMapReport::default();

    if sourcemap_path.is_none() && bundle_path.is_some() {
        let path = format!("{}.map", &bundle_path.as_ref().unwrap());
        sourcemap_report.sourcemap_path = Some(PathBuf::from(&path));
        args.push("--sourcemap-output".into());
        args.push(path);
    } else if let Some(path) = sourcemap_path {
        sourcemap_report.sourcemap_path = Some(PathBuf::from(path));
    }

    sourcemap_report.bundle_path = bundle_path.map(|x| PathBuf::from(x));

    let rv = process::Command::new(env::var("SENTRY_RN_REAL_NODE_BINARY").unwrap())
        .args(&args)
        .spawn()?
        .wait()?;
    propagate_exit_status(rv);

    let mut f = fs::File::create(env::var("SENTRY_RN_SOURCEMAP_REPORT").unwrap())?;
    serde_json::to_writer(&mut f, &sourcemap_report)?;

    Ok(())
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Write;
use std::io::Seek;
use std::path::Path;
use std::process::Command;

extern crate tools;
use tools::find_source_file;
use tools::file_format::analysis::{read_analysis, read_source, read_jumps};
use tools::format::format_file_data;
use tools::config;
use tools::languages;
use languages::FormatAs;

use tools::output::{PanelItem, PanelSection};

fn format_documentation(input_fname: &str, output_fname: &str) {
    let _ = Command::new("pandoc")
        .arg("--css")
        .arg("/static/css/pandoc.css")
        .arg("-o")
        .arg(output_fname)
        .arg("-w")
        .arg("html")
        .arg(input_fname)
        .status();
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let (base_args, fname_args) = args.split_at(3);

    let cfg = config::load(&base_args[1], false);
    println!("Config file read");

    let tree_name = &base_args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let jumps_fname = format!("{}/jumps", tree_config.paths.index_path);
    //let jumps : std::collections::HashMap<String, tools::analysis::Jump> = std::collections::HashMap::new();
    let jumps = read_jumps(&jumps_fname);
    println!("Jumps read");

    let (blame_commit, head_oid) = match &tree_config.git {
        &Some(ref git) => {
            let head_oid = git.repo.refname_to_id("HEAD").unwrap();
            let blame_commit = if let Some(ref blame_repo) = git.blame_repo {
                let blame_oid = blame_repo.refname_to_id("HEAD").unwrap();
                Some(blame_repo.find_commit(blame_oid).unwrap())
            } else {
                None
            };
            (blame_commit, Some(head_oid))
        },
        &None => (None, None),
    };

    for path in fname_args {
        println!("File {}", path);

        let output_fname = format!("{}/file/{}", tree_config.paths.index_path, path);
        let source_fname = find_source_file(path, &tree_config.paths.files_path, &tree_config.paths.objdir_path);

        let format = languages::select_formatting(path);
        match format {
            FormatAs::FormatDoc(_) => {
                let _ = format_documentation(&source_fname, &output_fname);
                continue;
            },
            _ => {},
        };

        let output_file = File::create(output_fname).unwrap();
        let mut writer = BufWriter::new(output_file);

        let source_file = match File::open(source_fname.clone()) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open file");
                continue;
            },
        };

        let p = Path::new(&source_fname);
        let metadata = fs::symlink_metadata(p).unwrap();
        if metadata.file_type().is_symlink() {
            let dest = fs::read_link(p).unwrap();
            write!(writer, "Symlink to {}", dest.to_str().unwrap()).unwrap();
            continue;
        }

        let mut reader = BufReader::new(&source_file);

        match format {
            FormatAs::Binary => {
                let _ = io::copy(&mut reader, &mut writer);
                continue;
            },
            _ => {},
        };

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &read_source);

        let mut input = String::new();
        match reader.read_to_string(&mut input) {
            Ok(_) => {},
            Err(_) => {
                let mut bytes = Vec::new();
                reader.seek(std::io::SeekFrom::Start(0)).unwrap();
                match reader.read_to_end(&mut bytes) {
                    Ok(_) => {
                        input.push_str(&bytes.iter().map(|c| *c as char).collect::<String>());
                    },
                    Err(e) => {
                        println!("Unable to read file: {:?}", e);
                        continue;
                    }
                }
            }
        }

        let panel = if path.contains("__GENERATED__") {
            vec![]
        } else if let Some(oid) = head_oid {
            vec![PanelSection {
                name: "Revision control".to_owned(),
                items: vec![PanelItem {
                    title: "Permalink".to_owned(),
                    link: format!("/{}/rev/{}/{}", tree_name, oid, path),
                    update_link_lineno: true,
                }, PanelItem {
                    title: "Log".to_owned(),
                    link: format!("{}/log/tip/{}", config::get_hg_root(tree_config), path),
                    update_link_lineno: false,
                 }, PanelItem {
                    title: "Raw".to_owned(),
                    link: format!("{}/raw-file/tip/{}", config::get_hg_root(tree_config), path),
                    update_link_lineno: false,
                }, PanelItem {
                    title: "Blame".to_owned(),
                    link: "javascript:alert('Hover over the gray bar on the left to see blame information.')".to_owned(),
                    update_link_lineno: false,
                }],
            }]
        } else {
            vec![]
        };

        let head_commit = head_oid
            .and_then(|oid| tree_config.git.as_ref().unwrap().repo.find_commit(oid).ok());

        format_file_data(&cfg,
                         tree_name,
                         &panel,
                         &head_commit,
                         &blame_commit,
                         path,
                         input,
                         &jumps,
                         &analysis,
                         &mut writer).unwrap();
    }
}
<commit_msg>Add link to switch between .h and .cpp files<commit_after>use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Write;
use std::io::Seek;
use std::path::Path;
use std::process::Command;

extern crate tools;
use tools::find_source_file;
use tools::file_format::analysis::{read_analysis, read_source, read_jumps};
use tools::format::format_file_data;
use tools::config;
use tools::languages;
use languages::FormatAs;

use tools::output::{PanelItem, PanelSection};

fn format_documentation(input_fname: &str, output_fname: &str) {
    let _ = Command::new("pandoc")
        .arg("--css")
        .arg("/static/css/pandoc.css")
        .arg("-o")
        .arg(output_fname)
        .arg("-w")
        .arg("html")
        .arg(input_fname)
        .status();
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let (base_args, fname_args) = args.split_at(3);

    let cfg = config::load(&base_args[1], false);
    println!("Config file read");

    let tree_name = &base_args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let jumps_fname = format!("{}/jumps", tree_config.paths.index_path);
    //let jumps : std::collections::HashMap<String, tools::analysis::Jump> = std::collections::HashMap::new();
    let jumps = read_jumps(&jumps_fname);
    println!("Jumps read");

    let (blame_commit, head_oid) = match &tree_config.git {
        &Some(ref git) => {
            let head_oid = git.repo.refname_to_id("HEAD").unwrap();
            let blame_commit = if let Some(ref blame_repo) = git.blame_repo {
                let blame_oid = blame_repo.refname_to_id("HEAD").unwrap();
                Some(blame_repo.find_commit(blame_oid).unwrap())
            } else {
                None
            };
            (blame_commit, Some(head_oid))
        },
        &None => (None, None),
    };

    for path in fname_args {
        println!("File {}", path);

        let output_fname = format!("{}/file/{}", tree_config.paths.index_path, path);
        let source_fname = find_source_file(path, &tree_config.paths.files_path, &tree_config.paths.objdir_path);

        let format = languages::select_formatting(path);
        match format {
            FormatAs::FormatDoc(_) => {
                let _ = format_documentation(&source_fname, &output_fname);
                continue;
            },
            _ => {},
        };

        let output_file = File::create(output_fname).unwrap();
        let mut writer = BufWriter::new(output_file);

        let source_file = match File::open(source_fname.clone()) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open file");
                continue;
            },
        };

        let path_wrapper = Path::new(&source_fname);
        let metadata = fs::symlink_metadata(path_wrapper).unwrap();
        if metadata.file_type().is_symlink() {
            let dest = fs::read_link(path_wrapper).unwrap();
            write!(writer, "Symlink to {}", dest.to_str().unwrap()).unwrap();
            continue;
        }

        let mut reader = BufReader::new(&source_file);

        match format {
            FormatAs::Binary => {
                let _ = io::copy(&mut reader, &mut writer);
                continue;
            },
            _ => {},
        };

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &read_source);

        let mut input = String::new();
        match reader.read_to_string(&mut input) {
            Ok(_) => {},
            Err(_) => {
                let mut bytes = Vec::new();
                reader.seek(std::io::SeekFrom::Start(0)).unwrap();
                match reader.read_to_end(&mut bytes) {
                    Ok(_) => {
                        input.push_str(&bytes.iter().map(|c| *c as char).collect::<String>());
                    },
                    Err(e) => {
                        println!("Unable to read file: {:?}", e);
                        continue;
                    }
                }
            }
        }

        let mut extension_mapping = HashMap::new();
        extension_mapping.insert("cpp", ("header", vec!["h", "hh", "hpp", "hxx"]));
        extension_mapping.insert("cc", ("header", vec!["h", "hh", "hpp", "hxx"]));
        extension_mapping.insert("cxx", ("header", vec!["h", "hh", "hpp", "hxx"]));
        extension_mapping.insert("h", ("source", vec!["cpp", "cc", "cxx"]));
        extension_mapping.insert("hh", ("source", vec!["cpp", "cc", "cxx"]));
        extension_mapping.insert("hpp", ("source", vec!["cpp", "cc", "cxx"]));
        extension_mapping.insert("hxx", ("source", vec!["cpp", "cc", "cxx"]));

        let extension = path_wrapper.extension().unwrap_or(&OsStr::new("")).to_str().unwrap();
        let show_header = match extension_mapping.get(extension) {
            Some(&(ref description, ref try_extensions)) => {
                let mut result = None;
                for try_ext in try_extensions {
                    let try_buf = path_wrapper.with_extension(try_ext);
                    let try_path = try_buf.as_path();
                    if try_path.exists() {
                        let (path_base, _) = path.split_at(path.len() - extension.len() - 1);
                        result = Some((description.to_owned(), format!("/{}/source/{}.{}", tree_name, path_base, try_ext)));
                        break;
                    }
                }
                result
            },
            None => {
                None
            }
        };

        let mut panel = if let Some((other_desc, other_path)) = show_header {
            vec![PanelSection {
                name: "Source code".to_owned(),
                items: vec![PanelItem {
                    title: format!("Go to {} file", other_desc),
                    link: other_path,
                    update_link_lineno: false,
                }],
            }]
        } else {
            vec![]
        };

        let mut rev_panel = if path.contains("__GENERATED__") {
            vec![]
        } else if let Some(oid) = head_oid {
            vec![PanelSection {
                name: "Revision control".to_owned(),
                items: vec![PanelItem {
                    title: "Permalink".to_owned(),
                    link: format!("/{}/rev/{}/{}", tree_name, oid, path),
                    update_link_lineno: true,
                }, PanelItem {
                    title: "Log".to_owned(),
                    link: format!("{}/log/tip/{}", config::get_hg_root(tree_config), path),
                    update_link_lineno: false,
                 }, PanelItem {
                    title: "Raw".to_owned(),
                    link: format!("{}/raw-file/tip/{}", config::get_hg_root(tree_config), path),
                    update_link_lineno: false,
                }, PanelItem {
                    title: "Blame".to_owned(),
                    link: "javascript:alert('Hover over the gray bar on the left to see blame information.')".to_owned(),
                    update_link_lineno: false,
                }],
            }]
        } else {
            vec![]
        };

        panel.append(&mut rev_panel);

        let head_commit = head_oid
            .and_then(|oid| tree_config.git.as_ref().unwrap().repo.find_commit(oid).ok());

        format_file_data(&cfg,
                         tree_name,
                         &panel,
                         &head_commit,
                         &blame_commit,
                         path,
                         input,
                         &jumps,
                         &analysis,
                         &mut writer).unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate lichen;

use lichen::parse::Parser;
use lichen::var::Var;
use lichen::eval::Evaluator;

// Test for mutable state
#[derive(Debug)]
struct Player {
    coins: f32,
    name: String
}

#[test]
fn state_mut() {
    let src = "\n
def global\n
;\n
\n
root\n
    @global.coins 1\n
    emit \"step\"\n
    @global.coins + 5\n
    emit global.coins\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0], 6.0 .into());
}

#[test]
fn parse_cust_fn() {
    let src = "root\n
    @coins (inc) 1 2 3\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    {
        let ev = Evaluator::new(&mut env);
        let _ = ev.last();
    }

    assert_eq!(data.coins, 6.0);
}

#[test]
fn parse_def_block() {
    let src = "root\n
    emit global.name global.size\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], Var::String("my-game".to_owned()));
    assert_eq!(vars[1], Var::Num(1.5 .to_owned()));
}

#[test]
fn validate_def_block() {
    let src = "root\n
    @global.size + 0.5\n
    @global.name \"other-game\"\n
    emit global.name global.size\n\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("other-game".to_owned()));
    assert_eq!(vars[1], Var::Num(2.0 .to_owned()));
}

#[test]
fn parse_when_block() {
    let src = "root\n
    needs_coins global.coins < 1\n
    has_no_name !global.name\n
    when {needs_coins @global.coins + 2, \n
         has_no_name @global.name \"new-name\"}\n
    emit global.name global.coins\n
;\n
def global\n
    coins 0\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[1], 2.0 .into());
    assert_eq!(vars[0], "new-name".into());
}

#[test]
fn save_state() {
    let src = "root\n
    next:now other\n
;\n
\n
other\n
    @coins 1\n
    next:await end\n
    emit \"bye\"\n
;\n
\n
end\n
    emit true\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    let state = {
        let mut ev = Evaluator::new(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 0.0);

    let state = {
        let mut ev = state.to_eval(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 1.0);

    //check if we held our place within node after await
    let ev = state.to_eval(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0],"bye".into());
}

#[test]
fn parse_sym_call_value() {
    let src = "def root\n
    tag \"--\"\n
;\n
\n
root\n
    @name (wrap) root.tag\n
    emit name\n
;";

    let p = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    let mut env = p.into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("--Pan--".to_owned()));
}

#[test]
fn validate_or_block() {
    let src = "root\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
\n
    @global.drunk true\n
\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
;\n
\n
def global\n
    drunk false
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], "not drunk".into());

    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], "is drunk".into());
}
<commit_msg>fix tests<commit_after>extern crate lichen;

use lichen::parse::Parser;
use lichen::var::Var;
use lichen::eval::Evaluator;

// Test for mutable state
#[derive(Debug)]
struct Player {
    coins: f32,
    name: String
}

#[test]
fn state_mut() {
    let src = "\n
def global\n
;\n
\n
root\n
    @global.coins 1\n
    emit \"step\"\n
    @global.coins + 5\n
    emit global.coins\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0], 6.0 .into());
}

#[test]
fn parse_cust_fn() {
    let src = "root\n
    @coins (inc) 1 2 3\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    {
        let ev = Evaluator::new(&mut env);
        let _ = ev.last();
    }

    assert_eq!(data.coins, 6.0);
}

#[test]
fn parse_def_block() {
    let src = "root\n
    emit global.name global.size\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], Var::String("my-game".to_owned()));
    assert_eq!(vars[1], Var::Num(1.5 .to_owned()));
}

#[test]
fn validate_def_block() {
    let src = "root\n
    @global.size + 0.5\n
    @global.name \"other-game\"\n
    emit global.name global.size\n\n
;\n
\n
def global\n
    name \"my-game\"\n
    size 1.5\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("other-game".to_owned()));
    assert_eq!(vars[1], Var::Num(2.0 .to_owned()));
}

#[test]
fn validate_when_block() {
    let src = "root\n
    needs_coins global.coins < 1\n
    when {needs_coins @global.coins + 2, \n
         !global.name @global.name \"new-name\"}\n
    emit global.name global.coins\n
;\n
def global\n
    coins 0\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[1], 2.0 .into());
    assert_eq!(vars[0], "new-name".into());
}

#[test]
fn save_state() {
    let src = "root\n
    next:now other\n
;\n
\n
other\n
    @coins 1\n
    next:await end\n
    emit \"bye\"\n
;\n
\n
end\n
    emit true\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let data = Player { coins: 0.0, name: "Pan".to_owned() };

    let state = {
        let mut ev = Evaluator::new(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 0.0);

    let state = {
        let mut ev = state.to_eval(&mut env);
        let (_,_) = ev.next().unwrap();
        ev.save()
    };

    assert_eq!(data.coins, 1.0);

    //check if we held our place within node after await
    let ev = state.to_eval(&mut env);
    let (vars,_) = ev.last().unwrap();
    assert_eq!(vars[0],"bye".into());
}

#[test]
fn parse_sym_call_value() {
    let src = "def root\n
    tag \"--\"\n
;\n
\n
root\n
    @name (wrap) root.tag\n
    emit name\n
;";

    let p = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    let mut env = p.into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();

    assert_eq!(vars[0], Var::String("--Pan--".to_owned()));
}

#[test]
fn validate_or_block() {
    let src = "root\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
\n
    @global.drunk true\n
\n
    if !global.drunk \"not drunk\"\n
    or \"is drunk\"\n
;\n
\n
def global\n
    drunk false
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();

    assert_eq!(vars[0], "not drunk".into());

    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], "is drunk".into());
}
<|endoftext|>