<commit_before>// Copyright 2017 CoreOS, Inc.
//
// 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.

//! packet metadata fetcher

use std::collections::HashMap;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;

use openssh_keys::PublicKey;
use pnet::util::MacAddr;
use update_ssh_keys::AuthorizedKeyEntry;

use errors::*;
use network::{self, Interface, Device, Section, NetworkRoute};
use providers::MetadataProvider;
use retry;
use util;

use ipnetwork::{self, IpNetwork, Ipv4Network, Ipv6Network};

#[derive(Clone, Debug, Deserialize)]
struct PacketData {
    id: String,
    hostname: String,
    iqn: String,
    plan: String,
    facility: String,
    tags: Vec<String>,
    ssh_keys: Vec<String>,
    network: PacketNetworkInfo,

    error: Option<String>,
    phone_home_url: String,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketNetworkInfo {
    interfaces: Vec<PacketInterfaceInfo>,
    addresses: Vec<PacketAddressInfo>,
    bonding: PacketBondingMode,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketBondingMode {
    mode: u32,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketInterfaceInfo {
    name: String,
    mac: String,
    bond: Option<String>,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketAddressInfo {
    id: String,
    address_family: i32,
    public: bool,
    management: bool,
    address: IpAddr,
    netmask: IpAddr,
    gateway: IpAddr,
}

#[derive(Clone, Debug)]
pub struct PacketProvider {
    data: PacketData,
}

impl PacketProvider {
    pub fn new() -> Result<PacketProvider> {
        let client = retry::Client::new()?;

        let data: PacketData = client
            .get(retry::Json, "http://metadata.packet.net/metadata".to_string())
            .send()?
            .ok_or("not found")?;

        Ok(PacketProvider{ data })
    }

    fn get_attrs(&self) -> Result<Vec<(String,String)>> {
        let mut attrs = Vec::new();
        let mut v4_public_counter = 0;
        let mut v4_private_counter = 0;
        let mut v6_public_counter = 0;
        let mut v6_private_counter = 0;
        for a in self.data.network.addresses.clone() {
            match (a.address,a.public) {
                (IpAddr::V4(a),true) => {
                    attrs.push((format!("PACKET_IPV4_PUBLIC_{}", v4_public_counter), format!("{}", a)));
                    v4_public_counter += 1;
                }
                (IpAddr::V4(a),false) => {
                    attrs.push((format!("PACKET_IPV4_PRIVATE_{}", v4_private_counter), format!("{}", a)));
                    v4_private_counter += 1;
                }
                (IpAddr::V6(a),true) => {
                    attrs.push((format!("PACKET_IPV6_PUBLIC_{}", v6_public_counter), format!("{}", a)));
                    v6_public_counter += 1;
                }
                (IpAddr::V6(a),false) => {
                    attrs.push((format!("PACKET_IPV6_PRIVATE_{}", v6_private_counter), format!("{}", a)));
                    v6_private_counter += 1;
                }
            }
        }
        attrs.push(("PACKET_HOSTNAME".to_owned(), self.data.hostname.clone()));
        attrs.push(("PACKET_PHONE_HOME_URL".to_owned(), self.data.phone_home_url.clone()));
        Ok(attrs)
    }

    fn get_dns_servers() -> Result<Vec<IpAddr>> {
        let f = File::open("/run/systemd/netif/state")
            .chain_err(|| "failed to open /run/systemd/netif/state")?;
        let ip_strings = util::key_lookup('=', "DNS", f)
            .chain_err(|| "failed to parse /run/systemd/netif/state")?
            .ok_or("DNS not found in netif state file")?;
        let mut addrs = Vec::new();
        for ip_string in ip_strings.split(' ') {
            addrs.push(IpAddr::from_str(ip_string)
                .chain_err(|| "failed to parse IP address")?);
        }
        if addrs.is_empty() {
            return Err("no DNS servers in /run/systemd/netif/state".into());
        }
        Ok(addrs)
    }

    fn parse_network(&self) -> Result<(Vec<Interface>,Vec<Device>)> {
        let netinfo = &self.data.network;
        let mut interfaces = Vec::new();
        let mut bonds = Vec::new();
        let dns_servers = PacketProvider::get_dns_servers()?;
        for i in netinfo.interfaces.clone() {
            let mac = MacAddr::from_str(&i.mac)
                .map_err(|err| Error::from(format!("{:?}", err)))
                .chain_err(|| format!("failed to parse mac address: '{}'", i.mac))?;
            interfaces.push(Interface {
                mac_address: Some(mac),
                bond: i.bond.clone(),
                name: None,
                priority: None,
                nameservers: Vec::new(),
                ip_addresses: Vec::new(),
                routes: Vec::new(),
                // the interface should be unmanaged if it doesn't have a bond
                // section
                unmanaged: i.bond.is_none(),
            });

            // if there is a bond key, make sure we have a bond device for it
            if let Some(ref bond_name) = i.bond {
                let bond = Interface {
                    name: Some(bond_name.clone()),
                    priority: Some(5),
                    nameservers: dns_servers.clone(),
                    mac_address: None,
                    bond: None,
                    ip_addresses: Vec::new(),
                    routes: Vec::new(),
                    unmanaged: false,
                };
                if !bonds.iter().any(|&(_, ref b): &(MacAddr, Interface)| &bond == b) {
                    bonds.push((mac, bond));
                }
            }
        }

        // according to the folks from packet, all the addresses given to us in the
        // network section should be attached to the first bond we find in the list
        // of interfaces. we should always have at least one bond listed, but if we
        // don't find any, we just print out a scary warning and don't attach the
        // addresses to anything.
        if bonds.is_empty() {
            warn!("no bond interfaces. addresses are left unassigned.");
            // the rest of the function operates on bonds, so just return
            return Ok((interfaces, vec![]));
        }

        // remove panics if the index is out of bounds, but we know that there is at
        // least one bond in the vector because we return if it's empty
        let (first_mac, mut first_bond) = bonds.remove(0);
        for a in netinfo.addresses.clone() {
            let prefix = ipnetwork::ip_mask_to_prefix(a.netmask)
                .chain_err(|| "invalid network mask")?;
            first_bond.ip_addresses.push(IpNetwork::new(a.address, prefix)
                                    .chain_err(|| "invalid IP address or prefix")?);
            let dest = match (a.public,a.address) {
                (false,IpAddr::V4(_)) =>
                    IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(10,0,0,0),8).unwrap()),
                (true,IpAddr::V4(_)) =>
                    IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(0,0,0,0),0).unwrap()),
                (_,IpAddr::V6(_)) =>
                    IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0,0,0,0,0,0,0,0),0).unwrap()),
            };
            first_bond.routes.push(NetworkRoute {
                destination: dest,
                gateway: a.gateway,
            });
        }
        bonds.push((first_mac, first_bond));

        let mut attrs = vec![
            ("TransmitHashPolicy".to_owned(), "layer3+4".to_owned()),
            ("MIIMonitorSec".to_owned(), ".1".to_owned()),
            ("UpDelaySec".to_owned(), ".2".to_owned()),
            ("DownDelaySec".to_owned(), ".2".to_owned()),
            ("Mode".to_owned(), network::bonding_mode_to_string(netinfo.bonding.mode)?),
        ];
        if netinfo.bonding.mode == network::BONDING_MODE_LACP {
            attrs.push(("LACPTransmitRate".to_owned(), "fast".to_owned()));
        }

        let mut network_devices = vec![];
        for (mac, bond) in bonds {
            network_devices.push(Device {
                name: bond.name.clone()
                    .ok_or("bond doesn't have a name, should be impossible")?,
                kind: "bond".to_owned(),
                mac_address: mac,
                priority: Some(5),
                sections: vec![
                    Section{
                        name: "Bond".to_owned(),
                        attributes: attrs.clone(),
                    }
                ],
            });
            // finally, make sure the bond interfaces are in the interface list
            interfaces.push(bond)
        }

        Ok((interfaces,network_devices))
    }
}

impl MetadataProvider for PacketProvider {
    fn attributes(&self) -> Result<HashMap<String, String>> {
        self.get_attrs().map(|attrs| attrs.into_iter().collect())
    }

    fn hostname(&self) -> Result<Option<String>> {
        Ok(Some(self.data.hostname.clone()))
    }

    fn ssh_keys(&self) -> Result<Vec<AuthorizedKeyEntry>> {
        let mut out = Vec::new();

        for key in &self.data.ssh_keys {
            let key = PublicKey::parse(&key)?;
            out.push(AuthorizedKeyEntry::Valid{key});
        }

        Ok(out)
    }

    fn networks(&self) -> Result<Vec<network::Interface>> {
        let (interfaces, _devices) = self.parse_network()?;

        Ok(interfaces)
    }

    fn network_devices(&self) -> Result<Vec<network::Device>> {
        let (_interfaces, devices) = self.parse_network()?;

        Ok(devices)
    }
}
<commit_msg>providers/packet: minor code cleanup<commit_after>// Copyright 2017 CoreOS, Inc.
//
// 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.

//! Metadata fetcher for Packet.net.
//!
//! Metadata JSON schema is described in their
//! [knowledge base](https://help.packet.net/article/37-metadata).

use std::collections::HashMap;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;

use openssh_keys::PublicKey;
use pnet::util::MacAddr;
use update_ssh_keys::AuthorizedKeyEntry;

use errors::*;
use network::{self, Interface, Device, Section, NetworkRoute};
use providers::MetadataProvider;
use retry;
use util;

use ipnetwork::{self, IpNetwork, Ipv4Network, Ipv6Network};

#[derive(Clone, Debug, Deserialize)]
struct PacketData {
    id: String,
    hostname: String,
    iqn: String,
    plan: String,
    facility: String,
    tags: Vec<String>,
    ssh_keys: Vec<String>,
    network: PacketNetworkInfo,

    error: Option<String>,
    phone_home_url: String,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketNetworkInfo {
    interfaces: Vec<PacketInterfaceInfo>,
    addresses: Vec<PacketAddressInfo>,
    bonding: PacketBondingMode,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketBondingMode {
    mode: u32,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketInterfaceInfo {
    name: String,
    mac: String,
    bond: Option<String>,
}

#[derive(Clone, Debug, Deserialize)]
struct PacketAddressInfo {
    id: String,
    address_family: i32,
    public: bool,
    management: bool,
    address: IpAddr,
    netmask: IpAddr,
    gateway: IpAddr,
}

#[derive(Clone, Debug)]
pub struct PacketProvider {
    data: PacketData,
}

impl PacketProvider {
    pub fn new() -> Result<PacketProvider> {
        let client = retry::Client::new()?;

        let data: PacketData = client
            .get(retry::Json, "http://metadata.packet.net/metadata".to_string())
            .send()?
            .ok_or("not found")?;

        Ok(PacketProvider{ data })
    }

    fn get_attrs(&self) -> Result<Vec<(String,String)>> {
        let mut attrs = Vec::new();
        let mut v4_public_counter = 0;
        let mut v4_private_counter = 0;
        let mut v6_public_counter = 0;
        let mut v6_private_counter = 0;
        for a in self.data.network.addresses.clone() {
            match (a.address,a.public) {
                (IpAddr::V4(a),true) => {
                    attrs.push((format!("PACKET_IPV4_PUBLIC_{}", v4_public_counter), format!("{}", a)));
                    v4_public_counter += 1;
                }
                (IpAddr::V4(a),false) => {
                    attrs.push((format!("PACKET_IPV4_PRIVATE_{}", v4_private_counter), format!("{}", a)));
                    v4_private_counter += 1;
                }
                (IpAddr::V6(a),true) => {
                    attrs.push((format!("PACKET_IPV6_PUBLIC_{}", v6_public_counter), format!("{}", a)));
                    v6_public_counter += 1;
                }
                (IpAddr::V6(a),false) => {
                    attrs.push((format!("PACKET_IPV6_PRIVATE_{}", v6_private_counter), format!("{}", a)));
                    v6_private_counter += 1;
                }
            }
        }
        attrs.push(("PACKET_HOSTNAME".to_owned(), self.data.hostname.clone()));
        attrs.push(("PACKET_PHONE_HOME_URL".to_owned(), self.data.phone_home_url.clone()));
        Ok(attrs)
    }

    fn get_dns_servers() -> Result<Vec<IpAddr>> {
        let f = File::open("/run/systemd/netif/state")
            .chain_err(|| "failed to open /run/systemd/netif/state")?;
        let ip_strings = util::key_lookup('=', "DNS", f)
            .chain_err(|| "failed to parse /run/systemd/netif/state")?
            .ok_or("DNS not found in netif state file")?;
        let mut addrs = Vec::new();
        for ip_string in ip_strings.split(' ') {
            addrs.push(IpAddr::from_str(ip_string)
                .chain_err(|| "failed to parse IP address")?);
        }
        if addrs.is_empty() {
            return Err("no DNS servers in /run/systemd/netif/state".into());
        }
        Ok(addrs)
    }

    fn parse_network(&self) -> Result<(Vec<Interface>,Vec<Device>)> {
        let netinfo = &self.data.network;
        let mut interfaces = Vec::new();
        let mut bonds = Vec::new();
        let dns_servers = PacketProvider::get_dns_servers()?;
        for i in netinfo.interfaces.clone() {
            let mac = MacAddr::from_str(&i.mac)
                .map_err(|err| Error::from(format!("{:?}", err)))
                .chain_err(|| format!("failed to parse mac address: '{}'", i.mac))?;
            interfaces.push(Interface {
                mac_address: Some(mac),
                bond: i.bond.clone(),
                name: None,
                priority: None,
                nameservers: Vec::new(),
                ip_addresses: Vec::new(),
                routes: Vec::new(),
                // the interface should be unmanaged if it doesn't have a bond
                // section
                unmanaged: i.bond.is_none(),
            });

            // if there is a bond key, make sure we have a bond device for it
            if let Some(ref bond_name) = i.bond {
                let bond = Interface {
                    name: Some(bond_name.clone()),
                    priority: Some(5),
                    nameservers: dns_servers.clone(),
                    mac_address: None,
                    bond: None,
                    ip_addresses: Vec::new(),
                    routes: Vec::new(),
                    unmanaged: false,
                };
                if !bonds.iter().any(|&(_, ref b): &(MacAddr, Interface)| &bond == b) {
                    bonds.push((mac, bond));
                }
            }
        }

        // According to the folks from packet, all the addresses given to us in the
        // network section should be attached to the first bond we find in the list
        // of interfaces. We should always have at least one bond listed, but if we
        // don't find any, we just print out a scary warning and don't attach the
        // addresses to anything.
        if let Some((_mac, ref mut first_bond)) = bonds.get_mut(0) {
            for a in netinfo.addresses.clone() {
                let prefix = ipnetwork::ip_mask_to_prefix(a.netmask)
                    .chain_err(|| "invalid network mask")?;
                first_bond.ip_addresses.push(IpNetwork::new(a.address, prefix)
                                        .chain_err(|| "invalid IP address or prefix")?);
                let dest = match (a.public, a.address) {
                    (false, IpAddr::V4(_)) =>
                        IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(10,0,0,0),8).unwrap()),
                    (true, IpAddr::V4(_)) =>
                        IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(0,0,0,0),0).unwrap()),
                    (_, IpAddr::V6(_)) =>
                        IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0,0,0,0,0,0,0,0),0).unwrap()),
                };
                first_bond.routes.push(NetworkRoute {
                    destination: dest,
                    gateway: a.gateway,
                });
            }
        } else {
            warn!("no bond interfaces. addresses are left unassigned.");
            // the rest of the function operates on bonds, so just return
            return Ok((interfaces, vec![]));
        }

        let mut attrs = vec![
            ("TransmitHashPolicy".to_owned(), "layer3+4".to_owned()),
            ("MIIMonitorSec".to_owned(), ".1".to_owned()),
            ("UpDelaySec".to_owned(), ".2".to_owned()),
            ("DownDelaySec".to_owned(), ".2".to_owned()),
            ("Mode".to_owned(), network::bonding_mode_to_string(netinfo.bonding.mode)?),
        ];
        if netinfo.bonding.mode == network::BONDING_MODE_LACP {
            attrs.push(("LACPTransmitRate".to_owned(), "fast".to_owned()));
        }

        let mut network_devices = Vec::with_capacity(bonds.len());
        for (mac, bond) in bonds {
            network_devices.push(Device {
                name: bond.name.clone()
                    .ok_or("bond doesn't have a name, should be impossible")?,
                kind: "bond".to_owned(),
                mac_address: mac,
                priority: Some(5),
                sections: vec![
                    Section{
                        name: "Bond".to_owned(),
                        attributes: attrs.clone(),
                    }
                ],
            });
            // finally, make sure the bond interfaces are in the interface list
            interfaces.push(bond)
        }

        Ok((interfaces,network_devices))
    }
}

impl MetadataProvider for PacketProvider {
    fn attributes(&self) -> Result<HashMap<String, String>> {
        self.get_attrs().map(|attrs| attrs.into_iter().collect())
    }

    fn hostname(&self) -> Result<Option<String>> {
        Ok(Some(self.data.hostname.clone()))
    }

    fn ssh_keys(&self) -> Result<Vec<AuthorizedKeyEntry>> {
        let mut out = Vec::new();

        for key in &self.data.ssh_keys {
            let key = PublicKey::parse(&key)?;
            out.push(AuthorizedKeyEntry::Valid{key});
        }

        Ok(out)
    }

    fn networks(&self) -> Result<Vec<network::Interface>> {
        let (interfaces, _devices) = self.parse_network()?;

        Ok(interfaces)
    }

    fn network_devices(&self) -> Result<Vec<network::Device>> {
        let (_interfaces, devices) = self.parse_network()?;

        Ok(devices)
    }
}
<|endoftext|>


<commit_before>//! Readiness tracking streams, backing I/O objects.
//!
//! This module contains the core type which is used to back all I/O on object
//! in `tokio-core`. The `PollEvented` type is the implementation detail of
//! all I/O. Each `PollEvented` manages registration with a reactor,
//! acquisition of a token, and tracking of the readiness state on the
//! underlying I/O primitive.

use std::io::{self, Read, Write};
use std::sync::atomic::{AtomicUsize, Ordering};

use futures::Async;
use mio;

use io::Io;
use reactor::{Handle, Remote};
use reactor::io_token::IoToken;

/// A concrete implementation of a stream of readiness notifications for I/O
/// objects that originates from an event loop.
///
/// Created by the `PollEvented::new` method, each `PollEvented` is
/// associated with a specific event loop and source of events that will be
/// registered with an event loop.
///
/// Each readiness stream has a number of methods to test whether the underlying
/// object is readable or writable. Once the methods return that an object is
/// readable/writable, then it will continue to do so until the `need_read` or
/// `need_write` methods are called.
///
/// That is, this object is typically wrapped in another form of I/O object.
/// It's the responsibility of the wrapper to inform the readiness stream when a
/// "would block" I/O event is seen. The readiness stream will then take care of
/// any scheduling necessary to get notified when the event is ready again.
pub struct PollEvented<E> {
    token: IoToken,
    handle: Remote,
    readiness: AtomicUsize,
    io: E,
}

impl<E: mio::Evented> PollEvented<E> {
    /// Creates a new readiness stream associated with the provided
    /// `loop_handle` and for the given `source`.
    ///
    /// This method returns a future which will resolve to the readiness stream
    /// when it's ready.
    pub fn new(io: E, handle: &Handle) -> io::Result<PollEvented<E>> {
        Ok(PollEvented {
            token: try!(IoToken::new(&io, handle)),
            handle: handle.remote().clone(),
            readiness: AtomicUsize::new(0),
            io: io,
        })
    }
}

impl<E> PollEvented<E> {
    /// Tests to see if this source is ready to be read from or not.
    ///
    /// If this stream is not ready for a read then `NotReady` will be returned
    /// and the current task will be scheduled to receive a notification when
    /// the stream is readable again. In other words, this method is only safe
    /// to call from within the context of a future's task, typically done in a
    /// `Future::poll` method.
    pub fn poll_read(&self) -> Async<()> {
        if self.readiness.load(Ordering::SeqCst) & 1 != 0 {
            return Async::Ready(())
        }
        self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst);
        if self.readiness.load(Ordering::SeqCst) & 1 != 0 {
            Async::Ready(())
        } else {
            self.token.schedule_read(&self.handle);
            Async::NotReady
        }
    }

    /// Tests to see if this source is ready to be written to or not.
    ///
    /// If this stream is not ready for a write then `NotReady` will be returned
    /// and the current task will be scheduled to receive a notification when
    /// the stream is writable again. In other words, this method is only safe
    /// to call from within the context of a future's task, typically done in a
    /// `Future::poll` method.
    pub fn poll_write(&self) -> Async<()> {
        if self.readiness.load(Ordering::SeqCst) & 2 != 0 {
            return Async::Ready(())
        }
        self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst);
        if self.readiness.load(Ordering::SeqCst) & 2 != 0 {
            Async::Ready(())
        } else {
            self.token.schedule_write(&self.handle);
            Async::NotReady
        }
    }

    /// Indicates to this source of events that the corresponding I/O object is
    /// no longer readable, but it needs to be.
    ///
    /// This function, like `poll_read`, is only safe to call from the context
    /// of a future's task (typically in a `Future::poll` implementation). It
    /// informs this readiness stream that the underlying object is no longer
    /// readable, typically because a "would block" error was seen.
    ///
    /// The flag indicating that this stream is readable is unset and the
    /// current task is scheduled to receive a notification when the stream is
    /// then again readable.
    pub fn need_read(&self) {
        self.readiness.fetch_and(!1, Ordering::SeqCst);
        self.token.schedule_read(&self.handle)
    }

    /// Indicates to this source of events that the corresponding I/O object is
    /// no longer writable, but it needs to be.
    ///
    /// This function, like `poll_write`, is only safe to call from the context
    /// of a future's task (typically in a `Future::poll` implementation). It
    /// informs this readiness stream that the underlying object is no longer
    /// writable, typically because a "would block" error was seen.
    ///
    /// The flag indicating that this stream is writable is unset and the
    /// current task is scheduled to receive a notification when the stream is
    /// then again writable.
    pub fn need_write(&self) {
        self.readiness.fetch_and(!2, Ordering::SeqCst);
        self.token.schedule_write(&self.handle)
    }

    /// Returns a reference to the event loop handle that this readiness stream
    /// is associated with.
    pub fn remote(&self) -> &Remote {
        &self.handle
    }

    /// Returns a shared reference to the underlying I/O object this readiness
    /// stream is wrapping.
    pub fn get_ref(&self) -> &E {
        &self.io
    }

    /// Returns a mutable reference to the underlying I/O object this readiness
    /// stream is wrapping.
    pub fn get_mut(&mut self) -> &mut E {
        &mut self.io
    }
}

impl<E: Read> Read for PollEvented<E> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_read() {
            return Err(mio::would_block())
        }
        let r = self.get_mut().read(buf);
        if is_wouldblock(&r) {
            self.need_read();
        }
        return r
    }
}

impl<E: Write> Write for PollEvented<E> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_mut().write(buf);
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }

    fn flush(&mut self) -> io::Result<()> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_mut().flush();
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }
}

impl<E: Read + Write> Io for PollEvented<E> {
    fn poll_read(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_read(self)
    }

    fn poll_write(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_write(self)
    }
}

impl<'a, E> Read for &'a PollEvented<E>
    where &'a E: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_read() {
            return Err(mio::would_block())
        }
        let r = self.get_ref().read(buf);
        if is_wouldblock(&r) {
            self.need_read();
        }
        return r
    }
}

impl<'a, E> Write for &'a PollEvented<E>
    where &'a E: Write,
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_ref().write(buf);
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }

    fn flush(&mut self) -> io::Result<()> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_ref().flush();
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }
}

impl<'a, E> Io for &'a PollEvented<E>
    where &'a E: Read + Write,
{
    fn poll_read(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_read(self)
    }

    fn poll_write(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_write(self)
    }
}

fn is_wouldblock<T>(r: &io::Result<T>) -> bool {
    match *r {
        Ok(_) => false,
        Err(ref e) => e.kind() == io::ErrorKind::WouldBlock,
    }
}

impl<E> Drop for PollEvented<E> {
    fn drop(&mut self) {
        self.token.drop_source(&self.handle);
    }
}
<commit_msg>Indiciate need_read requires poll_read<commit_after>//! Readiness tracking streams, backing I/O objects.
//!
//! This module contains the core type which is used to back all I/O on object
//! in `tokio-core`. The `PollEvented` type is the implementation detail of
//! all I/O. Each `PollEvented` manages registration with a reactor,
//! acquisition of a token, and tracking of the readiness state on the
//! underlying I/O primitive.

use std::io::{self, Read, Write};
use std::sync::atomic::{AtomicUsize, Ordering};

use futures::Async;
use mio;

use io::Io;
use reactor::{Handle, Remote};
use reactor::io_token::IoToken;

/// A concrete implementation of a stream of readiness notifications for I/O
/// objects that originates from an event loop.
///
/// Created by the `PollEvented::new` method, each `PollEvented` is
/// associated with a specific event loop and source of events that will be
/// registered with an event loop.
///
/// Each readiness stream has a number of methods to test whether the underlying
/// object is readable or writable. Once the methods return that an object is
/// readable/writable, then it will continue to do so until the `need_read` or
/// `need_write` methods are called.
///
/// That is, this object is typically wrapped in another form of I/O object.
/// It's the responsibility of the wrapper to inform the readiness stream when a
/// "would block" I/O event is seen. The readiness stream will then take care of
/// any scheduling necessary to get notified when the event is ready again.
pub struct PollEvented<E> {
    token: IoToken,
    handle: Remote,
    readiness: AtomicUsize,
    io: E,
}

impl<E: mio::Evented> PollEvented<E> {
    /// Creates a new readiness stream associated with the provided
    /// `loop_handle` and for the given `source`.
    ///
    /// This method returns a future which will resolve to the readiness stream
    /// when it's ready.
    pub fn new(io: E, handle: &Handle) -> io::Result<PollEvented<E>> {
        Ok(PollEvented {
            token: try!(IoToken::new(&io, handle)),
            handle: handle.remote().clone(),
            readiness: AtomicUsize::new(0),
            io: io,
        })
    }
}

impl<E> PollEvented<E> {
    /// Tests to see if this source is ready to be read from or not.
    ///
    /// If this stream is not ready for a read then `NotReady` will be returned
    /// and the current task will be scheduled to receive a notification when
    /// the stream is readable again. In other words, this method is only safe
    /// to call from within the context of a future's task, typically done in a
    /// `Future::poll` method.
    pub fn poll_read(&self) -> Async<()> {
        if self.readiness.load(Ordering::SeqCst) & 1 != 0 {
            return Async::Ready(())
        }
        self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst);
        if self.readiness.load(Ordering::SeqCst) & 1 != 0 {
            Async::Ready(())
        } else {
            self.token.schedule_read(&self.handle);
            Async::NotReady
        }
    }

    /// Tests to see if this source is ready to be written to or not.
    ///
    /// If this stream is not ready for a write then `NotReady` will be returned
    /// and the current task will be scheduled to receive a notification when
    /// the stream is writable again. In other words, this method is only safe
    /// to call from within the context of a future's task, typically done in a
    /// `Future::poll` method.
    pub fn poll_write(&self) -> Async<()> {
        if self.readiness.load(Ordering::SeqCst) & 2 != 0 {
            return Async::Ready(())
        }
        self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst);
        if self.readiness.load(Ordering::SeqCst) & 2 != 0 {
            Async::Ready(())
        } else {
            self.token.schedule_write(&self.handle);
            Async::NotReady
        }
    }

    /// Indicates to this source of events that the corresponding I/O object is
    /// no longer readable, but it needs to be.
    ///
    /// This function, like `poll_read`, is only safe to call from the context
    /// of a future's task (typically in a `Future::poll` implementation). It
    /// informs this readiness stream that the underlying object is no longer
    /// readable, typically because a "would block" error was seen.
    ///
    /// The flag indicating that this stream is readable is unset and the
    /// current task is scheduled to receive a notification when the stream is
    /// then again readable.
    ///
    /// Note that it is also only valid to call this method if `poll_read`
    /// previously indicated that the object is readable. That is, this function
    /// must always be paired with calls to `poll_read` previously.
    pub fn need_read(&self) {
        self.readiness.fetch_and(!1, Ordering::SeqCst);
        self.token.schedule_read(&self.handle)
    }

    /// Indicates to this source of events that the corresponding I/O object is
    /// no longer writable, but it needs to be.
    ///
    /// This function, like `poll_write`, is only safe to call from the context
    /// of a future's task (typically in a `Future::poll` implementation). It
    /// informs this readiness stream that the underlying object is no longer
    /// writable, typically because a "would block" error was seen.
    ///
    /// The flag indicating that this stream is writable is unset and the
    /// current task is scheduled to receive a notification when the stream is
    /// then again writable.
    ///
    /// Note that it is also only valid to call this method if `poll_write`
    /// previously indicated that the object is writeable. That is, this function
    /// must always be paired with calls to `poll_write` previously.
    pub fn need_write(&self) {
        self.readiness.fetch_and(!2, Ordering::SeqCst);
        self.token.schedule_write(&self.handle)
    }

    /// Returns a reference to the event loop handle that this readiness stream
    /// is associated with.
    pub fn remote(&self) -> &Remote {
        &self.handle
    }

    /// Returns a shared reference to the underlying I/O object this readiness
    /// stream is wrapping.
    pub fn get_ref(&self) -> &E {
        &self.io
    }

    /// Returns a mutable reference to the underlying I/O object this readiness
    /// stream is wrapping.
    pub fn get_mut(&mut self) -> &mut E {
        &mut self.io
    }
}

impl<E: Read> Read for PollEvented<E> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_read() {
            return Err(mio::would_block())
        }
        let r = self.get_mut().read(buf);
        if is_wouldblock(&r) {
            self.need_read();
        }
        return r
    }
}

impl<E: Write> Write for PollEvented<E> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_mut().write(buf);
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }

    fn flush(&mut self) -> io::Result<()> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_mut().flush();
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }
}

impl<E: Read + Write> Io for PollEvented<E> {
    fn poll_read(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_read(self)
    }

    fn poll_write(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_write(self)
    }
}

impl<'a, E> Read for &'a PollEvented<E>
    where &'a E: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_read() {
            return Err(mio::would_block())
        }
        let r = self.get_ref().read(buf);
        if is_wouldblock(&r) {
            self.need_read();
        }
        return r
    }
}

impl<'a, E> Write for &'a PollEvented<E>
    where &'a E: Write,
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_ref().write(buf);
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }

    fn flush(&mut self) -> io::Result<()> {
        if let Async::NotReady = self.poll_write() {
            return Err(mio::would_block())
        }
        let r = self.get_ref().flush();
        if is_wouldblock(&r) {
            self.need_write();
        }
        return r
    }
}

impl<'a, E> Io for &'a PollEvented<E>
    where &'a E: Read + Write,
{
    fn poll_read(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_read(self)
    }

    fn poll_write(&mut self) -> Async<()> {
        <PollEvented<E>>::poll_write(self)
    }
}

fn is_wouldblock<T>(r: &io::Result<T>) -> bool {
    match *r {
        Ok(_) => false,
        Err(ref e) => e.kind() == io::ErrorKind::WouldBlock,
    }
}

impl<E> Drop for PollEvented<E> {
    fn drop(&mut self) {
        self.token.drop_source(&self.handle);
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::time::{Instant, Duration};
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    WindowedContext,
    PossiblyCurrent,
    dpi::LogicalSize,
    window::{WindowBuilder, Fullscreen},
    event::{
        Event as GlutinEvent,
        StartCause,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop},
};
use tokio::{
    sync::Mutex,
    runtime::{Runtime, Handle},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
    event_loop_notifier::{EventLoopNotifier, MainThreadAction},
};

/// The maximum rendering FPS allowed
///
/// Rendering is intentionally throttled to avoid too much contention over the display list. If
/// multiple turtles are used or if many lines are drawn quickly, we may get >= 1 redraw request
/// per *millisecond* this is far too many redraws. Limiting to this rate helps avoid that.
const MAX_RENDERING_FPS: u64 = 60;

// 1,000,000 us in 1 s
const MICROS_PER_SEC: u64 = 1_000_000;

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The runtime for driving async code
    let runtime = Runtime::new()
        .expect("unable to spawn tokio runtime to run turtle server process");

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();
    // Create the proxy that will be given to the thread managing IPC
    let event_loop_proxy = event_loop.create_proxy();
    let event_loop_notifier = Arc::new(EventLoopNotifier::new(event_loop_proxy));

    let window_builder = {
        let drawing = runtime.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size, gl_context.window().scale_factor());

    // For rate limiting rendering
    let min_render_delay = Duration::from_micros(MICROS_PER_SEC / MAX_RENDERING_FPS);
    // Subtracting the delay so we do an initial render right away
    let mut last_render = Instant::now() - min_render_delay;
    event_loop.run(move |event, _, control_flow| match event {
        GlutinEvent::NewEvents(StartCause::Init) => {
            // Spawn the actual server thread(s) that will handle incoming IPC messages and
            // asynchronous update the shared state
            //
            // Note that putting this code here instead of before the event loop causes the
            // `Turtle::new()`, etc. methods not to return before the window opens. Those methods
            // can't return because the connection handshake cannot complete before the thread used
            // for IPC is spawned.
            let handle = runtime.handle().clone();
            spawn_async_server(handle, app.clone(), display_list.clone(), event_loop_notifier.clone());
        },

        GlutinEvent::NewEvents(StartCause::ResumeTimeReached {..}) => {
            // A render was delayed in the `RedrawRequested` so let's try to do it again now that
            // we have resumed
            gl_context.window().request_redraw();
        },

        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then redraw if necessary
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then redraw if necessary
        },

        GlutinEvent::UserEvent(MainThreadAction::Redraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetTitle(title)) => {
            gl_context.window().set_title(&title);
        },

        GlutinEvent::UserEvent(MainThreadAction::SetSize(size)) => {
            gl_context.window().set_inner_size(size);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsMaximized(is_maximized)) => {
            gl_context.window().set_maximized(is_maximized);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsFullscreen(is_fullscreen)) => {
            gl_context.window().set_fullscreen(if is_fullscreen {
                Some(Fullscreen::Borderless(gl_context.window().current_monitor()))
            } else { None });

            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            // Check if we just rendered
            let last_render_delay = last_render.elapsed();
            if last_render_delay < min_render_delay {
                let remaining = min_render_delay - last_render_delay;
                *control_flow = ControlFlow::WaitUntil(Instant::now() + remaining);
                return;
            }

            let handle = runtime.handle();
            handle.block_on(redraw(&app, &display_list, &gl_context, &mut renderer));
            *control_flow = ControlFlow::Wait;
            last_render = Instant::now();
        },

        _ => {},
    });
}

async fn redraw(
    app: &App,
    display_list: &Mutex<DisplayList>,
    gl_context: &WindowedContext<PossiblyCurrent>,
    renderer: &mut Renderer,
) {
    let drawing = {
        // Hold the drawing lock for as little time as possible
        let drawing = app.drawing_mut().await;
        drawing.clone()
    };

    // Locking the turtles before the display list to be consistent with all of the request
    // handlers. Inconsistent lock ordering can cause deadlock.
    let mut turtles = Vec::new();
    for id in app.turtle_ids().await {
        turtles.push(app.turtle(id).await);
    }
    // Very important to have all the data locked before rendering. Do not want renderer to have
    // to figure out how to lock.
    let mut locked_turtles = Vec::with_capacity(turtles.len());
    for turtle in &turtles {
        locked_turtles.push(turtle.lock().await);
    }
    // Renderer only needs (read-only) access to TurtleState
    // Doing this also decouples renderer code from runtime by not having to
    // know about the `MutexGuard` type in tokio
    let turtle_states = locked_turtles.iter().map(|t| &t.state);

    let display_list = display_list.lock().await;

    let draw_size = gl_context.window().inner_size();
    renderer.render(draw_size, &display_list, &drawing, turtle_states);
    gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        unreachable!("bug: windows can only be created on the main thread");
    }
}

fn spawn_async_server(
    handle: Handle,
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: Arc<EventLoopNotifier>,
) {
    // Spawn root task
    handle.spawn(async {
        let conn = ServerConnection::connect_stdin().await
            .expect("unable to establish turtle server connection");
        super::serve(conn, app, display_list, event_loop).await;
    });
}
<commit_msg>Better error if user accidentally opens window outside main thread by forgetting turtle::start()<commit_after>use std::thread;
use std::time::{Instant, Duration};
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    WindowedContext,
    PossiblyCurrent,
    dpi::LogicalSize,
    window::{WindowBuilder, Fullscreen},
    event::{
        Event as GlutinEvent,
        StartCause,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop},
};
use tokio::{
    sync::Mutex,
    runtime::{Runtime, Handle},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
    event_loop_notifier::{EventLoopNotifier, MainThreadAction},
};

/// The maximum rendering FPS allowed
///
/// Rendering is intentionally throttled to avoid too much contention over the display list. If
/// multiple turtles are used or if many lines are drawn quickly, we may get >= 1 redraw request
/// per *millisecond* this is far too many redraws. Limiting to this rate helps avoid that.
const MAX_RENDERING_FPS: u64 = 60;

// 1,000,000 us in 1 s
const MICROS_PER_SEC: u64 = 1_000_000;

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The runtime for driving async code
    let runtime = Runtime::new()
        .expect("unable to spawn tokio runtime to run turtle server process");

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();
    // Create the proxy that will be given to the thread managing IPC
    let event_loop_proxy = event_loop.create_proxy();
    let event_loop_notifier = Arc::new(EventLoopNotifier::new(event_loop_proxy));

    let window_builder = {
        let drawing = runtime.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size, gl_context.window().scale_factor());

    // For rate limiting rendering
    let min_render_delay = Duration::from_micros(MICROS_PER_SEC / MAX_RENDERING_FPS);
    // Subtracting the delay so we do an initial render right away
    let mut last_render = Instant::now() - min_render_delay;
    event_loop.run(move |event, _, control_flow| match event {
        GlutinEvent::NewEvents(StartCause::Init) => {
            // Spawn the actual server thread(s) that will handle incoming IPC messages and
            // asynchronous update the shared state
            //
            // Note that putting this code here instead of before the event loop causes the
            // `Turtle::new()`, etc. methods not to return before the window opens. Those methods
            // can't return because the connection handshake cannot complete before the thread used
            // for IPC is spawned.
            let handle = runtime.handle().clone();
            spawn_async_server(handle, app.clone(), display_list.clone(), event_loop_notifier.clone());
        },

        GlutinEvent::NewEvents(StartCause::ResumeTimeReached {..}) => {
            // A render was delayed in the `RedrawRequested` so let's try to do it again now that
            // we have resumed
            gl_context.window().request_redraw();
        },

        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then redraw if necessary
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then redraw if necessary
        },

        GlutinEvent::UserEvent(MainThreadAction::Redraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetTitle(title)) => {
            gl_context.window().set_title(&title);
        },

        GlutinEvent::UserEvent(MainThreadAction::SetSize(size)) => {
            gl_context.window().set_inner_size(size);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsMaximized(is_maximized)) => {
            gl_context.window().set_maximized(is_maximized);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::UserEvent(MainThreadAction::SetIsFullscreen(is_fullscreen)) => {
            gl_context.window().set_fullscreen(if is_fullscreen {
                Some(Fullscreen::Borderless(gl_context.window().current_monitor()))
            } else { None });

            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            // Check if we just rendered
            let last_render_delay = last_render.elapsed();
            if last_render_delay < min_render_delay {
                let remaining = min_render_delay - last_render_delay;
                *control_flow = ControlFlow::WaitUntil(Instant::now() + remaining);
                return;
            }

            let handle = runtime.handle();
            handle.block_on(redraw(&app, &display_list, &gl_context, &mut renderer));
            *control_flow = ControlFlow::Wait;
            last_render = Instant::now();
        },

        _ => {},
    });
}

async fn redraw(
    app: &App,
    display_list: &Mutex<DisplayList>,
    gl_context: &WindowedContext<PossiblyCurrent>,
    renderer: &mut Renderer,
) {
    let drawing = {
        // Hold the drawing lock for as little time as possible
        let drawing = app.drawing_mut().await;
        drawing.clone()
    };

    // Locking the turtles before the display list to be consistent with all of the request
    // handlers. Inconsistent lock ordering can cause deadlock.
    let mut turtles = Vec::new();
    for id in app.turtle_ids().await {
        turtles.push(app.turtle(id).await);
    }
    // Very important to have all the data locked before rendering. Do not want renderer to have
    // to figure out how to lock.
    let mut locked_turtles = Vec::with_capacity(turtles.len());
    for turtle in &turtles {
        locked_turtles.push(turtle.lock().await);
    }
    // Renderer only needs (read-only) access to TurtleState
    // Doing this also decouples renderer code from runtime by not having to
    // know about the `MutexGuard` type in tokio
    let turtle_states = locked_turtles.iter().map(|t| &t.state);

    let display_list = display_list.lock().await;

    let draw_size = gl_context.window().inner_size();
    renderer.render(draw_size, &display_list, &drawing, turtle_states);
    gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        //
        // It's easy for a user to accidentally cause this panic if they call `Turtle::new()` in a
        // new thread. This message is meant to point them to the solution: `turtle::start()`
        panic!("Windows can only be created on the main thread. \
                Make sure you have called `turtle::start()` at the beginning of your program. \
                See: <https://docs.rs/turtle/*/turtle/fn.start.html>");
    }
}

fn spawn_async_server(
    handle: Handle,
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: Arc<EventLoopNotifier>,
) {
    // Spawn root task
    handle.spawn(async {
        let conn = ServerConnection::connect_stdin().await
            .expect("unable to establish turtle server connection");
        super::serve(conn, app, display_list, event_loop).await;
    });
}
<|endoftext|>


<commit_before>pub mod mut_message_client;

use std::env;
use std::io::*;
use std::mem;
use std::net::*;
use std::panic::{catch_unwind, UnwindSafe};
use std::process::{Command, Stdio};
use std::str::FromStr;
use std::sync::*;
use std::time::*;
use std::thread;

use tokio_core::reactor::Core;

use trust_dns::client::*;
use trust_dns::op::Message;
use trust_dns::rr::*;
use trust_dns::rr::rdata::{DNSSECRData, DNSSECRecordType};
use trust_dns::rr::dnssec::*;

use self::mut_message_client::MutMessageHandle;

/// Spins up a Server and handles shutting it down after running the test
#[allow(dead_code)]
pub fn named_test_harness<F, R>(toml: &str, test: F)
where
    F: FnOnce(u16, u16) -> R + UnwindSafe,
{
    // find a random port to listen on
    let (test_port, test_tls_port) = {
        let server = UdpSocket::bind(("0.0.0.0", 0)).unwrap();
        let server_addr = server.local_addr().unwrap();
        let test_port = server_addr.port();

        let server = UdpSocket::bind(("0.0.0.0", 0)).unwrap();
        let server_addr = server.local_addr().unwrap();
        let test_tls_port = server_addr.port();

        assert!(test_port != test_tls_port);
        (test_port, test_tls_port)
    };

    let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or_else(|_| ".".to_owned());
    println!("using server src path: {}", server_path);

    let mut named = Command::new(&format!("{}/../target/debug/named", server_path))
        .stdout(Stdio::piped())
        .arg("-d")
        .arg(&format!(
            "--config={}/tests/named_test_configs/{}",
            server_path,
            toml
        ))
        .arg(&format!(
            "--zonedir={}/tests/named_test_configs",
            server_path
        ))
        .arg(&format!("--port={}", test_port))
        .arg(&format!("--tls-port={}", test_tls_port))
        .spawn()
        .expect("failed to start named");

    let mut named_out = BufReader::new(mem::replace(&mut named.stdout, None).expect("no stdout"));

    // forced thread killer
    let succeeded = Arc::new(atomic::AtomicBool::new(false));
    let succeeded_clone = succeeded.clone();
    let killer_join = thread::Builder::new()
        .name("thread_killer".to_string())
        .spawn(move || {
            let succeeded = succeeded_clone;

            let mut kill_named = || {
                println!("killing named");

                if let Err(e) = named.kill() {
                    println!("warning: failed to kill named: {:?}", e);
                }
            };

            for _ in 0..30 {
                thread::sleep(Duration::from_secs(1));
                if succeeded.load(atomic::Ordering::Relaxed) {
                    kill_named();
                    return;
                }
            }

            kill_named();
            panic!("timeout");
        })
        .expect("could not start thread killer");

    // we should get the correct output before 1000 lines...
    let mut output = String::new();
    let mut found = false;
    for _ in 0..1000 {
        output.clear();
        named_out
            .read_line(&mut output)
            .expect("could not read stdout");
        if !output.is_empty() {
            stdout().write(b"SRV: ").unwrap();
            stdout().write(output.as_bytes()).unwrap();
        }
        if output.contains("awaiting connections...") {
            found = true;
            break;
        }
    }

    stdout().flush().unwrap();
    assert!(found);

    // spawn a thread to capture stdout
    let succeeded_clone = succeeded.clone();
    thread::Builder::new()
        .name("named stdout".into())
        .spawn(move || {
            let succeeded = succeeded_clone;
            while !succeeded.load(atomic::Ordering::Relaxed) {
                output.clear();
                named_out
                    .read_line(&mut output)
                    .expect("could not read stdout");
                if !output.is_empty() {
                    stdout().write(b"SRV: ").unwrap();
                    stdout().write(output.as_bytes()).unwrap();
                }
            }
        })
        .expect("no thread available");

    println!("running test...");

    let result = catch_unwind(move || test(test_port, test_tls_port));

    println!("test completed");
    succeeded.store(true, atomic::Ordering::Relaxed);
    killer_join.join().expect("join failed");

    assert!(result.is_ok(), "test failed");
}

pub fn query_message<C: ClientHandle>(
    io_loop: &mut Core,
    client: &mut C,
    name: Name,
    record_type: RecordType,
) -> Message {
    println!("sending request");
    let response = io_loop.run(client.query(name.clone(), DNSClass::IN, record_type));
    println!("got response: {:#?}", response);
    response.expect("request failed")
}

// This only validates that a query to the server works, it shouldn't be used for more than this.
//  i.e. more complex checks live with the clients and authorities to validate deeper funcionality
#[allow(dead_code)]
pub fn query_a<C: ClientHandle>(io_loop: &mut Core, client: &mut C) {
    let name = Name::from_labels(vec!["www", "example", "com"]);
    let response = query_message(io_loop, client, name, RecordType::A);
    let record = &response.answers()[0];

    if let RData::A(ref address) = *record.rdata() {
        assert!(address == &Ipv4Addr::new(127, 0, 0, 1))
    } else {
        panic!("wrong RDATA")
    }
}

// This only validates that a query to the server works, it shouldn't be used for more than this.
//  i.e. more complex checks live with the clients and authorities to validate deeper funcionality
#[allow(dead_code)]
pub fn query_all_dnssec(
    io_loop: &mut Core,
    client: BasicClientHandle,
    algorithm: Algorithm,
    with_rfc6975: bool,
) {
    let name = Name::from_str("example.com.").unwrap();
    let mut client = MutMessageHandle::new(client);
    client.dnssec_ok = true;
    if with_rfc6975 {
        client.support_algorithms = Some(SupportedAlgorithms::from_vec(&[algorithm]));
    }

    let response = query_message(
        io_loop,
        &mut client,
        name,
        RecordType::DNSSEC(DNSSECRecordType::DNSKEY),
    );

    let dnskey = response
        .answers()
        .iter()
        .filter(|r| {
            r.rr_type() == RecordType::DNSSEC(DNSSECRecordType::DNSKEY)
        })
        .map(|r| {
            if let RData::DNSSEC(DNSSECRData::DNSKEY(ref dnskey)) = *r.rdata() {
                dnskey.clone()
            } else {
                panic!("wrong RDATA")
            }
        })
        .find(|d| d.algorithm() == algorithm);
    assert!(dnskey.is_some(), "DNSKEY not found");

    let rrsig = response
        .answers()
        .iter()
        .filter(|r| {
            r.rr_type() == RecordType::DNSSEC(DNSSECRecordType::RRSIG)
        })
        .map(|r| {
            if let RData::DNSSEC(DNSSECRData::SIG(ref rrsig)) = *r.rdata() {
                rrsig.clone()
            } else {
                panic!("wrong RDATA")
            }
        })
        .filter(|rrsig| rrsig.algorithm() == algorithm)
        .find(|rrsig| {
            rrsig.type_covered() == RecordType::DNSSEC(DNSSECRecordType::DNSKEY)
        });
    assert!(rrsig.is_some(), "Associated RRSIG not found");
}

#[allow(dead_code)]
pub fn query_all_dnssec_with_rfc6975(
    io_loop: &mut Core,
    client: BasicClientHandle,
    algorithm: Algorithm,
) {
    query_all_dnssec(io_loop, client, algorithm, true)
}

#[allow(dead_code)]
pub fn query_all_dnssec_wo_rfc6975(
    io_loop: &mut Core,
    client: BasicClientHandle,
    algorithm: Algorithm,
) {
    query_all_dnssec(io_loop, client, algorithm, false)
}
<commit_msg>Ensure all data is output<commit_after>pub mod mut_message_client;

use std::env;
use std::io::*;
use std::mem;
use std::net::*;
use std::panic::{catch_unwind, UnwindSafe};
use std::process::{Command, Stdio};
use std::str::FromStr;
use std::sync::*;
use std::time::*;
use std::thread;

use tokio_core::reactor::Core;

use trust_dns::client::*;
use trust_dns::op::Message;
use trust_dns::rr::*;
use trust_dns::rr::rdata::{DNSSECRData, DNSSECRecordType};
use trust_dns::rr::dnssec::*;

use self::mut_message_client::MutMessageHandle;

/// Spins up a Server and handles shutting it down after running the test
#[allow(dead_code)]
pub fn named_test_harness<F, R>(toml: &str, test: F)
where
    F: FnOnce(u16, u16) -> R + UnwindSafe,
{
    // find a random port to listen on
    let (test_port, test_tls_port) = {
        let server = UdpSocket::bind(("0.0.0.0", 0)).unwrap();
        let server_addr = server.local_addr().unwrap();
        let test_port = server_addr.port();

        let server = UdpSocket::bind(("0.0.0.0", 0)).unwrap();
        let server_addr = server.local_addr().unwrap();
        let test_tls_port = server_addr.port();

        assert!(test_port != test_tls_port);
        (test_port, test_tls_port)
    };

    let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or_else(|_| ".".to_owned());
    println!("using server src path: {}", server_path);

    let mut named = Command::new(&format!("{}/../target/debug/named", server_path))
        .stdout(Stdio::piped())
        .arg("-d")
        .arg(&format!(
            "--config={}/tests/named_test_configs/{}",
            server_path,
            toml
        ))
        .arg(&format!(
            "--zonedir={}/tests/named_test_configs",
            server_path
        ))
        .arg(&format!("--port={}", test_port))
        .arg(&format!("--tls-port={}", test_tls_port))
        .spawn()
        .expect("failed to start named");

    let mut named_out = BufReader::new(mem::replace(&mut named.stdout, None).expect("no stdout"));

    // forced thread killer
    let succeeded = Arc::new(atomic::AtomicBool::new(false));
    let succeeded_clone = succeeded.clone();
    let killer_join = thread::Builder::new()
        .name("thread_killer".to_string())
        .spawn(move || {
            let succeeded = succeeded_clone;

            let mut kill_named = || {
                println!("killing named");

                if let Err(e) = named.kill() {
                    println!("warning: failed to kill named: {:?}", e);
                }
            };

            for _ in 0..30 {
                thread::sleep(Duration::from_secs(1));
                if succeeded.load(atomic::Ordering::Relaxed) {
                    kill_named();
                    return;
                }
            }

            kill_named();
            panic!("timeout");
        })
        .expect("could not start thread killer");

    // we should get the correct output before 1000 lines...
    let mut output = String::new();
    let mut found = false;
    for _ in 0..1000 {
        output.clear();
        named_out
            .read_line(&mut output)
            .expect("could not read stdout");
        if !output.is_empty() {
            stdout().write_all(b"SRV: ").unwrap();
            stdout().write_all(output.as_bytes()).unwrap();
        }
        if output.contains("awaiting connections...") {
            found = true;
            break;
        }
    }

    stdout().flush().unwrap();
    assert!(found);

    // spawn a thread to capture stdout
    let succeeded_clone = succeeded.clone();
    thread::Builder::new()
        .name("named stdout".into())
        .spawn(move || {
            let succeeded = succeeded_clone;
            while !succeeded.load(atomic::Ordering::Relaxed) {
                output.clear();
                named_out
                    .read_line(&mut output)
                    .expect("could not read stdout");
                if !output.is_empty() {
                    stdout().write_all(b"SRV: ").unwrap();
                    stdout().write_all(output.as_bytes()).unwrap();
                }
            }
        })
        .expect("no thread available");

    println!("running test...");

    let result = catch_unwind(move || test(test_port, test_tls_port));

    println!("test completed");
    succeeded.store(true, atomic::Ordering::Relaxed);
    killer_join.join().expect("join failed");

    assert!(result.is_ok(), "test failed");
}

pub fn query_message<C: ClientHandle>(
    io_loop: &mut Core,
    client: &mut C,
    name: Name,
    record_type: RecordType,
) -> Message {
    println!("sending request");
    let response = io_loop.run(client.query(name.clone(), DNSClass::IN, record_type));
    println!("got response: {:#?}", response);
    response.expect("request failed")
}

// This only validates that a query to the server works, it shouldn't be used for more than this.
//  i.e. more complex checks live with the clients and authorities to validate deeper funcionality
#[allow(dead_code)]
pub fn query_a<C: ClientHandle>(io_loop: &mut Core, client: &mut C) {
    let name = Name::from_labels(vec!["www", "example", "com"]);
    let response = query_message(io_loop, client, name, RecordType::A);
    let record = &response.answers()[0];

    if let RData::A(ref address) = *record.rdata() {
        assert!(address == &Ipv4Addr::new(127, 0, 0, 1))
    } else {
        panic!("wrong RDATA")
    }
}

// This only validates that a query to the server works, it shouldn't be used for more than this.
//  i.e. more complex checks live with the clients and authorities to validate deeper funcionality
#[allow(dead_code)]
pub fn query_all_dnssec(
    io_loop: &mut Core,
    client: BasicClientHandle,
    algorithm: Algorithm,
    with_rfc6975: bool,
) {
    let name = Name::from_str("example.com.").unwrap();
    let mut client = MutMessageHandle::new(client);
    client.dnssec_ok = true;
    if with_rfc6975 {
        client.support_algorithms = Some(SupportedAlgorithms::from_vec(&[algorithm]));
    }

    let response = query_message(
        io_loop,
        &mut client,
        name,
        RecordType::DNSSEC(DNSSECRecordType::DNSKEY),
    );

    let dnskey = response
        .answers()
        .iter()
        .filter(|r| {
            r.rr_type() == RecordType::DNSSEC(DNSSECRecordType::DNSKEY)
        })
        .map(|r| {
            if let RData::DNSSEC(DNSSECRData::DNSKEY(ref dnskey)) = *r.rdata() {
                dnskey.clone()
            } else {
                panic!("wrong RDATA")
            }
        })
        .find(|d| d.algorithm() == algorithm);
    assert!(dnskey.is_some(), "DNSKEY not found");

    let rrsig = response
        .answers()
        .iter()
        .filter(|r| {
            r.rr_type() == RecordType::DNSSEC(DNSSECRecordType::RRSIG)
        })
        .map(|r| {
            if let RData::DNSSEC(DNSSECRData::SIG(ref rrsig)) = *r.rdata() {
                rrsig.clone()
            } else {
                panic!("wrong RDATA")
            }
        })
        .filter(|rrsig| rrsig.algorithm() == algorithm)
        .find(|rrsig| {
            rrsig.type_covered() == RecordType::DNSSEC(DNSSECRecordType::DNSKEY)
        });
    assert!(rrsig.is_some(), "Associated RRSIG not found");
}

#[allow(dead_code)]
pub fn query_all_dnssec_with_rfc6975(
    io_loop: &mut Core,
    client: BasicClientHandle,
    algorithm: Algorithm,
) {
    query_all_dnssec(io_loop, client, algorithm, true)
}

#[allow(dead_code)]
pub fn query_all_dnssec_wo_rfc6975(
    io_loop: &mut Core,
    client: BasicClientHandle,
    algorithm: Algorithm,
) {
    query_all_dnssec(io_loop, client, algorithm, false)
}
<|endoftext|>


<commit_before>use std::io::{self, prelude::Write};
use std::time::Duration;

use super::OutputFormatter;
use crate::{
    console::{ConsoleTestState, OutputLocation},
    test_result::TestResult,
    time,
    types::TestDesc,
};

pub struct JunitFormatter<T> {
    out: OutputLocation<T>,
    results: Vec<(TestDesc, TestResult, Duration)>,
}

impl<T: Write> JunitFormatter<T> {
    pub fn new(out: OutputLocation<T>) -> Self {
        Self { out, results: Vec::new() }
    }

    fn write_message(&mut self, s: &str) -> io::Result<()> {
        assert!(!s.contains('\n'));

        self.out.write_all(s.as_ref())
    }
}

impl<T: Write> OutputFormatter for JunitFormatter<T> {
    fn write_run_start(&mut self, _test_count: usize) -> io::Result<()> {
        // We write xml header on run start
        self.write_message(&"<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
    }

    fn write_test_start(&mut self, _desc: &TestDesc) -> io::Result<()> {
        // We do not output anything on test start.
        Ok(())
    }

    fn write_timeout(&mut self, _desc: &TestDesc) -> io::Result<()> {
        // We do not output anything on test timeout.
        Ok(())
    }

    fn write_result(
        &mut self,
        desc: &TestDesc,
        result: &TestResult,
        exec_time: Option<&time::TestExecTime>,
        _stdout: &[u8],
        _state: &ConsoleTestState,
    ) -> io::Result<()> {
        // Because testsuit node holds some of the information as attributes, we can't write it
        // until all of the tests has ran. Instead of writting every result as they come in, we add
        // them to a Vec and write them all at once when run is complete.
        let duration = exec_time.map(|t| t.0.clone()).unwrap_or_default();
        self.results.push((desc.clone(), result.clone(), duration));
        Ok(())
    }
    fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
        self.write_message("<testsuites>")?;

        self.write_message(&*format!(
            "<testsuite name=\"test\" package=\"test\" id=\"0\" \
             errors=\"0\" \
             failures=\"{}\" \
             tests=\"{}\" \
             skipped=\"{}\" \
             >",
            state.failed, state.total, state.ignored
        ))?;
        for (desc, result, duration) in std::mem::replace(&mut self.results, Vec::new()) {
            match result {
                TestResult::TrIgnored => { /* no-op */ }
                TestResult::TrFailed => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\">",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                    self.write_message("<failure type=\"assert\"/>")?;
                    self.write_message("</testcase>")?;
                }

                TestResult::TrFailedMsg(ref m) => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\">",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                    self.write_message(&*format!("<failure message=\"{}\" type=\"assert\"/>", m))?;
                    self.write_message("</testcase>")?;
                }

                TestResult::TrTimedFail => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\">",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                    self.write_message("<failure type=\"timeout\"/>")?;
                    self.write_message("</testcase>")?;
                }

                TestResult::TrBench(ref b) => {
                    self.write_message(&*format!(
                        "<testcase classname=\"benchmark.global\" \
                         name=\"{}\" time=\"{}\" />",
                        desc.name.as_slice(),
                        b.ns_iter_summ.sum
                    ))?;
                }

                TestResult::TrOk | TestResult::TrAllowedFail => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\"/>",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                }
            }
        }
        self.write_message("<system-out/>")?;
        self.write_message("<system-err/>")?;
        self.write_message("</testsuite>")?;
        self.write_message("</testsuites>")?;

        Ok(state.failed == 0)
    }
}
<commit_msg>Update junit.rs<commit_after>use std::io::{self, prelude::Write};
use std::time::Duration;

use super::OutputFormatter;
use crate::{
    console::{ConsoleTestState, OutputLocation},
    test_result::TestResult,
    time,
    types::TestDesc,
};

pub struct JunitFormatter<T> {
    out: OutputLocation<T>,
    results: Vec<(TestDesc, TestResult, Duration)>,
}

impl<T: Write> JunitFormatter<T> {
    pub fn new(out: OutputLocation<T>) -> Self {
        Self { out, results: Vec::new() }
    }

    fn write_message(&mut self, s: &str) -> io::Result<()> {
        assert!(!s.contains('\n'));

        self.out.write_all(s.as_ref())
    }
}

impl<T: Write> OutputFormatter for JunitFormatter<T> {
    fn write_run_start(&mut self, _test_count: usize) -> io::Result<()> {
        // We write xml header on run start
        self.write_message(&"<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
    }

    fn write_test_start(&mut self, _desc: &TestDesc) -> io::Result<()> {
        // We do not output anything on test start.
        Ok(())
    }

    fn write_timeout(&mut self, _desc: &TestDesc) -> io::Result<()> {
        // We do not output anything on test timeout.
        Ok(())
    }

    fn write_result(
        &mut self,
        desc: &TestDesc,
        result: &TestResult,
        exec_time: Option<&time::TestExecTime>,
        _stdout: &[u8],
        _state: &ConsoleTestState,
    ) -> io::Result<()> {
        // Because the testsuit node holds some of the information as attributes, we can't write it
        // until all of the tests has ran. Instead of writting every result as they come in, we add
        // them to a Vec and write them all at once when run is complete.
        let duration = exec_time.map(|t| t.0.clone()).unwrap_or_default();
        self.results.push((desc.clone(), result.clone(), duration));
        Ok(())
    }
    fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
        self.write_message("<testsuites>")?;

        self.write_message(&*format!(
            "<testsuite name=\"test\" package=\"test\" id=\"0\" \
             errors=\"0\" \
             failures=\"{}\" \
             tests=\"{}\" \
             skipped=\"{}\" \
             >",
            state.failed, state.total, state.ignored
        ))?;
        for (desc, result, duration) in std::mem::replace(&mut self.results, Vec::new()) {
            match result {
                TestResult::TrIgnored => { /* no-op */ }
                TestResult::TrFailed => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\">",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                    self.write_message("<failure type=\"assert\"/>")?;
                    self.write_message("</testcase>")?;
                }

                TestResult::TrFailedMsg(ref m) => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\">",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                    self.write_message(&*format!("<failure message=\"{}\" type=\"assert\"/>", m))?;
                    self.write_message("</testcase>")?;
                }

                TestResult::TrTimedFail => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\">",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                    self.write_message("<failure type=\"timeout\"/>")?;
                    self.write_message("</testcase>")?;
                }

                TestResult::TrBench(ref b) => {
                    self.write_message(&*format!(
                        "<testcase classname=\"benchmark.global\" \
                         name=\"{}\" time=\"{}\" />",
                        desc.name.as_slice(),
                        b.ns_iter_summ.sum
                    ))?;
                }

                TestResult::TrOk | TestResult::TrAllowedFail => {
                    self.write_message(&*format!(
                        "<testcase classname=\"test.global\" \
                         name=\"{}\" time=\"{}\"/>",
                        desc.name.as_slice(),
                        duration.as_secs()
                    ))?;
                }
            }
        }
        self.write_message("<system-out/>")?;
        self.write_message("<system-err/>")?;
        self.write_message("</testsuite>")?;
        self.write_message("</testsuites>")?;

        Ok(state.failed == 0)
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::Write;

fn main() {
    loop {
        print!("scalyc>");
        io::stdout().flush().unwrap();
        let mut card = String::new();
        io::stdin() .read_line(&mut card).expect("failed to read line");
        print!("{}", card);
    }
}
<commit_msg>break the loop<commit_after>use std::io;
use std::io::Write;

fn main() {
    loop {
        print!("scalyc>");
        io::stdout().flush().unwrap();
        let mut card = String::new();
        io::stdin().read_line(&mut card).expect("failed to read line");
        let ch = card.chars().next().unwrap();
        if ch == '%' {
            break;
        }

        print!("{}", card);
    }
}
<|endoftext|>