<commit_before>//! Main interface for a stream of events the Supervisor can send out
//! in the course of its operations.
//!
//! Currently, the Supervisor is able to send events to a [NATS][1]
//! server. The `init_stream` function must be called
//! before sending events to initialize the publishing thread in the
//! background. Thereafter, you can pass "event" structs to the
//! `event` function, which will publish the event to the stream.
//!
//! All events are published under the "habitat" subject.
//!
//! [1]:https://github.com/nats-io/nats-server

mod error;
mod nats_message_stream;
mod types;

pub(crate) use self::types::ServiceMetadata;
use self::types::{EventMessage,
                  EventMetadata,
                  HealthCheckEvent,
                  ServiceStartedEvent,
                  ServiceStoppedEvent,
                  ServiceUpdateStartedEvent};
use crate::manager::{service::{HealthCheckHookStatus,
                               HealthCheckResult,
                               ProcessOutput,
                               Service,
                               StandardStreams},
                     sys::Sys};
use clap::ArgMatches;
pub use error::{Error,
                Result};
use habitat_common::types::{AutomateAuthToken,
                            EventStreamConnectMethod,
                            EventStreamMetadata,
                            EventStreamServerCertificate};
use habitat_core::{package::ident::PackageIdent,
                   service::HealthCheckInterval};
use nats_message_stream::{NatsMessage,
                          NatsMessageStream};
use prost_types::Duration as ProstDuration;
use state::Container;
use std::{net::SocketAddr,
          sync::Once,
          time::Duration};
use tokio::runtime::Handle;

const SERVICE_STARTED_SUBJECT: &str = "habitat.event.service_started";
const SERVICE_STOPPED_SUBJECT: &str = "habitat.event.service_stopped";
const SERVICE_UPDATE_STARTED_SUBJECT: &str = "habitat.event.service_update_started";
const HEALTHCHECK_SUBJECT: &str = "habitat.event.healthcheck";

static INIT: Once = Once::new();
lazy_static! {
    // TODO (CM): When const fn support lands in stable, we can ditch
    // this lazy_static call.

    /// Reference to the event stream.
    static ref NATS_MESSAGE_STREAM: Container = Container::new();
    /// Core information that is shared between all events.
    static ref EVENT_CORE: Container = Container::new();
}

/// Starts a new thread for sending events to a NATS Streaming
/// server. Stashes the handle to the stream, as well as the core
/// event information that will be a part of all events, in a global
/// static reference for access later.
///
/// TODO (DM): It is unfortunate we have to pass a handle to the tokio runtime here. It would be
/// more idiomatic if we spawned a single top level future and used tokio::spawn within that future.
pub fn init_stream(config: EventStreamConfig,
                   event_core: EventCore,
                   runtime: &Handle)
                   -> Result<()> {
    // call_once can't return a Result (or anything), so we'll fake it
    // by hanging onto any error we might receive.
    let mut return_value: Result<()> = Ok(());

    INIT.call_once(|| {
            let conn_info = EventStreamConnectionInfo::new(&event_core.supervisor_id, config);
            match nats_message_stream::init(conn_info, runtime) {
                Ok(stream) => {
                    NATS_MESSAGE_STREAM.set(stream);
                    EVENT_CORE.set(event_core);
                }
                Err(e) => return_value = Err(e),
            }
        });

    return_value
}

/// Captures all event stream-related configuration options that would
/// be passed in by a user
#[derive(Clone, Debug)]
pub struct EventStreamConfig {
    environment:        String,
    application:        String,
    site:               Option<String>,
    meta:               EventStreamMetadata,
    token:              AutomateAuthToken,
    url:                String,
    connect_method:     EventStreamConnectMethod,
    server_certificate: Option<EventStreamServerCertificate>,
}

impl<'a> From<&'a ArgMatches<'a>> for EventStreamConfig {
    fn from(m: &ArgMatches) -> Self {
        EventStreamConfig { environment:        m.value_of("EVENT_STREAM_ENVIRONMENT")
                                                 .map(str::to_string)
                                                 .expect("Required option for EventStream feature"),
                            application:        m.value_of("EVENT_STREAM_APPLICATION")
                                                 .map(str::to_string)
                                                 .expect("Required option for EventStream feature"),
                            site:               m.value_of("EVENT_STREAM_SITE").map(str::to_string),
                            meta:               EventStreamMetadata::from(m),
                            token:              AutomateAuthToken::from(m),
                            url:                m.value_of("EVENT_STREAM_URL")
                                                 .map(str::to_string)
                                                 .expect("Required option for EventStream feature"),
                            connect_method:     EventStreamConnectMethod::from(m),
                            server_certificate: EventStreamServerCertificate::from_arg_matches(m), }
    }
}

/// All the information needed to establish a connection to a NATS
/// Streaming server.
pub struct EventStreamConnectionInfo {
    pub name:               String,
    pub verbose:            bool,
    pub cluster_uri:        String,
    pub auth_token:         AutomateAuthToken,
    pub connect_method:     EventStreamConnectMethod,
    pub server_certificate: Option<EventStreamServerCertificate>,
}

impl EventStreamConnectionInfo {
    pub fn new(supervisor_id: &str, config: EventStreamConfig) -> Self {
        EventStreamConnectionInfo { name:               format!("hab_client_{}", supervisor_id),
                                    verbose:            true,
                                    cluster_uri:        config.url,
                                    auth_token:         config.token,
                                    connect_method:     config.connect_method,
                                    server_certificate: config.server_certificate, }
    }
}

/// A collection of data that will be present in all events. Rather
/// than baking this into the structure of each event, we represent it
/// once and merge the information into the final rendered form of the
/// event.
///
/// This prevents us from having to thread information throughout the
/// system, just to get it to the places where the events are
/// generated (e.g., not all code has direct access to the
/// Supervisor's ID).
#[derive(Clone, Debug)]
pub struct EventCore {
    /// The unique identifier of the Supervisor sending the event.
    supervisor_id: String,
    ip_address: SocketAddr,
    fqdn: String,
    application: String,
    environment: String,
    site: Option<String>,
    meta: EventStreamMetadata,
}

impl EventCore {
    pub fn new(config: &EventStreamConfig, sys: &Sys, fqdn: String) -> Self {
        EventCore { supervisor_id: sys.member_id.clone(),
                    ip_address: sys.gossip_listen(),
                    fqdn,
                    environment: config.environment.clone(),
                    application: config.application.clone(),
                    site: config.site.clone(),
                    meta: config.meta.clone() }
    }
}

/// Send an event for the start of a Service.
pub fn service_started(service: &Service) {
    if stream_initialized() {
        publish(SERVICE_STARTED_SUBJECT,
                ServiceStartedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

/// Send an event for the stop of a Service.
pub fn service_stopped(service: &Service) {
    if stream_initialized() {
        publish(SERVICE_STOPPED_SUBJECT,
                ServiceStoppedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

/// Send an event at the start of a Service update.
pub fn service_update_started(service: &Service, update: &PackageIdent) {
    if stream_initialized() {
        publish(SERVICE_UPDATE_STARTED_SUBJECT,
                ServiceUpdateStartedEvent { event_metadata:       None,
                                            service_metadata:
                                                Some(service.to_service_metadata()),
                                            update_package_ident: update.clone().to_string(), });
    }
}

// Takes metadata directly, rather than a `&Service` like other event
// functions, because of how the asynchronous health checking
// currently works. Revisit when async/await + Pin is all stabilized.
pub fn health_check(metadata: ServiceMetadata,
                    health_check_result: HealthCheckResult,
                    health_check_hook_status: HealthCheckHookStatus,
                    health_check_interval: HealthCheckInterval) {
    if stream_initialized() {
        let health_check_result: types::HealthCheckResult = health_check_result.into();
        let maybe_duration = health_check_hook_status.maybe_duration();
        let maybe_process_output = health_check_hook_status.maybe_process_output();
        let exit_status = maybe_process_output.as_ref()
                                              .and_then(|o| o.exit_status().code());
        let StandardStreams { stdout, stderr } =
            maybe_process_output.map(ProcessOutput::standard_streams)
                                .unwrap_or_default();

        let prost_interval = ProstDuration::from(Duration::from(health_check_interval));

        publish(HEALTHCHECK_SUBJECT,
                HealthCheckEvent { service_metadata: Some(metadata),
                                   event_metadata: None,
                                   result: i32::from(health_check_result),
                                   execution: maybe_duration.map(Duration::into),
                                   exit_status,
                                   stdout,
                                   stderr,
                                   interval: Some(prost_interval) });
    }
}

////////////////////////////////////////////////////////////////////////

/// Internal helper function to know whether or not to go to the trouble of
/// creating event structures. If the event stream hasn't been
/// initialized, then we shouldn't need to do anything.
fn stream_initialized() -> bool { NATS_MESSAGE_STREAM.try_get::<NatsMessageStream>().is_some() }

/// Publish an event. This is the main interface that client code will
/// use.
///
/// If `init_stream` has not been called already, this function will
/// be a no-op.
fn publish(subject: &'static str, mut event: impl EventMessage) {
    if let Some(stream) = NATS_MESSAGE_STREAM.try_get::<NatsMessageStream>() {
        // TODO (CM): Yeah... this is looking pretty gross. The
        // intention is to be able to timestamp the events right as
        // they go out.
        //
        // We *could* set the time when we convert the EventCore to a
        // EventMetadata struct, but that seems odd.
        //
        // It probably means that this structure just isn't the right
        // one.
        //
        // The ugliness is at least contained, though.
        debug!("Publishing to event stream: event {:?} ", event);
        event.event_metadata(EventMetadata { occurred_at:
                                                 Some(std::time::SystemTime::now().into()),
                                             ..EVENT_CORE.get::<EventCore>().to_event_metadata() });

        let packet = NatsMessage::new(subject, event.to_bytes());
        stream.send(packet);
    }
}

#[cfg(test)]
mod tests {
    use super::{nats_message_stream::NatsMessageStream,
                *};
    use crate::prost::Message;
    use futures::{channel::mpsc as futures_mpsc,
                  stream::StreamExt};
    #[cfg(windows)]
    use habitat_core::os::process::windows_child::ExitStatus;
    use habitat_core::service::HealthCheckInterval;
    #[cfg(unix)]
    use std::{os::unix::process::ExitStatusExt,
              process::ExitStatus};

    #[tokio::test]
    #[cfg(any(unix, windows))]
    async fn health_check_event() {
        let (tx, rx) = futures_mpsc::unbounded();
        NATS_MESSAGE_STREAM.set(NatsMessageStream::new(tx));
        EVENT_CORE.set(EventCore { supervisor_id: String::from("supervisor_id"),
                                   ip_address:    "127.0.0.1:8080".parse().unwrap(),
                                   fqdn:          String::from("fqdn"),
                                   application:   String::from("application"),
                                   environment:   String::from("environment"),
                                   site:          None,
                                   meta:          EventStreamMetadata::default(), });
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Ok,
                     HealthCheckHookStatus::NoHook,
                     HealthCheckInterval::default());
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Warning,
                     HealthCheckHookStatus::FailedToRun(Duration::from_secs(5)),
                     HealthCheckInterval::default());
        #[cfg(windows)]
        let exit_status = ExitStatus::from(2);
        #[cfg(unix)]
        let exit_status = ExitStatus::from_raw(2);
        let process_output =
            ProcessOutput::from_raw(StandardStreams { stdout: Some(String::from("stdout")),
                                                      stderr: Some(String::from("stderr")), },
                                    exit_status);
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Critical,
                     HealthCheckHookStatus::Ran(process_output, Duration::from_secs(10)),
                     HealthCheckInterval::default());
        #[cfg(windows)]
        let exit_status = ExitStatus::from(3);
        #[cfg(unix)]
        let exit_status = ExitStatus::from_raw(3);
        let process_output =
            ProcessOutput::from_raw(StandardStreams { stdout: None,
                                                      stderr: Some(String::from("stderr")), },
                                    exit_status);
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Unknown,
                     HealthCheckHookStatus::Ran(process_output, Duration::from_secs(15)),
                     HealthCheckInterval::default());
        let events = rx.take(4).collect::<Vec<_>>().await;

        let event = HealthCheckEvent::decode(events[0].payload()).unwrap();
        assert_eq!(event.result, 0);
        assert_eq!(event.execution, None);
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, None);
        assert_eq!(event.stderr, None);

        let default_interval = HealthCheckInterval::default();
        let prost_interval = ProstDuration::from(Duration::from(default_interval));
        let prost_interval_option = Some(prost_interval);

        assert_eq!(event.interval, prost_interval_option);

        let event = HealthCheckEvent::decode(events[1].payload()).unwrap();
        assert_eq!(event.result, 1);
        assert_eq!(event.execution.unwrap().seconds, 5);
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, None);
        assert_eq!(event.stderr, None);

        let event = HealthCheckEvent::decode(events[2].payload()).unwrap();
        assert_eq!(event.result, 2);
        assert_eq!(event.execution.unwrap().seconds, 10);
        #[cfg(windows)]
        assert_eq!(event.exit_status, Some(2));
        // `ExitStatus::from_raw` sets the signal not the code
        #[cfg(unix)]
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, Some(String::from("stdout")));
        assert_eq!(event.stderr, Some(String::from("stderr")));

        let event = HealthCheckEvent::decode(events[3].payload()).unwrap();
        assert_eq!(event.result, 3);
        assert_eq!(event.execution.unwrap().seconds, 15);
        #[cfg(windows)]
        assert_eq!(event.exit_status, Some(3));
        // `ExitStatus::from_raw` sets the signal not the code
        #[cfg(unix)]
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, None);
        assert_eq!(event.stderr, Some(String::from("stderr")));
    }
}
<commit_msg>Use Storage instead of Container<commit_after>//! Main interface for a stream of events the Supervisor can send out
//! in the course of its operations.
//!
//! Currently, the Supervisor is able to send events to a [NATS][1]
//! server. The `init_stream` function must be called
//! before sending events to initialize the publishing thread in the
//! background. Thereafter, you can pass "event" structs to the
//! `event` function, which will publish the event to the stream.
//!
//! All events are published under the "habitat" subject.
//!
//! [1]:https://github.com/nats-io/nats-server

mod error;
mod nats_message_stream;
mod types;

pub(crate) use self::types::ServiceMetadata;
use self::types::{EventMessage,
                  EventMetadata,
                  HealthCheckEvent,
                  ServiceStartedEvent,
                  ServiceStoppedEvent,
                  ServiceUpdateStartedEvent};
use crate::manager::{service::{HealthCheckHookStatus,
                               HealthCheckResult,
                               ProcessOutput,
                               Service,
                               StandardStreams},
                     sys::Sys};
use clap::ArgMatches;
pub use error::{Error,
                Result};
use habitat_common::types::{AutomateAuthToken,
                            EventStreamConnectMethod,
                            EventStreamMetadata,
                            EventStreamServerCertificate};
use habitat_core::{package::ident::PackageIdent,
                   service::HealthCheckInterval};
use nats_message_stream::{NatsMessage,
                          NatsMessageStream};
use prost_types::Duration as ProstDuration;
use state::Storage;
use std::{net::SocketAddr,
          sync::Once,
          time::Duration};
use tokio::runtime::Handle;

const SERVICE_STARTED_SUBJECT: &str = "habitat.event.service_started";
const SERVICE_STOPPED_SUBJECT: &str = "habitat.event.service_stopped";
const SERVICE_UPDATE_STARTED_SUBJECT: &str = "habitat.event.service_update_started";
const HEALTHCHECK_SUBJECT: &str = "habitat.event.healthcheck";

static INIT: Once = Once::new();
lazy_static! {
    // TODO (CM): When const fn support lands in stable, we can ditch
    // this lazy_static call.

    /// Reference to the event stream.
    static ref NATS_MESSAGE_STREAM: Storage<NatsMessageStream> = Storage::new();
    /// Core information that is shared between all events.
    static ref EVENT_CORE: Storage<EventCore> = Storage::new();
}

/// Starts a new thread for sending events to a NATS Streaming
/// server. Stashes the handle to the stream, as well as the core
/// event information that will be a part of all events, in a global
/// static reference for access later.
///
/// TODO (DM): It is unfortunate we have to pass a handle to the tokio runtime here. It would be
/// more idiomatic if we spawned a single top level future and used tokio::spawn within that future.
pub fn init_stream(config: EventStreamConfig,
                   event_core: EventCore,
                   runtime: &Handle)
                   -> Result<()> {
    // call_once can't return a Result (or anything), so we'll fake it
    // by hanging onto any error we might receive.
    let mut return_value: Result<()> = Ok(());

    INIT.call_once(|| {
            let conn_info = EventStreamConnectionInfo::new(&event_core.supervisor_id, config);
            match nats_message_stream::init(conn_info, runtime) {
                Ok(stream) => {
                    NATS_MESSAGE_STREAM.set(stream);
                    EVENT_CORE.set(event_core);
                }
                Err(e) => return_value = Err(e),
            }
        });

    return_value
}

/// Captures all event stream-related configuration options that would
/// be passed in by a user
#[derive(Clone, Debug)]
pub struct EventStreamConfig {
    environment:        String,
    application:        String,
    site:               Option<String>,
    meta:               EventStreamMetadata,
    token:              AutomateAuthToken,
    url:                String,
    connect_method:     EventStreamConnectMethod,
    server_certificate: Option<EventStreamServerCertificate>,
}

impl<'a> From<&'a ArgMatches<'a>> for EventStreamConfig {
    fn from(m: &ArgMatches) -> Self {
        EventStreamConfig { environment:        m.value_of("EVENT_STREAM_ENVIRONMENT")
                                                 .map(str::to_string)
                                                 .expect("Required option for EventStream feature"),
                            application:        m.value_of("EVENT_STREAM_APPLICATION")
                                                 .map(str::to_string)
                                                 .expect("Required option for EventStream feature"),
                            site:               m.value_of("EVENT_STREAM_SITE").map(str::to_string),
                            meta:               EventStreamMetadata::from(m),
                            token:              AutomateAuthToken::from(m),
                            url:                m.value_of("EVENT_STREAM_URL")
                                                 .map(str::to_string)
                                                 .expect("Required option for EventStream feature"),
                            connect_method:     EventStreamConnectMethod::from(m),
                            server_certificate: EventStreamServerCertificate::from_arg_matches(m), }
    }
}

/// All the information needed to establish a connection to a NATS
/// Streaming server.
pub struct EventStreamConnectionInfo {
    pub name:               String,
    pub verbose:            bool,
    pub cluster_uri:        String,
    pub auth_token:         AutomateAuthToken,
    pub connect_method:     EventStreamConnectMethod,
    pub server_certificate: Option<EventStreamServerCertificate>,
}

impl EventStreamConnectionInfo {
    pub fn new(supervisor_id: &str, config: EventStreamConfig) -> Self {
        EventStreamConnectionInfo { name:               format!("hab_client_{}", supervisor_id),
                                    verbose:            true,
                                    cluster_uri:        config.url,
                                    auth_token:         config.token,
                                    connect_method:     config.connect_method,
                                    server_certificate: config.server_certificate, }
    }
}

/// A collection of data that will be present in all events. Rather
/// than baking this into the structure of each event, we represent it
/// once and merge the information into the final rendered form of the
/// event.
///
/// This prevents us from having to thread information throughout the
/// system, just to get it to the places where the events are
/// generated (e.g., not all code has direct access to the
/// Supervisor's ID).
#[derive(Clone, Debug)]
pub struct EventCore {
    /// The unique identifier of the Supervisor sending the event.
    supervisor_id: String,
    ip_address: SocketAddr,
    fqdn: String,
    application: String,
    environment: String,
    site: Option<String>,
    meta: EventStreamMetadata,
}

impl EventCore {
    pub fn new(config: &EventStreamConfig, sys: &Sys, fqdn: String) -> Self {
        EventCore { supervisor_id: sys.member_id.clone(),
                    ip_address: sys.gossip_listen(),
                    fqdn,
                    environment: config.environment.clone(),
                    application: config.application.clone(),
                    site: config.site.clone(),
                    meta: config.meta.clone() }
    }
}

/// Send an event for the start of a Service.
pub fn service_started(service: &Service) {
    if stream_initialized() {
        publish(SERVICE_STARTED_SUBJECT,
                ServiceStartedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

/// Send an event for the stop of a Service.
pub fn service_stopped(service: &Service) {
    if stream_initialized() {
        publish(SERVICE_STOPPED_SUBJECT,
                ServiceStoppedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

/// Send an event at the start of a Service update.
pub fn service_update_started(service: &Service, update: &PackageIdent) {
    if stream_initialized() {
        publish(SERVICE_UPDATE_STARTED_SUBJECT,
                ServiceUpdateStartedEvent { event_metadata:       None,
                                            service_metadata:
                                                Some(service.to_service_metadata()),
                                            update_package_ident: update.clone().to_string(), });
    }
}

// Takes metadata directly, rather than a `&Service` like other event
// functions, because of how the asynchronous health checking
// currently works. Revisit when async/await + Pin is all stabilized.
pub fn health_check(metadata: ServiceMetadata,
                    health_check_result: HealthCheckResult,
                    health_check_hook_status: HealthCheckHookStatus,
                    health_check_interval: HealthCheckInterval) {
    if stream_initialized() {
        let health_check_result: types::HealthCheckResult = health_check_result.into();
        let maybe_duration = health_check_hook_status.maybe_duration();
        let maybe_process_output = health_check_hook_status.maybe_process_output();
        let exit_status = maybe_process_output.as_ref()
                                              .and_then(|o| o.exit_status().code());
        let StandardStreams { stdout, stderr } =
            maybe_process_output.map(ProcessOutput::standard_streams)
                                .unwrap_or_default();

        let prost_interval = ProstDuration::from(Duration::from(health_check_interval));

        publish(HEALTHCHECK_SUBJECT,
                HealthCheckEvent { service_metadata: Some(metadata),
                                   event_metadata: None,
                                   result: i32::from(health_check_result),
                                   execution: maybe_duration.map(Duration::into),
                                   exit_status,
                                   stdout,
                                   stderr,
                                   interval: Some(prost_interval) });
    }
}

////////////////////////////////////////////////////////////////////////

/// Internal helper function to know whether or not to go to the trouble of
/// creating event structures. If the event stream hasn't been
/// initialized, then we shouldn't need to do anything.
fn stream_initialized() -> bool { NATS_MESSAGE_STREAM.try_get().is_some() }

/// Publish an event. This is the main interface that client code will
/// use.
///
/// If `init_stream` has not been called already, this function will
/// be a no-op.
fn publish(subject: &'static str, mut event: impl EventMessage) {
    if let Some(stream) = NATS_MESSAGE_STREAM.try_get() {
        // TODO (CM): Yeah... this is looking pretty gross. The
        // intention is to be able to timestamp the events right as
        // they go out.
        //
        // We *could* set the time when we convert the EventCore to a
        // EventMetadata struct, but that seems odd.
        //
        // It probably means that this structure just isn't the right
        // one.
        //
        // The ugliness is at least contained, though.
        debug!("Publishing to event stream: event {:?} ", event);
        event.event_metadata(EventMetadata { occurred_at:
                                                 Some(std::time::SystemTime::now().into()),
                                             ..EVENT_CORE.get().to_event_metadata() });

        let packet = NatsMessage::new(subject, event.to_bytes());
        stream.send(packet);
    }
}

#[cfg(test)]
mod tests {
    use super::{nats_message_stream::NatsMessageStream,
                *};
    use crate::prost::Message;
    use futures::{channel::mpsc as futures_mpsc,
                  stream::StreamExt};
    #[cfg(windows)]
    use habitat_core::os::process::windows_child::ExitStatus;
    use habitat_core::service::HealthCheckInterval;
    #[cfg(unix)]
    use std::{os::unix::process::ExitStatusExt,
              process::ExitStatus};

    #[tokio::test]
    #[cfg(any(unix, windows))]
    async fn health_check_event() {
        let (tx, rx) = futures_mpsc::unbounded();
        NATS_MESSAGE_STREAM.set(NatsMessageStream::new(tx));
        EVENT_CORE.set(EventCore { supervisor_id: String::from("supervisor_id"),
                                   ip_address:    "127.0.0.1:8080".parse().unwrap(),
                                   fqdn:          String::from("fqdn"),
                                   application:   String::from("application"),
                                   environment:   String::from("environment"),
                                   site:          None,
                                   meta:          EventStreamMetadata::default(), });
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Ok,
                     HealthCheckHookStatus::NoHook,
                     HealthCheckInterval::default());
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Warning,
                     HealthCheckHookStatus::FailedToRun(Duration::from_secs(5)),
                     HealthCheckInterval::default());
        #[cfg(windows)]
        let exit_status = ExitStatus::from(2);
        #[cfg(unix)]
        let exit_status = ExitStatus::from_raw(2);
        let process_output =
            ProcessOutput::from_raw(StandardStreams { stdout: Some(String::from("stdout")),
                                                      stderr: Some(String::from("stderr")), },
                                    exit_status);
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Critical,
                     HealthCheckHookStatus::Ran(process_output, Duration::from_secs(10)),
                     HealthCheckInterval::default());
        #[cfg(windows)]
        let exit_status = ExitStatus::from(3);
        #[cfg(unix)]
        let exit_status = ExitStatus::from_raw(3);
        let process_output =
            ProcessOutput::from_raw(StandardStreams { stdout: None,
                                                      stderr: Some(String::from("stderr")), },
                                    exit_status);
        health_check(ServiceMetadata::default(),
                     HealthCheckResult::Unknown,
                     HealthCheckHookStatus::Ran(process_output, Duration::from_secs(15)),
                     HealthCheckInterval::default());
        let events = rx.take(4).collect::<Vec<_>>().await;

        let event = HealthCheckEvent::decode(events[0].payload()).unwrap();
        assert_eq!(event.result, 0);
        assert_eq!(event.execution, None);
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, None);
        assert_eq!(event.stderr, None);

        let default_interval = HealthCheckInterval::default();
        let prost_interval = ProstDuration::from(Duration::from(default_interval));
        let prost_interval_option = Some(prost_interval);

        assert_eq!(event.interval, prost_interval_option);

        let event = HealthCheckEvent::decode(events[1].payload()).unwrap();
        assert_eq!(event.result, 1);
        assert_eq!(event.execution.unwrap().seconds, 5);
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, None);
        assert_eq!(event.stderr, None);

        let event = HealthCheckEvent::decode(events[2].payload()).unwrap();
        assert_eq!(event.result, 2);
        assert_eq!(event.execution.unwrap().seconds, 10);
        #[cfg(windows)]
        assert_eq!(event.exit_status, Some(2));
        // `ExitStatus::from_raw` sets the signal not the code
        #[cfg(unix)]
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, Some(String::from("stdout")));
        assert_eq!(event.stderr, Some(String::from("stderr")));

        let event = HealthCheckEvent::decode(events[3].payload()).unwrap();
        assert_eq!(event.result, 3);
        assert_eq!(event.execution.unwrap().seconds, 15);
        #[cfg(windows)]
        assert_eq!(event.exit_status, Some(3));
        // `ExitStatus::from_raw` sets the signal not the code
        #[cfg(unix)]
        assert_eq!(event.exit_status, None);
        assert_eq!(event.stdout, None);
        assert_eq!(event.stderr, Some(String::from("stderr")));
    }
}
<|endoftext|>


<commit_before>// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! jsonrpc server over unix sockets
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate json_ipc_server;
//! extern crate rand;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use json_ipc_server::Server;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let server = Server::new("/tmp/json-ipc-test.ipc", &Arc::new(io)).unwrap();
//!     ::std::thread::spawn(move || server.run());
//! }
//! ```

use mio::*;
use mio::unix::*;
use bytes::{Buf, ByteBuf, MutByteBuf};
use std::io;
use jsonrpc_core::IoHandler;
use std::sync::*;
use std::sync::atomic::*;
use std;
use slab;
#[cfg(test)]
use tests;

const SERVER: Token = Token(0);
const MAX_CONCURRENT_CONNECTIONS: usize = 16;

struct SocketConnection {
    socket: UnixStream,
    buf: Option<ByteBuf>,
    mut_buf: Option<MutByteBuf>,
    token: Option<Token>,
    interest: EventSet,
}

type Slab<T> = slab::Slab<T, Token>;

impl SocketConnection {
    fn new(sock: UnixStream) -> Self {
        SocketConnection {
            socket: sock,
            buf: None,
            mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
            token: None,
            interest: EventSet::hup(),
        }
    }

    fn writable(&mut self, event_loop: &mut EventLoop<RpcServer>, _handler: &IoHandler) -> io::Result<()> {
        use std::io::Write;
        if let Some(buf) = self.buf.take() {
            let result = try!(self.socket.write_all(&buf.bytes()));
            trace!(target: "ipc", "Write response: {} bytes", result);
        }

        self.interest.remove(EventSet::writable());
        self.interest.insert(EventSet::readable());

        event_loop.reregister(&self.socket, self.token.unwrap(), self.interest, PollOpt::edge() | PollOpt::oneshot())
    }

    fn readable(&mut self, event_loop: &mut EventLoop<RpcServer>, handler: &IoHandler) -> io::Result<()> {
        let mut buf = self.mut_buf.take().unwrap_or_else(|| panic!("unwrapping mutable buffer which is None"));

        match self.socket.try_read_buf(&mut buf) {
            Ok(None) => {
                self.mut_buf = Some(buf);
            }
            Ok(Some(_)) => {
                String::from_utf8(buf.bytes().to_vec())
                    .map(|rpc_msg| {
                        trace!(target: "ipc", "Request: {}", rpc_msg);
                        let response: Option<String> = handler.handle_request(&rpc_msg);
                        if let Some(response_str) = response {
                            let response_bytes = response_str.into_bytes();
                            self.buf = Some(ByteBuf::from_slice(&response_bytes));
                            trace!(target: "ipc", "Response: {}", response_str);
                        }
                    }).unwrap();

                self.mut_buf = Some(ByteBuf::mut_with_capacity(2048));

                self.interest.remove(EventSet::readable());
                self.interest.insert(EventSet::writable());
            }
            Err(e) => {
                trace!(target: "ipc", "Error receiving data: {:?}", e);
                self.interest.remove(EventSet::readable());
            }

        };

        event_loop.reregister(&self.socket, self.token.unwrap(), self.interest, PollOpt::edge() | PollOpt::oneshot())
    }
}

struct RpcServer {
    socket: UnixListener,
    connections: Slab<SocketConnection>,
    io_handler: Arc<IoHandler>,
}

pub struct Server {
    rpc_server: Arc<RwLock<RpcServer>>,
    event_loop: Arc<RwLock<EventLoop<RpcServer>>>,
    is_stopping: Arc<AtomicBool>,
    is_stopped: Arc<AtomicBool>,
    addr: String,
}

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    NotStarted,
    AlreadyStopping,
    NotStopped,
    IsStopping,
}

impl std::convert::From<std::io::Error> for Error {
    fn from(io_error: std::io::Error) -> Error {
        Error::Io(io_error)
    }
}

impl Server {
    /// New server
    pub fn new(socket_addr: &str, io_handler: &Arc<IoHandler>) -> Result<Server, Error> {
        let (server, event_loop) = try!(RpcServer::start(socket_addr, io_handler));
        Ok(Server {
            rpc_server: Arc::new(RwLock::new(server)),
            event_loop: Arc::new(RwLock::new(event_loop)),
            is_stopping: Arc::new(AtomicBool::new(false)),
            is_stopped: Arc::new(AtomicBool::new(true)),
            addr: socket_addr.to_owned(),
        })
    }

    /// Run server (in current thread)
    pub fn run(&self) {
        let mut event_loop = self.event_loop.write().unwrap();
        let mut server = self.rpc_server.write().unwrap();
        event_loop.run(&mut server).unwrap();
    }

    /// Poll server requests (for manual async scenarios)
    pub fn poll(&self) {
        let mut event_loop = self.event_loop.write().unwrap();
        let mut server = self.rpc_server.write().unwrap();

        event_loop.run_once(&mut server, Some(100)).unwrap();
    }

    /// Run server (in separate thread)
    pub fn run_async(&self) -> Result<(), Error> {
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::IsStopping) }
        if !self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStopped) }

        self.is_stopped.store(false, Ordering::Relaxed);

        let event_loop = self.event_loop.clone();
        let server = self.rpc_server.clone();
        let thread_stopping = self.is_stopping.clone();
        let thread_stopped = self.is_stopped.clone();
        std::thread::spawn(move || {
            let mut event_loop = event_loop.write().unwrap();
            let mut server = server.write().unwrap();
            while !thread_stopping.load(Ordering::Relaxed) {
                event_loop.run_once(&mut server, Some(100)).unwrap();
            }
            thread_stopped.store(true, Ordering::Relaxed);
        });
        Ok(())
    }

    pub fn stop_async(&self) -> Result<(), Error> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop(&self) -> Result<(), Error> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        while !self.is_stopped.load(Ordering::Relaxed) { std::thread::sleep(std::time::Duration::new(0, 10)); }
        Ok(())
    }
}

impl Drop for Server {
    fn drop(&mut self) {
        self.stop().unwrap_or_else(|_| {}); // ignore error - can be stopped already
        ::std::fs::remove_file(&self.addr).unwrap_or_else(|_| {}); // ignoer error - server could have never been started
    }
}

impl RpcServer {

    /// start ipc rpc server (blocking)
    pub fn start(addr: &str, io_handler: &Arc<IoHandler>) -> Result<(RpcServer, EventLoop<RpcServer>), Error> {
        let mut event_loop = try!(EventLoop::new());
        ::std::fs::remove_file(addr).unwrap_or_else(|_| {} ); // ignore error (if no file)
        let socket = try!(UnixListener::bind(&addr));
        event_loop.register(&socket, SERVER, EventSet::readable(), PollOpt::edge()).unwrap();
        let server = RpcServer {
            socket: socket,
            connections: Slab::new_starting_at(Token(1), MAX_CONCURRENT_CONNECTIONS),
            io_handler: io_handler.clone(),
        };
        Ok((server, event_loop))
    }

    fn accept(&mut self, event_loop: &mut EventLoop<RpcServer>) -> io::Result<()> {
        let new_client_socket = self.socket.accept().unwrap().unwrap();
        let connection = SocketConnection::new(new_client_socket);
        if self.connections.count() >= MAX_CONCURRENT_CONNECTIONS {
            let oldest_token = self.connections.iter().nth(0)
                .expect("fatal: impossible since count > MAX_CONCURRENT_CONNECTIONS and we request first one")
                .token.unwrap().clone();
            self.connections.remove(oldest_token);
        }
        let token = self.connections.insert(connection).ok().expect("fatal: Could not add connection to slab (memory issue?)");

        self.connections[token].token = Some(token);
        event_loop.register(
            &self.connections[token].socket,
            token,
            EventSet::readable(),
            PollOpt::edge() | PollOpt::oneshot()
        ).ok().expect("fatal: could not register socket with event loop (memory issue?)");

        Ok(())
    }

    fn connection_readable(&mut self, event_loop: &mut EventLoop<RpcServer>, tok: Token) -> io::Result<()> {
        let io_handler = self.io_handler.clone();
        self.connection(tok).readable(event_loop, &io_handler)
    }

    fn connection_writable(&mut self, event_loop: &mut EventLoop<RpcServer>, tok: Token) -> io::Result<()> {
        let io_handler = self.io_handler.clone();
        self.connection(tok).writable(event_loop, &io_handler)
    }

    fn connection<'a>(&'a mut self, tok: Token) -> &'a mut SocketConnection {
        &mut self.connections[tok]
    }
}

impl Handler for RpcServer {
    type Timeout = usize;
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<RpcServer>, token: Token, events: EventSet) {
        if events.is_readable() {
            match token {
                SERVER => self.accept(event_loop).unwrap(),
                _ => self.connection_readable(event_loop, token).unwrap()
            };
        }

        if events.is_writable() {
            match token {
                SERVER => { },
                _ => self.connection_writable(event_loop, token).unwrap_or_else(|_| {}), // todo: disqualify connection from list on error
            };
        }
    }
}

#[test]
pub fn test_reqrep_poll() {
    let addr = tests::random_ipc_endpoint();
    let io = tests::dummy_io_handler();
    let server = Server::new(&addr, &io).unwrap();
    std::thread::spawn(move || {
        loop {
            server.poll();
        }
    });

    let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#;
    let response = r#"{"jsonrpc":"2.0","result":"hello 42! you sent 23","id":1}"#;
    assert_eq!(String::from_utf8(tests::dummy_request(&addr, request.as_bytes())).unwrap(), response.to_string());

    std::thread::sleep(std::time::Duration::from_millis(500));
}

#[test]
pub fn test_file_removed() {
    let addr = tests::random_ipc_endpoint();
    let io = tests::dummy_io_handler();
    {
        let server = Server::new(&addr, &io).unwrap();
        server.run_async().unwrap();
        std::thread::sleep(std::time::Duration::from_millis(50));

        let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#;
        let response = r#"{"jsonrpc":"2.0","result":"hello 42! you sent 23","id":1}"#;
        assert_eq!(String::from_utf8(tests::dummy_request(&addr, request.as_bytes())).unwrap(), response.to_string());
    }
    assert!(::std::fs::metadata(addr).is_err()); // err is file not exists
}
<commit_msg>fix result<commit_after>// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! jsonrpc server over unix sockets
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate json_ipc_server;
//! extern crate rand;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use json_ipc_server::Server;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let server = Server::new("/tmp/json-ipc-test.ipc", &Arc::new(io)).unwrap();
//!     ::std::thread::spawn(move || server.run());
//! }
//! ```

use mio::*;
use mio::unix::*;
use bytes::{Buf, ByteBuf, MutByteBuf};
use std::io;
use jsonrpc_core::IoHandler;
use std::sync::*;
use std::sync::atomic::*;
use std;
use slab;
#[cfg(test)]
use tests;

const SERVER: Token = Token(0);
const MAX_CONCURRENT_CONNECTIONS: usize = 16;

struct SocketConnection {
    socket: UnixStream,
    buf: Option<ByteBuf>,
    mut_buf: Option<MutByteBuf>,
    token: Option<Token>,
    interest: EventSet,
}

type Slab<T> = slab::Slab<T, Token>;

impl SocketConnection {
    fn new(sock: UnixStream) -> Self {
        SocketConnection {
            socket: sock,
            buf: None,
            mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
            token: None,
            interest: EventSet::hup(),
        }
    }

    fn writable(&mut self, event_loop: &mut EventLoop<RpcServer>, _handler: &IoHandler) -> io::Result<()> {
        use std::io::Write;
        if let Some(buf) = self.buf.take() {
            try!(self.socket.write_all(&buf.bytes()));
        }

        self.interest.remove(EventSet::writable());
        self.interest.insert(EventSet::readable());

        event_loop.reregister(&self.socket, self.token.unwrap(), self.interest, PollOpt::edge() | PollOpt::oneshot())
    }

    fn readable(&mut self, event_loop: &mut EventLoop<RpcServer>, handler: &IoHandler) -> io::Result<()> {
        let mut buf = self.mut_buf.take().unwrap_or_else(|| panic!("unwrapping mutable buffer which is None"));

        match self.socket.try_read_buf(&mut buf) {
            Ok(None) => {
                self.mut_buf = Some(buf);
            }
            Ok(Some(_)) => {
                String::from_utf8(buf.bytes().to_vec())
                    .map(|rpc_msg| {
                        trace!(target: "ipc", "Request: {}", rpc_msg);
                        let response: Option<String> = handler.handle_request(&rpc_msg);
                        if let Some(response_str) = response {
                            let response_bytes = response_str.into_bytes();
                            self.buf = Some(ByteBuf::from_slice(&response_bytes));
                            trace!(target: "ipc", "Response: {}", response_str);
                        }
                    }).unwrap();

                self.mut_buf = Some(ByteBuf::mut_with_capacity(2048));

                self.interest.remove(EventSet::readable());
                self.interest.insert(EventSet::writable());
            }
            Err(e) => {
                trace!(target: "ipc", "Error receiving data: {:?}", e);
                self.interest.remove(EventSet::readable());
            }

        };

        event_loop.reregister(&self.socket, self.token.unwrap(), self.interest, PollOpt::edge() | PollOpt::oneshot())
    }
}

struct RpcServer {
    socket: UnixListener,
    connections: Slab<SocketConnection>,
    io_handler: Arc<IoHandler>,
}

pub struct Server {
    rpc_server: Arc<RwLock<RpcServer>>,
    event_loop: Arc<RwLock<EventLoop<RpcServer>>>,
    is_stopping: Arc<AtomicBool>,
    is_stopped: Arc<AtomicBool>,
    addr: String,
}

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    NotStarted,
    AlreadyStopping,
    NotStopped,
    IsStopping,
}

impl std::convert::From<std::io::Error> for Error {
    fn from(io_error: std::io::Error) -> Error {
        Error::Io(io_error)
    }
}

impl Server {
    /// New server
    pub fn new(socket_addr: &str, io_handler: &Arc<IoHandler>) -> Result<Server, Error> {
        let (server, event_loop) = try!(RpcServer::start(socket_addr, io_handler));
        Ok(Server {
            rpc_server: Arc::new(RwLock::new(server)),
            event_loop: Arc::new(RwLock::new(event_loop)),
            is_stopping: Arc::new(AtomicBool::new(false)),
            is_stopped: Arc::new(AtomicBool::new(true)),
            addr: socket_addr.to_owned(),
        })
    }

    /// Run server (in current thread)
    pub fn run(&self) {
        let mut event_loop = self.event_loop.write().unwrap();
        let mut server = self.rpc_server.write().unwrap();
        event_loop.run(&mut server).unwrap();
    }

    /// Poll server requests (for manual async scenarios)
    pub fn poll(&self) {
        let mut event_loop = self.event_loop.write().unwrap();
        let mut server = self.rpc_server.write().unwrap();

        event_loop.run_once(&mut server, Some(100)).unwrap();
    }

    /// Run server (in separate thread)
    pub fn run_async(&self) -> Result<(), Error> {
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::IsStopping) }
        if !self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStopped) }

        self.is_stopped.store(false, Ordering::Relaxed);

        let event_loop = self.event_loop.clone();
        let server = self.rpc_server.clone();
        let thread_stopping = self.is_stopping.clone();
        let thread_stopped = self.is_stopped.clone();
        std::thread::spawn(move || {
            let mut event_loop = event_loop.write().unwrap();
            let mut server = server.write().unwrap();
            while !thread_stopping.load(Ordering::Relaxed) {
                event_loop.run_once(&mut server, Some(100)).unwrap();
            }
            thread_stopped.store(true, Ordering::Relaxed);
        });
        Ok(())
    }

    pub fn stop_async(&self) -> Result<(), Error> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop(&self) -> Result<(), Error> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        while !self.is_stopped.load(Ordering::Relaxed) { std::thread::sleep(std::time::Duration::new(0, 10)); }
        Ok(())
    }
}

impl Drop for Server {
    fn drop(&mut self) {
        self.stop().unwrap_or_else(|_| {}); // ignore error - can be stopped already
        ::std::fs::remove_file(&self.addr).unwrap_or_else(|_| {}); // ignoer error - server could have never been started
    }
}

impl RpcServer {

    /// start ipc rpc server (blocking)
    pub fn start(addr: &str, io_handler: &Arc<IoHandler>) -> Result<(RpcServer, EventLoop<RpcServer>), Error> {
        let mut event_loop = try!(EventLoop::new());
        ::std::fs::remove_file(addr).unwrap_or_else(|_| {} ); // ignore error (if no file)
        let socket = try!(UnixListener::bind(&addr));
        event_loop.register(&socket, SERVER, EventSet::readable(), PollOpt::edge()).unwrap();
        let server = RpcServer {
            socket: socket,
            connections: Slab::new_starting_at(Token(1), MAX_CONCURRENT_CONNECTIONS),
            io_handler: io_handler.clone(),
        };
        Ok((server, event_loop))
    }

    fn accept(&mut self, event_loop: &mut EventLoop<RpcServer>) -> io::Result<()> {
        let new_client_socket = self.socket.accept().unwrap().unwrap();
        let connection = SocketConnection::new(new_client_socket);
        if self.connections.count() >= MAX_CONCURRENT_CONNECTIONS {
            let oldest_token = self.connections.iter().nth(0)
                .expect("fatal: impossible since count > MAX_CONCURRENT_CONNECTIONS and we request first one")
                .token.unwrap().clone();
            self.connections.remove(oldest_token);
        }
        let token = self.connections.insert(connection).ok().expect("fatal: Could not add connection to slab (memory issue?)");

        self.connections[token].token = Some(token);
        event_loop.register(
            &self.connections[token].socket,
            token,
            EventSet::readable(),
            PollOpt::edge() | PollOpt::oneshot()
        ).ok().expect("fatal: could not register socket with event loop (memory issue?)");

        Ok(())
    }

    fn connection_readable(&mut self, event_loop: &mut EventLoop<RpcServer>, tok: Token) -> io::Result<()> {
        let io_handler = self.io_handler.clone();
        self.connection(tok).readable(event_loop, &io_handler)
    }

    fn connection_writable(&mut self, event_loop: &mut EventLoop<RpcServer>, tok: Token) -> io::Result<()> {
        let io_handler = self.io_handler.clone();
        self.connection(tok).writable(event_loop, &io_handler)
    }

    fn connection<'a>(&'a mut self, tok: Token) -> &'a mut SocketConnection {
        &mut self.connections[tok]
    }
}

impl Handler for RpcServer {
    type Timeout = usize;
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<RpcServer>, token: Token, events: EventSet) {
        if events.is_readable() {
            match token {
                SERVER => self.accept(event_loop).unwrap(),
                _ => self.connection_readable(event_loop, token).unwrap()
            };
        }

        if events.is_writable() {
            match token {
                SERVER => { },
                _ => self.connection_writable(event_loop, token).unwrap_or_else(|_| {}), // todo: disqualify connection from list on error
            };
        }
    }
}

#[test]
pub fn test_reqrep_poll() {
    let addr = tests::random_ipc_endpoint();
    let io = tests::dummy_io_handler();
    let server = Server::new(&addr, &io).unwrap();
    std::thread::spawn(move || {
        loop {
            server.poll();
        }
    });

    let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#;
    let response = r#"{"jsonrpc":"2.0","result":"hello 42! you sent 23","id":1}"#;
    assert_eq!(String::from_utf8(tests::dummy_request(&addr, request.as_bytes())).unwrap(), response.to_string());

    std::thread::sleep(std::time::Duration::from_millis(500));
}

#[test]
pub fn test_file_removed() {
    let addr = tests::random_ipc_endpoint();
    let io = tests::dummy_io_handler();
    {
        let server = Server::new(&addr, &io).unwrap();
        server.run_async().unwrap();
        std::thread::sleep(std::time::Duration::from_millis(50));

        let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#;
        let response = r#"{"jsonrpc":"2.0","result":"hello 42! you sent 23","id":1}"#;
        assert_eq!(String::from_utf8(tests::dummy_request(&addr, request.as_bytes())).unwrap(), response.to_string());
    }
    assert!(::std::fs::metadata(addr).is_err()); // err is file not exists
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use memory::Memory;
use extensions::Incrementor;

pub fn add(first: &Cell<u8>, second: &Cell<u8>) {
    debug!("add: {} {}", first.get(), second.get());
    let new_value = first.get() + second.get();
    first.set(new_value);
}

pub fn ld_reg_to_reg(target: &Cell<u8>, source: &Cell<u8>) {
    debug!("ld_reg_to_reg: {}", source.get());
    let val = source.get();
    target.set(val);
}

/// Loads the memory pointed to by the next two bytes into a register
pub fn ld_next_byte_to_reg(mem: Memory, pc: &Cell<u16>, reg: &Cell<u8>) {
    let val = mem.read_byte(pc.get());
    debug!("ld_next_byte_to_reg: {} {}", pc.get(), val);
    pc.increment();
    reg.set(val);
}

/// Performs no operation and consumes a cycle
pub fn nop() {
    println!("nop");
}

#[test]
fn test_add_reg_with_reg() {
    let first = Cell::new(5);
    let second = Cell::new(9);

    add(&first, &second);
    assert!(first.get() == 14);
}

#[test]
fn test_ld_next_byte_to_reg() {
    let mem = Memory::new();
    let pc = Cell::new(11);
    let reg = Cell::new(0);

    mem.write_byte(11, 0xFF);

    ld_next_byte_to_reg(mem, &pc, &reg);
    assert!(reg.get() == 0xFF);
    assert!(pc.get() == 12);

}

#[test]
fn test_ld_reg_to_reg() {
    let target = Cell::new(5);
    let source = Cell::new(10);

    ld_reg_to_reg(&target, &source);

    assert!(target.get() == 10);
    assert!(source.get() == 10);
}
<commit_msg>Add documentation<commit_after>use std::cell::Cell;
use memory::Memory;
use extensions::Incrementor;

/// Add the value of two registers and store it in the first register
pub fn add(first: &Cell<u8>, second: &Cell<u8>) {
    debug!("add: {} {}", first.get(), second.get());
    let new_value = first.get() + second.get();
    first.set(new_value);
}

/// Load the value from one register into another
pub fn ld_reg_to_reg(target: &Cell<u8>, source: &Cell<u8>) {
    debug!("ld_reg_to_reg: {}", source.get());
    let val = source.get();
    target.set(val);
}

/// Loads the memory pointed to by the next two bytes into a register
pub fn ld_next_byte_to_reg(mem: Memory, pc: &Cell<u16>, reg: &Cell<u8>) {
    let val = mem.read_byte(pc.get());
    debug!("ld_next_byte_to_reg: {} {}", pc.get(), val);
    pc.increment();
    reg.set(val);
}

/// Performs no operation and consumes a cycle
pub fn nop() {
    println!("nop");
}

#[test]
fn test_add_reg_with_reg() {
    let first = Cell::new(5);
    let second = Cell::new(9);

    add(&first, &second);
    assert!(first.get() == 14);
}

#[test]
fn test_ld_next_byte_to_reg() {
    let mem = Memory::new();
    let pc = Cell::new(11);
    let reg = Cell::new(0);

    mem.write_byte(11, 0xFF);

    ld_next_byte_to_reg(mem, &pc, &reg);
    assert!(reg.get() == 0xFF);
    assert!(pc.get() == 12);

}

#[test]
fn test_ld_reg_to_reg() {
    let target = Cell::new(5);
    let source = Cell::new(10);

    ld_reg_to_reg(&target, &source);

    assert!(target.get() == 10);
    assert!(source.get() == 10);
}
<|endoftext|>


<commit_before>#![cfg(target_os = "macos")]

use CreationError;
use CreationError::OsError;
use ContextError;
use GlAttributes;
use GlContext;
use PixelFormat;
use PixelFormatRequirements;
use Robustness;
use WindowAttributes;
use os::macos::ActivationPolicy;

use objc::runtime::{BOOL, NO};

use cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions, CGLSetParameter, kCGLCPSurfaceOpacity};

use cocoa::base::{id, nil};
use cocoa::foundation::NSAutoreleasePool;
use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat};

use core_foundation::base::TCFType;
use core_foundation::string::CFString;
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};

use std;
use std::str::FromStr;
use std::ops::Deref;

use libc;

use winit;
use winit::os::macos::WindowExt;
pub use winit::{MonitorId, NativeMonitorId, get_available_monitors, get_primary_monitor};
pub use self::headless::HeadlessContext;
pub use self::headless::PlatformSpecificHeadlessBuilderAttributes;

mod headless;
mod helpers;

#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {
    pub activation_policy: ActivationPolicy,
}

pub struct Window {
    context: std::sync::Arc<IdRef>,
    pixel_format: PixelFormat,
    winit_window: winit::Window,
}

pub struct EventsLoop {
    winit_events_loop: winit::EventsLoop,
    window_contexts: std::sync::Mutex<std::collections::HashMap<winit::WindowId, std::sync::Weak<IdRef>>>,
}

impl EventsLoop {
    /// Builds a new events loop.
    pub fn new() -> EventsLoop {
        EventsLoop {
            winit_events_loop: winit::EventsLoop::new(),
            window_contexts: std::sync::Mutex::new(std::collections::HashMap::new()),
        }
    }

    // If a resize event was received for a window, update the GL context for that window but only
    // if that window is still alive.
    fn update_context_on_window_resized(&self, event: &winit::Event) {
        match *event {
            winit::Event::WindowEvent { window_id, event: winit::WindowEvent::Resized(..) } => {
                if let Ok(window_contexts) = self.window_contexts.lock() {
                    if let Some(context) = window_contexts[&window_id].upgrade() {
                        unsafe { context.update(); }
                    }
                }
            },
            _ => ()
        }
    }

    /// Fetches all the events that are pending, calls the callback function for each of them,
    /// and returns.
    #[inline]
    pub fn poll_events<F>(&self, mut callback: F)
        where F: FnMut(winit::Event)
    {
        self.winit_events_loop.poll_events(|event| {
            self.update_context_on_window_resized(&event);
            callback(event);
        });
    }

    /// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback.
    #[inline]
    pub fn run_forever<F>(&self, mut callback: F)
        where F: FnMut(winit::Event)
    {
        self.winit_events_loop.run_forever(|event| {
            self.update_context_on_window_resized(&event);
            callback(event);
        })
    }

    /// If we called `run_forever()`, stops the process of waiting for events.
    #[inline]
    pub fn interrupt(&self) {
        self.winit_events_loop.interrupt()
    }
}

unsafe impl Send for Window {}
unsafe impl Sync for Window {}

impl Window {
    pub fn new(events_loop: &EventsLoop,
               win_attribs: &WindowAttributes,
               pf_reqs: &PixelFormatRequirements,
               opengl: &GlAttributes<&Window>,
               _pl_attribs: &PlatformSpecificWindowBuilderAttributes,
               winit_builder: winit::WindowBuilder)
               -> Result<Window, CreationError> {
        if opengl.sharing.is_some() {
            unimplemented!()
        }

        match opengl.robustness {
            Robustness::RobustNoResetNotification |
            Robustness::RobustLoseContextOnReset => {
                return Err(CreationError::RobustnessNotSupported);
            }
            _ => (),
        }

        let winit_window = winit_builder.build(&events_loop.winit_events_loop).unwrap();
        let view = winit_window.get_nsview() as id;
        let (context, pf) = match Window::create_context(view, pf_reqs, opengl, win_attribs.transparent) {
            Ok((context, pf)) => (std::sync::Arc::new(context), pf),
            Err(e) => {
                return Err(OsError(format!("Couldn't create OpenGL context: {}", e)));
            }
        };

        // Store a copy of the `context`'s `IdRef` so that we can `update` it on `Resized` events.
        if let Ok(mut window_contexts) = events_loop.window_contexts.lock() {
            window_contexts.insert(winit_window.id(), std::sync::Arc::downgrade(&context));
        }

        let window = Window {
            context: context,
            pixel_format: pf,
            winit_window: winit_window,
        };

        Ok(window)
    }

    fn create_context(view: id,
                      pf_reqs: &PixelFormatRequirements,
                      opengl: &GlAttributes<&Window>,
                      transparent: bool)
                      -> Result<(IdRef, PixelFormat), CreationError> {
        let attributes = try!(helpers::build_nsattributes(pf_reqs, opengl));
        unsafe {
            let pixelformat = IdRef::new(NSOpenGLPixelFormat::alloc(nil)
                .initWithAttributes_(&attributes));

            if let Some(pixelformat) = pixelformat.non_nil() {

                // TODO: Add context sharing
                let context = IdRef::new(NSOpenGLContext::alloc(nil)
                    .initWithFormat_shareContext_(*pixelformat, nil));

                if let Some(cxt) = context.non_nil() {
                    let pf = {
                        let get_attr = |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 {
                            let mut value = 0;

                            NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_(
                                *pixelformat,
                                &mut value,
                                attrib,
                                NSOpenGLContext::currentVirtualScreen(*cxt));

                            value
                        };

                        PixelFormat {
                            hardware_accelerated: get_attr(appkit::NSOpenGLPFAAccelerated) != 0,
                            color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) - get_attr(appkit::NSOpenGLPFAAlphaSize)) as u8,
                            alpha_bits: get_attr(appkit::NSOpenGLPFAAlphaSize) as u8,
                            depth_bits: get_attr(appkit::NSOpenGLPFADepthSize) as u8,
                            stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) as u8,
                            stereoscopy: get_attr(appkit::NSOpenGLPFAStereo) != 0,
                            double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) != 0,
                            multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) > 0 {
                                Some(get_attr(appkit::NSOpenGLPFASamples) as u16)
                            } else {
                                None
                            },
                            srgb: true,
                        }
                    };

                    cxt.setView_(view);
                    let value = if opengl.vsync { 1 } else { 0 };
                    cxt.setValues_forParameter_(&value, appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval);

                    if transparent {
                        let mut opacity = 0;
                        CGLSetParameter(cxt.CGLContextObj() as *mut _, kCGLCPSurfaceOpacity, &mut opacity);
                    }

                    CGLEnable(cxt.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions);

                    Ok((cxt, pf))
                } else {
                    Err(CreationError::NotSupported)
                }
            } else {
                Err(CreationError::NoAvailablePixelFormat)
            }
        }
    }

    pub fn set_title(&self, title: &str) {
        self.winit_window.set_title(title)
    }

    #[inline]
    pub fn as_winit_window(&self) -> &winit::Window {
        &self.winit_window
    }

    #[inline]
    pub fn as_winit_window_mut(&mut self) -> &mut winit::Window {
        &mut self.winit_window
    }

    pub fn show(&self) {
        self.winit_window.show()
    }

    pub fn hide(&self) {
        self.winit_window.hide()
    }

    pub fn get_position(&self) -> Option<(i32, i32)> {
        self.winit_window.get_position()
    }

    pub fn set_position(&self, x: i32, y: i32) {
        self.winit_window.set_position(x, y)
    }

    pub fn get_inner_size(&self) -> Option<(u32, u32)> {
        self.winit_window.get_inner_size()
    }

    pub fn get_inner_size_points(&self) -> Option<(u32, u32)> {
        self.winit_window.get_inner_size()
    }

    pub fn get_inner_size_pixels(&self) -> Option<(u32, u32)> {
        self.winit_window.get_inner_size().map(|(x, y)| {
            let hidpi = self.hidpi_factor();
            ((x as f32 * hidpi) as u32, (y as f32 * hidpi) as u32)
        })
    }

    pub fn get_outer_size(&self) -> Option<(u32, u32)> {
        self.winit_window.get_outer_size()
    }

    pub fn set_inner_size(&self, x: u32, y: u32) {
        self.winit_window.set_inner_size(x, y)
    }

    #[allow(deprecated)]
    pub unsafe fn platform_display(&self) -> *mut libc::c_void {
        self.winit_window.platform_display()
    }

    #[allow(deprecated)]
    pub unsafe fn platform_window(&self) -> *mut libc::c_void {
        self.winit_window.platform_window()
    }

    pub fn set_cursor(&self, cursor: winit::MouseCursor) {
        self.winit_window.set_cursor(cursor);
    }

    pub fn hidpi_factor(&self) -> f32 {
        self.winit_window.hidpi_factor()
    }

    pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
        self.winit_window.set_cursor_position(x, y)
    }

    pub fn set_cursor_state(&self, state: winit::CursorState) -> Result<(), String> {
        self.winit_window.set_cursor_state(state)
    }

    pub fn id(&self) -> winit::WindowId {
        self.winit_window.id()
    }
}

impl GlContext for Window {
    #[inline]
    unsafe fn make_current(&self) -> Result<(), ContextError> {
        let _: () = msg_send![**self.context, update];
        self.context.makeCurrentContext();
        Ok(())
    }

    #[inline]
    fn is_current(&self) -> bool {
        unsafe {
            let current = NSOpenGLContext::currentContext(nil);
            if current != nil {
                let is_equal: BOOL = msg_send![current, isEqual:**self.context];
                is_equal != NO
            } else {
                false
            }
        }
    }

    fn get_proc_address(&self, addr: &str) -> *const () {
        let symbol_name: CFString = FromStr::from_str(addr).unwrap();
        let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
        let framework =
            unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) };
        let symbol = unsafe {
            CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
        };
        symbol as *const _
    }

    #[inline]
    fn swap_buffers(&self) -> Result<(), ContextError> {
        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            self.context.flushBuffer();
            let _: () = msg_send![pool, release];
        }
        Ok(())
    }

    #[inline]
    fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    #[inline]
    fn get_pixel_format(&self) -> PixelFormat {
        self.pixel_format.clone()
    }
}

struct IdRef(id);

impl IdRef {
    fn new(i: id) -> IdRef {
        IdRef(i)
    }

    #[allow(dead_code)]
    fn retain(i: id) -> IdRef {
        if i != nil {
            let _: id = unsafe { msg_send![i, retain] };
        }
        IdRef(i)
    }

    fn non_nil(self) -> Option<IdRef> {
        if self.0 == nil { None } else { Some(self) }
    }
}

impl Drop for IdRef {
    fn drop(&mut self) {
        if self.0 != nil {
            let _: () = unsafe { msg_send![self.0, release] };
        }
    }
}

impl Deref for IdRef {
    type Target = id;
    fn deref<'a>(&'a self) -> &'a id {
        &self.0
    }
}

impl Clone for IdRef {
    fn clone(&self) -> IdRef {
        if self.0 != nil {
            let _: id = unsafe { msg_send![self.0, retain] };
        }
        IdRef(self.0)
    }
}
<commit_msg>Fix transparency by not using ambiguous WindowAttributes arg<commit_after>#![cfg(target_os = "macos")]

use CreationError;
use CreationError::OsError;
use ContextError;
use GlAttributes;
use GlContext;
use PixelFormat;
use PixelFormatRequirements;
use Robustness;
use WindowAttributes;
use os::macos::ActivationPolicy;

use objc::runtime::{BOOL, NO};

use cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions, CGLSetParameter, kCGLCPSurfaceOpacity};

use cocoa::base::{id, nil};
use cocoa::foundation::NSAutoreleasePool;
use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat};

use core_foundation::base::TCFType;
use core_foundation::string::CFString;
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};

use std;
use std::str::FromStr;
use std::ops::Deref;

use libc;

use winit;
use winit::os::macos::WindowExt;
pub use winit::{MonitorId, NativeMonitorId, get_available_monitors, get_primary_monitor};
pub use self::headless::HeadlessContext;
pub use self::headless::PlatformSpecificHeadlessBuilderAttributes;

mod headless;
mod helpers;

#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {
    pub activation_policy: ActivationPolicy,
}

pub struct Window {
    context: std::sync::Arc<IdRef>,
    pixel_format: PixelFormat,
    winit_window: winit::Window,
}

pub struct EventsLoop {
    winit_events_loop: winit::EventsLoop,
    window_contexts: std::sync::Mutex<std::collections::HashMap<winit::WindowId, std::sync::Weak<IdRef>>>,
}

impl EventsLoop {
    /// Builds a new events loop.
    pub fn new() -> EventsLoop {
        EventsLoop {
            winit_events_loop: winit::EventsLoop::new(),
            window_contexts: std::sync::Mutex::new(std::collections::HashMap::new()),
        }
    }

    // If a resize event was received for a window, update the GL context for that window but only
    // if that window is still alive.
    fn update_context_on_window_resized(&self, event: &winit::Event) {
        match *event {
            winit::Event::WindowEvent { window_id, event: winit::WindowEvent::Resized(..) } => {
                if let Ok(window_contexts) = self.window_contexts.lock() {
                    if let Some(context) = window_contexts[&window_id].upgrade() {
                        unsafe { context.update(); }
                    }
                }
            },
            _ => ()
        }
    }

    /// Fetches all the events that are pending, calls the callback function for each of them,
    /// and returns.
    #[inline]
    pub fn poll_events<F>(&self, mut callback: F)
        where F: FnMut(winit::Event)
    {
        self.winit_events_loop.poll_events(|event| {
            self.update_context_on_window_resized(&event);
            callback(event);
        });
    }

    /// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback.
    #[inline]
    pub fn run_forever<F>(&self, mut callback: F)
        where F: FnMut(winit::Event)
    {
        self.winit_events_loop.run_forever(|event| {
            self.update_context_on_window_resized(&event);
            callback(event);
        })
    }

    /// If we called `run_forever()`, stops the process of waiting for events.
    #[inline]
    pub fn interrupt(&self) {
        self.winit_events_loop.interrupt()
    }
}

unsafe impl Send for Window {}
unsafe impl Sync for Window {}

impl Window {
    pub fn new(events_loop: &EventsLoop,
               _win_attribs: &WindowAttributes,
               pf_reqs: &PixelFormatRequirements,
               opengl: &GlAttributes<&Window>,
               _pl_attribs: &PlatformSpecificWindowBuilderAttributes,
               winit_builder: winit::WindowBuilder)
               -> Result<Window, CreationError> {
        if opengl.sharing.is_some() {
            unimplemented!()
        }

        match opengl.robustness {
            Robustness::RobustNoResetNotification |
            Robustness::RobustLoseContextOnReset => {
                return Err(CreationError::RobustnessNotSupported);
            }
            _ => (),
        }

        let transparent = winit_builder.window.transparent;
        let winit_window = winit_builder.build(&events_loop.winit_events_loop).unwrap();
        let view = winit_window.get_nsview() as id;
        let (context, pf) = match Window::create_context(view, pf_reqs, opengl, transparent) {
            Ok((context, pf)) => (std::sync::Arc::new(context), pf),
            Err(e) => {
                return Err(OsError(format!("Couldn't create OpenGL context: {}", e)));
            }
        };

        // Store a copy of the `context`'s `IdRef` so that we can `update` it on `Resized` events.
        if let Ok(mut window_contexts) = events_loop.window_contexts.lock() {
            window_contexts.insert(winit_window.id(), std::sync::Arc::downgrade(&context));
        }

        let window = Window {
            context: context,
            pixel_format: pf,
            winit_window: winit_window,
        };

        Ok(window)
    }

    fn create_context(view: id,
                      pf_reqs: &PixelFormatRequirements,
                      opengl: &GlAttributes<&Window>,
                      transparent: bool)
                      -> Result<(IdRef, PixelFormat), CreationError> {
        let attributes = try!(helpers::build_nsattributes(pf_reqs, opengl));
        unsafe {
            let pixelformat = IdRef::new(NSOpenGLPixelFormat::alloc(nil)
                .initWithAttributes_(&attributes));

            if let Some(pixelformat) = pixelformat.non_nil() {

                // TODO: Add context sharing
                let context = IdRef::new(NSOpenGLContext::alloc(nil)
                    .initWithFormat_shareContext_(*pixelformat, nil));

                if let Some(cxt) = context.non_nil() {
                    let pf = {
                        let get_attr = |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 {
                            let mut value = 0;

                            NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_(
                                *pixelformat,
                                &mut value,
                                attrib,
                                NSOpenGLContext::currentVirtualScreen(*cxt));

                            value
                        };

                        PixelFormat {
                            hardware_accelerated: get_attr(appkit::NSOpenGLPFAAccelerated) != 0,
                            color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) - get_attr(appkit::NSOpenGLPFAAlphaSize)) as u8,
                            alpha_bits: get_attr(appkit::NSOpenGLPFAAlphaSize) as u8,
                            depth_bits: get_attr(appkit::NSOpenGLPFADepthSize) as u8,
                            stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) as u8,
                            stereoscopy: get_attr(appkit::NSOpenGLPFAStereo) != 0,
                            double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) != 0,
                            multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) > 0 {
                                Some(get_attr(appkit::NSOpenGLPFASamples) as u16)
                            } else {
                                None
                            },
                            srgb: true,
                        }
                    };

                    cxt.setView_(view);
                    let value = if opengl.vsync { 1 } else { 0 };
                    cxt.setValues_forParameter_(&value, appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval);

                    if transparent {
                        let mut opacity = 0;
                        CGLSetParameter(cxt.CGLContextObj() as *mut _, kCGLCPSurfaceOpacity, &mut opacity);
                    }

                    CGLEnable(cxt.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions);

                    Ok((cxt, pf))
                } else {
                    Err(CreationError::NotSupported)
                }
            } else {
                Err(CreationError::NoAvailablePixelFormat)
            }
        }
    }

    pub fn set_title(&self, title: &str) {
        self.winit_window.set_title(title)
    }

    #[inline]
    pub fn as_winit_window(&self) -> &winit::Window {
        &self.winit_window
    }

    #[inline]
    pub fn as_winit_window_mut(&mut self) -> &mut winit::Window {
        &mut self.winit_window
    }

    pub fn show(&self) {
        self.winit_window.show()
    }

    pub fn hide(&self) {
        self.winit_window.hide()
    }

    pub fn get_position(&self) -> Option<(i32, i32)> {
        self.winit_window.get_position()
    }

    pub fn set_position(&self, x: i32, y: i32) {
        self.winit_window.set_position(x, y)
    }

    pub fn get_inner_size(&self) -> Option<(u32, u32)> {
        self.winit_window.get_inner_size()
    }

    pub fn get_inner_size_points(&self) -> Option<(u32, u32)> {
        self.winit_window.get_inner_size()
    }

    pub fn get_inner_size_pixels(&self) -> Option<(u32, u32)> {
        self.winit_window.get_inner_size().map(|(x, y)| {
            let hidpi = self.hidpi_factor();
            ((x as f32 * hidpi) as u32, (y as f32 * hidpi) as u32)
        })
    }

    pub fn get_outer_size(&self) -> Option<(u32, u32)> {
        self.winit_window.get_outer_size()
    }

    pub fn set_inner_size(&self, x: u32, y: u32) {
        self.winit_window.set_inner_size(x, y)
    }

    #[allow(deprecated)]
    pub unsafe fn platform_display(&self) -> *mut libc::c_void {
        self.winit_window.platform_display()
    }

    #[allow(deprecated)]
    pub unsafe fn platform_window(&self) -> *mut libc::c_void {
        self.winit_window.platform_window()
    }

    pub fn set_cursor(&self, cursor: winit::MouseCursor) {
        self.winit_window.set_cursor(cursor);
    }

    pub fn hidpi_factor(&self) -> f32 {
        self.winit_window.hidpi_factor()
    }

    pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
        self.winit_window.set_cursor_position(x, y)
    }

    pub fn set_cursor_state(&self, state: winit::CursorState) -> Result<(), String> {
        self.winit_window.set_cursor_state(state)
    }

    pub fn id(&self) -> winit::WindowId {
        self.winit_window.id()
    }
}

impl GlContext for Window {
    #[inline]
    unsafe fn make_current(&self) -> Result<(), ContextError> {
        let _: () = msg_send![**self.context, update];
        self.context.makeCurrentContext();
        Ok(())
    }

    #[inline]
    fn is_current(&self) -> bool {
        unsafe {
            let current = NSOpenGLContext::currentContext(nil);
            if current != nil {
                let is_equal: BOOL = msg_send![current, isEqual:**self.context];
                is_equal != NO
            } else {
                false
            }
        }
    }

    fn get_proc_address(&self, addr: &str) -> *const () {
        let symbol_name: CFString = FromStr::from_str(addr).unwrap();
        let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
        let framework =
            unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) };
        let symbol = unsafe {
            CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
        };
        symbol as *const _
    }

    #[inline]
    fn swap_buffers(&self) -> Result<(), ContextError> {
        unsafe {
            let pool = NSAutoreleasePool::new(nil);
            self.context.flushBuffer();
            let _: () = msg_send![pool, release];
        }
        Ok(())
    }

    #[inline]
    fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    #[inline]
    fn get_pixel_format(&self) -> PixelFormat {
        self.pixel_format.clone()
    }
}

struct IdRef(id);

impl IdRef {
    fn new(i: id) -> IdRef {
        IdRef(i)
    }

    #[allow(dead_code)]
    fn retain(i: id) -> IdRef {
        if i != nil {
            let _: id = unsafe { msg_send![i, retain] };
        }
        IdRef(i)
    }

    fn non_nil(self) -> Option<IdRef> {
        if self.0 == nil { None } else { Some(self) }
    }
}

impl Drop for IdRef {
    fn drop(&mut self) {
        if self.0 != nil {
            let _: () = unsafe { msg_send![self.0, release] };
        }
    }
}

impl Deref for IdRef {
    type Target = id;
    fn deref<'a>(&'a self) -> &'a id {
        &self.0
    }
}

impl Clone for IdRef {
    fn clone(&self) -> IdRef {
        if self.0 != nil {
            let _: id = unsafe { msg_send![self.0, retain] };
        }
        IdRef(self.0)
    }
}
<|endoftext|>