<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with sessions.
//!
//! Sessions are created per action request and should be used as the only way
//! to communicate with the server. Sessions are responsible for handling action
//! errors (when something goes wrong) or notifying the server that the action
//! finished (by sending appropriate status signal).
//!
//! They also keep track of various statistics (such as number of transferred
//! bytes, action runtime, etc.) and stop the execution if they exceed limits
//! for a particular request.

mod demand;
mod error;
mod response;
mod time;

use std::convert::TryInto;

use log::{error, info};

use crate::action;
use crate::message;
pub use self::demand::{Demand, Header, Payload};
pub use self::error::{Error, ParseError, MissingFieldError, RegexParseError,
                      UnsupportedValueError, UnknownEnumValueError};
// TODO: Revisit visibility once comms refactoring is complete.
pub use self::response::{Response, Status};
pub use self::time::time_from_micros;

/// A specialized `Result` type for sessions.
pub type Result<T> = std::result::Result<T, Error>;

/// Abstraction for various kinds of sessions.
pub trait Session {
    /// Sends a reply to the flow that call the action.
    fn reply<I>(&mut self, item: I) -> Result<()>
    where I: action::Item + 'static;

    /// Sends an adressed parcel.
    fn send<P>(&mut self, parcel: crate::message::sink::AddressedParcel<P>) -> Result<()>
    where P: crate::message::sink::Parcel + 'static;

    /// Sends a heartbeat signal to the Fleetspeak process.
    fn heartbeat(&mut self) {
        // TODO: Create a real implementation.
    }
}

/// A session implementation that uses real Fleetspeak connection.
///
/// This is a normal session type that that is associated with some flow on the
/// server. It keeps track of the responses it sends and collects statistics
/// about network and runtime utilization to kill the action if it is needed.
pub struct FleetspeakSession {
    response_builder: crate::message::ResponseBuilder,
}

impl FleetspeakSession {

    /// Creates a new Fleetspeak session for the given `request` object.
    fn new(request_id: crate::message::RequestId) -> FleetspeakSession {
        FleetspeakSession {
            response_builder: crate::message::ResponseBuilder::new(request_id),
        }
    }

    pub fn handle(request: crate::message::Request) {
        let mut session = FleetspeakSession::new(request.id());

        let result = crate::action::dispatch(&mut session, request);
        session.response_builder.status(result).send();

        // TODO(panhania@): Consider returning the status so that the parent can
        // log appropriate message.
    }
}

impl Session for FleetspeakSession {

    fn reply<I: crate::action::Item>(&mut self, item: I) -> Result<()> {
        // TODO(panhania@): Enforce limits.
        self.response_builder.item(item).send();

        Ok(())
    }

    fn send<P>(&mut self, parcel: crate::message::sink::AddressedParcel<P>) -> Result<()>
    where
        P: crate::message::sink::Parcel,
    {
        crate::message::fleetspeak::send_raw(parcel.try_into()?);

        Ok(())
    }
}

/// Sends a session response to the server.
///
/// Note that this function is not exposed on purpose. Actions should send
/// responses through session objects which introduce a layer of safety. `send`
/// is a low-level utility supposed to be used internally.
fn send<R>(response: Response<R>) -> Result<()>
where
    R: action::Response,
{
    let message = response.try_into()?;
    message::fleetspeak::send_raw(message);

    Ok(())
}

#[cfg(test)]
pub mod test {

    use std::any::Any;
    use std::collections::HashMap;

    use super::*;
    use crate::message::sink::Sink;

    /// A session implementation intended to be used in tests.
    ///
    /// Testing actions with normal session objects can be quite hard, since
    /// they communicate with the outside world (through Fleetspeak). Since we
    /// want to keep the tests minimal and not waste resources on unneeded I/O,
    /// using real sessions is not an option.
    ///
    /// Instead, one can use a `Fake` session. It simply accumulates responses
    /// that the action sends and lets the creator inspect them later.
    pub struct FakeSession {
        replies: Vec<Box<dyn Any>>,
        parcels: HashMap<Sink, Vec<Box<dyn Any>>>,
    }

    impl FakeSession {

        /// Constructs a new fake session.
        pub fn new() -> FakeSession {
            FakeSession {
                replies: Vec::new(),
                parcels: std::collections::HashMap::new(),
            }
        }

        /// Yields the number of replies that this session sent so far.
        pub fn reply_count(&self) -> usize {
            self.replies.len()
        }

        /// Retrieves a reply corresponding to the given id.
        ///
        /// The identifier corresponding to the first response is 0, the second
        /// one is 1 and so on.
        ///
        /// This method will panic if a reply with the specified `id` does not
        /// exist or if it exists but has a wrong type.
        pub fn reply<R>(&self, id: usize) -> &R
        where
            R: crate::action::Item + 'static,
        {
            match self.replies().nth(id) {
                Some(reply) => reply,
                None => panic!("no reply #{}", id),
            }
        }

        /// Constructs an iterator over session replies.
        ///
        /// The iterator will panic (but not immediately) if some reply has an
        /// incorrect type.
        pub fn replies<R>(&self) -> impl Iterator<Item = &R>
        where
            R: crate::action::Item + 'static
        {
            self.replies.iter().map(|reply| {
                reply.downcast_ref().expect("unexpected reply type")
            })
        }

        /// Yields the number of parcels sent so far to the specified sink.
        pub fn parcel_count(&self, sink: Sink) -> usize {
            match self.parcels.get(&sink) {
                Some(parcels) => parcels.len(),
                None => 0,
            }
        }

        /// Retrieves a parcel with the given id sent to a particular sink.
        ///
        /// The identifier corresponding to the first parcel to the particular
        /// sink is 0, to the second one (to the same sink) is 1 and so on.
        ///
        /// This method will panic if a reply with the specified `id` to the
        /// given `sink` does not exist or if it exists but has wrong type.
        pub fn parcel<P>(&self, sink: Sink, id: usize) -> &P
        where
            P: crate::message::sink::Parcel + 'static,
        {
            match self.parcels(sink).nth(id) {
                Some(parcel) => parcel,
                None => panic!("no parcel #{} for sink '{:?}'", id, sink),
            }
        }

        /// Constructs an iterator over session parcels for the given sink.
        ///
        /// The iterator will panic (but not immediately) if some parcels has
        /// an incorrect type.
        pub fn parcels<P>(&self, sink: Sink) -> impl Iterator<Item = &P>
        where
            P: crate::message::sink::Parcel + 'static,
        {
            // Since the empty iterator (as defined in the standard library) is
            // a specific type, it cannot be returned in one branch but not in
            // another branch.
            //
            // Instead, we use the fact that `Option` is an iterator and then we
            // squash it with `Iterator::flatten`.
            let parcels = self.parcels.get(&sink).into_iter().flatten();

            parcels.map(move |parcel| match parcel.downcast_ref() {
                Some(parcel) => parcel,
                None => panic!("unexpected parcel type in sink '{:?}'", sink),
            })
        }
    }

    impl Session for FakeSession {

        fn reply<I>(&mut self, item: I) -> Result<()>
        where
            I: crate::action::Item + 'static,
        {
            self.replies.push(Box::new(item));

            Ok(())
        }

        fn send<P>(&mut self, parcel: crate::message::sink::AddressedParcel<P>) -> Result<()>
        where
            P: crate::message::sink::Parcel + 'static,
        {
            let sink = parcel.sink();

            let parcels = self.parcels.entry(sink).or_insert_with(Vec::new);
            parcels.push(Box::new(parcel.unpack()));

            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::message::sink;

    #[test]
    fn test_fake_reply_count() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(()).unwrap();
            session.reply(()).unwrap();
            session.reply(()).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        assert_eq!(session.reply_count(), 3);
    }

    #[test]
    fn test_fake_parcel_count() {

        // TODO: Extend this test with more sinks (once we have some more sinks
        // defined).

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(crate::message::sink::STARTUP.address(())).unwrap();
            session.send(crate::message::sink::STARTUP.address(())).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        assert_eq!(session.parcel_count(crate::message::sink::STARTUP), 2);
    }

    #[test]
    fn test_fake_reply_correct_response() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(StringResponse::from("foo")).unwrap();
            session.reply(StringResponse::from("bar")).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        assert_eq!(session.reply::<StringResponse>(0).0, "foo");
        assert_eq!(session.reply::<StringResponse>(1).0, "bar");
    }

    #[test]
    #[should_panic(expected = "no reply #0")]
    fn test_fake_reply_incorrect_response_id() {

        fn handle<S: Session>(_: &mut S, _: ()) {
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.reply::<()>(0);
    }

    #[test]
    #[should_panic(expected = "unexpected reply type")]
    fn test_fake_reply_incorrect_response_type() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(StringResponse::from("quux")).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.reply::<()>(0);
    }

    #[test]
    fn test_fake_parcel_correct_parcel() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(StringResponse::from("foo")))
                .unwrap();
            session.send(sink::STARTUP.address(StringResponse::from("bar")))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let response_foo = session.parcel::<StringResponse>(sink::STARTUP, 0);
        let response_bar = session.parcel::<StringResponse>(sink::STARTUP, 1);
        assert_eq!(response_foo.0, "foo");
        assert_eq!(response_bar.0, "bar");
    }

    #[test]
    #[should_panic(expected = "no parcel #42")]
    fn test_fake_parcel_incorrect_parcel_id() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(()))
                .unwrap();
            session.send(sink::STARTUP.address(()))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.parcel::<()>(sink::STARTUP, 42);
    }

    #[test]
    #[should_panic(expected = "unexpected parcel type")]
    fn test_fake_parcel_incorrect_parcel_type() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(StringResponse::from("quux")))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.parcel::<()>(sink::STARTUP, 0);
    }

    #[test]
    fn test_fake_replies_no_parcels() {

        fn handle<S: Session>(_: &mut S, _: ()) {
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut replies = session.replies::<()>();
        assert_eq!(replies.next(), None);
    }

    #[test]
    fn test_fake_replies_multiple_parcels() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(StringResponse::from("foo")).unwrap();
            session.reply(StringResponse::from("bar")).unwrap();
            session.reply(StringResponse::from("baz")).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut replies = session.replies::<StringResponse>();
        assert_eq!(replies.next().unwrap().0, "foo");
        assert_eq!(replies.next().unwrap().0, "bar");
        assert_eq!(replies.next().unwrap().0, "baz");
        assert_eq!(replies.next(), None);
    }

    #[test]
    fn test_fake_parcels_no_parcels() {

        fn handle<S: Session>(_: &mut S, _: ()) {
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut parcels = session.parcels::<()>(crate::message::sink::STARTUP);
        assert_eq!(parcels.next(), None);
    }

    #[test]
    fn test_fake_parcels_multiple_parcels() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(StringResponse::from("foo")))
                .unwrap();
            session.send(sink::STARTUP.address(StringResponse::from("bar")))
                .unwrap();
            session.send(sink::STARTUP.address(StringResponse::from("baz")))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut parcels = session.parcels::<StringResponse>(sink::STARTUP);
        assert_eq!(parcels.next().unwrap().0, "foo");
        assert_eq!(parcels.next().unwrap().0, "bar");
        assert_eq!(parcels.next().unwrap().0, "baz");
        assert_eq!(parcels.next(), None);
    }

    #[derive(Debug, PartialEq, Eq)]
    struct StringResponse(String);

    impl<S: Into<String>> From<S> for StringResponse {

        fn from(string: S) -> StringResponse {
            StringResponse(string.into())
        }
    }

    impl action::Item for StringResponse {

        const RDF_NAME: &'static str = "RDFString";

        type Proto = protobuf::well_known_types::StringValue;

        fn into_proto(self) -> protobuf::well_known_types::StringValue {
            let mut proto = protobuf::well_known_types::StringValue::new();
            proto.set_value(self.0);

            proto
        }
    }

    impl crate::message::sink::Parcel for StringResponse {

        const RDF_NAME: &'static str = "RDFString";

        type Proto = protobuf::well_known_types::StringValue;

        fn into_proto(self) -> protobuf::well_known_types::StringValue {
            let mut proto = protobuf::well_known_types::StringValue::new();
            proto.set_value(self.0);

            proto
        }
    }
}
<commit_msg>Fix compiler warnings.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with sessions.
//!
//! Sessions are created per action request and should be used as the only way
//! to communicate with the server. Sessions are responsible for handling action
//! errors (when something goes wrong) or notifying the server that the action
//! finished (by sending appropriate status signal).
//!
//! They also keep track of various statistics (such as number of transferred
//! bytes, action runtime, etc.) and stop the execution if they exceed limits
//! for a particular request.

mod demand;
mod error;
mod response;
mod time;

use std::convert::TryInto;

use crate::action;
pub use self::demand::{Demand, Header, Payload};
pub use self::error::{Error, ParseError, MissingFieldError, RegexParseError,
                      UnsupportedValueError, UnknownEnumValueError};
// TODO: Revisit visibility once comms refactoring is complete.
pub use self::response::{Response, Status};
pub use self::time::time_from_micros;

/// A specialized `Result` type for sessions.
pub type Result<T> = std::result::Result<T, Error>;

/// Abstraction for various kinds of sessions.
pub trait Session {
    /// Sends a reply to the flow that call the action.
    fn reply<I>(&mut self, item: I) -> Result<()>
    where I: action::Item + 'static;

    /// Sends an adressed parcel.
    fn send<P>(&mut self, parcel: crate::message::sink::AddressedParcel<P>) -> Result<()>
    where P: crate::message::sink::Parcel + 'static;

    /// Sends a heartbeat signal to the Fleetspeak process.
    fn heartbeat(&mut self) {
        // TODO: Create a real implementation.
    }
}

/// A session implementation that uses real Fleetspeak connection.
///
/// This is a normal session type that that is associated with some flow on the
/// server. It keeps track of the responses it sends and collects statistics
/// about network and runtime utilization to kill the action if it is needed.
pub struct FleetspeakSession {
    response_builder: crate::message::ResponseBuilder,
}

impl FleetspeakSession {

    /// Creates a new Fleetspeak session for the given `request` object.
    fn new(request_id: crate::message::RequestId) -> FleetspeakSession {
        FleetspeakSession {
            response_builder: crate::message::ResponseBuilder::new(request_id),
        }
    }

    pub fn handle(request: crate::message::Request) {
        let mut session = FleetspeakSession::new(request.id());

        let result = crate::action::dispatch(&mut session, request);
        session.response_builder.status(result).send();

        // TODO(panhania@): Consider returning the status so that the parent can
        // log appropriate message.
    }
}

impl Session for FleetspeakSession {

    fn reply<I: crate::action::Item>(&mut self, item: I) -> Result<()> {
        // TODO(panhania@): Enforce limits.
        self.response_builder.item(item).send();

        Ok(())
    }

    fn send<P>(&mut self, parcel: crate::message::sink::AddressedParcel<P>) -> Result<()>
    where
        P: crate::message::sink::Parcel,
    {
        crate::message::fleetspeak::send_raw(parcel.try_into()?);

        Ok(())
    }
}

#[cfg(test)]
pub mod test {

    use std::any::Any;
    use std::collections::HashMap;

    use super::*;
    use crate::message::sink::Sink;

    /// A session implementation intended to be used in tests.
    ///
    /// Testing actions with normal session objects can be quite hard, since
    /// they communicate with the outside world (through Fleetspeak). Since we
    /// want to keep the tests minimal and not waste resources on unneeded I/O,
    /// using real sessions is not an option.
    ///
    /// Instead, one can use a `Fake` session. It simply accumulates responses
    /// that the action sends and lets the creator inspect them later.
    pub struct FakeSession {
        replies: Vec<Box<dyn Any>>,
        parcels: HashMap<Sink, Vec<Box<dyn Any>>>,
    }

    impl FakeSession {

        /// Constructs a new fake session.
        pub fn new() -> FakeSession {
            FakeSession {
                replies: Vec::new(),
                parcels: std::collections::HashMap::new(),
            }
        }

        /// Yields the number of replies that this session sent so far.
        pub fn reply_count(&self) -> usize {
            self.replies.len()
        }

        /// Retrieves a reply corresponding to the given id.
        ///
        /// The identifier corresponding to the first response is 0, the second
        /// one is 1 and so on.
        ///
        /// This method will panic if a reply with the specified `id` does not
        /// exist or if it exists but has a wrong type.
        pub fn reply<R>(&self, id: usize) -> &R
        where
            R: crate::action::Item + 'static,
        {
            match self.replies().nth(id) {
                Some(reply) => reply,
                None => panic!("no reply #{}", id),
            }
        }

        /// Constructs an iterator over session replies.
        ///
        /// The iterator will panic (but not immediately) if some reply has an
        /// incorrect type.
        pub fn replies<R>(&self) -> impl Iterator<Item = &R>
        where
            R: crate::action::Item + 'static
        {
            self.replies.iter().map(|reply| {
                reply.downcast_ref().expect("unexpected reply type")
            })
        }

        /// Yields the number of parcels sent so far to the specified sink.
        pub fn parcel_count(&self, sink: Sink) -> usize {
            match self.parcels.get(&sink) {
                Some(parcels) => parcels.len(),
                None => 0,
            }
        }

        /// Retrieves a parcel with the given id sent to a particular sink.
        ///
        /// The identifier corresponding to the first parcel to the particular
        /// sink is 0, to the second one (to the same sink) is 1 and so on.
        ///
        /// This method will panic if a reply with the specified `id` to the
        /// given `sink` does not exist or if it exists but has wrong type.
        pub fn parcel<P>(&self, sink: Sink, id: usize) -> &P
        where
            P: crate::message::sink::Parcel + 'static,
        {
            match self.parcels(sink).nth(id) {
                Some(parcel) => parcel,
                None => panic!("no parcel #{} for sink '{:?}'", id, sink),
            }
        }

        /// Constructs an iterator over session parcels for the given sink.
        ///
        /// The iterator will panic (but not immediately) if some parcels has
        /// an incorrect type.
        pub fn parcels<P>(&self, sink: Sink) -> impl Iterator<Item = &P>
        where
            P: crate::message::sink::Parcel + 'static,
        {
            // Since the empty iterator (as defined in the standard library) is
            // a specific type, it cannot be returned in one branch but not in
            // another branch.
            //
            // Instead, we use the fact that `Option` is an iterator and then we
            // squash it with `Iterator::flatten`.
            let parcels = self.parcels.get(&sink).into_iter().flatten();

            parcels.map(move |parcel| match parcel.downcast_ref() {
                Some(parcel) => parcel,
                None => panic!("unexpected parcel type in sink '{:?}'", sink),
            })
        }
    }

    impl Session for FakeSession {

        fn reply<I>(&mut self, item: I) -> Result<()>
        where
            I: crate::action::Item + 'static,
        {
            self.replies.push(Box::new(item));

            Ok(())
        }

        fn send<P>(&mut self, parcel: crate::message::sink::AddressedParcel<P>) -> Result<()>
        where
            P: crate::message::sink::Parcel + 'static,
        {
            let sink = parcel.sink();

            let parcels = self.parcels.entry(sink).or_insert_with(Vec::new);
            parcels.push(Box::new(parcel.unpack()));

            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::message::sink;

    #[test]
    fn test_fake_reply_count() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(()).unwrap();
            session.reply(()).unwrap();
            session.reply(()).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        assert_eq!(session.reply_count(), 3);
    }

    #[test]
    fn test_fake_parcel_count() {

        // TODO: Extend this test with more sinks (once we have some more sinks
        // defined).

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(crate::message::sink::STARTUP.address(())).unwrap();
            session.send(crate::message::sink::STARTUP.address(())).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        assert_eq!(session.parcel_count(crate::message::sink::STARTUP), 2);
    }

    #[test]
    fn test_fake_reply_correct_response() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(StringResponse::from("foo")).unwrap();
            session.reply(StringResponse::from("bar")).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        assert_eq!(session.reply::<StringResponse>(0).0, "foo");
        assert_eq!(session.reply::<StringResponse>(1).0, "bar");
    }

    #[test]
    #[should_panic(expected = "no reply #0")]
    fn test_fake_reply_incorrect_response_id() {

        fn handle<S: Session>(_: &mut S, _: ()) {
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.reply::<()>(0);
    }

    #[test]
    #[should_panic(expected = "unexpected reply type")]
    fn test_fake_reply_incorrect_response_type() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(StringResponse::from("quux")).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.reply::<()>(0);
    }

    #[test]
    fn test_fake_parcel_correct_parcel() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(StringResponse::from("foo")))
                .unwrap();
            session.send(sink::STARTUP.address(StringResponse::from("bar")))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let response_foo = session.parcel::<StringResponse>(sink::STARTUP, 0);
        let response_bar = session.parcel::<StringResponse>(sink::STARTUP, 1);
        assert_eq!(response_foo.0, "foo");
        assert_eq!(response_bar.0, "bar");
    }

    #[test]
    #[should_panic(expected = "no parcel #42")]
    fn test_fake_parcel_incorrect_parcel_id() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(()))
                .unwrap();
            session.send(sink::STARTUP.address(()))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.parcel::<()>(sink::STARTUP, 42);
    }

    #[test]
    #[should_panic(expected = "unexpected parcel type")]
    fn test_fake_parcel_incorrect_parcel_type() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(StringResponse::from("quux")))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        session.parcel::<()>(sink::STARTUP, 0);
    }

    #[test]
    fn test_fake_replies_no_parcels() {

        fn handle<S: Session>(_: &mut S, _: ()) {
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut replies = session.replies::<()>();
        assert_eq!(replies.next(), None);
    }

    #[test]
    fn test_fake_replies_multiple_parcels() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.reply(StringResponse::from("foo")).unwrap();
            session.reply(StringResponse::from("bar")).unwrap();
            session.reply(StringResponse::from("baz")).unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut replies = session.replies::<StringResponse>();
        assert_eq!(replies.next().unwrap().0, "foo");
        assert_eq!(replies.next().unwrap().0, "bar");
        assert_eq!(replies.next().unwrap().0, "baz");
        assert_eq!(replies.next(), None);
    }

    #[test]
    fn test_fake_parcels_no_parcels() {

        fn handle<S: Session>(_: &mut S, _: ()) {
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut parcels = session.parcels::<()>(crate::message::sink::STARTUP);
        assert_eq!(parcels.next(), None);
    }

    #[test]
    fn test_fake_parcels_multiple_parcels() {

        fn handle<S: Session>(session: &mut S, _: ()) {
            session.send(sink::STARTUP.address(StringResponse::from("foo")))
                .unwrap();
            session.send(sink::STARTUP.address(StringResponse::from("bar")))
                .unwrap();
            session.send(sink::STARTUP.address(StringResponse::from("baz")))
                .unwrap();
        }

        let mut session = test::FakeSession::new();
        handle(&mut session, ());

        let mut parcels = session.parcels::<StringResponse>(sink::STARTUP);
        assert_eq!(parcels.next().unwrap().0, "foo");
        assert_eq!(parcels.next().unwrap().0, "bar");
        assert_eq!(parcels.next().unwrap().0, "baz");
        assert_eq!(parcels.next(), None);
    }

    #[derive(Debug, PartialEq, Eq)]
    struct StringResponse(String);

    impl<S: Into<String>> From<S> for StringResponse {

        fn from(string: S) -> StringResponse {
            StringResponse(string.into())
        }
    }

    impl action::Item for StringResponse {

        const RDF_NAME: &'static str = "RDFString";

        type Proto = protobuf::well_known_types::StringValue;

        fn into_proto(self) -> protobuf::well_known_types::StringValue {
            let mut proto = protobuf::well_known_types::StringValue::new();
            proto.set_value(self.0);

            proto
        }
    }

    impl crate::message::sink::Parcel for StringResponse {

        const RDF_NAME: &'static str = "RDFString";

        type Proto = protobuf::well_known_types::StringValue;

        fn into_proto(self) -> protobuf::well_known_types::StringValue {
            let mut proto = protobuf::well_known_types::StringValue::new();
            proto.set_value(self.0);

            proto
        }
    }
}
<|endoftext|>


<commit_before>// Queries.
use crate::bounding_volume::{BoundingSphere, AABB};
use crate::math::{Isometry, Vector};
use na::{self, RealField, Unit};
use crate::query::{PointQuery, RayCast};
use crate::shape::{CompositeShape, ConvexPolyhedron, DeformableShape, FeatureId, SupportMap};
use std::ops::Deref;
use std::sync::Arc;
use downcast_rs::Downcast;

pub trait ShapeClone<N: RealField> {
    fn clone_box(&self) -> Box<Shape<N>> {
        unimplemented!()
    }
}

impl<N: RealField, T: 'static + Shape<N> + Clone> ShapeClone<N> for T {
    fn clone_box(&self) -> Box<Shape<N>> {
        Box::new(self.clone())
    }
}

/// Trait implemented by all shapes supported by ncollide.
///
/// This allows dynamic inspection of the shape capabilities.
pub trait Shape<N: RealField>: Send + Sync + Downcast + ShapeClone<N> {
    /// The AABB of `self` transformed by `m`.
    #[inline]
    fn aabb(&self, m: &Isometry<N>) -> AABB<N>;

    /// The AABB of `self`.
    #[inline]
    fn local_aabb(&self) -> AABB<N> {
        self.aabb(&Isometry::identity())
    }

    /// The bounding sphere of `self` transformed by `m`.
    #[inline]
    fn bounding_sphere(&self, m: &Isometry<N>) -> BoundingSphere<N> {
        let aabb = self.aabb(m);
        BoundingSphere::new(aabb.center(), aabb.half_extents().norm_squared())
    }

    /// The bounding sphere of `self`.
    #[inline]
    fn local_bounding_sphere(&self) -> BoundingSphere<N> {
        let aabb = self.local_aabb();
        BoundingSphere::new(aabb.center(), aabb.half_extents().norm())
    }

    /// Check if if the feature `_feature` of the `i-th` subshape of `self` transformed by `m` has a tangent
    /// cone that contains `dir` at the point `pt`.
    // NOTE: for the moment, we assume the tangent cone is the same for the whole feature.
    #[inline]
    fn tangent_cone_contains_dir(
        &self,
        _feature: FeatureId,
        _m: &Isometry<N>,
        _deformations: Option<&[N]>,
        _dir: &Unit<Vector<N>>,
    ) -> bool;

    /// Returns the id of the subshape containing the specified feature.
    ///
    /// If several subshape contains the same feature, any one is returned.
    fn subshape_containing_feature(&self, _i: FeatureId) -> usize {
        0
    }

    /// The `RayCast` implementation of `self`.
    #[inline]
    fn as_ray_cast(&self) -> Option<&RayCast<N>> {
        None
    }

    /// The `PointQuery` implementation of `self`.
    #[inline]
    fn as_point_query(&self) -> Option<&PointQuery<N>> {
        None
    }

    /// The convex polyhedron representation of `self` if applicable.
    #[inline]
    fn as_convex_polyhedron(&self) -> Option<&ConvexPolyhedron<N>> {
        None
    }

    /// The support mapping of `self` if applicable.
    #[inline]
    fn as_support_map(&self) -> Option<&SupportMap<N>> {
        None
    }

    /// The composite shape representation of `self` if applicable.
    #[inline]
    fn as_composite_shape(&self) -> Option<&CompositeShape<N>> {
        None
    }

    /// The deformable shape representation of `self` if applicable.
    #[inline]
    fn as_deformable_shape(&self) -> Option<&DeformableShape<N>> {
        None
    }

    /// The mutable deformable shape representation of `self` if applicable.
    #[inline]
    fn as_deformable_shape_mut(&mut self) -> Option<&mut DeformableShape<N>> {
        None
    }

    /// Whether `self` uses a convex polyhedron representation.
    #[inline]
    fn is_convex_polyhedron(&self) -> bool {
        self.as_convex_polyhedron().is_some()
    }

    /// Whether `self` uses a support-mapping based representation.
    #[inline]
    fn is_support_map(&self) -> bool {
        self.as_support_map().is_some()
    }

    /// Whether `self` uses a composite shape-based representation.
    #[inline]
    fn is_composite_shape(&self) -> bool {
        self.as_composite_shape().is_some()
    }

    /// Whether `self` uses a composite shape-based representation.
    #[inline]
    fn is_deformable_shape(&self) -> bool {
        self.as_deformable_shape().is_some()
    }
}

impl_downcast!(Shape<N> where N: RealField);

/// Trait for casting shapes to its exact represetation.
impl<N: RealField> Shape<N> {
    /// Tests if this shape has a specific type `T`.
    #[inline]
    pub fn is_shape<T: Shape<N>>(&self) -> bool {
        self.is::<T>()
    }

    /// Performs the cast.
    #[inline]
    pub fn as_shape<T: Shape<N>>(&self) -> Option<&T> {
        self.downcast_ref()
    }
}

impl<N: RealField> Clone for Box<Shape<N>> {
    fn clone(&self) -> Box<Shape<N>> {
        self.clone_box()
    }
}

/// A shared handle to an abstract shape.
///
/// This can be mutated using COW.
#[derive(Clone)]
pub struct ShapeHandle<N: RealField>(Arc<Box<Shape<N>>>);

impl<N: RealField> ShapeHandle<N> {
    /// Creates a sharable shape handle from a shape.
    #[inline]
    pub fn new<S: Shape<N> + Clone>(shape: S) -> ShapeHandle<N> {
        ShapeHandle(Arc::new(Box::new(shape)))
    }

    pub(crate) fn make_mut(&mut self) -> &mut Shape<N> {
        &mut **Arc::make_mut(&mut self.0)
    }
}

impl<N: RealField> AsRef<Shape<N>> for ShapeHandle<N> {
    #[inline]
    fn as_ref(&self) -> &Shape<N> {
        &*self.deref()
    }
}

impl<N: RealField> Deref for ShapeHandle<N> {
    type Target = Shape<N>;

    #[inline]
    fn deref(&self) -> &Shape<N> {
        &**self.0.deref()
    }
}<commit_msg>Fix default `Shape::bounding_sphere` implementation<commit_after>// Queries.
use crate::bounding_volume::{BoundingSphere, AABB};
use crate::math::{Isometry, Vector};
use na::{self, RealField, Unit};
use crate::query::{PointQuery, RayCast};
use crate::shape::{CompositeShape, ConvexPolyhedron, DeformableShape, FeatureId, SupportMap};
use std::ops::Deref;
use std::sync::Arc;
use downcast_rs::Downcast;

pub trait ShapeClone<N: RealField> {
    fn clone_box(&self) -> Box<Shape<N>> {
        unimplemented!()
    }
}

impl<N: RealField, T: 'static + Shape<N> + Clone> ShapeClone<N> for T {
    fn clone_box(&self) -> Box<Shape<N>> {
        Box::new(self.clone())
    }
}

/// Trait implemented by all shapes supported by ncollide.
///
/// This allows dynamic inspection of the shape capabilities.
pub trait Shape<N: RealField>: Send + Sync + Downcast + ShapeClone<N> {
    /// The AABB of `self` transformed by `m`.
    #[inline]
    fn aabb(&self, m: &Isometry<N>) -> AABB<N>;

    /// The AABB of `self`.
    #[inline]
    fn local_aabb(&self) -> AABB<N> {
        self.aabb(&Isometry::identity())
    }

    /// The bounding sphere of `self` transformed by `m`.
    #[inline]
    fn bounding_sphere(&self, m: &Isometry<N>) -> BoundingSphere<N> {
        let aabb = self.aabb(m);
        BoundingSphere::new(aabb.center(), aabb.half_extents().norm())
    }

    /// The bounding sphere of `self`.
    #[inline]
    fn local_bounding_sphere(&self) -> BoundingSphere<N> {
        let aabb = self.local_aabb();
        BoundingSphere::new(aabb.center(), aabb.half_extents().norm())
    }

    /// Check if if the feature `_feature` of the `i-th` subshape of `self` transformed by `m` has a tangent
    /// cone that contains `dir` at the point `pt`.
    // NOTE: for the moment, we assume the tangent cone is the same for the whole feature.
    #[inline]
    fn tangent_cone_contains_dir(
        &self,
        _feature: FeatureId,
        _m: &Isometry<N>,
        _deformations: Option<&[N]>,
        _dir: &Unit<Vector<N>>,
    ) -> bool;

    /// Returns the id of the subshape containing the specified feature.
    ///
    /// If several subshape contains the same feature, any one is returned.
    fn subshape_containing_feature(&self, _i: FeatureId) -> usize {
        0
    }

    /// The `RayCast` implementation of `self`.
    #[inline]
    fn as_ray_cast(&self) -> Option<&RayCast<N>> {
        None
    }

    /// The `PointQuery` implementation of `self`.
    #[inline]
    fn as_point_query(&self) -> Option<&PointQuery<N>> {
        None
    }

    /// The convex polyhedron representation of `self` if applicable.
    #[inline]
    fn as_convex_polyhedron(&self) -> Option<&ConvexPolyhedron<N>> {
        None
    }

    /// The support mapping of `self` if applicable.
    #[inline]
    fn as_support_map(&self) -> Option<&SupportMap<N>> {
        None
    }

    /// The composite shape representation of `self` if applicable.
    #[inline]
    fn as_composite_shape(&self) -> Option<&CompositeShape<N>> {
        None
    }

    /// The deformable shape representation of `self` if applicable.
    #[inline]
    fn as_deformable_shape(&self) -> Option<&DeformableShape<N>> {
        None
    }

    /// The mutable deformable shape representation of `self` if applicable.
    #[inline]
    fn as_deformable_shape_mut(&mut self) -> Option<&mut DeformableShape<N>> {
        None
    }

    /// Whether `self` uses a convex polyhedron representation.
    #[inline]
    fn is_convex_polyhedron(&self) -> bool {
        self.as_convex_polyhedron().is_some()
    }

    /// Whether `self` uses a support-mapping based representation.
    #[inline]
    fn is_support_map(&self) -> bool {
        self.as_support_map().is_some()
    }

    /// Whether `self` uses a composite shape-based representation.
    #[inline]
    fn is_composite_shape(&self) -> bool {
        self.as_composite_shape().is_some()
    }

    /// Whether `self` uses a composite shape-based representation.
    #[inline]
    fn is_deformable_shape(&self) -> bool {
        self.as_deformable_shape().is_some()
    }
}

impl_downcast!(Shape<N> where N: RealField);

/// Trait for casting shapes to its exact represetation.
impl<N: RealField> Shape<N> {
    /// Tests if this shape has a specific type `T`.
    #[inline]
    pub fn is_shape<T: Shape<N>>(&self) -> bool {
        self.is::<T>()
    }

    /// Performs the cast.
    #[inline]
    pub fn as_shape<T: Shape<N>>(&self) -> Option<&T> {
        self.downcast_ref()
    }
}

impl<N: RealField> Clone for Box<Shape<N>> {
    fn clone(&self) -> Box<Shape<N>> {
        self.clone_box()
    }
}

/// A shared handle to an abstract shape.
///
/// This can be mutated using COW.
#[derive(Clone)]
pub struct ShapeHandle<N: RealField>(Arc<Box<Shape<N>>>);

impl<N: RealField> ShapeHandle<N> {
    /// Creates a sharable shape handle from a shape.
    #[inline]
    pub fn new<S: Shape<N> + Clone>(shape: S) -> ShapeHandle<N> {
        ShapeHandle(Arc::new(Box::new(shape)))
    }

    pub(crate) fn make_mut(&mut self) -> &mut Shape<N> {
        &mut **Arc::make_mut(&mut self.0)
    }
}

impl<N: RealField> AsRef<Shape<N>> for ShapeHandle<N> {
    #[inline]
    fn as_ref(&self) -> &Shape<N> {
        &*self.deref()
    }
}

impl<N: RealField> Deref for ShapeHandle<N> {
    type Target = Shape<N>;

    #[inline]
    fn deref(&self) -> &Shape<N> {
        &**self.0.deref()
    }
}<|endoftext|>


<commit_before>// * This file is part of the uutils coreutils package.
// *
// * (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempdir dyld dylib dragonflybsd optgrps libstdbuf

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf;
use std::process::Command;
use tempfile::tempdir;
use tempfile::TempDir;
use uucore::parse_size::parse_size;
use uucore::InvalidEncodingHandling;

static ABOUT: &str =
    "Run COMMAND, with modified buffering operations for its standard streams.\n\n\
     Mandatory arguments to long options are mandatory for short options too.";
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
     This option is invalid with standard input.\n\n\
     If MODE is '0' the corresponding stream will be unbuffered.\n\n\
     Otherwise MODE is a number which may be followed by one of the following:\n\n\
     KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
     In this case the corresponding stream will be fully buffered with the buffer size set to \
     MODE bytes.\n\n\
     NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
     that will override corresponding settings changed by 'stdbuf'.\n\
     Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
     and are thus unaffected by 'stdbuf' settings.\n";

mod options {
    pub const INPUT: &str = "input";
    pub const INPUT_SHORT: &str = "i";
    pub const OUTPUT: &str = "output";
    pub const OUTPUT_SHORT: &str = "o";
    pub const ERROR: &str = "error";
    pub const ERROR_SHORT: &str = "e";
    pub const COMMAND: &str = "command";
}

fn usage() -> String {
    format!("{0} OPTION... COMMAND", uucore::execution_phrase())
}

const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));

enum BufferType {
    Default,
    Line,
    Size(usize),
}

struct ProgramOptions {
    stdin: BufferType,
    stdout: BufferType,
    stderr: BufferType,
}

impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
    type Error = ProgramOptionsError;

    fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
        Ok(ProgramOptions {
            stdin: check_option(matches, options::INPUT)?,
            stdout: check_option(matches, options::OUTPUT)?,
            stderr: check_option(matches, options::ERROR)?,
        })
    }
}

struct ProgramOptionsError(String);

#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd"
))]
fn preload_strings() -> (&'static str, &'static str) {
    ("LD_PRELOAD", "so")
}

#[cfg(target_vendor = "apple")]
fn preload_strings() -> (&'static str, &'static str) {
    ("DYLD_LIBRARY_PATH", "dylib")
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd",
    target_vendor = "apple"
)))]
fn preload_strings() -> (&'static str, &'static str) {
    crash!(1, "Command not supported for this operating system!")
}

fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
    match matches.value_of(name) {
        Some(value) => match value {
            "L" => {
                if name == options::INPUT {
                    Err(ProgramOptionsError(
                        "line buffering stdin is meaningless".to_string(),
                    ))
                } else {
                    Ok(BufferType::Line)
                }
            }
            x => parse_size(x).map_or_else(
                |e| crash!(125, "invalid mode {}", e),
                |m| Ok(BufferType::Size(m)),
            ),
        },
        None => Ok(BufferType::Default),
    }
}

fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
    match buffer_type {
        BufferType::Size(m) => {
            command.env(buffer_name, m.to_string());
        }
        BufferType::Line => {
            command.env(buffer_name, "L");
        }
        BufferType::Default => {}
    }
}

fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
    let (preload, extension) = preload_strings();
    let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);

    let mut file = File::create(&inject_path)?;
    file.write_all(STDBUF_INJECT)?;

    Ok((preload.to_owned(), inject_path))
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let options = ProgramOptions::try_from(&matches).unwrap_or_else(|e| {
        crash!(
            125,
            "{}\nTry '{} --help' for more information.",
            e.0,
            uucore::execution_phrase()
        )
    });

    let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap();
    let mut command = Command::new(command_values.next().unwrap());
    let command_params: Vec<&str> = command_values.collect();

    let mut tmp_dir = tempdir().unwrap();
    let (preload_env, libstdbuf) = crash_if_err!(1, get_preload_env(&mut tmp_dir));
    command.env(preload_env, libstdbuf);
    set_command_env(&mut command, "_STDBUF_I", options.stdin);
    set_command_env(&mut command, "_STDBUF_O", options.stdout);
    set_command_env(&mut command, "_STDBUF_E", options.stderr);
    command.args(command_params);

    let mut process = match command.spawn() {
        Ok(p) => p,
        Err(e) => crash!(1, "failed to execute process: {}", e),
    };
    match process.wait() {
        Ok(status) => match status.code() {
            Some(i) => i,
            None => crash!(1, "process killed by signal {}", status.signal().unwrap()),
        },
        Err(e) => crash!(1, "{}", e),
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help(LONG_HELP)
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::with_name(options::INPUT)
                .long(options::INPUT)
                .short(options::INPUT_SHORT)
                .help("adjust standard input stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::OUTPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::OUTPUT)
                .long(options::OUTPUT)
                .short(options::OUTPUT_SHORT)
                .help("adjust standard output stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::ERROR)
                .long(options::ERROR)
                .short(options::ERROR_SHORT)
                .help("adjust standard error stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::OUTPUT]),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .multiple(true)
                .takes_value(true)
                .hidden(true)
                .required(true),
        )
}
<commit_msg>stdbuf: return UResult from uumain() function<commit_after>// * This file is part of the uutils coreutils package.
// *
// * (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempdir dyld dylib dragonflybsd optgrps libstdbuf

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf;
use std::process::Command;
use tempfile::tempdir;
use tempfile::TempDir;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::parse_size::parse_size;
use uucore::InvalidEncodingHandling;

static ABOUT: &str =
    "Run COMMAND, with modified buffering operations for its standard streams.\n\n\
     Mandatory arguments to long options are mandatory for short options too.";
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
     This option is invalid with standard input.\n\n\
     If MODE is '0' the corresponding stream will be unbuffered.\n\n\
     Otherwise MODE is a number which may be followed by one of the following:\n\n\
     KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
     In this case the corresponding stream will be fully buffered with the buffer size set to \
     MODE bytes.\n\n\
     NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
     that will override corresponding settings changed by 'stdbuf'.\n\
     Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
     and are thus unaffected by 'stdbuf' settings.\n";

mod options {
    pub const INPUT: &str = "input";
    pub const INPUT_SHORT: &str = "i";
    pub const OUTPUT: &str = "output";
    pub const OUTPUT_SHORT: &str = "o";
    pub const ERROR: &str = "error";
    pub const ERROR_SHORT: &str = "e";
    pub const COMMAND: &str = "command";
}

fn usage() -> String {
    format!("{0} OPTION... COMMAND", uucore::execution_phrase())
}

const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));

enum BufferType {
    Default,
    Line,
    Size(usize),
}

struct ProgramOptions {
    stdin: BufferType,
    stdout: BufferType,
    stderr: BufferType,
}

impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
    type Error = ProgramOptionsError;

    fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
        Ok(ProgramOptions {
            stdin: check_option(matches, options::INPUT)?,
            stdout: check_option(matches, options::OUTPUT)?,
            stderr: check_option(matches, options::ERROR)?,
        })
    }
}

struct ProgramOptionsError(String);

#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd"
))]
fn preload_strings() -> (&'static str, &'static str) {
    ("LD_PRELOAD", "so")
}

#[cfg(target_vendor = "apple")]
fn preload_strings() -> (&'static str, &'static str) {
    ("DYLD_LIBRARY_PATH", "dylib")
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd",
    target_vendor = "apple"
)))]
fn preload_strings() -> (&'static str, &'static str) {
    crash!(1, "Command not supported for this operating system!")
}

fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
    match matches.value_of(name) {
        Some(value) => match value {
            "L" => {
                if name == options::INPUT {
                    Err(ProgramOptionsError(
                        "line buffering stdin is meaningless".to_string(),
                    ))
                } else {
                    Ok(BufferType::Line)
                }
            }
            x => parse_size(x).map_or_else(
                |e| crash!(125, "invalid mode {}", e),
                |m| Ok(BufferType::Size(m)),
            ),
        },
        None => Ok(BufferType::Default),
    }
}

fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
    match buffer_type {
        BufferType::Size(m) => {
            command.env(buffer_name, m.to_string());
        }
        BufferType::Line => {
            command.env(buffer_name, "L");
        }
        BufferType::Default => {}
    }
}

fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
    let (preload, extension) = preload_strings();
    let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);

    let mut file = File::create(&inject_path)?;
    file.write_all(STDBUF_INJECT)?;

    Ok((preload.to_owned(), inject_path))
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let options = ProgramOptions::try_from(&matches).map_err(|e| UUsageError::new(125, e.0))?;

    let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap();
    let mut command = Command::new(command_values.next().unwrap());
    let command_params: Vec<&str> = command_values.collect();

    let mut tmp_dir = tempdir().unwrap();
    let (preload_env, libstdbuf) = get_preload_env(&mut tmp_dir).map_err_context(String::new)?;
    command.env(preload_env, libstdbuf);
    set_command_env(&mut command, "_STDBUF_I", options.stdin);
    set_command_env(&mut command, "_STDBUF_O", options.stdout);
    set_command_env(&mut command, "_STDBUF_E", options.stderr);
    command.args(command_params);

    let mut process = command
        .spawn()
        .map_err_context(|| "failed to execute process".to_string())?;
    let status = process.wait().map_err_context(String::new)?;
    match status.code() {
        Some(i) => {
            if i == 0 {
                Ok(())
            } else {
                Err(i.into())
            }
        }
        None => Err(USimpleError::new(
            1,
            format!("process killed by signal {}", status.signal().unwrap()),
        )),
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help(LONG_HELP)
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::with_name(options::INPUT)
                .long(options::INPUT)
                .short(options::INPUT_SHORT)
                .help("adjust standard input stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::OUTPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::OUTPUT)
                .long(options::OUTPUT)
                .short(options::OUTPUT_SHORT)
                .help("adjust standard output stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::ERROR)
                .long(options::ERROR)
                .short(options::ERROR_SHORT)
                .help("adjust standard error stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::OUTPUT]),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .multiple(true)
                .takes_value(true)
                .hidden(true)
                .required(true),
        )
}
<|endoftext|>


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

//! A module that provides a high-level interface for working with stored data.
//!
//! # Database
//!
//! A [`Database`] is the underlying type for working with stored data. Internally `Database` is
//! a key-value storage with reading isolation and atomic writing. This is an embedded database,
//! which means that only the Exonum process can access it. You can interact with a `Database` from
//! different threads by cloning its instance.
//!
//! Exonum provides two types of database: [`RocksDB`] and [`MemoryDB`]. Also, you can make your
//! own implementations of [`Database`] trait. See its documentation for more.
//!
//! # Snapshot and Fork
//!
//! There is no way to directly interact with data in the database.
//!
//! If you need to read the data, you can create a [`Snapshot`] using method [`snapshot`][1]
//! of the `Database` instance. Snapshots provide a read isolation, so you are guaranteed to work
//! with consistent values even if the data in the database changes between reads. `Snapshot`
//! provides all the necessary methods for reading data from the database, so `&Storage`
//! is used as a storage view for creating read-only [indices] representation.
//!
//! If you need to make any changes to the data, you need to create a [`Fork`] using method
//! [`fork`][2] of the `Database` instance. As well as `Snapshot`, `Fork` provides read isolation
//! and also allows you to create a sequence of changes to the database that are specified
//! as [`Patch`]. Later you can atomically merge a patch into the database using method [`merge`].
//!
//! # `StorageKey` and `StorageValue` traits
//!
//! If you need to use your own data types as keys or values in storage, you need to implement
//! the [`StorageKey`] or [`StorageValue`] traits respectively. These traits are already
//! implemented for most standard types. See their documentation for more.
//!
//! # Indices
//!
//! Indexes are structures that represent high-level data collections that are stored in a database.
//! This concept is similar to tables in relational databases. All indexes are stored in a single
//! key-value table, but internally the keys of each index will be prefixed with the unique index
//! prefixes, so the data of the different indexes do not conflict with each other. The interfaces
//! of the indexes are similar to ordinary collections (like arrays, maps and sets), but their data
//! is located in persistent storage.
//!
//! Merklized indices support the ability to generate the cryptographic proofs about the inclusion
//! of data in them. Having such proof external client may verify locally that received data was
//! really authorized by the validators without having to replicate the entire blockchain contents.
//!
//! Exonum implements the following indices:
//!
//! - [`Entry`] is a specific index that stores only one value. Useful for global values, such as
//! configuration. Similar to combination of [`Box`] and [`Option`] from standard library.
//! - [`ListIndex`] is a list of items stored in sequential order. Similar to [`Vec`] collection
//! from standard library.
//! - [`SparseListIndex`] is a list of items stored in sequential order. Similar to `ListIndex`
//! but it may contain indexes without elements.
//! - [`MapIndex`] is a map of keys and values. Similar to [`BTreeMap`] collection from standard
//! library.
//! - [`ProofListIndex`] is a Merklized version of `ListIndex` which supports the cryptographic
//! proofs of existence and is implemented as a Merkle tree.
//! - [`ProofMapIndex`] is a Merklized version of `MapIndex` which supports the cryptographic
//! proofs of existence and is implemented as a Merkle Patricia tree.
//! - [`KeySetIndex`] and [`ValueSetIndex`] is a set of items, similar to [`BTreeSet`] and
//! [`HashSet`] from standard library.
//!
//! If necessary, it's possible to implement new specific types of indexes. To do this, you need
//! to create a wrapper over [`BaseIndex`] structure. See their documentation for more.
//!
//! [`Database`]: trait.Database.html
//! [`RocksDB`]: struct.RocksDB.html
//! [`MemoryDB`]: struct.MemoryDB.html
//! [`Snapshot`]: trait.Snapshot.html
//! [`Fork`]: struct.Fork.html
//! [`Patch`]: type.Patch.html
//! [1]: trait.Database.html#tymethod.snapshot
//! [2]: trait.Database.html#method.fork
//! [`merge`]: trait.Database.html#tymethod.merge
//! [`StorageKey`]: trait.StorageKey.html
//! [`StorageValue`]: trait.StorageValue.html
//! [`Entry`]: struct.Entry.html
//! [`ListIndex`]: list_index/struct.ListIndex.html
//! [`SparseListIndex`]: sparse_list_index/struct.SparseListIndex.html
//! [`MapIndex`]: map_index/struct.MapIndex.html
//! [`ProofListIndex`]: proof_list_index/struct.ProofListIndex.html
//! [`ProofMapIndex`]: proof_map_index/struct.ProofMapIndex.html
//! [`KeySetIndex`]: key_set_index/struct.KeySetIndex.html
//! [`ValueSetIndex`]: value_set_index/struct.ValueSetIndex.html
//! [`BaseIndex`]: base_index/struct.BaseIndex.html
//! [indices]: #indices
//! [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
//! [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
//! [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
//! [`BTreeSet`]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html
//! [`HashSet`]: https://doc.rust-lang.org/std/collections/struct.HashSet.html

pub use self::error::Error;
pub use self::db::{Database, Snapshot, Fork, Patch, Changes, Iterator, Iter};

pub use self::rocksdb::{RocksDB, RocksDBOptions};
pub use self::memorydb::MemoryDB;

pub use self::keys::StorageKey;
pub use self::values::StorageValue;

pub use self::entry::Entry;

pub use self::base_index::{BaseIndex, BaseIndexIter};
pub use self::map_index::MapIndex;
pub use self::list_index::ListIndex;
pub use self::sparse_list_index::SparseListIndex;
pub use self::key_set_index::KeySetIndex;
pub use self::value_set_index::ValueSetIndex;
pub use self::proof_list_index::{ProofListIndex, ListProof};
pub use self::proof_map_index::{ProofMapIndex, MapProof};

/// A specialized `Result` type for I/O operations with storage.
pub type Result<T> = ::std::result::Result<T, Error>;

mod error;
mod db;
mod rocksdb;
mod memorydb;

mod keys;
mod values;

mod entry;

pub mod base_index;

pub mod map_index;
pub mod list_index;
pub mod sparse_list_index;
pub mod key_set_index;
pub mod value_set_index;
pub mod proof_list_index;
pub mod proof_map_index;

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

//! A module that provides a high-level interface for working with stored data.
//!
//! # Database
//!
//! A [`Database`] is the underlying type for working with stored data. Internally `Database` is
//! a key-value storage with reading isolation and atomic writing. This is an embedded database,
//! which means that only the Exonum process can access it. You can interact with a `Database` from
//! different threads by cloning its instance.
//!
//! Exonum provides two types of database: [`RocksDB`] and [`MemoryDB`]. Also, you can make your
//! own implementations of [`Database`] trait. See its documentation for more.
//!
//! # Snapshot and Fork
//!
//! There is no way to directly interact with data in the database.
//!
//! If you need to read the data, you can create a [`Snapshot`] using method [`snapshot`][1]
//! of the `Database` instance. Snapshots provide a read isolation, so you are guaranteed to work
//! with consistent values even if the data in the database changes between reads. `Snapshot`
//! provides all the necessary methods for reading data from the database, so `&Storage`
//! is used as a storage view for creating read-only [indices] representation.
//!
//! If you need to make any changes to the data, you need to create a [`Fork`] using method
//! [`fork`][2] of the `Database` instance. As well as `Snapshot`, `Fork` provides read isolation
//! and also allows you to create a sequence of changes to the database that are specified
//! as [`Patch`]. Later you can atomically merge a patch into the database using method [`merge`].
//!
//! # `StorageKey` and `StorageValue` traits
//!
//! If you need to use your own data types as keys or values in storage, you need to implement
//! the [`StorageKey`] or [`StorageValue`] traits respectively. These traits are already
//! implemented for most standard types. See their documentation for more.
//!
//! # Indices
//!
//! Indexes are structures that represent high-level data collections that are stored in a database.
//! This concept is similar to tables in relational databases. All indexes are stored in a single
//! key-value table, but internally the keys of each index will be prefixed with the unique index
//! prefixes, so the data of the different indexes do not conflict with each other. The interfaces
//! of the indexes are similar to ordinary collections (like arrays, maps and sets), but their data
//! is located in persistent storage.
//!
//! Merklized indices support the ability to generate the cryptographic proofs about the inclusion
//! of data in them. Having such proof external client may verify locally that received data was
//! really authorized by the validators without having to replicate the entire blockchain contents.
//!
//! Exonum implements the following indices:
//!
//! - [`Entry`] is a specific index that stores only one value. Useful for global values, such as
//! configuration. Similar to combination of [`Box`] and [`Option`] from standard library.
//! - [`ListIndex`] is a list of items stored in sequential order. Similar to [`Vec`] collection
//! from standard library.
//! - [`SparseListIndex`] is a list of items stored in sequential order. Similar to `ListIndex`
//! but it may contain indexes without elements.
//! - [`MapIndex`] is a map of keys and values. Similar to [`BTreeMap`] collection from standard
//! library.
//! - [`ProofListIndex`] is a Merklized version of `ListIndex` which supports the cryptographic
//! proofs of existence and is implemented as a Merkle tree.
//! - [`ProofMapIndex`] is a Merklized version of `MapIndex` which supports the cryptographic
//! proofs of existence and is implemented as a Merkle Patricia tree.
//! - [`KeySetIndex`] and [`ValueSetIndex`] is a set of items, similar to [`BTreeSet`] and
//! [`HashSet`] from standard library.
//!
//! If necessary, it's possible to implement new specific types of indexes. To do this, you need
//! to create a wrapper over [`BaseIndex`] structure. See their documentation for more.
//!
//! [`Database`]: trait.Database.html
//! [`RocksDB`]: struct.RocksDB.html
//! [`MemoryDB`]: struct.MemoryDB.html
//! [`Snapshot`]: trait.Snapshot.html
//! [`Fork`]: struct.Fork.html
//! [`Patch`]: type.Patch.html
//! [1]: trait.Database.html#tymethod.snapshot
//! [2]: trait.Database.html#method.fork
//! [`merge`]: trait.Database.html#tymethod.merge
//! [`StorageKey`]: trait.StorageKey.html
//! [`StorageValue`]: trait.StorageValue.html
//! [`Entry`]: struct.Entry.html
//! [`ListIndex`]: list_index/struct.ListIndex.html
//! [`SparseListIndex`]: sparse_list_index/struct.SparseListIndex.html
//! [`MapIndex`]: map_index/struct.MapIndex.html
//! [`ProofListIndex`]: proof_list_index/struct.ProofListIndex.html
//! [`ProofMapIndex`]: proof_map_index/struct.ProofMapIndex.html
//! [`KeySetIndex`]: key_set_index/struct.KeySetIndex.html
//! [`ValueSetIndex`]: value_set_index/struct.ValueSetIndex.html
//! [`BaseIndex`]: base_index/struct.BaseIndex.html
//! [indices]: #indices
//! [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
//! [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
//! [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
//! [`BTreeSet`]: https://doc.rust-lang.org/std/collections/struct.BTreeSet.html
//! [`HashSet`]: https://doc.rust-lang.org/std/collections/struct.HashSet.html

pub use self::error::Error;
pub use self::db::{Database, Snapshot, Fork, Patch, Change, Changes, Iterator, Iter};

pub use self::rocksdb::{RocksDB, RocksDBOptions};
pub use self::memorydb::MemoryDB;

pub use self::keys::StorageKey;
pub use self::values::StorageValue;

pub use self::entry::Entry;

pub use self::base_index::{BaseIndex, BaseIndexIter};
pub use self::map_index::MapIndex;
pub use self::list_index::ListIndex;
pub use self::sparse_list_index::SparseListIndex;
pub use self::key_set_index::KeySetIndex;
pub use self::value_set_index::ValueSetIndex;
pub use self::proof_list_index::{ProofListIndex, ListProof};
pub use self::proof_map_index::{ProofMapIndex, MapProof};

/// A specialized `Result` type for I/O operations with storage.
pub type Result<T> = ::std::result::Result<T, Error>;

mod error;
mod db;
mod rocksdb;
mod memorydb;

mod keys;
mod values;

mod entry;

pub mod base_index;

pub mod map_index;
pub mod list_index;
pub mod sparse_list_index;
pub mod key_set_index;
pub mod value_set_index;
pub mod proof_list_index;
pub mod proof_map_index;

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>
use protos::{StudyQuestion, StudyQuestions, StudyQuestionAnswer};
use std::error::Error;

pub struct UserStudyQuestions {
    pub replay_filename: String,
    pub answer_lines: Vec<String>,
}
impl UserStudyQuestions {
    pub fn get_questions(&mut self) -> Result<Option<StudyQuestions>, Box<Error>> {
        use scaii_core;
        use std::fs::File;
        use std::io::prelude::*;
        use std::io::BufReader;
        println!("Looking for question file for replay {}", self.replay_filename);
        let mut questionfile_path = scaii_core::get_default_replay_dir()?;
        let question_filename = get_question_filename_for_replay(&self.replay_filename);
        println!("question_filename is {}", question_filename);
        questionfile_path.push(question_filename.as_str());
        println!("path is {:?}", questionfile_path);

        let f = File::open(questionfile_path)?;
        let reader = BufReader::new(f);

        let mut questions_vec: Vec<StudyQuestion> = Vec::new();
        for line in reader.lines() {
            let question_string = line.unwrap();
            println!("STUDY QUESTION: {}", question_string);
            let question: StudyQuestion = self.get_study_question(&question_string)?;
            questions_vec.push(question);
        }
        let study_questions = StudyQuestions {
            study_questions: questions_vec,
        };
        Ok(Some(study_questions))
    }

    fn get_study_question(&mut self, line : &String) -> Result<StudyQuestion, Box<Error>> {
        use super::ReplayError;
        let line_parts_iterator = line.split(";");
        let vec: Vec<&str> = line_parts_iterator.collect();
        if vec.len() < 2 {
            return Err(Box::new(ReplayError::new(&format!("study question line needs at least two fields: <step>;<question>;<answer1>..."))));
        }
        let step: String = vec[0].to_string();
        let question : String = vec[1].to_string();
        let mut answer_vec : Vec<String> = Vec::new();
        for x in 2..vec.len() {
            answer_vec.push(vec[x].to_string());
        }
        Ok(StudyQuestion {
            step: step,
            question: question,
            answers: answer_vec,
        })
    }

    pub fn persist_study_question_answer(&mut self, sqa: StudyQuestionAnswer) -> Result<(), Box<Error>> {
        use std::fs::{File, OpenOptions,remove_file};
        use std::io::Write;
        use scaii_core;
        println!("{:?}", &sqa);
        let user_id = sqa.user_id;
        let answer = sqa.answer;
        let question = sqa.question;
        let question_number = sqa.question_number;
        let step = sqa.step;
        let output_line = format!("{};{};{};{}", question_number, step, question, answer);
        self.answer_lines.push(output_line);

        if step == "summary".to_string() {
            let mut answerfile_path = scaii_core::get_default_replay_dir()?;
            // append to file called replayX_answers_userID_treatmentID.txt
            // or ??? replayX_answers_userID_sessionID_treatmentID.txt
            let filename = get_answer_filename_for_replay_and_user(&self.replay_filename, &user_id);
            answerfile_path.push(filename);
            if answerfile_path.exists(){
                remove_file(&answerfile_path)?;
            }
            println!("answerfile_path is {:?}", answerfile_path);
            File::create(&answerfile_path)?;
            let mut file = OpenOptions::new().append(true).open(answerfile_path).unwrap();
            for line in &self.answer_lines {
                file.write_all(line.as_bytes())?;
                file.write_all("\n".to_string().as_bytes())?;
                //writeln!(file, format!("{}",line))?;
            }
        }

        Ok(())
    }
}

fn get_question_filename_for_replay(filename : &String) -> String {
    let filename_parts_iterator = filename.split(".");
    let vec: Vec<&str> = filename_parts_iterator.collect();
    format!("{}_questions.txt", vec[0])
}

// replayX_answers_userID_treatmentID.txt
fn get_answer_filename_for_replay_and_user(filename : &String, user_id: &String) -> String {
    let filename_parts_iterator = filename.split(".");
    let vec: Vec<&str> = filename_parts_iterator.collect();
    format!("{}_answers_{}_treatment1.txt", vec[0], user_id)
}

<commit_msg>if no question file present, don't complain<commit_after>
use protos::{StudyQuestion, StudyQuestions, StudyQuestionAnswer};
use std::error::Error;

pub struct UserStudyQuestions {
    pub replay_filename: String,
    pub answer_lines: Vec<String>,
}
impl UserStudyQuestions {
    pub fn get_questions(&mut self) -> Result<Option<StudyQuestions>, Box<Error>> {
        use scaii_core;
        use std::fs::File;
        use std::io::prelude::*;
        use std::io::BufReader;
        println!("Looking for question file for replay {}", self.replay_filename);
        let mut questionfile_path = scaii_core::get_default_replay_dir()?;
        let question_filename = get_question_filename_for_replay(&self.replay_filename);
        println!("question_filename is {}", question_filename);
        questionfile_path.push(question_filename.as_str());
        println!("path is {:?}", questionfile_path);
        if !questionfile_path.exists() {
            return Ok(None);
        }
        let f = File::open(questionfile_path)?;
        let reader = BufReader::new(f);

        let mut questions_vec: Vec<StudyQuestion> = Vec::new();
        for line in reader.lines() {
            let question_string = line.unwrap();
            println!("STUDY QUESTION: {}", question_string);
            let question: StudyQuestion = self.get_study_question(&question_string)?;
            questions_vec.push(question);
        }
        let study_questions = StudyQuestions {
            study_questions: questions_vec,
        };
        Ok(Some(study_questions))
    }

    fn get_study_question(&mut self, line : &String) -> Result<StudyQuestion, Box<Error>> {
        use super::ReplayError;
        let line_parts_iterator = line.split(";");
        let vec: Vec<&str> = line_parts_iterator.collect();
        if vec.len() < 2 {
            return Err(Box::new(ReplayError::new(&format!("study question line needs at least two fields: <step>;<question>;<answer1>..."))));
        }
        let step: String = vec[0].to_string();
        let question : String = vec[1].to_string();
        let mut answer_vec : Vec<String> = Vec::new();
        for x in 2..vec.len() {
            answer_vec.push(vec[x].to_string());
        }
        Ok(StudyQuestion {
            step: step,
            question: question,
            answers: answer_vec,
        })
    }

    pub fn persist_study_question_answer(&mut self, sqa: StudyQuestionAnswer) -> Result<(), Box<Error>> {
        use std::fs::{File, OpenOptions,remove_file};
        use std::io::Write;
        use scaii_core;
        println!("{:?}", &sqa);
        let user_id = sqa.user_id;
        let answer = sqa.answer;
        let question = sqa.question;
        let question_number = sqa.question_number;
        let step = sqa.step;
        let output_line = format!("{};{};{};{}", question_number, step, question, answer);
        self.answer_lines.push(output_line);

        if step == "summary".to_string() {
            let mut answerfile_path = scaii_core::get_default_replay_dir()?;
            // append to file called replayX_answers_userID_treatmentID.txt
            // or ??? replayX_answers_userID_sessionID_treatmentID.txt
            let filename = get_answer_filename_for_replay_and_user(&self.replay_filename, &user_id);
            answerfile_path.push(filename);
            if answerfile_path.exists(){
                remove_file(&answerfile_path)?;
            }
            println!("answerfile_path is {:?}", answerfile_path);
            File::create(&answerfile_path)?;
            let mut file = OpenOptions::new().append(true).open(answerfile_path).unwrap();
            for line in &self.answer_lines {
                file.write_all(line.as_bytes())?;
                file.write_all("\n".to_string().as_bytes())?;
                //writeln!(file, format!("{}",line))?;
            }
        }

        Ok(())
    }
}

fn get_question_filename_for_replay(filename : &String) -> String {
    let filename_parts_iterator = filename.split(".");
    let vec: Vec<&str> = filename_parts_iterator.collect();
    format!("{}_questions.txt", vec[0])
}

// replayX_answers_userID_treatmentID.txt
fn get_answer_filename_for_replay_and_user(filename : &String, user_id: &String) -> String {
    let filename_parts_iterator = filename.split(".");
    let vec: Vec<&str> = filename_parts_iterator.collect();
    format!("{}_answers_{}_treatment1.txt", vec[0], user_id)
}

<|endoftext|>


<commit_before>use serde::Serialize;
use serde_json::Value;

use std::cmp;

use exonum::storage::{Map, List, Result as StorageResult};
use exonum::crypto::Hash;
use exonum::blockchain::{Schema, Blockchain};

use api::HexField;

pub struct BlockchainExplorer<'a> {
    blockchain: &'a Blockchain,
}

pub trait TransactionInfo: Serialize {}

#[derive(Debug, Serialize)]
pub struct BlockInfo {
    height: u64,
    proposer: u32,

    hash: HexField<Hash>,
    state_hash: HexField<Hash>,
    tx_hash: HexField<Hash>,
    tx_count: u64,
    precommits_count: u64,
    txs: Option<Vec<Value>>,
}

impl<'a> BlockchainExplorer<'a> {
    pub fn new(blockchain: &'a Blockchain) -> BlockchainExplorer {
        BlockchainExplorer { blockchain: blockchain }
    }

    pub fn tx_info(&self, tx_hash: &Hash) -> StorageResult<Option<Value>> {
        let tx = Schema::new(&self.blockchain.view())
            .transactions()
            .get(tx_hash)?;
        match tx {
            Some(raw_tx) => {
                Ok(self.blockchain
                       .tx_from_raw(raw_tx)
                       .and_then(|t| Some(t.info())))
            }
            None => Ok(None),
        }

    }

    pub fn block_info(&self,
                      block_hash: &Hash,
                      full_info: bool)
                      -> StorageResult<Option<BlockInfo>> {
        let b = self.blockchain.clone();
        let view = b.view();
        let schema = Schema::new(&view);
        let block = schema.blocks().get(block_hash)?;
        if let Some(block) = block {
            let height = block.height();
            let (txs, txs_count) = {
                if full_info {
                    let txs = self.block_txs(block.height())?;
                    let txs_count = txs.len() as u64;
                    (Some(txs), txs_count)
                } else {
                    (None, schema.block_txs(height).len()? as u64)
                }
            };

            let config = schema.get_actual_configuration()?;
            // TODO Find more common solution
            // FIXME this code was copied from state.rs
            let proposer = ((height + block.propose_round() as u64) %
                            (config.validators.len() as u64)) as u32;

            let precommits_count = schema.precommits(block_hash).len()? as u64;
            let info = BlockInfo {
                height: height,
                proposer: proposer,

                hash: HexField(*block_hash),
                state_hash: HexField(*block.state_hash()),
                tx_hash: HexField(*block.tx_hash()),
                tx_count: txs_count,
                precommits_count: precommits_count,
                txs: txs,
            };
            Ok(Some(info))
        } else {
            Ok(None)
        }
    }

    fn block_txs(&self, height: u64) -> StorageResult<Vec<Value>> {
        let b = self.blockchain.clone();
        let view = b.view();
        let schema = Schema::new(&view);
        let txs = schema.block_txs(height);
        let tx_count = txs.len()?;

        let mut v = Vec::new();
        for i in 0..tx_count {
            if let Some(tx_hash) = txs.get(i)? {
                if let Some(tx_info) = self.tx_info(&tx_hash)? {
                    v.push(tx_info);
                }
            }
        }
        Ok(v)
    }

    pub fn block_info_with_height(&self, height: u64) -> StorageResult<Option<BlockInfo>> {
        if let Some(block_hash) = Schema::new(&self.blockchain.view()).block_hash_by_height(height)? {
            self.block_info(&block_hash, true)
        } else {
            Ok(None)
        }
    }

    pub fn blocks_range(&self, count: u64, from: Option<u64>) -> StorageResult<Vec<BlockInfo>> {
        let b = self.blockchain.clone();
        let view = b.view();
        let schema = Schema::new(&view);
        let hashes = schema.block_hashes_by_height();

        let max_len = hashes.len()?;
        let to = from.map(|x| cmp::min(x, max_len)).unwrap_or(max_len);
        let from = to.checked_sub(count).unwrap_or(0);

        let mut v = Vec::new();
        for height in (from..to).rev() {
            if let Some(ref h) = hashes.get(height)? {
                if let Some(block_info) = self.block_info(h, false)? {
                    v.push(block_info);
                }
            }
        }
        Ok(v)
    }
}
<commit_msg>Fix formatting after merge<commit_after>use serde::Serialize;
use serde_json::Value;

use std::cmp;

use exonum::storage::{Map, List, Result as StorageResult};
use exonum::crypto::Hash;
use exonum::blockchain::{Schema, Blockchain};

use api::HexField;

pub struct BlockchainExplorer<'a> {
    blockchain: &'a Blockchain,
}

pub trait TransactionInfo: Serialize {}

#[derive(Debug, Serialize)]
pub struct BlockInfo {
    height: u64,
    proposer: u32,

    hash: HexField<Hash>,
    state_hash: HexField<Hash>,
    tx_hash: HexField<Hash>,
    tx_count: u64,
    precommits_count: u64,
    txs: Option<Vec<Value>>,
}

impl<'a> BlockchainExplorer<'a> {
    pub fn new(blockchain: &'a Blockchain) -> BlockchainExplorer {
        BlockchainExplorer { blockchain: blockchain }
    }

    pub fn tx_info(&self, tx_hash: &Hash) -> StorageResult<Option<Value>> {
        let tx = Schema::new(&self.blockchain.view())
            .transactions()
            .get(tx_hash)?;
        match tx {
            Some(raw_tx) => {
                Ok(self.blockchain
                       .tx_from_raw(raw_tx)
                       .and_then(|t| Some(t.info())))
            }
            None => Ok(None),
        }

    }

    pub fn block_info(&self,
                      block_hash: &Hash,
                      full_info: bool)
                      -> StorageResult<Option<BlockInfo>> {
        let b = self.blockchain.clone();
        let view = b.view();
        let schema = Schema::new(&view);
        let block = schema.blocks().get(block_hash)?;
        if let Some(block) = block {
            let height = block.height();
            let (txs, txs_count) = {
                if full_info {
                    let txs = self.block_txs(block.height())?;
                    let txs_count = txs.len() as u64;
                    (Some(txs), txs_count)
                } else {
                    (None, schema.block_txs(height).len()? as u64)
                }
            };

            let config = schema.get_actual_configuration()?;
            // TODO Find more common solution
            // FIXME this code was copied from state.rs
            let proposer = ((height + block.propose_round() as u64) %
                            (config.validators.len() as u64)) as u32;

            let precommits_count = schema.precommits(block_hash).len()? as u64;
            let info = BlockInfo {
                height: height,
                proposer: proposer,

                hash: HexField(*block_hash),
                state_hash: HexField(*block.state_hash()),
                tx_hash: HexField(*block.tx_hash()),
                tx_count: txs_count,
                precommits_count: precommits_count,
                txs: txs,
            };
            Ok(Some(info))
        } else {
            Ok(None)
        }
    }

    fn block_txs(&self, height: u64) -> StorageResult<Vec<Value>> {
        let b = self.blockchain.clone();
        let view = b.view();
        let schema = Schema::new(&view);
        let txs = schema.block_txs(height);
        let tx_count = txs.len()?;

        let mut v = Vec::new();
        for i in 0..tx_count {
            if let Some(tx_hash) = txs.get(i)? {
                if let Some(tx_info) = self.tx_info(&tx_hash)? {
                    v.push(tx_info);
                }
            }
        }
        Ok(v)
    }

    pub fn block_info_with_height(&self, height: u64) -> StorageResult<Option<BlockInfo>> {
        if let Some(block_hash) = Schema::new(&self.blockchain.view())
               .block_hash_by_height(height)? {
            self.block_info(&block_hash, true)
        } else {
            Ok(None)
        }
    }

    pub fn blocks_range(&self, count: u64, from: Option<u64>) -> StorageResult<Vec<BlockInfo>> {
        let b = self.blockchain.clone();
        let view = b.view();
        let schema = Schema::new(&view);
        let hashes = schema.block_hashes_by_height();

        let max_len = hashes.len()?;
        let to = from.map(|x| cmp::min(x, max_len)).unwrap_or(max_len);
        let from = to.checked_sub(count).unwrap_or(0);

        let mut v = Vec::new();
        for height in (from..to).rev() {
            if let Some(ref h) = hashes.get(height)? {
                if let Some(block_info) = self.block_info(h, false)? {
                    v.push(block_info);
                }
            }
        }
        Ok(v)
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::path::Path;
use std::io::prelude::*;
use ast;
use data::Shape;
use process::{self, Process};
use resolve::module_loader::{Module, ModuleLoader};
use session::Session;

pub fn run(fname: &str) {
    let fname = Path::new(fname);
    let success = match fs::metadata(fname) {
        Ok(ref meta) if meta.is_file() => run_file(fname),
        Ok(ref meta) if meta.is_dir() => {
            let mut success = true;
            for entry in fs::read_dir(fname).unwrap() {
                let path = entry.unwrap().path();
                if path.to_str().unwrap().ends_with(".signalspec") {
                    success &= run_file(&path);
                }
            }
            success
        }
        _ => {
            println!("Could not open {}", fname.to_string_lossy());
            false
        }
    };
    ::std::process::exit( if success { 0 } else { 1 } );
}

pub fn run_file(fname: &Path) -> bool {
    println!("Running tests for {}", fname.to_string_lossy());

    let sess = Session::new(None);
    let loader = ModuleLoader::new(&sess);

    let source = match fs::File::open(fname) {
        Ok(mut file) => {
            let mut source = String::new();
            file.read_to_string(&mut source).unwrap();
            source
        }
        Err(..) => {
            println!("\tCould not open {}", fname.to_string_lossy());
            return false;
        }
    };

    let ast = match loader.parse(&source) {
        Ok(m) => m,
        Err(e) => {
            println!("\tParse error: {}", e);
            return false;
        }
    };

    let modscope = loader.resolve_module(ast);

    let mut count = 0;
    let mut success = true;
    for entry in &ast.defs {
        match *entry {
            ast::ModuleEntry::Test(ref t) => {
                count += 1;
                print!("\tTest #{}:", count);
                let r = run_test(&sess, &modscope, t);
                println!(" {}", if r {"ok"} else {"FAIL"} );
                success &= r;
            }
            _ => {}
        }
    }

    success
}

fn run_ast(sess: &Session, module: &Module, ast: &Vec<ast::Process>) -> bool {
    let mut processes = vec![];
    let mut shape = Shape::null();

    for process_ast in ast {
        let process = process::resolve_process(sess, module, &shape, process_ast);
        shape = process.shape_up().clone();
        processes.push(process);
    }

    process::run_process_chain(processes)
}

fn run_test(sess: &Session, module: &Module, test: &ast::Test) -> bool {
    // If the test uses `@both`, generate `@up` and `@dn` versions and run them
    if let Some((&ast::Process::Literal(ast::ProcessLiteralDirection::Both, ref ty, ref blk), rest)) = test.processes.split_first() {
        let mut up_version = vec![ast::Process::Literal(ast::ProcessLiteralDirection::Up, ty.clone(), blk.clone())];
        up_version.extend(rest.iter().cloned());
        let success_up = run_ast(sess, module, &up_version) ^ test.should_fail;

        let mut down_version = vec![ast::Process::Literal(ast::ProcessLiteralDirection::Down, ty.clone(), blk.clone())];
        down_version.extend(rest.iter().cloned());
        let success_down = run_ast(sess, module, &down_version) ^ test.should_fail;

        success_up && success_down
    } else {
        run_ast(sess, module, &test.processes) ^ test.should_fail
    }
}
<commit_msg>test_runner: label pass/fail for each direction in @both<commit_after>use std::fs;
use std::path::Path;
use std::io::prelude::*;
use ast;
use data::Shape;
use process::{self, Process};
use resolve::module_loader::{Module, ModuleLoader};
use session::Session;

pub fn run(fname: &str) {
    let fname = Path::new(fname);
    let success = match fs::metadata(fname) {
        Ok(ref meta) if meta.is_file() => run_file(fname),
        Ok(ref meta) if meta.is_dir() => {
            let mut success = true;
            for entry in fs::read_dir(fname).unwrap() {
                let path = entry.unwrap().path();
                if path.to_str().unwrap().ends_with(".signalspec") {
                    success &= run_file(&path);
                }
            }
            success
        }
        _ => {
            println!("Could not open {}", fname.to_string_lossy());
            false
        }
    };
    ::std::process::exit( if success { 0 } else { 1 } );
}

pub fn run_file(fname: &Path) -> bool {
    println!("Running tests for {}", fname.to_string_lossy());

    let sess = Session::new(None);
    let loader = ModuleLoader::new(&sess);

    let source = match fs::File::open(fname) {
        Ok(mut file) => {
            let mut source = String::new();
            file.read_to_string(&mut source).unwrap();
            source
        }
        Err(..) => {
            println!("\tCould not open {}", fname.to_string_lossy());
            return false;
        }
    };

    let ast = match loader.parse(&source) {
        Ok(m) => m,
        Err(e) => {
            println!("\tParse error: {}", e);
            return false;
        }
    };

    let modscope = loader.resolve_module(ast);

    let mut count = 0;
    let mut success = true;
    for entry in &ast.defs {
        match *entry {
            ast::ModuleEntry::Test(ref t) => {
                count += 1;
                print!("\tTest #{}:", count);
                let r = run_test(&sess, &modscope, t);
                success &= r;
            }
            _ => {}
        }
    }

    success
}

fn run_ast(sess: &Session, module: &Module, ast: &Vec<ast::Process>) -> bool {
    let mut processes = vec![];
    let mut shape = Shape::null();

    for process_ast in ast {
        let process = process::resolve_process(sess, module, &shape, process_ast);
        shape = process.shape_up().clone();
        processes.push(process);
    }

    process::run_process_chain(processes)
}

fn run_test(sess: &Session, module: &Module, test: &ast::Test) -> bool {
    // If the test uses `@both`, generate `@up` and `@dn` versions and run them
    if let Some((&ast::Process::Literal(ast::ProcessLiteralDirection::Both, ref ty, ref blk), rest)) = test.processes.split_first() {
        let mut up_version = vec![ast::Process::Literal(ast::ProcessLiteralDirection::Up, ty.clone(), blk.clone())];
        up_version.extend(rest.iter().cloned());
        let success_up = run_ast(sess, module, &up_version) ^ test.should_fail;

        print!(" up:{}", if success_up { "pass" } else { "FAIL" });

        let mut down_version = vec![ast::Process::Literal(ast::ProcessLiteralDirection::Down, ty.clone(), blk.clone())];
        down_version.extend(rest.iter().cloned());
        let success_down = run_ast(sess, module, &down_version) ^ test.should_fail;

        println!(" down:{}", if success_down { "pass" } else { "FAIL" });

        success_up && success_down
    } else {
        let success = run_ast(sess, module, &test.processes) ^ test.should_fail;
        println!(" {}", if success { "pass" } else { "FAIL" });
        success
    }
}
<|endoftext|>