<commit_before>use std::collections::BTreeSet;

use value::{Value, Field, Tuple};
use relation::{Relation, SingleSelect, Reference, MultiSelect};
use primitive::Primitive;

#[derive(Clone, Debug)]
pub struct Table {
    pub insert: Option<SingleSelect>,
    pub remove: Option<SingleSelect>,
}

#[derive(Clone, Debug)]
pub struct Union {
    pub selects: Vec<SingleSelect>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ConstraintOp {
    EQ,
    NEQ,
    LT,
    GT,
    LTE,
    GTE,
}

#[derive(Clone, Debug)]
pub struct Constraint {
    pub left: Reference,
    pub op: ConstraintOp,
    pub right: Reference,
}

impl Constraint {
    fn is_satisfied_by(&self, tuples: &[Tuple]) -> bool {
        let left = self.left.resolve(tuples);
        let right = self.right.resolve(tuples);
        match self.op {
            ConstraintOp::EQ => left == right,
            ConstraintOp::NEQ => left != right,
            ConstraintOp::LT => left < right,
            ConstraintOp::GT => left > right,
            ConstraintOp::LTE => left <= right,
            ConstraintOp::GTE => left >= right,
        }
    }
}

#[derive(Clone, Debug)]
pub enum JoinSource {
    Relation{
        input: usize
    },
    Primitive{
        primitive: Primitive,
        arguments: Vec<Reference>,
        fields: Vec<Field>,
        // TODO `fields` is here just to hack a Tuple in - will go away when we stop using Tuple
    },
}

#[derive(Clone, Debug)]
pub struct Join {
    pub sources: Vec<JoinSource>,
    pub constraints: Vec<Vec<Constraint>>,
    pub select: MultiSelect,
}

#[derive(Clone, Debug)]
pub struct Aggregate {
    pub outer: SingleSelect,
    pub inner: SingleSelect,
    pub limit_from: Option<Reference>,
    pub limit_to: Option<Reference>,
    pub select: MultiSelect,
}

#[derive(Clone, Debug)]
pub enum View {
    Table(Table),
    Union(Union),
    Join(Join),
    Aggregate(Aggregate),
}

fn join_step<'a>(join: &'a Join, inputs: &[&'a Relation], tuples: &mut Vec<Tuple<'a>>, index: &mut BTreeSet<Vec<Value>>) {
    let ix = tuples.len();
    if ix == join.sources.len() {
        index.insert(join.select.select(&tuples[..]));
    } else {
        match join.sources[ix] {
            JoinSource::Relation{input} => {
                for tuple in inputs[input].iter() {
                    tuples.push(tuple);
                    if join.constraints[ix].iter().all(|constraint| constraint.is_satisfied_by(&tuples[..])) {
                        join_step(join, inputs, tuples, index)
                    }
                    tuples.pop();
                }
            }
            JoinSource::Primitive{ref primitive, ref arguments, ref fields} => {
                let output = {
                    let arguments = arguments.iter().map(|reference|
                        reference.resolve(&tuples[..])
                        ).collect::<Vec<_>>();
                    primitive.eval(&arguments[..])
                };
                for values in output.into_iter() {
                    let tuple = Tuple{fields: &fields[..], names: &fields[..], values: &values[..]};
                    // promise the borrow checker that we will pop `tuple` before leaving this scope
                    let tuple = unsafe{ ::std::mem::transmute::<Tuple, Tuple<'a>>(tuple) };
                    tuples.push(tuple);
                    if join.constraints[ix].iter().all(|constraint| constraint.is_satisfied_by(&tuples[..])) {
                        join_step(join, inputs, tuples, index)
                    }
                    tuples.pop();
                }

            }
        }
    }
}

impl View {
    pub fn run(&self, old_output: &Relation, inputs: &[&Relation]) -> Option<Relation> {
        match *self {
            View::Table(_) => None,
            View::Union(ref union) => {
                assert_eq!(union.selects.len(), inputs.len());
                let mut output = Relation::with_fields(old_output.fields.clone(), old_output.names.clone());
                for select in union.selects.iter() {
                    for values in select.select(&inputs[..]) {
                        output.index.insert(values);
                    }
                }
                Some(output)
            }
            View::Join(ref join) => {
                assert_eq!(join.constraints.len(), inputs.len());
                let mut output = Relation::with_fields(old_output.fields.clone(), old_output.names.clone());
                let mut tuples = Vec::with_capacity(join.sources.len());
                join_step(join, inputs, &mut tuples, &mut output.index);
                Some(output)
            }
            View::Aggregate(ref aggregate) => {
                let mut output = Relation::with_fields(old_output.fields.clone(), old_output.names.clone());
                let mut outer = aggregate.outer.select(&inputs[..]);
                let mut inner = aggregate.inner.select(&inputs[..]);
                outer.sort();
                outer.dedup();
                inner.sort();
                let mut group_start = 0;
                for outer_values in outer.iter() {
                    let mut group_end = group_start;
                    while inner[group_end][0..outer_values.len()] == outer_values[..] {
                        group_end += 1;
                    }
                    let outer_tuple = Tuple{
                        fields: &aggregate.outer.fields[..],
                        names: &aggregate.outer.fields[..],
                        values: &outer_values[..]
                    };
                    let inputs = &[outer_tuple];
                    let limit_from = match aggregate.limit_from {
                        None => group_start,
                        Some(ref reference) => group_start + reference.resolve(inputs).as_usize(),
                    };
                    let limit_to = match aggregate.limit_to {
                        None => group_end,
                        Some(ref reference) => group_start + reference.resolve(inputs).as_usize(),
                    };
                    for inner_values in &inner[limit_from..limit_to] {
                        let inner_tuple = Tuple{
                            fields: &aggregate.inner.fields[..],
                            names: &aggregate.inner.fields[..],
                            values: &inner_values[..]
                        };
                        output.index.insert(aggregate.select.select(&[inner_tuple]));
                    }
                    group_start = group_end;
                }
                Some(output)
            }
        }
    }
}<commit_msg>Remove outdated assert<commit_after>use std::collections::BTreeSet;

use value::{Value, Field, Tuple};
use relation::{Relation, SingleSelect, Reference, MultiSelect};
use primitive::Primitive;

#[derive(Clone, Debug)]
pub struct Table {
    pub insert: Option<SingleSelect>,
    pub remove: Option<SingleSelect>,
}

#[derive(Clone, Debug)]
pub struct Union {
    pub selects: Vec<SingleSelect>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ConstraintOp {
    EQ,
    NEQ,
    LT,
    GT,
    LTE,
    GTE,
}

#[derive(Clone, Debug)]
pub struct Constraint {
    pub left: Reference,
    pub op: ConstraintOp,
    pub right: Reference,
}

impl Constraint {
    fn is_satisfied_by(&self, tuples: &[Tuple]) -> bool {
        let left = self.left.resolve(tuples);
        let right = self.right.resolve(tuples);
        match self.op {
            ConstraintOp::EQ => left == right,
            ConstraintOp::NEQ => left != right,
            ConstraintOp::LT => left < right,
            ConstraintOp::GT => left > right,
            ConstraintOp::LTE => left <= right,
            ConstraintOp::GTE => left >= right,
        }
    }
}

#[derive(Clone, Debug)]
pub enum JoinSource {
    Relation{
        input: usize
    },
    Primitive{
        primitive: Primitive,
        arguments: Vec<Reference>,
        fields: Vec<Field>,
        // TODO `fields` is here just to hack a Tuple in - will go away when we stop using Tuple
    },
}

#[derive(Clone, Debug)]
pub struct Join {
    pub sources: Vec<JoinSource>,
    pub constraints: Vec<Vec<Constraint>>,
    pub select: MultiSelect,
}

#[derive(Clone, Debug)]
pub struct Aggregate {
    pub outer: SingleSelect,
    pub inner: SingleSelect,
    pub limit_from: Option<Reference>,
    pub limit_to: Option<Reference>,
    pub select: MultiSelect,
}

#[derive(Clone, Debug)]
pub enum View {
    Table(Table),
    Union(Union),
    Join(Join),
    Aggregate(Aggregate),
}

fn join_step<'a>(join: &'a Join, inputs: &[&'a Relation], tuples: &mut Vec<Tuple<'a>>, index: &mut BTreeSet<Vec<Value>>) {
    let ix = tuples.len();
    if ix == join.sources.len() {
        index.insert(join.select.select(&tuples[..]));
    } else {
        match join.sources[ix] {
            JoinSource::Relation{input} => {
                for tuple in inputs[input].iter() {
                    tuples.push(tuple);
                    if join.constraints[ix].iter().all(|constraint| constraint.is_satisfied_by(&tuples[..])) {
                        join_step(join, inputs, tuples, index)
                    }
                    tuples.pop();
                }
            }
            JoinSource::Primitive{ref primitive, ref arguments, ref fields} => {
                let output = {
                    let arguments = arguments.iter().map(|reference|
                        reference.resolve(&tuples[..])
                        ).collect::<Vec<_>>();
                    primitive.eval(&arguments[..])
                };
                for values in output.into_iter() {
                    let tuple = Tuple{fields: &fields[..], names: &fields[..], values: &values[..]};
                    // promise the borrow checker that we will pop `tuple` before leaving this scope
                    let tuple = unsafe{ ::std::mem::transmute::<Tuple, Tuple<'a>>(tuple) };
                    tuples.push(tuple);
                    if join.constraints[ix].iter().all(|constraint| constraint.is_satisfied_by(&tuples[..])) {
                        join_step(join, inputs, tuples, index)
                    }
                    tuples.pop();
                }

            }
        }
    }
}

impl View {
    pub fn run(&self, old_output: &Relation, inputs: &[&Relation]) -> Option<Relation> {
        match *self {
            View::Table(_) => None,
            View::Union(ref union) => {
                assert_eq!(union.selects.len(), inputs.len());
                let mut output = Relation::with_fields(old_output.fields.clone(), old_output.names.clone());
                for select in union.selects.iter() {
                    for values in select.select(&inputs[..]) {
                        output.index.insert(values);
                    }
                }
                Some(output)
            }
            View::Join(ref join) => {
                let mut output = Relation::with_fields(old_output.fields.clone(), old_output.names.clone());
                let mut tuples = Vec::with_capacity(join.sources.len());
                join_step(join, inputs, &mut tuples, &mut output.index);
                Some(output)
            }
            View::Aggregate(ref aggregate) => {
                let mut output = Relation::with_fields(old_output.fields.clone(), old_output.names.clone());
                let mut outer = aggregate.outer.select(&inputs[..]);
                let mut inner = aggregate.inner.select(&inputs[..]);
                outer.sort();
                outer.dedup();
                inner.sort();
                let mut group_start = 0;
                for outer_values in outer.iter() {
                    let mut group_end = group_start;
                    while inner[group_end][0..outer_values.len()] == outer_values[..] {
                        group_end += 1;
                    }
                    let outer_tuple = Tuple{
                        fields: &aggregate.outer.fields[..],
                        names: &aggregate.outer.fields[..],
                        values: &outer_values[..]
                    };
                    let inputs = &[outer_tuple];
                    let limit_from = match aggregate.limit_from {
                        None => group_start,
                        Some(ref reference) => group_start + reference.resolve(inputs).as_usize(),
                    };
                    let limit_to = match aggregate.limit_to {
                        None => group_end,
                        Some(ref reference) => group_start + reference.resolve(inputs).as_usize(),
                    };
                    for inner_values in &inner[limit_from..limit_to] {
                        let inner_tuple = Tuple{
                            fields: &aggregate.inner.fields[..],
                            names: &aggregate.inner.fields[..],
                            values: &inner_values[..]
                        };
                        output.index.insert(aggregate.select.select(&[inner_tuple]));
                    }
                    group_start = group_end;
                }
                Some(output)
            }
        }
    }
}<|endoftext|>


<commit_before>//
// Copyright 2018 The Project Oak Authors
//
// 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.
//

extern crate byteorder;
extern crate fmt;
#[macro_use]
extern crate log;
extern crate protobuf;

use byteorder::WriteBytesExt;
use proto::oak_api::OakStatus;
use protobuf::ProtobufEnum;
use std::io;
use std::io::Write;

pub mod grpc;
pub mod proto;
pub mod storage;
#[cfg(test)]
mod tests;
pub mod wasm;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
pub type Handle = u64;

// Build a chunk of memory that is suitable for passing to wasm::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[Handle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(wasm::SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space.write_u64::<byteorder::LittleEndian>(*handle).unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

/// Wait for one or more of the provided handles to become ready for reading
/// from.
///
/// This is a convenience wrapper around the [`wasm::wait_on_channels`] host
/// function. This version is easier to use in Rust but is less efficient
/// (because the notification space is re-created on each invocation).
pub fn wait_on_channels(handles: &[Handle]) -> Result<Vec<Handle>, OakStatus> {
    let mut space = new_handle_space(handles);
    unsafe {
        let status = wasm::wait_on_channels(space.as_mut_ptr(), handles.len() as u32);
        match OakStatus::from_i32(status) {
            Some(OakStatus::OK) => (),
            Some(err) => return Err(err),
            None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
        }
        let mut results = Vec::with_capacity(handles.len());
        for i in 0..handles.len() {
            if space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] != 0 {
                results.push(handles[i]);
            }
        }
        Ok(results)
    }
}

/// Read a message from a channel.
///
/// The provided vectors for received data and associated handles will be
/// resized to accomodate the information in the message.
pub fn channel_read(handle: Handle, buf: &mut Vec<u8>, handles: &mut Vec<Handle>) -> OakStatus {
    // Try reading from the channel twice: first with provided vectors, then
    // with vectors that have been resized to meet size requirements.
    for resized in &[false, true] {
        let mut actual_size: u32 = 0;
        let mut actual_handle_count: u32 = 0;
        let status = OakStatus::from_i32(unsafe {
            wasm::channel_read(
                handle,
                buf.as_mut_ptr(),
                buf.capacity(),
                &mut actual_size,
                handles.as_mut_ptr() as *mut u8,
                handles.capacity(),
                &mut actual_handle_count,
            )
        });
        match status {
            Some(OakStatus::OK) => {
                unsafe {
                    buf.set_len(actual_size as usize);
                    // Handles are written in little-endian order, which matches Wasm spec.
                    handles.set_len(actual_handle_count as usize);
                };
                return OakStatus::OK;
            }
            Some(OakStatus::ERR_BUFFER_TOO_SMALL) => {
                if *resized {
                    return OakStatus::ERR_BUFFER_TOO_SMALL;
                }
                // Can escape the match if buffer is too small and !resized.
            }
            Some(s) => {
                return s;
            }
            None => {
                return OakStatus::ERR_INTERNAL;
            }
        }

        // Extend the vector to be large enough for the message
        debug!("Got {}, need {}", buf.capacity(), actual_size);
        if (actual_size as usize) < buf.len() {
            error!(
                "Internal error: provided {} bytes for receive, asked for {}",
                buf.len(),
                actual_size
            );
            return OakStatus::ERR_INTERNAL;
        }
        let extra = (actual_size as usize) - buf.len();
        buf.reserve(extra);
    }
    error!("unreachable code reached");
    OakStatus::ERR_INTERNAL
}

/// Create a new unidirectional channel.
///
/// On success, returns [`Handle`] values for the write and read halves
/// (respectively).
pub fn channel_create() -> Result<(Handle, Handle), OakStatus> {
    let mut write: Handle = 0;
    let mut read: Handle = 0;
    match OakStatus::from_i32(unsafe {
        wasm::channel_create(&mut write as *mut u64, &mut read as *mut u64) // @@@ check endianness
    }) {
        Some(OakStatus::OK) => Ok((write, read)),
        Some(err) => Err(err),
        None => Err(OakStatus::OAK_STATUS_UNSPECIFIED),
    }
}

/// Close the specified channel [`Handle`].
pub fn channel_close(handle: Handle) -> OakStatus {
    match OakStatus::from_i32(unsafe { wasm::channel_close(handle) }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Determine the [`Handle`] for a pre-defined channel, identified by its
/// `port_name`.
pub fn channel_find(port_name: &str) -> Handle {
    unsafe { wasm::channel_find(port_name.as_ptr(), port_name.len()) }
}

/// Map an [`OakStatus`] value to the nearest available [`std::io::Result`].
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unspecified Oak status value",
        )),
        Some(OakStatus::OK) => Ok(val),
        Some(OakStatus::ERR_BAD_HANDLE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
        }
        Some(OakStatus::ERR_INVALID_ARGS) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Invalid arguments",
        )),
        Some(OakStatus::ERR_CHANNEL_CLOSED) => Err(io::Error::new(
            io::ErrorKind::ConnectionReset,
            "Channel closed",
        )),
        Some(OakStatus::ERR_BUFFER_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Buffer too small",
        )),
        Some(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Handle space too small",
        )),
        Some(OakStatus::ERR_OUT_OF_RANGE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
        }
        Some(OakStatus::ERR_INTERNAL) => {
            Err(io::Error::new(io::ErrorKind::Other, "Internal error"))
        }
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}
/// Convenience wrapper for the send half of a channel, to allow use of the
/// [`std::io::Write`] trait.
pub struct SendChannelHalf {
    handle: Handle,
}

impl SendChannelHalf {
    pub fn new(handle: Handle) -> SendChannelHalf {
        SendChannelHalf { handle }
    }

    pub fn write_message(&mut self, buf: &[u8], handles: &[Handle]) -> std::io::Result<()> {
        result_from_status(
            OakStatus::from_i32(unsafe {
                wasm::channel_write(
                    self.handle,
                    buf.as_ptr(),
                    buf.len(),
                    handles.as_ptr() as *const u8, // Wasm spec defines this as little-endian
                    handles.len(),
                )
            }),
            (),
        )
    }
}

impl Write for SendChannelHalf {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self.write_message(buf, &[]) {
            Ok(_) => Ok(buf.len()),
            Err(e) => Err(e),
        }
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Return an instance of the [`std::io::Write`] trait that emits messages to
/// the Node's logging channel.
///
/// Assumes that the Node has a pre-configured channel to the logging
/// pseudo-Node that is identified by the default port name (`"log"`).
pub fn logging_channel() -> impl Write {
    let logging_channel = SendChannelHalf::new(channel_find("log"));
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

/// Convenience wrapper for the receive half of a channel.
///
/// This helps when the underlying [`Handle`] is known to be for a receive half.
pub struct ReceiveChannelHalf {
    handle: Handle,
}

impl ReceiveChannelHalf {
    pub fn new(handle: Handle) -> ReceiveChannelHalf {
        ReceiveChannelHalf { handle }
    }

    pub fn read_message(
        &mut self,
        buf: &mut Vec<u8>,
        handles: &mut Vec<Handle>,
    ) -> std::io::Result<()> {
        result_from_status(Some(channel_read(self.handle, buf, handles)), ())
    }
}

/// Install a panic hook that logs [panic information].
///
/// Logs panic infomation to the logging channel, if one is set.
///
/// [panic information]: std::panic::PanicInfo
pub fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKNOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<commit_msg>Add oak::channel_write wrapper<commit_after>//
// Copyright 2018 The Project Oak Authors
//
// 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.
//

extern crate byteorder;
extern crate fmt;
#[macro_use]
extern crate log;
extern crate protobuf;

use byteorder::WriteBytesExt;
use proto::oak_api::OakStatus;
use protobuf::ProtobufEnum;
use std::io;
use std::io::Write;

pub mod grpc;
pub mod proto;
pub mod storage;
#[cfg(test)]
mod tests;
pub mod wasm;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
pub type Handle = u64;

// Build a chunk of memory that is suitable for passing to wasm::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[Handle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(wasm::SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space.write_u64::<byteorder::LittleEndian>(*handle).unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

/// Wait for one or more of the provided handles to become ready for reading
/// from.
///
/// This is a convenience wrapper around the [`wasm::wait_on_channels`] host
/// function. This version is easier to use in Rust but is less efficient
/// (because the notification space is re-created on each invocation).
pub fn wait_on_channels(handles: &[Handle]) -> Result<Vec<Handle>, OakStatus> {
    let mut space = new_handle_space(handles);
    unsafe {
        let status = wasm::wait_on_channels(space.as_mut_ptr(), handles.len() as u32);
        match OakStatus::from_i32(status) {
            Some(OakStatus::OK) => (),
            Some(err) => return Err(err),
            None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
        }
        let mut results = Vec::with_capacity(handles.len());
        for i in 0..handles.len() {
            if space[i * wasm::SPACE_BYTES_PER_HANDLE + (wasm::SPACE_BYTES_PER_HANDLE - 1)] != 0 {
                results.push(handles[i]);
            }
        }
        Ok(results)
    }
}

/// Read a message from a channel.
///
/// The provided vectors for received data and associated handles will be
/// resized to accomodate the information in the message.
pub fn channel_read(handle: Handle, buf: &mut Vec<u8>, handles: &mut Vec<Handle>) -> OakStatus {
    // Try reading from the channel twice: first with provided vectors, then
    // with vectors that have been resized to meet size requirements.
    for resized in &[false, true] {
        let mut actual_size: u32 = 0;
        let mut actual_handle_count: u32 = 0;
        let status = OakStatus::from_i32(unsafe {
            wasm::channel_read(
                handle,
                buf.as_mut_ptr(),
                buf.capacity(),
                &mut actual_size,
                handles.as_mut_ptr() as *mut u8,
                handles.capacity(),
                &mut actual_handle_count,
            )
        });
        match status {
            Some(OakStatus::OK) => {
                unsafe {
                    buf.set_len(actual_size as usize);
                    // Handles are written in little-endian order, which matches Wasm spec.
                    handles.set_len(actual_handle_count as usize);
                };
                return OakStatus::OK;
            }
            Some(OakStatus::ERR_BUFFER_TOO_SMALL) => {
                if *resized {
                    return OakStatus::ERR_BUFFER_TOO_SMALL;
                }
                // Can escape the match if buffer is too small and !resized.
            }
            Some(s) => {
                return s;
            }
            None => {
                return OakStatus::ERR_INTERNAL;
            }
        }

        // Extend the vector to be large enough for the message
        debug!("Got {}, need {}", buf.capacity(), actual_size);
        if (actual_size as usize) < buf.len() {
            error!(
                "Internal error: provided {} bytes for receive, asked for {}",
                buf.len(),
                actual_size
            );
            return OakStatus::ERR_INTERNAL;
        }
        let extra = (actual_size as usize) - buf.len();
        buf.reserve(extra);
    }
    error!("unreachable code reached");
    OakStatus::ERR_INTERNAL
}

/// Write a message to a channel.
pub fn channel_write(handle: Handle, buf: &[u8], handles: &[Handle]) -> OakStatus {
    match OakStatus::from_i32(unsafe {
        wasm::channel_write(
            handle,
            buf.as_ptr(),
            buf.len(),
            handles.as_ptr() as *const u8, // Wasm spec defines this as little-endian
            handles.len(),
        )
    }) {
        Some(s) => s,
        None => OakStatus::ERR_INTERNAL,
    }
}

/// Create a new unidirectional channel.
///
/// On success, returns [`Handle`] values for the write and read halves
/// (respectively).
pub fn channel_create() -> Result<(Handle, Handle), OakStatus> {
    let mut write: Handle = 0;
    let mut read: Handle = 0;
    match OakStatus::from_i32(unsafe {
        wasm::channel_create(&mut write as *mut u64, &mut read as *mut u64) // @@@ check endianness
    }) {
        Some(OakStatus::OK) => Ok((write, read)),
        Some(err) => Err(err),
        None => Err(OakStatus::OAK_STATUS_UNSPECIFIED),
    }
}

/// Close the specified channel [`Handle`].
pub fn channel_close(handle: Handle) -> OakStatus {
    match OakStatus::from_i32(unsafe { wasm::channel_close(handle) }) {
        Some(s) => s,
        None => OakStatus::OAK_STATUS_UNSPECIFIED,
    }
}

/// Determine the [`Handle`] for a pre-defined channel, identified by its
/// `port_name`.
pub fn channel_find(port_name: &str) -> Handle {
    unsafe { wasm::channel_find(port_name.as_ptr(), port_name.len()) }
}

/// Map an [`OakStatus`] value to the nearest available [`std::io::Result`].
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unspecified Oak status value",
        )),
        Some(OakStatus::OK) => Ok(val),
        Some(OakStatus::ERR_BAD_HANDLE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
        }
        Some(OakStatus::ERR_INVALID_ARGS) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Invalid arguments",
        )),
        Some(OakStatus::ERR_CHANNEL_CLOSED) => Err(io::Error::new(
            io::ErrorKind::ConnectionReset,
            "Channel closed",
        )),
        Some(OakStatus::ERR_BUFFER_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Buffer too small",
        )),
        Some(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Handle space too small",
        )),
        Some(OakStatus::ERR_OUT_OF_RANGE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
        }
        Some(OakStatus::ERR_INTERNAL) => {
            Err(io::Error::new(io::ErrorKind::Other, "Internal error"))
        }
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}
/// Convenience wrapper for the send half of a channel, to allow use of the
/// [`std::io::Write`] trait.
pub struct SendChannelHalf {
    handle: Handle,
}

impl SendChannelHalf {
    pub fn new(handle: Handle) -> SendChannelHalf {
        SendChannelHalf { handle }
    }

    pub fn write_message(&mut self, buf: &[u8], handles: &[Handle]) -> std::io::Result<()> {
        result_from_status(Some(channel_write(self.handle, buf, handles)), ())
    }
}

impl Write for SendChannelHalf {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self.write_message(buf, &[]) {
            Ok(_) => Ok(buf.len()),
            Err(e) => Err(e),
        }
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Return an instance of the [`std::io::Write`] trait that emits messages to
/// the Node's logging channel.
///
/// Assumes that the Node has a pre-configured channel to the logging
/// pseudo-Node that is identified by the default port name (`"log"`).
pub fn logging_channel() -> impl Write {
    let logging_channel = SendChannelHalf::new(channel_find("log"));
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

/// Convenience wrapper for the receive half of a channel.
///
/// This helps when the underlying [`Handle`] is known to be for a receive half.
pub struct ReceiveChannelHalf {
    handle: Handle,
}

impl ReceiveChannelHalf {
    pub fn new(handle: Handle) -> ReceiveChannelHalf {
        ReceiveChannelHalf { handle }
    }

    pub fn read_message(
        &mut self,
        buf: &mut Vec<u8>,
        handles: &mut Vec<Handle>,
    ) -> std::io::Result<()> {
        result_from_status(Some(channel_read(self.handle, buf, handles)), ())
    }
}

/// Install a panic hook that logs [panic information].
///
/// Logs panic infomation to the logging channel, if one is set.
///
/// [panic information]: std::panic::PanicInfo
pub fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKNOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types, non_snake_case)]
#![crate_type = "rlib"]

extern crate block;
extern crate cocoa;
extern crate core_foundation;
extern crate core_graphics;
extern crate libc;
#[macro_use]
extern crate objc;

mod classes;
mod constants;
mod functions;
mod protocols;
mod types;

pub use classes::{MTLArgument, MTLArgumentAccess, MTLArgumentType, MTLArrayType,
                  MTLCompileOptions, MTLDataType, MTLStructMember, MTLStructType,
                  MTLVertexAttribute};

pub use constants::{MTLCommandBufferStatus, MTLCompareFunction, MTLFeatureSet, MTLPipelineOption,
                    MTLPixelFormat};

pub use functions::{MTLClearColorMake, MTLCopyAllDevices, MTLCreateSystemDefaultDevice,
                    MTLOriginMake, MTLRegionMake1D, MTLRegionMake2D, MTLRegionMake3D, MTLSizeMake};

pub use protocols::{MTLCommandBuffer, MTLCommandQueue, MTLComputePipelineState, MTLDevice,
                    MTLDrawable, MTLFunction, MTLFunctionType, MTLLanguageVersion, MTLLibrary,
                    MTLLibraryError, MTLLibraryErrorDomain,
                    MTLNewComputePipelineStateCompletionHandler,
                    MTLNewComputePipelineStateWithReflectionCompletionHandler,
                    MTLNewLibraryCompletionHandler,
                    MTLNewRenderPipelineStateWithReflectionCompletionHandler,
                    MTLNewRenderPipleineStateCompletionHandler, MTLRenderPipelineError,
                    MTLRenderPipelineErrorDomain, MTLTexture, MTLTextureType};

pub use types::{MTLClearColor, MTLDispatchThreadgroupsIndirectArguments,
                MTLDrawIndexedPrimitivesIndirectArguments, MTLDrawPrimitivesIndirectArguments,
                MTLOrigin, MTLRegion, MTLScissorRect, MTLSize, MTLViewport};
<commit_msg>Make metal-sys no_std<commit_after>#![no_std]
#![allow(non_camel_case_types, non_snake_case)]
#![crate_type = "rlib"]

extern crate block;
extern crate cocoa;
extern crate core_foundation;
extern crate core_graphics;
extern crate libc;
#[macro_use]
extern crate objc;

mod classes;
mod constants;
mod functions;
mod protocols;
mod types;

pub use classes::{MTLArgument, MTLArgumentAccess, MTLArgumentType, MTLArrayType,
                  MTLCompileOptions, MTLDataType, MTLStructMember, MTLStructType,
                  MTLVertexAttribute};

pub use constants::{MTLCommandBufferStatus, MTLCompareFunction, MTLFeatureSet, MTLPipelineOption,
                    MTLPixelFormat};

pub use functions::{MTLClearColorMake, MTLCopyAllDevices, MTLCreateSystemDefaultDevice,
                    MTLOriginMake, MTLRegionMake1D, MTLRegionMake2D, MTLRegionMake3D, MTLSizeMake};

pub use protocols::{MTLCommandBuffer, MTLCommandQueue, MTLComputePipelineState, MTLDevice,
                    MTLDrawable, MTLFunction, MTLFunctionType, MTLLanguageVersion, MTLLibrary,
                    MTLLibraryError, MTLLibraryErrorDomain,
                    MTLNewComputePipelineStateCompletionHandler,
                    MTLNewComputePipelineStateWithReflectionCompletionHandler,
                    MTLNewLibraryCompletionHandler,
                    MTLNewRenderPipelineStateWithReflectionCompletionHandler,
                    MTLNewRenderPipleineStateCompletionHandler, MTLRenderPipelineError,
                    MTLRenderPipelineErrorDomain, MTLTexture, MTLTextureType};

pub use types::{MTLClearColor, MTLDispatchThreadgroupsIndirectArguments,
                MTLDrawIndexedPrimitivesIndirectArguments, MTLDrawPrimitivesIndirectArguments,
                MTLOrigin, MTLRegion, MTLScissorRect, MTLSize, MTLViewport};
<|endoftext|>


<commit_before>use std::{thread, time};
use *;

#[test]
fn test_listen() {
    fn run_app<S: ?Sized + AsRef<str>>(address: &S, timeout: u64) -> Result<()> {
        let service = VarlinkService::new(
            "org.varlink",
            "test service",
            "0.1",
            "http://varlink.org",
            vec![], // Your varlink interfaces go here
        );

        if let Err(e) = listen(service, &address, 10, timeout) {
            if e.kind() != ErrorKind::Timeout {
                panic!("Error listen: {:#?}", e);
            }
        }
        Ok(())
    }

    fn run_client_app<S: ?Sized + AsRef<str>>(address: &S) -> Result<()> {
        let conn = Connection::new(address)?;
        let mut call = OrgVarlinkServiceClient::new(conn.clone());
        {
            let info = call.get_info()?;
            assert_eq!(&info.vendor, "org.varlink");
            assert_eq!(&info.product, "test service");
            assert_eq!(&info.version, "0.1");
            assert_eq!(&info.url, "http://varlink.org");
            assert_eq!(
                info.interfaces.get(0).unwrap().as_ref(),
                "org.varlink.service"
            );
        }
        let e = call.get_interface_description("org.varlink.unknown");
        assert!(e.is_err());

        match e.err().unwrap().kind() {
            ErrorKind::InvalidParameter(i) => assert_eq!(i, "interface".to_string()),
            kind => {
                panic!("Unknown error {:?}", kind);
            }
        }

        let e = MethodCall::<GetInfoArgs, ServiceInfo, Error>::new(
            conn.clone(),
            "org.varlink.service.GetInfos",
            GetInfoArgs {},
        ).call();

        match e.err().unwrap().kind() {
            ErrorKind::MethodNotFound(i) => {
                assert_eq!(i, "org.varlink.service.GetInfos".to_string())
            }
            kind => {
                panic!("Unknown error {:?}", kind);
            }
        }

        let e = MethodCall::<GetInfoArgs, ServiceInfo, Error>::new(
            conn.clone(),
            "org.varlink.unknowninterface.Foo",
            GetInfoArgs {},
        ).call();

        match e.err().unwrap().kind() {
            ErrorKind::InterfaceNotFound(i) => {
                assert_eq!(i, "org.varlink.unknowninterface".to_string())
            }
            kind => {
                panic!("Unknown error {:?}", kind);
            }
        }

        let description = call.get_interface_description("org.varlink.service")?;

        assert_eq!(
            &description.description.unwrap(),
            r#"# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: []string
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
"#
        );

        Ok(())
    }

    let address = "unix:/tmp/test_listen_timeout";

    let child = thread::spawn(move || {
        if let Err(e) = run_app(address, 3) {
            panic!("error: {}", e);
        }
    });

    // give server time to start
    thread::sleep(time::Duration::from_secs(1));

    assert!(run_client_app(address).is_ok());

    assert!(child.join().is_ok());
}
<commit_msg>varlink: first test for handle()<commit_after>use serde_json::{from_slice, from_value};
use std::{thread, time};
use *;

#[test]
fn test_listen() {
    fn run_app<S: ?Sized + AsRef<str>>(address: &S, timeout: u64) -> Result<()> {
        let service = VarlinkService::new(
            "org.varlink",
            "test service",
            "0.1",
            "http://varlink.org",
            vec![], // Your varlink interfaces go here
        );

        if let Err(e) = listen(service, &address, 10, timeout) {
            if e.kind() != ErrorKind::Timeout {
                panic!("Error listen: {:#?}", e);
            }
        }
        Ok(())
    }

    fn run_client_app<S: ?Sized + AsRef<str>>(address: &S) -> Result<()> {
        let conn = Connection::new(address)?;
        let mut call = OrgVarlinkServiceClient::new(conn.clone());
        {
            let info = call.get_info()?;
            assert_eq!(&info.vendor, "org.varlink");
            assert_eq!(&info.product, "test service");
            assert_eq!(&info.version, "0.1");
            assert_eq!(&info.url, "http://varlink.org");
            assert_eq!(
                info.interfaces.get(0).unwrap().as_ref(),
                "org.varlink.service"
            );
        }
        let e = call.get_interface_description("org.varlink.unknown");
        assert!(e.is_err());

        match e.err().unwrap().kind() {
            ErrorKind::InvalidParameter(i) => assert_eq!(i, "interface".to_string()),
            kind => {
                panic!("Unknown error {:?}", kind);
            }
        }

        let e = MethodCall::<GetInfoArgs, ServiceInfo, Error>::new(
            conn.clone(),
            "org.varlink.service.GetInfos",
            GetInfoArgs {},
        ).call();

        match e.err().unwrap().kind() {
            ErrorKind::MethodNotFound(i) => {
                assert_eq!(i, "org.varlink.service.GetInfos".to_string())
            }
            kind => {
                panic!("Unknown error {:?}", kind);
            }
        }

        let e = MethodCall::<GetInfoArgs, ServiceInfo, Error>::new(
            conn.clone(),
            "org.varlink.unknowninterface.Foo",
            GetInfoArgs {},
        ).call();

        match e.err().unwrap().kind() {
            ErrorKind::InterfaceNotFound(i) => {
                assert_eq!(i, "org.varlink.unknowninterface".to_string())
            }
            kind => {
                panic!("Unknown error {:?}", kind);
            }
        }

        let description = call.get_interface_description("org.varlink.service")?;

        assert_eq!(
            &description.description.unwrap(),
            r#"# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
  vendor: string,
  product: string,
  version: string,
  url: string,
  interfaces: []string
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
"#
        );

        Ok(())
    }

    let address = "unix:/tmp/test_listen_timeout";

    let child = thread::spawn(move || {
        if let Err(e) = run_app(address, 3) {
            panic!("error: {}", e);
        }
    });

    // give server time to start
    thread::sleep(time::Duration::from_secs(1));

    assert!(run_client_app(address).is_ok());

    assert!(child.join().is_ok());
}

#[test]
fn test_handle() {
    let service = VarlinkService::new(
        "org.varlink",
        "test service",
        "0.1",
        "http://varlink.org",
        vec![],
    );

    let mut br = concat!(r#"{"method" : "org.varlink.service.GetInfo"}"#, "\0").as_bytes();
    let mut w = vec![];
    let r = service.handle(&mut br, &mut w, None);
    assert!(r.is_ok());
    assert!(r.unwrap().is_none());

    w.pop();

    assert_eq!(
        w,
        concat!(
            r#"{"parameters":{"interfaces":["org.varlink.service"],"product":"test service","#,
            r#""url":"http://varlink.org","vendor":"org.varlink","version":"0.1"}}"#
        ).as_bytes()
    );

    let reply = from_slice::<Reply>(&w).unwrap();

    match from_value::<ServiceInfo>(reply.parameters.unwrap()) {
        Ok(si) => {
            assert_eq!(
                si,
                ServiceInfo {
                    vendor: "org.varlink".into(),
                    product: "test service".into(),
                    version: "0.1".into(),
                    url: "http://varlink.org".into(),
                    interfaces: vec!["org.varlink.service".into()],
                }
            );
        }
        Err(e) => panic!(e.to_string()),
    };
}
<|endoftext|>


<commit_before>//! Read configurations files.

#![allow(clippy::module_name_repetitions)]

use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
use serde::Deserialize;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::{env, fmt, fs, io};

/// Conf with parse errors
#[derive(Default)]
pub struct TryConf {
    pub conf: Conf,
    pub errors: Vec<String>,
}

impl TryConf {
    fn from_error(error: impl Error) -> Self {
        Self {
            conf: Conf::default(),
            errors: vec![error.to_string()],
        }
    }
}

macro_rules! define_Conf {
    ($(
        #[$doc:meta]
        $(#[conf_deprecated($dep:literal)])?
        ($name:ident: $ty:ty = $default:expr),
    )*) => {
        /// Clippy lint configuration
        pub struct Conf {
            $(#[$doc] pub $name: $ty,)*
        }

        mod defaults {
            $(pub fn $name() -> $ty { $default })*
        }

        impl Default for Conf {
            fn default() -> Self {
                Self { $($name: defaults::$name(),)* }
            }
        }

        impl<'de> Deserialize<'de> for TryConf {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
                deserializer.deserialize_map(ConfVisitor)
            }
        }

        #[derive(Deserialize)]
        #[serde(field_identifier, rename_all = "kebab-case")]
        #[allow(non_camel_case_types)]
        enum Field { $($name,)* third_party, }

        struct ConfVisitor;

        impl<'de> Visitor<'de> for ConfVisitor {
            type Value = TryConf;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("Conf")
            }

            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
                let mut errors = Vec::new();
                $(let mut $name = None;)*
                // could get `Field` here directly, but get `str` first for diagnostics
                while let Some(name) = map.next_key::<&str>()? {
                    match Field::deserialize(name.into_deserializer())? {
                        $(Field::$name => {
                            $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
                            match map.next_value() {
                                Err(e) => errors.push(e.to_string()),
                                Ok(value) => match $name {
                                    Some(_) => errors.push(format!("duplicate field `{}`", name)),
                                    None => $name = Some(value),
                                }
                            }
                        })*
                        // white-listed; ignore
                        Field::third_party => drop(map.next_value::<IgnoredAny>())
                    }
                }
                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
                Ok(TryConf { conf, errors })
            }
        }
    };
}

// N.B., this macro is parsed by util/lintlib.py
define_Conf! {
    /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports
    (msrv: Option<String> = None),
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
    (cognitive_complexity_threshold: u64 = 25),
    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
    (cyclomatic_complexity_threshold: Option<u64> = None),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents: Vec<String> = [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub", "GitLab",
        "IPv4", "IPv6",
        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
        "NaN", "NaNs",
        "OAuth", "GraphQL",
        "OCaml",
        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
        "WebGL",
        "TensorFlow",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
        "CamelCase",
    ].iter().map(ToString::to_string).collect()),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold: u64 = 7),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold: u64 = 250),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold: u64 = 4),
    /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack: u64 = 200),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold: u64 = 3),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
    (enum_variant_size_threshold: u64 = 200),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold: u64 = 1),
    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
    (literal_representation_threshold: u64 = 16384),
    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
    (trivial_copy_size_limit: Option<u64> = None),
    /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
    (pass_by_value_size_limit: u64 = 256),
    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
    (too_many_lines_threshold: u64 = 100),
    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
    (array_size_threshold: u64 = 512_000),
    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
    (vec_box_size_threshold: u64 = 4096),
    /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
    (max_trait_bounds: u64 = 3),
    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
    (max_struct_bools: u64 = 3),
    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
    (max_fn_params_bools: u64 = 3),
    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
    (warn_on_all_wildcard_imports: bool = false),
    /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
    (disallowed_methods: Vec<String> = Vec::new()),
    /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
    (unreadable_literal_lint_fractions: bool = true),
    /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
    (upper_case_acronyms_aggressive: bool = false),
    /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
    (cargo_ignore_publish: bool = false),
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
    // If neither of those exist, use ".".
    let mut current = env::var_os("CLIPPY_CONF_DIR")
        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
        .map_or_else(|| PathBuf::from("."), PathBuf::from);
    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
                _ => {},
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &Path) -> TryConf {
    let content = match fs::read_to_string(path) {
        Err(e) => return TryConf::from_error(e),
        Ok(content) => content,
    };
    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
}
<commit_msg>Add a missing lint to MSRV config doc<commit_after>//! Read configurations files.

#![allow(clippy::module_name_repetitions)]

use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
use serde::Deserialize;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::{env, fmt, fs, io};

/// Conf with parse errors
#[derive(Default)]
pub struct TryConf {
    pub conf: Conf,
    pub errors: Vec<String>,
}

impl TryConf {
    fn from_error(error: impl Error) -> Self {
        Self {
            conf: Conf::default(),
            errors: vec![error.to_string()],
        }
    }
}

macro_rules! define_Conf {
    ($(
        #[$doc:meta]
        $(#[conf_deprecated($dep:literal)])?
        ($name:ident: $ty:ty = $default:expr),
    )*) => {
        /// Clippy lint configuration
        pub struct Conf {
            $(#[$doc] pub $name: $ty,)*
        }

        mod defaults {
            $(pub fn $name() -> $ty { $default })*
        }

        impl Default for Conf {
            fn default() -> Self {
                Self { $($name: defaults::$name(),)* }
            }
        }

        impl<'de> Deserialize<'de> for TryConf {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
                deserializer.deserialize_map(ConfVisitor)
            }
        }

        #[derive(Deserialize)]
        #[serde(field_identifier, rename_all = "kebab-case")]
        #[allow(non_camel_case_types)]
        enum Field { $($name,)* third_party, }

        struct ConfVisitor;

        impl<'de> Visitor<'de> for ConfVisitor {
            type Value = TryConf;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("Conf")
            }

            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
                let mut errors = Vec::new();
                $(let mut $name = None;)*
                // could get `Field` here directly, but get `str` first for diagnostics
                while let Some(name) = map.next_key::<&str>()? {
                    match Field::deserialize(name.into_deserializer())? {
                        $(Field::$name => {
                            $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
                            match map.next_value() {
                                Err(e) => errors.push(e.to_string()),
                                Ok(value) => match $name {
                                    Some(_) => errors.push(format!("duplicate field `{}`", name)),
                                    None => $name = Some(value),
                                }
                            }
                        })*
                        // white-listed; ignore
                        Field::third_party => drop(map.next_value::<IgnoredAny>())
                    }
                }
                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
                Ok(TryConf { conf, errors })
            }
        }
    };
}

// N.B., this macro is parsed by util/lintlib.py
define_Conf! {
    /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports
    (msrv: Option<String> = None),
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
    (cognitive_complexity_threshold: u64 = 25),
    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
    (cyclomatic_complexity_threshold: Option<u64> = None),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents: Vec<String> = [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub", "GitLab",
        "IPv4", "IPv6",
        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
        "NaN", "NaNs",
        "OAuth", "GraphQL",
        "OCaml",
        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
        "WebGL",
        "TensorFlow",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
        "CamelCase",
    ].iter().map(ToString::to_string).collect()),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold: u64 = 7),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold: u64 = 250),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold: u64 = 4),
    /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack: u64 = 200),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold: u64 = 3),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
    (enum_variant_size_threshold: u64 = 200),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold: u64 = 1),
    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
    (literal_representation_threshold: u64 = 16384),
    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
    (trivial_copy_size_limit: Option<u64> = None),
    /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
    (pass_by_value_size_limit: u64 = 256),
    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
    (too_many_lines_threshold: u64 = 100),
    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
    (array_size_threshold: u64 = 512_000),
    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
    (vec_box_size_threshold: u64 = 4096),
    /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
    (max_trait_bounds: u64 = 3),
    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
    (max_struct_bools: u64 = 3),
    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
    (max_fn_params_bools: u64 = 3),
    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
    (warn_on_all_wildcard_imports: bool = false),
    /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
    (disallowed_methods: Vec<String> = Vec::new()),
    /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
    (unreadable_literal_lint_fractions: bool = true),
    /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other
    (upper_case_acronyms_aggressive: bool = false),
    /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
    (cargo_ignore_publish: bool = false),
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
    // If neither of those exist, use ".".
    let mut current = env::var_os("CLIPPY_CONF_DIR")
        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
        .map_or_else(|| PathBuf::from("."), PathBuf::from);
    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
                _ => {},
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &Path) -> TryConf {
    let content = match fs::read_to_string(path) {
        Err(e) => return TryConf::from_error(e),
        Ok(content) => content,
    };
    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
}
<|endoftext|>


<commit_before>//! Crate which contain the virtual machine which executes gluon programs
#![doc(html_root_url = "https://docs.rs/gluon_vm/0.5.0")]
// # GLUON
#![recursion_limit = "1024"]

#[macro_use]
extern crate bitflags;
extern crate codespan;
#[macro_use]
extern crate collect_mac;
#[cfg(test)]
extern crate env_logger;
#[doc(hidden)]
pub extern crate frunk_core;
#[macro_use]
extern crate futures;
extern crate itertools;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mopa;
extern crate pretty;
#[macro_use]
extern crate quick_error;
#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive_state;
#[cfg(feature = "serde_state")]
#[macro_use]
extern crate serde_state as serde;
#[cfg(feature = "serde_state")]
extern crate serde_json;

#[macro_use]
extern crate gluon_base as base;
extern crate gluon_check as check;
#[macro_use]
#[cfg(feature = "serde_state")]
extern crate gluon_codegen;

#[macro_use]
#[cfg(feature = "serde")]
pub mod serialization;

#[macro_use]
pub mod api;
pub mod channel;
pub mod compiler;
pub mod core;
pub mod debug;
pub mod dynamic;
#[macro_use]
pub mod future;
pub mod gc;
pub mod lazy;
pub mod macros;
pub mod primitives;
pub mod reference;
pub mod stack;
pub mod thread;
pub mod types;
pub mod vm;

mod array;
mod derive;
mod interner;
mod source_map;
mod value;

use std::fmt;
use std::marker::PhantomData;

use api::{ValueRef, VmType};
use base::metadata::Metadata;
use base::symbol::Symbol;
use base::types::ArcType;
use stack::Stacktrace;
use thread::{RootedThread, RootedValue, Thread};
use types::VmIndex;
use value::{Value, ValueRepr};

unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
    ::std::mem::transmute(x)
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
pub struct Variants<'a>(ValueRepr, PhantomData<&'a Value>);

impl<'a> Variants<'a> {
    /// Creates a new `Variants` value which assumes that `value` is rooted for the lifetime of the
    /// value
    pub unsafe fn new(value: &Value) -> Variants {
        Variants::with_root(value.clone(), value)
    }

    pub(crate) unsafe fn with_root<T: ?Sized>(value: Value, _root: &T) -> Variants {
        Variants(value.get_repr(), PhantomData)
    }

    pub fn get_value(&self) -> Value {
        self.0.into()
    }

    /// Returns an instance of `ValueRef` which allows users to safely retrieve the interals of a
    /// value
    pub fn as_ref(&self) -> ValueRef<'a> {
        unsafe { ValueRef::rooted_new(self.0) }
    }
}

impl<'a> gc::Traverseable for Variants<'a> {
    fn traverse(&self, gc: &mut gc::Gc) {
        self.0.traverse(gc);
    }
}

/// Type returned from vm functions which may fail
pub type Result<T> = ::std::result::Result<T, Error>;

quick_error! {
    /// Representation of all possible errors that can occur when interacting with the `vm` crate
    #[derive(Debug, PartialEq, Clone)]
    pub enum Error {
        Dead {
        }
        UndefinedBinding(symbol: String) {
            display("Binding `{}` is not defined", symbol)
        }
        UndefinedField(typ: ArcType, field: String) {
            display("Type `{}` does not have the field `{}`", typ, field)
        }
        TypeAlreadyExists(symbol: String) {
            display("Type `{}` already exists", symbol)
        }
        GlobalAlreadyExists(symbol: Symbol) {
            display("Global `{}` already exists", symbol)
        }
        MetadataDoesNotExist(symbol: String) {
            display("No metadata exists for `{}`", symbol)
        }
        WrongType(expected: ArcType, actual: ArcType) {
            display("Expected a value of type `{}` but the returned type was `{}`",
                    expected, actual)
        }
        OutOfMemory { limit: usize, needed: usize } {
            display("Thread is out of memory: Limit {}, needed {}", limit, needed)
        }
        StackOverflow(limit: VmIndex) {
            display("The stack has overflowed: Limit `{}`", limit)
        }
        Message(err: String) {
            display("{}", err)
            from()
        }
        Interrupted {
            display("Thread was interrupted")
        }
        Panic(err: String, stacktrace: Option<Stacktrace>) {
            display("{}", Panic { err, stacktrace })
        }
    }
}

struct Panic<'a> {
    err: &'a String,
    stacktrace: &'a Option<Stacktrace>,
}

impl<'a> fmt::Display for Panic<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Panic { err, stacktrace } = *self;
        write!(f, "{}", err)?;
        if let Some(ref stacktrace) = *stacktrace {
            write!(f, "\n\n{}", stacktrace)?;
        }
        Ok(())
    }
}

pub type ExternLoader = fn(&Thread) -> Result<ExternModule>;

pub struct ExternModule {
    pub metadata: Metadata,
    pub value: RootedValue<RootedThread>,
    pub typ: ArcType,
}

impl ExternModule {
    pub fn new<'vm, T>(thread: &'vm Thread, value: T) -> Result<ExternModule>
    where
        T: VmType + api::Pushable<'vm> + Send + Sync,
    {
        ExternModule::with_metadata(thread, value, Metadata::default())
    }

    pub fn with_metadata<'vm, T>(
        thread: &'vm Thread,
        value: T,
        metadata: Metadata,
    ) -> Result<ExternModule>
    where
        T: VmType + api::Pushable<'vm> + Send + Sync,
    {
        Ok(ExternModule {
            value: value.marshal(thread)?,
            typ: T::make_forall_type(thread),
            metadata,
        })
    }
}

/// Internal types and functions exposed to the main `gluon` crate
pub mod internal {
    pub use interner::InternedStr;
    pub use value::{Value, ValuePrinter};
    pub use vm::Global;
}
<commit_msg>chore: Fix html_root_url in vm<commit_after>//! Crate which contain the virtual machine which executes gluon programs
#![doc(html_root_url = "https://docs.rs/gluon_vm/0.8.1")] // # GLUON
#![recursion_limit = "1024"]

#[macro_use]
extern crate bitflags;
extern crate codespan;
#[macro_use]
extern crate collect_mac;
#[cfg(test)]
extern crate env_logger;
#[doc(hidden)]
pub extern crate frunk_core;
#[macro_use]
extern crate futures;
extern crate itertools;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mopa;
extern crate pretty;
#[macro_use]
extern crate quick_error;
#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive_state;
#[cfg(feature = "serde_state")]
#[macro_use]
extern crate serde_state as serde;
#[cfg(feature = "serde_state")]
extern crate serde_json;

#[macro_use]
extern crate gluon_base as base;
extern crate gluon_check as check;
#[macro_use]
#[cfg(feature = "serde_state")]
extern crate gluon_codegen;

#[macro_use]
#[cfg(feature = "serde")]
pub mod serialization;

#[macro_use]
pub mod api;
pub mod channel;
pub mod compiler;
pub mod core;
pub mod debug;
pub mod dynamic;
#[macro_use]
pub mod future;
pub mod gc;
pub mod lazy;
pub mod macros;
pub mod primitives;
pub mod reference;
pub mod stack;
pub mod thread;
pub mod types;
pub mod vm;

mod array;
mod derive;
mod interner;
mod source_map;
mod value;

use std::fmt;
use std::marker::PhantomData;

use api::{ValueRef, VmType};
use base::metadata::Metadata;
use base::symbol::Symbol;
use base::types::ArcType;
use stack::Stacktrace;
use thread::{RootedThread, RootedValue, Thread};
use types::VmIndex;
use value::{Value, ValueRepr};

unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
    ::std::mem::transmute(x)
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
pub struct Variants<'a>(ValueRepr, PhantomData<&'a Value>);

impl<'a> Variants<'a> {
    /// Creates a new `Variants` value which assumes that `value` is rooted for the lifetime of the
    /// value
    pub unsafe fn new(value: &Value) -> Variants {
        Variants::with_root(value.clone(), value)
    }

    pub(crate) unsafe fn with_root<T: ?Sized>(value: Value, _root: &T) -> Variants {
        Variants(value.get_repr(), PhantomData)
    }

    pub fn get_value(&self) -> Value {
        self.0.into()
    }

    /// Returns an instance of `ValueRef` which allows users to safely retrieve the interals of a
    /// value
    pub fn as_ref(&self) -> ValueRef<'a> {
        unsafe { ValueRef::rooted_new(self.0) }
    }
}

impl<'a> gc::Traverseable for Variants<'a> {
    fn traverse(&self, gc: &mut gc::Gc) {
        self.0.traverse(gc);
    }
}

/// Type returned from vm functions which may fail
pub type Result<T> = ::std::result::Result<T, Error>;

quick_error! {
    /// Representation of all possible errors that can occur when interacting with the `vm` crate
    #[derive(Debug, PartialEq, Clone)]
    pub enum Error {
        Dead {
        }
        UndefinedBinding(symbol: String) {
            display("Binding `{}` is not defined", symbol)
        }
        UndefinedField(typ: ArcType, field: String) {
            display("Type `{}` does not have the field `{}`", typ, field)
        }
        TypeAlreadyExists(symbol: String) {
            display("Type `{}` already exists", symbol)
        }
        GlobalAlreadyExists(symbol: Symbol) {
            display("Global `{}` already exists", symbol)
        }
        MetadataDoesNotExist(symbol: String) {
            display("No metadata exists for `{}`", symbol)
        }
        WrongType(expected: ArcType, actual: ArcType) {
            display("Expected a value of type `{}` but the returned type was `{}`",
                    expected, actual)
        }
        OutOfMemory { limit: usize, needed: usize } {
            display("Thread is out of memory: Limit {}, needed {}", limit, needed)
        }
        StackOverflow(limit: VmIndex) {
            display("The stack has overflowed: Limit `{}`", limit)
        }
        Message(err: String) {
            display("{}", err)
            from()
        }
        Interrupted {
            display("Thread was interrupted")
        }
        Panic(err: String, stacktrace: Option<Stacktrace>) {
            display("{}", Panic { err, stacktrace })
        }
    }
}

struct Panic<'a> {
    err: &'a String,
    stacktrace: &'a Option<Stacktrace>,
}

impl<'a> fmt::Display for Panic<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Panic { err, stacktrace } = *self;
        write!(f, "{}", err)?;
        if let Some(ref stacktrace) = *stacktrace {
            write!(f, "\n\n{}", stacktrace)?;
        }
        Ok(())
    }
}

pub type ExternLoader = fn(&Thread) -> Result<ExternModule>;

pub struct ExternModule {
    pub metadata: Metadata,
    pub value: RootedValue<RootedThread>,
    pub typ: ArcType,
}

impl ExternModule {
    pub fn new<'vm, T>(thread: &'vm Thread, value: T) -> Result<ExternModule>
    where
        T: VmType + api::Pushable<'vm> + Send + Sync,
    {
        ExternModule::with_metadata(thread, value, Metadata::default())
    }

    pub fn with_metadata<'vm, T>(
        thread: &'vm Thread,
        value: T,
        metadata: Metadata,
    ) -> Result<ExternModule>
    where
        T: VmType + api::Pushable<'vm> + Send + Sync,
    {
        Ok(ExternModule {
            value: value.marshal(thread)?,
            typ: T::make_forall_type(thread),
            metadata,
        })
    }
}

/// Internal types and functions exposed to the main `gluon` crate
pub mod internal {
    pub use interner::InternedStr;
    pub use value::{Value, ValuePrinter};
    pub use vm::Global;
}
<|endoftext|>


<commit_before>//! The SubscriptionManager used to manage subscription based RPCs.
//!
//! The manager provides four main things in terms of functionality:
//!
//! 1. The ability to create unique subscription IDs through the
//! use of the `IdProvider` trait. Two implementations are availble
//! out of the box, a `NumericIdProvider` and a `RandomStringIdProvider`.
//!
//! 2. An executor with which to drive `Future`s to completion.
//!
//! 3. A way to add new subscriptions. Subscriptions should come in the form
//! of a `Stream`. These subscriptions will be transformed into notifications
//! by the manager, which can be consumed by the client.
//!
//! 4. A way to cancel any currently active subscription.

use std::collections::HashMap;
use std::iter;
use std::sync::{
	atomic::{AtomicUsize, Ordering},
	Arc,
};

use crate::core::futures::sync::oneshot;
use crate::core::futures::{future as future01, Future as Future01};
use crate::{
	typed::{Sink, Subscriber},
	SubscriptionId,
};

use log::{error, warn};
use parking_lot::Mutex;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};

/// Alias for an implementation of `futures::future::Executor`.
pub type TaskExecutor = Arc<dyn future01::Executor<Box<dyn Future01<Item = (), Error = ()> + Send>> + Send + Sync>;

type ActiveSubscriptions = Arc<Mutex<HashMap<SubscriptionId, oneshot::Sender<()>>>>;

/// Trait used to provide unique subscription IDs.
pub trait IdProvider {
	/// A unique ID used to identify a subscription.
	type Id: Default + Into<SubscriptionId>;

	/// Returns the next ID for the subscription.
	fn next_id(&self) -> Self::Id;
}

/// Provides a thread-safe incrementing integer which
/// can be used as a subscription ID.
#[derive(Clone, Debug)]
pub struct NumericIdProvider {
	current_id: Arc<AtomicUsize>,
}

impl NumericIdProvider {
	/// Create a new NumericIdProvider.
	pub fn new() -> Self {
		Default::default()
	}

	/// Create a new NumericIdProvider starting from
	/// the given ID.
	pub fn with_id(id: AtomicUsize) -> Self {
		Self {
			current_id: Arc::new(id),
		}
	}
}

impl IdProvider for NumericIdProvider {
	type Id = u64;

	fn next_id(&self) -> Self::Id {
		self.current_id.fetch_add(1, Ordering::AcqRel) as u64
	}
}

impl Default for NumericIdProvider {
	fn default() -> Self {
		NumericIdProvider {
			current_id: Arc::new(AtomicUsize::new(1)),
		}
	}
}

/// Used to generate random strings for use as
/// subscription IDs.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct RandomStringIdProvider {
	len: usize,
}

impl RandomStringIdProvider {
	/// Create a new RandomStringIdProvider.
	pub fn new() -> Self {
		Default::default()
	}

	/// Create a new RandomStringIdProvider, which will generate
	/// random id strings of the given length.
	pub fn with_len(len: usize) -> Self {
		Self { len }
	}
}

impl IdProvider for RandomStringIdProvider {
	type Id = String;

	fn next_id(&self) -> Self::Id {
		let mut rng = thread_rng();
		let id: String = iter::repeat(())
			.map(|()| rng.sample(Alphanumeric))
			.take(self.len)
			.collect();
		id
	}
}

impl Default for RandomStringIdProvider {
	fn default() -> Self {
		Self { len: 16 }
	}
}

/// Subscriptions manager.
///
/// Takes care of assigning unique subscription ids and
/// driving the sinks into completion.
#[derive(Clone)]
pub struct SubscriptionManager<I: IdProvider = RandomStringIdProvider> {
	id_provider: I,
	active_subscriptions: ActiveSubscriptions,
	executor: TaskExecutor,
}

impl SubscriptionManager {
	/// Creates a new SubscriptionManager.
	///
	/// Uses `RandomStringIdProvider` as the ID provider.
	pub fn new(executor: TaskExecutor) -> Self {
		Self {
			id_provider: RandomStringIdProvider::default(),
			active_subscriptions: Default::default(),
			executor,
		}
	}
}

impl<I: IdProvider> SubscriptionManager<I> {
	/// Creates a new SubscriptionManager with the specified
	/// ID provider.
	pub fn with_id_provider(id_provider: I, executor: TaskExecutor) -> Self {
		Self {
			id_provider,
			active_subscriptions: Default::default(),
			executor,
		}
	}

	/// Borrows the internal task executor.
	///
	/// This can be used to spawn additional tasks on the underlying event loop.
	pub fn executor(&self) -> &TaskExecutor {
		&self.executor
	}

	/// Creates new subscription for given subscriber.
	///
	/// Second parameter is a function that converts Subscriber Sink into a Future.
	/// This future will be driven to completion by the underlying event loop
	pub fn add<T, E, G, R, F>(&self, subscriber: Subscriber<T, E>, into_future: G) -> SubscriptionId
	where
		G: FnOnce(Sink<T, E>) -> R,
		R: future01::IntoFuture<Future = F, Item = (), Error = ()>,
		F: future01::Future<Item = (), Error = ()> + Send + 'static,
	{
		let id = self.id_provider.next_id();
		let subscription_id: SubscriptionId = id.into();
		if let Ok(sink) = subscriber.assign_id(subscription_id.clone()) {
			let (tx, rx) = oneshot::channel();
			let future = into_future(sink)
				.into_future()
				.select(rx.map_err(|e| warn!("Error timing out: {:?}", e)))
				.then(|_| Ok(()));

			self.active_subscriptions.lock().insert(subscription_id.clone(), tx);
			if self.executor.execute(Box::new(future)).is_err() {
				error!("Failed to spawn RPC subscription task");
			}
		}

		subscription_id
	}

	/// Cancel subscription.
	///
	/// Returns true if subscription existed or false otherwise.
	pub fn cancel(&self, id: SubscriptionId) -> bool {
		if let Some(tx) = self.active_subscriptions.lock().remove(&id) {
			let _ = tx.send(());
			return true;
		}

		false
	}
}

impl<I: Default + IdProvider> SubscriptionManager<I> {
	/// Creates a new SubscriptionManager.
	pub fn with_executor(executor: TaskExecutor) -> Self {
		Self {
			id_provider: Default::default(),
			active_subscriptions: Default::default(),
			executor,
		}
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::typed::Subscriber;
	use futures::{compat::Future01CompatExt, executor, FutureExt};
	use futures::{stream, StreamExt, TryStreamExt};

	use crate::core::futures::sink::Sink as Sink01;
	use crate::core::futures::stream::Stream as Stream01;

	// Executor shared by all tests.
	//
	// This shared executor is used to prevent `Too many open files` errors
	// on systems with a lot of cores.
	lazy_static::lazy_static! {
		static ref EXECUTOR: executor::ThreadPool = executor::ThreadPool::new()
			.expect("Failed to create thread pool executor for tests");
	}

	pub struct TestTaskExecutor;
	type Boxed01Future01 = Box<dyn future01::Future<Item = (), Error = ()> + Send + 'static>;

	impl future01::Executor<Boxed01Future01> for TestTaskExecutor {
		fn execute(&self, future: Boxed01Future01) -> std::result::Result<(), future01::ExecuteError<Boxed01Future01>> {
			EXECUTOR.spawn_ok(future.compat().map(drop));
			Ok(())
		}
	}

	#[test]
	fn making_a_numeric_id_provider_works() {
		let provider = NumericIdProvider::new();
		let expected_id = 1;
		let actual_id = provider.next_id();

		assert_eq!(actual_id, expected_id);
	}

	#[test]
	fn default_numeric_id_provider_works() {
		let provider: NumericIdProvider = Default::default();
		let expected_id = 1;
		let actual_id = provider.next_id();

		assert_eq!(actual_id, expected_id);
	}

	#[test]
	fn numeric_id_provider_with_id_works() {
		let provider = NumericIdProvider::with_id(AtomicUsize::new(5));
		let expected_id = 5;
		let actual_id = provider.next_id();

		assert_eq!(actual_id, expected_id);
	}

	#[test]
	fn random_string_provider_returns_id_with_correct_default_len() {
		let provider = RandomStringIdProvider::new();
		let expected_len = 16;
		let actual_len = provider.next_id().len();

		assert_eq!(actual_len, expected_len);
	}

	#[test]
	fn random_string_provider_returns_id_with_correct_user_given_len() {
		let expected_len = 10;
		let provider = RandomStringIdProvider::with_len(expected_len);
		let actual_len = provider.next_id().len();

		assert_eq!(actual_len, expected_len);
	}

	#[test]
	fn new_subscription_manager_defaults_to_random_string_provider() {
		let manager = SubscriptionManager::new(Arc::new(TestTaskExecutor));
		let subscriber = Subscriber::<u64>::new_test("test_subTest").0;
		let stream = stream::iter(vec![Ok(1)]).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		if let SubscriptionId::String(_) = id {
			assert!(true)
		} else {
			assert!(false, "Expected SubscriptionId::String");
		}
	}

	#[test]
	fn new_subscription_manager_works_with_numeric_id_provider() {
		let id_provider = NumericIdProvider::default();
		let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor));

		let subscriber = Subscriber::<u64>::new_test("test_subTest").0;
		let stream = stream::iter(vec![Ok(1)]).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		if let SubscriptionId::Number(_) = id {
			assert!(true)
		} else {
			assert!(false, "Expected SubscriptionId::Number");
		}
	}

	#[test]
	fn new_subscription_manager_works_with_random_string_provider() {
		let id_provider = RandomStringIdProvider::default();
		let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor));

		let subscriber = Subscriber::<u64>::new_test("test_subTest").0;
		let stream = stream::iter(vec![Ok(1)]).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		if let SubscriptionId::String(_) = id {
			assert!(true)
		} else {
			assert!(false, "Expected SubscriptionId::String");
		}
	}

	#[test]
	fn subscription_is_canceled_if_it_existed() {
		let manager = SubscriptionManager::<NumericIdProvider>::with_executor(Arc::new(TestTaskExecutor));
		// Need to bind receiver here (unlike the other tests) or else the subscriber
		// will think the client has disconnected and not update `active_subscriptions`
		let (subscriber, _recv, _) = Subscriber::<u64>::new_test("test_subTest");

		let (mut tx, rx) = futures::channel::mpsc::channel(8);
		tx.start_send(1).unwrap();
		let stream = rx.map(|v| Ok::<_, ()>(v)).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		let is_cancelled = manager.cancel(id);
		assert!(is_cancelled);
	}

	#[test]
	fn subscription_is_not_canceled_because_it_didnt_exist() {
		let manager = SubscriptionManager::new(Arc::new(TestTaskExecutor));

		let id: SubscriptionId = 23u32.into();
		let is_cancelled = manager.cancel(id);
		let is_not_cancelled = !is_cancelled;

		assert!(is_not_cancelled);
	}
}
<commit_msg>Use matches! macro (#553)<commit_after>//! The SubscriptionManager used to manage subscription based RPCs.
//!
//! The manager provides four main things in terms of functionality:
//!
//! 1. The ability to create unique subscription IDs through the
//! use of the `IdProvider` trait. Two implementations are availble
//! out of the box, a `NumericIdProvider` and a `RandomStringIdProvider`.
//!
//! 2. An executor with which to drive `Future`s to completion.
//!
//! 3. A way to add new subscriptions. Subscriptions should come in the form
//! of a `Stream`. These subscriptions will be transformed into notifications
//! by the manager, which can be consumed by the client.
//!
//! 4. A way to cancel any currently active subscription.

use std::collections::HashMap;
use std::iter;
use std::sync::{
	atomic::{AtomicUsize, Ordering},
	Arc,
};

use crate::core::futures::sync::oneshot;
use crate::core::futures::{future as future01, Future as Future01};
use crate::{
	typed::{Sink, Subscriber},
	SubscriptionId,
};

use log::{error, warn};
use parking_lot::Mutex;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};

/// Alias for an implementation of `futures::future::Executor`.
pub type TaskExecutor = Arc<dyn future01::Executor<Box<dyn Future01<Item = (), Error = ()> + Send>> + Send + Sync>;

type ActiveSubscriptions = Arc<Mutex<HashMap<SubscriptionId, oneshot::Sender<()>>>>;

/// Trait used to provide unique subscription IDs.
pub trait IdProvider {
	/// A unique ID used to identify a subscription.
	type Id: Default + Into<SubscriptionId>;

	/// Returns the next ID for the subscription.
	fn next_id(&self) -> Self::Id;
}

/// Provides a thread-safe incrementing integer which
/// can be used as a subscription ID.
#[derive(Clone, Debug)]
pub struct NumericIdProvider {
	current_id: Arc<AtomicUsize>,
}

impl NumericIdProvider {
	/// Create a new NumericIdProvider.
	pub fn new() -> Self {
		Default::default()
	}

	/// Create a new NumericIdProvider starting from
	/// the given ID.
	pub fn with_id(id: AtomicUsize) -> Self {
		Self {
			current_id: Arc::new(id),
		}
	}
}

impl IdProvider for NumericIdProvider {
	type Id = u64;

	fn next_id(&self) -> Self::Id {
		self.current_id.fetch_add(1, Ordering::AcqRel) as u64
	}
}

impl Default for NumericIdProvider {
	fn default() -> Self {
		NumericIdProvider {
			current_id: Arc::new(AtomicUsize::new(1)),
		}
	}
}

/// Used to generate random strings for use as
/// subscription IDs.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct RandomStringIdProvider {
	len: usize,
}

impl RandomStringIdProvider {
	/// Create a new RandomStringIdProvider.
	pub fn new() -> Self {
		Default::default()
	}

	/// Create a new RandomStringIdProvider, which will generate
	/// random id strings of the given length.
	pub fn with_len(len: usize) -> Self {
		Self { len }
	}
}

impl IdProvider for RandomStringIdProvider {
	type Id = String;

	fn next_id(&self) -> Self::Id {
		let mut rng = thread_rng();
		let id: String = iter::repeat(())
			.map(|()| rng.sample(Alphanumeric))
			.take(self.len)
			.collect();
		id
	}
}

impl Default for RandomStringIdProvider {
	fn default() -> Self {
		Self { len: 16 }
	}
}

/// Subscriptions manager.
///
/// Takes care of assigning unique subscription ids and
/// driving the sinks into completion.
#[derive(Clone)]
pub struct SubscriptionManager<I: IdProvider = RandomStringIdProvider> {
	id_provider: I,
	active_subscriptions: ActiveSubscriptions,
	executor: TaskExecutor,
}

impl SubscriptionManager {
	/// Creates a new SubscriptionManager.
	///
	/// Uses `RandomStringIdProvider` as the ID provider.
	pub fn new(executor: TaskExecutor) -> Self {
		Self {
			id_provider: RandomStringIdProvider::default(),
			active_subscriptions: Default::default(),
			executor,
		}
	}
}

impl<I: IdProvider> SubscriptionManager<I> {
	/// Creates a new SubscriptionManager with the specified
	/// ID provider.
	pub fn with_id_provider(id_provider: I, executor: TaskExecutor) -> Self {
		Self {
			id_provider,
			active_subscriptions: Default::default(),
			executor,
		}
	}

	/// Borrows the internal task executor.
	///
	/// This can be used to spawn additional tasks on the underlying event loop.
	pub fn executor(&self) -> &TaskExecutor {
		&self.executor
	}

	/// Creates new subscription for given subscriber.
	///
	/// Second parameter is a function that converts Subscriber Sink into a Future.
	/// This future will be driven to completion by the underlying event loop
	pub fn add<T, E, G, R, F>(&self, subscriber: Subscriber<T, E>, into_future: G) -> SubscriptionId
	where
		G: FnOnce(Sink<T, E>) -> R,
		R: future01::IntoFuture<Future = F, Item = (), Error = ()>,
		F: future01::Future<Item = (), Error = ()> + Send + 'static,
	{
		let id = self.id_provider.next_id();
		let subscription_id: SubscriptionId = id.into();
		if let Ok(sink) = subscriber.assign_id(subscription_id.clone()) {
			let (tx, rx) = oneshot::channel();
			let future = into_future(sink)
				.into_future()
				.select(rx.map_err(|e| warn!("Error timing out: {:?}", e)))
				.then(|_| Ok(()));

			self.active_subscriptions.lock().insert(subscription_id.clone(), tx);
			if self.executor.execute(Box::new(future)).is_err() {
				error!("Failed to spawn RPC subscription task");
			}
		}

		subscription_id
	}

	/// Cancel subscription.
	///
	/// Returns true if subscription existed or false otherwise.
	pub fn cancel(&self, id: SubscriptionId) -> bool {
		if let Some(tx) = self.active_subscriptions.lock().remove(&id) {
			let _ = tx.send(());
			return true;
		}

		false
	}
}

impl<I: Default + IdProvider> SubscriptionManager<I> {
	/// Creates a new SubscriptionManager.
	pub fn with_executor(executor: TaskExecutor) -> Self {
		Self {
			id_provider: Default::default(),
			active_subscriptions: Default::default(),
			executor,
		}
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::typed::Subscriber;
	use futures::{compat::Future01CompatExt, executor, FutureExt};
	use futures::{stream, StreamExt, TryStreamExt};

	use crate::core::futures::sink::Sink as Sink01;
	use crate::core::futures::stream::Stream as Stream01;

	// Executor shared by all tests.
	//
	// This shared executor is used to prevent `Too many open files` errors
	// on systems with a lot of cores.
	lazy_static::lazy_static! {
		static ref EXECUTOR: executor::ThreadPool = executor::ThreadPool::new()
			.expect("Failed to create thread pool executor for tests");
	}

	pub struct TestTaskExecutor;
	type Boxed01Future01 = Box<dyn future01::Future<Item = (), Error = ()> + Send + 'static>;

	impl future01::Executor<Boxed01Future01> for TestTaskExecutor {
		fn execute(&self, future: Boxed01Future01) -> std::result::Result<(), future01::ExecuteError<Boxed01Future01>> {
			EXECUTOR.spawn_ok(future.compat().map(drop));
			Ok(())
		}
	}

	#[test]
	fn making_a_numeric_id_provider_works() {
		let provider = NumericIdProvider::new();
		let expected_id = 1;
		let actual_id = provider.next_id();

		assert_eq!(actual_id, expected_id);
	}

	#[test]
	fn default_numeric_id_provider_works() {
		let provider: NumericIdProvider = Default::default();
		let expected_id = 1;
		let actual_id = provider.next_id();

		assert_eq!(actual_id, expected_id);
	}

	#[test]
	fn numeric_id_provider_with_id_works() {
		let provider = NumericIdProvider::with_id(AtomicUsize::new(5));
		let expected_id = 5;
		let actual_id = provider.next_id();

		assert_eq!(actual_id, expected_id);
	}

	#[test]
	fn random_string_provider_returns_id_with_correct_default_len() {
		let provider = RandomStringIdProvider::new();
		let expected_len = 16;
		let actual_len = provider.next_id().len();

		assert_eq!(actual_len, expected_len);
	}

	#[test]
	fn random_string_provider_returns_id_with_correct_user_given_len() {
		let expected_len = 10;
		let provider = RandomStringIdProvider::with_len(expected_len);
		let actual_len = provider.next_id().len();

		assert_eq!(actual_len, expected_len);
	}

	#[test]
	fn new_subscription_manager_defaults_to_random_string_provider() {
		let manager = SubscriptionManager::new(Arc::new(TestTaskExecutor));
		let subscriber = Subscriber::<u64>::new_test("test_subTest").0;
		let stream = stream::iter(vec![Ok(1)]).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		assert!(matches!(id, SubscriptionId::String(_)))
	}

	#[test]
	fn new_subscription_manager_works_with_numeric_id_provider() {
		let id_provider = NumericIdProvider::default();
		let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor));

		let subscriber = Subscriber::<u64>::new_test("test_subTest").0;
		let stream = stream::iter(vec![Ok(1)]).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		assert!(matches!(id, SubscriptionId::Number(_)))
	}

	#[test]
	fn new_subscription_manager_works_with_random_string_provider() {
		let id_provider = RandomStringIdProvider::default();
		let manager = SubscriptionManager::with_id_provider(id_provider, Arc::new(TestTaskExecutor));

		let subscriber = Subscriber::<u64>::new_test("test_subTest").0;
		let stream = stream::iter(vec![Ok(1)]).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		assert!(matches!(id, SubscriptionId::String(_)))
	}

	#[test]
	fn subscription_is_canceled_if_it_existed() {
		let manager = SubscriptionManager::<NumericIdProvider>::with_executor(Arc::new(TestTaskExecutor));
		// Need to bind receiver here (unlike the other tests) or else the subscriber
		// will think the client has disconnected and not update `active_subscriptions`
		let (subscriber, _recv, _) = Subscriber::<u64>::new_test("test_subTest");

		let (mut tx, rx) = futures::channel::mpsc::channel(8);
		tx.start_send(1).unwrap();
		let stream = rx.map(|v| Ok::<_, ()>(v)).compat();

		let id = manager.add(subscriber, |sink| {
			let stream = stream.map(|res| Ok(res));

			sink.sink_map_err(|_| ()).send_all(stream).map(|_| ())
		});

		let is_cancelled = manager.cancel(id);
		assert!(is_cancelled);
	}

	#[test]
	fn subscription_is_not_canceled_because_it_didnt_exist() {
		let manager = SubscriptionManager::new(Arc::new(TestTaskExecutor));

		let id: SubscriptionId = 23u32.into();
		let is_cancelled = manager.cancel(id);
		let is_not_cancelled = !is_cancelled;

		assert!(is_not_cancelled);
	}
}
<|endoftext|>