<commit_before>use std::ptr;
use std::ffi::CStr;
use std::borrow::Cow;

use mongo_c_driver_wrapper::bindings;

use bson::{Bson,Document};

use super::Result;
use super::CommandAndFindOptions;
use super::{BsoncError,InvalidParamsError};
use super::bsonc::Bsonc;
use super::bulk_operation::BulkOperation;
use super::client::Client;
use super::cursor;
use super::cursor::{Cursor,TailingCursor};
use super::database::Database;
use super::flags::{Flags,FlagsValue,InsertFlag,QueryFlag,RemoveFlag};
use super::write_concern::WriteConcern;
use super::read_prefs::ReadPrefs;

pub enum CreatedBy<'a> {
    Client(&'a Client<'a>),
    Database(&'a Database<'a>)
}

pub struct Collection<'a> {
    _created_by: CreatedBy<'a>,
    inner:      *mut bindings::mongoc_collection_t
}

pub struct BulkOperationOptions {
    pub ordered:       bool,
    pub write_concern: WriteConcern
}

impl BulkOperationOptions {
    pub fn default() -> BulkOperationOptions {
        BulkOperationOptions {
            ordered:       false,
            write_concern: WriteConcern::new()
        }
    }
}

pub struct CountOptions {
    pub query_flags: Flags<QueryFlag>,
    pub skip:        u32,
    pub limit:       u32,
    pub opts:        Option<Document>,
    pub read_prefs:  Option<ReadPrefs>
}

impl CountOptions {
    pub fn default() -> CountOptions {
        CountOptions {
            query_flags: Flags::new(),
            skip:        0,
            limit:       0,
            opts:        None,
            read_prefs:  None
        }
    }
}

pub struct InsertOptions {
    pub insert_flags:  Flags<InsertFlag>,
    pub write_concern: WriteConcern
}

impl InsertOptions {
    pub fn default() -> InsertOptions {
        InsertOptions {
            insert_flags:  Flags::new(),
            write_concern: WriteConcern::new()
        }
    }
}

pub struct RemoveOptions {
    pub remove_flags:  Flags<RemoveFlag>,
    pub write_concern: WriteConcern
}

impl RemoveOptions {
    pub fn default() -> RemoveOptions {
        RemoveOptions {
            remove_flags:  Flags::new(),
            write_concern: WriteConcern::new()
        }
    }
}

pub struct TailOptions {
    pub wait_time_ms: u32,
    pub max_retries:  u32
}

impl TailOptions {
    pub fn default() -> TailOptions {
        TailOptions {
            wait_time_ms: 500,
            max_retries:  5
        }
    }
}

impl<'a> Collection<'a> {
    pub fn new(
        created_by: CreatedBy<'a>,
        inner:      *mut bindings::mongoc_collection_t
    ) -> Collection<'a> {
        assert!(!inner.is_null());
        Collection {
            _created_by: created_by,
            inner:       inner
        }
    }

    /// Execute a command on the collection
    ///
    /// See: http://api.mongodb.org/c/current/mongoc_collection_command.html
    pub fn command(
        &'a self,
        command: Document,
        options: Option<&CommandAndFindOptions>
    ) -> Result<Cursor<'a>> {
        assert!(!self.inner.is_null());

        let default_options = CommandAndFindOptions::default();
        let options         = options.unwrap_or(&default_options);

        let inner = unsafe {
            bindings::mongoc_collection_command(
                self.inner,
                options.query_flags.flags(),
                options.skip,
                options.limit,
                options.batch_size,
                try!(Bsonc::from_document(&command)).inner(),
                match options.fields {
                    Some(ref f) => {
                        try!(Bsonc::from_document(f)).inner()
                    },
                    None => ptr::null()
                },
                match options.read_prefs {
                    Some(ref prefs) => prefs.inner(),
                    None => ptr::null()
                }
            )
        };

        if inner.is_null() {
            return Err(InvalidParamsError.into())
        }

        Ok(Cursor::new(cursor::CreatedBy::Collection(self), inner))
    }

    pub fn count(
        &self,
        query:   &Document,
        options: Option<&CountOptions>
    ) -> Result<i64> {
        assert!(!self.inner.is_null());

        let default_options = CountOptions::default();
        let options         = options.unwrap_or(&default_options);

        let mut error = BsoncError::empty();
        let count = unsafe {
            bindings::mongoc_collection_count_with_opts(
                self.inner,
                options.query_flags.flags(),
                try!(Bsonc::from_document(query)).inner(),
                options.skip as i64,
                options.limit as i64,
                match options.opts {
                    Some(ref o) => try!(Bsonc::from_document(o)).inner(),
                    None        => ptr::null()
                },
                match options.read_prefs {
                    Some(ref prefs) => prefs.inner(),
                    None            => ptr::null()
                },
                error.mut_inner()
            )
        };

        if error.is_empty() {
            Ok(count)
        } else {
            Err(error.into())
        }
    }

    pub fn create_bulk_operation(
        &'a self,
        options: Option<&BulkOperationOptions>
    ) -> BulkOperation<'a> {
        assert!(!self.inner.is_null());

        let default_options = BulkOperationOptions::default();
        let options         = options.unwrap_or(&default_options);

        let inner = unsafe {
            bindings::mongoc_collection_create_bulk_operation(
                self.inner,
                options.ordered as u8,
                options.write_concern.inner()
            )
        };

        BulkOperation::new(self, inner)
    }

    pub fn drop(&mut self) -> Result<()> {
        assert!(!self.inner.is_null());
        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_drop(
                self.inner,
                error.mut_inner()
            )
        };
        if success == 0 {
            assert!(!error.is_empty());
            return Err(error.into())
        }
        Ok(())
    }

    pub fn find(
        &'a self,
        query:   &Document,
        options: Option<&CommandAndFindOptions>
    ) -> Result<Cursor<'a>> {
        assert!(!self.inner.is_null());

        let default_options = CommandAndFindOptions::default();
        let options         = options.unwrap_or(&default_options);

        let inner = unsafe {
            bindings::mongoc_collection_find(
                self.inner,
                options.query_flags.flags(),
                options.skip,
                options.limit,
                options.batch_size,
                try!(Bsonc::from_document(query)).inner(),
                match options.fields {
                    Some(ref f) => {
                        try!(Bsonc::from_document(f)).inner()
                    },
                    None => ptr::null()
                },
                match options.read_prefs {
                    Some(ref prefs) => prefs.inner(),
                    None => ptr::null()
                }
            )
        };

        if inner.is_null() {
            return Err(InvalidParamsError.into())
        }

        Ok(Cursor::new(cursor::CreatedBy::Collection(self), inner))
    }

    pub fn get_name(&self) -> Cow<str> {
        let cstr = unsafe {
            CStr::from_ptr(bindings::mongoc_collection_get_name(self.inner))
        };
        String::from_utf8_lossy(cstr.to_bytes())
    }

    pub fn insert(
        &'a self,
        document: &Document,
        options:  Option<&InsertOptions>
    ) -> Result<()> {
        assert!(!self.inner.is_null());

        let default_options = InsertOptions::default();
        let options         = options.unwrap_or(&default_options);

        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_insert(
                self.inner,
                options.insert_flags.flags(),
                try!(Bsonc::from_document(&document)).inner(),
                options.write_concern.inner(),
                error.mut_inner()
            )
        };

        if success == 1 {
            Ok(())
        } else {
            Err(error.into())
        }
    }

    pub fn remove(
        &self,
        selector: &Document,
        options:  Option<&RemoveOptions>
    ) -> Result<()> {
        assert!(!self.inner.is_null());

        let default_options = RemoveOptions::default();
        let options         = options.unwrap_or(&default_options);

        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_remove(
                self.inner,
                options.remove_flags.flags(),
                try!(Bsonc::from_document(&selector)).inner(),
                options.write_concern.inner(),
                error.mut_inner()
            )
        };

        if success == 1 {
            Ok(())
        } else {
            Err(error.into())
        }
    }

    pub fn save(
        &self,
        document:      &Document,
        write_concern: Option<&WriteConcern>
    ) -> Result<()> {
        assert!(!self.inner.is_null());

        let default_write_concern = WriteConcern::new();
        let write_concern         = write_concern.unwrap_or(&default_write_concern);

        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_save(
                self.inner,
                try!(Bsonc::from_document(&document)).inner(),
                write_concern.inner(),
                error.mut_inner()
            )
        };

        if success == 1 {
            Ok(())
        } else {
            Err(error.into())
        }
    }

    /// Tails a query
    ///
    /// Takes ownership of query and options because they could be
    /// modified and reused when the connections is disrupted and
    /// we need to restart the query. The query will be placed in a
    /// $query key, so the function can add configuration needed for
    /// proper tailing.
    ///
    /// The query is executed when iterating, so this function doesn't
    /// return a result itself.
    ///
    /// The necessary flags to configure a tailing query will be added
    /// to the configured flags if you choose to supply options.
    pub fn tail(
        &'a self,
        query:        Document,
        find_options: Option<CommandAndFindOptions>,
        tail_options: Option<TailOptions>
    ) -> TailingCursor<'a> {
        let mut query_with_options = Document::new();
        query_with_options.insert(
            "$query".to_string(),
            Bson::Document(query)
        );
        query_with_options.insert("$natural".to_string(), Bson::I32(1));

        TailingCursor::new(
            self,
            query_with_options,
            find_options.unwrap_or(CommandAndFindOptions::default()),
            tail_options.unwrap_or(TailOptions::default())
        )
    }
}

impl<'a> Drop for Collection<'a> {
    fn drop(&mut self) {
        assert!(!self.inner.is_null());
        unsafe {
            bindings::mongoc_collection_destroy(self.inner);
        }
    }
}

#[cfg(test)]
mod tests {
    use bson;
    use super::super::uri::Uri;
    use super::super::client::ClientPool;
    use super::super::flags;

    #[test]
    fn test_command() {
        let uri      = Uri::new("mongodb://localhost:27017/");
        let pool     = ClientPool::new(uri, None);
        let client   = pool.pop();
        let collection = client.get_collection("rust_driver_test", "items");

        let mut command = bson::Document::new();
        command.insert("ping".to_string(), bson::Bson::I32(1));

        let result = collection.command(command, None).unwrap().next().unwrap().unwrap();
        assert!(result.contains_key("ok"));
    }

    #[test]
    fn test_mutation_and_finding() {
        let uri        = Uri::new("mongodb://localhost:27017/");
        let pool       = ClientPool::new(uri, None);
        let client     = pool.pop();
        let mut collection = client.get_collection("rust_driver_test", "items");
        collection.drop().unwrap_or(());

        assert_eq!("items", collection.get_name().to_mut());

        let mut document = bson::Document::new();
        document.insert("key_1".to_string(), bson::Bson::String("Value 1".to_string()));
        document.insert("key_2".to_string(), bson::Bson::String("Value 2".to_string()));
        assert!(collection.insert(&document, None).is_ok());

        let mut second_document = bson::Document::new();
        second_document.insert("key_1".to_string(), bson::Bson::String("Value 3".to_string()));
        assert!(collection.insert(&second_document, None).is_ok());

        let query = bson::Document::new();

        // Count the documents in the collection
        assert_eq!(2, collection.count(&query, None).unwrap());

        // Find the documents
        assert_eq!(
            collection.find(&document, None).unwrap().next().unwrap().unwrap().get("key_1").unwrap().to_json(),
            bson::Bson::String("Value 1".to_string()).to_json()
        );
        let mut found_document = collection.find(&second_document, None).unwrap().next().unwrap().unwrap();
        assert_eq!(
            found_document.get("key_1").unwrap().to_json(),
            bson::Bson::String("Value 3".to_string()).to_json()
        );

        // Update the second document
        found_document.insert("key_1".to_string(), bson::Bson::String("Value 4".to_string()));
        assert!(collection.save(&found_document, None).is_ok());

        // Reload and check value
        let found_document = collection.find(&found_document, None).unwrap().next().unwrap().unwrap();
        assert_eq!(
            found_document.get("key_1").unwrap().to_json(),
            bson::Bson::String("Value 4".to_string()).to_json()
        );

        // Remove one
        assert!(collection.remove(&found_document, None).is_ok());

        // Count again
        assert_eq!(1, collection.count(&query, None).unwrap());

        // Find the document and see if it has the keys we expect
        {
            let mut cursor = collection.find(&query, None).unwrap();
            let next_document = cursor.next().unwrap().unwrap();
            assert!(next_document.contains_key("key_1"));
            assert!(next_document.contains_key("key_2"));
        }

        // Find the document with fields set
        {
            let mut fields = bson::Document::new();
            fields.insert("key_1".to_string(), bson::Bson::Boolean(true));
            let options = super::super::CommandAndFindOptions {
                query_flags: flags::Flags::new(),
                skip:        0,
                limit:       0,
                batch_size:  0,
                fields:      Some(fields),
                read_prefs:  None
            };
            let mut cursor = collection.find(&query, Some(&options)).unwrap();
            let next_document = cursor.next().unwrap().unwrap();
            assert!(next_document.contains_key("key_1"));
            assert!(!next_document.contains_key("key_2"));
        }

        // Drop collection
        collection.drop().unwrap();
        assert_eq!(0, collection.count(&query, None).unwrap());
    }

    #[test]
    fn test_insert_failure() {
        let uri        = Uri::new("mongodb://localhost:27018/"); // There should be no mongo server here
        let pool       = ClientPool::new(uri, None);
        let client     = pool.pop();
        let collection = client.get_collection("rust_driver_test", "items");
        let document   = bson::Document::new();

        let result = collection.insert(&document, None);
        assert!(result.is_err());
        assert_eq!(
            "MongoError (BsoncError: Failed to connect to target host: localhost:27018)",
            format!("{:?}", result.err().unwrap())
        );
    }
}
<commit_msg>Add test for multiple queries with same fields object<commit_after>use std::ptr;
use std::ffi::CStr;
use std::borrow::Cow;

use mongo_c_driver_wrapper::bindings;

use bson::{Bson,Document};

use super::Result;
use super::CommandAndFindOptions;
use super::{BsoncError,InvalidParamsError};
use super::bsonc::Bsonc;
use super::bulk_operation::BulkOperation;
use super::client::Client;
use super::cursor;
use super::cursor::{Cursor,TailingCursor};
use super::database::Database;
use super::flags::{Flags,FlagsValue,InsertFlag,QueryFlag,RemoveFlag};
use super::write_concern::WriteConcern;
use super::read_prefs::ReadPrefs;

pub enum CreatedBy<'a> {
    Client(&'a Client<'a>),
    Database(&'a Database<'a>)
}

pub struct Collection<'a> {
    _created_by: CreatedBy<'a>,
    inner:      *mut bindings::mongoc_collection_t
}

pub struct BulkOperationOptions {
    pub ordered:       bool,
    pub write_concern: WriteConcern
}

impl BulkOperationOptions {
    pub fn default() -> BulkOperationOptions {
        BulkOperationOptions {
            ordered:       false,
            write_concern: WriteConcern::new()
        }
    }
}

pub struct CountOptions {
    pub query_flags: Flags<QueryFlag>,
    pub skip:        u32,
    pub limit:       u32,
    pub opts:        Option<Document>,
    pub read_prefs:  Option<ReadPrefs>
}

impl CountOptions {
    pub fn default() -> CountOptions {
        CountOptions {
            query_flags: Flags::new(),
            skip:        0,
            limit:       0,
            opts:        None,
            read_prefs:  None
        }
    }
}

pub struct InsertOptions {
    pub insert_flags:  Flags<InsertFlag>,
    pub write_concern: WriteConcern
}

impl InsertOptions {
    pub fn default() -> InsertOptions {
        InsertOptions {
            insert_flags:  Flags::new(),
            write_concern: WriteConcern::new()
        }
    }
}

pub struct RemoveOptions {
    pub remove_flags:  Flags<RemoveFlag>,
    pub write_concern: WriteConcern
}

impl RemoveOptions {
    pub fn default() -> RemoveOptions {
        RemoveOptions {
            remove_flags:  Flags::new(),
            write_concern: WriteConcern::new()
        }
    }
}

pub struct TailOptions {
    pub wait_time_ms: u32,
    pub max_retries:  u32
}

impl TailOptions {
    pub fn default() -> TailOptions {
        TailOptions {
            wait_time_ms: 500,
            max_retries:  5
        }
    }
}

impl<'a> Collection<'a> {
    pub fn new(
        created_by: CreatedBy<'a>,
        inner:      *mut bindings::mongoc_collection_t
    ) -> Collection<'a> {
        assert!(!inner.is_null());
        Collection {
            _created_by: created_by,
            inner:       inner
        }
    }

    /// Execute a command on the collection
    ///
    /// See: http://api.mongodb.org/c/current/mongoc_collection_command.html
    pub fn command(
        &'a self,
        command: Document,
        options: Option<&CommandAndFindOptions>
    ) -> Result<Cursor<'a>> {
        assert!(!self.inner.is_null());

        let default_options = CommandAndFindOptions::default();
        let options         = options.unwrap_or(&default_options);

        let inner = unsafe {
            bindings::mongoc_collection_command(
                self.inner,
                options.query_flags.flags(),
                options.skip,
                options.limit,
                options.batch_size,
                try!(Bsonc::from_document(&command)).inner(),
                match options.fields {
                    Some(ref f) => {
                        try!(Bsonc::from_document(f)).inner()
                    },
                    None => ptr::null()
                },
                match options.read_prefs {
                    Some(ref prefs) => prefs.inner(),
                    None => ptr::null()
                }
            )
        };

        if inner.is_null() {
            return Err(InvalidParamsError.into())
        }

        Ok(Cursor::new(cursor::CreatedBy::Collection(self), inner))
    }

    pub fn count(
        &self,
        query:   &Document,
        options: Option<&CountOptions>
    ) -> Result<i64> {
        assert!(!self.inner.is_null());

        let default_options = CountOptions::default();
        let options         = options.unwrap_or(&default_options);

        let mut error = BsoncError::empty();
        let count = unsafe {
            bindings::mongoc_collection_count_with_opts(
                self.inner,
                options.query_flags.flags(),
                try!(Bsonc::from_document(query)).inner(),
                options.skip as i64,
                options.limit as i64,
                match options.opts {
                    Some(ref o) => try!(Bsonc::from_document(o)).inner(),
                    None        => ptr::null()
                },
                match options.read_prefs {
                    Some(ref prefs) => prefs.inner(),
                    None            => ptr::null()
                },
                error.mut_inner()
            )
        };

        if error.is_empty() {
            Ok(count)
        } else {
            Err(error.into())
        }
    }

    pub fn create_bulk_operation(
        &'a self,
        options: Option<&BulkOperationOptions>
    ) -> BulkOperation<'a> {
        assert!(!self.inner.is_null());

        let default_options = BulkOperationOptions::default();
        let options         = options.unwrap_or(&default_options);

        let inner = unsafe {
            bindings::mongoc_collection_create_bulk_operation(
                self.inner,
                options.ordered as u8,
                options.write_concern.inner()
            )
        };

        BulkOperation::new(self, inner)
    }

    pub fn drop(&mut self) -> Result<()> {
        assert!(!self.inner.is_null());
        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_drop(
                self.inner,
                error.mut_inner()
            )
        };
        if success == 0 {
            assert!(!error.is_empty());
            return Err(error.into())
        }
        Ok(())
    }

    pub fn find(
        &'a self,
        query:   &Document,
        options: Option<&CommandAndFindOptions>
    ) -> Result<Cursor<'a>> {
        assert!(!self.inner.is_null());

        let default_options = CommandAndFindOptions::default();
        let options         = options.unwrap_or(&default_options);

        let inner = unsafe {
            bindings::mongoc_collection_find(
                self.inner,
                options.query_flags.flags(),
                options.skip,
                options.limit,
                options.batch_size,
                try!(Bsonc::from_document(query)).inner(),
                match options.fields {
                    Some(ref f) => {
                        try!(Bsonc::from_document(f)).inner()
                    },
                    None => ptr::null()
                },
                match options.read_prefs {
                    Some(ref prefs) => prefs.inner(),
                    None => ptr::null()
                }
            )
        };

        if inner.is_null() {
            return Err(InvalidParamsError.into())
        }

        Ok(Cursor::new(cursor::CreatedBy::Collection(self), inner))
    }

    pub fn get_name(&self) -> Cow<str> {
        let cstr = unsafe {
            CStr::from_ptr(bindings::mongoc_collection_get_name(self.inner))
        };
        String::from_utf8_lossy(cstr.to_bytes())
    }

    pub fn insert(
        &'a self,
        document: &Document,
        options:  Option<&InsertOptions>
    ) -> Result<()> {
        assert!(!self.inner.is_null());

        let default_options = InsertOptions::default();
        let options         = options.unwrap_or(&default_options);

        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_insert(
                self.inner,
                options.insert_flags.flags(),
                try!(Bsonc::from_document(&document)).inner(),
                options.write_concern.inner(),
                error.mut_inner()
            )
        };

        if success == 1 {
            Ok(())
        } else {
            Err(error.into())
        }
    }

    pub fn remove(
        &self,
        selector: &Document,
        options:  Option<&RemoveOptions>
    ) -> Result<()> {
        assert!(!self.inner.is_null());

        let default_options = RemoveOptions::default();
        let options         = options.unwrap_or(&default_options);

        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_remove(
                self.inner,
                options.remove_flags.flags(),
                try!(Bsonc::from_document(&selector)).inner(),
                options.write_concern.inner(),
                error.mut_inner()
            )
        };

        if success == 1 {
            Ok(())
        } else {
            Err(error.into())
        }
    }

    pub fn save(
        &self,
        document:      &Document,
        write_concern: Option<&WriteConcern>
    ) -> Result<()> {
        assert!(!self.inner.is_null());

        let default_write_concern = WriteConcern::new();
        let write_concern         = write_concern.unwrap_or(&default_write_concern);

        let mut error = BsoncError::empty();
        let success = unsafe {
            bindings::mongoc_collection_save(
                self.inner,
                try!(Bsonc::from_document(&document)).inner(),
                write_concern.inner(),
                error.mut_inner()
            )
        };

        if success == 1 {
            Ok(())
        } else {
            Err(error.into())
        }
    }

    /// Tails a query
    ///
    /// Takes ownership of query and options because they could be
    /// modified and reused when the connections is disrupted and
    /// we need to restart the query. The query will be placed in a
    /// $query key, so the function can add configuration needed for
    /// proper tailing.
    ///
    /// The query is executed when iterating, so this function doesn't
    /// return a result itself.
    ///
    /// The necessary flags to configure a tailing query will be added
    /// to the configured flags if you choose to supply options.
    pub fn tail(
        &'a self,
        query:        Document,
        find_options: Option<CommandAndFindOptions>,
        tail_options: Option<TailOptions>
    ) -> TailingCursor<'a> {
        let mut query_with_options = Document::new();
        query_with_options.insert(
            "$query".to_string(),
            Bson::Document(query)
        );
        query_with_options.insert("$natural".to_string(), Bson::I32(1));

        TailingCursor::new(
            self,
            query_with_options,
            find_options.unwrap_or(CommandAndFindOptions::default()),
            tail_options.unwrap_or(TailOptions::default())
        )
    }
}

impl<'a> Drop for Collection<'a> {
    fn drop(&mut self) {
        assert!(!self.inner.is_null());
        unsafe {
            bindings::mongoc_collection_destroy(self.inner);
        }
    }
}

#[cfg(test)]
mod tests {
    use bson;
    use super::super::uri::Uri;
    use super::super::client::ClientPool;
    use super::super::flags;

    #[test]
    fn test_command() {
        let uri      = Uri::new("mongodb://localhost:27017/");
        let pool     = ClientPool::new(uri, None);
        let client   = pool.pop();
        let collection = client.get_collection("rust_driver_test", "items");

        let mut command = bson::Document::new();
        command.insert("ping".to_string(), bson::Bson::I32(1));

        let result = collection.command(command, None).unwrap().next().unwrap().unwrap();
        assert!(result.contains_key("ok"));
    }

    #[test]
    fn test_mutation_and_finding() {
        let uri        = Uri::new("mongodb://localhost:27017/");
        let pool       = ClientPool::new(uri, None);
        let client     = pool.pop();
        let mut collection = client.get_collection("rust_driver_test", "items");
        collection.drop().unwrap_or(());

        assert_eq!("items", collection.get_name().to_mut());

        let mut document = bson::Document::new();
        document.insert("key_1".to_string(), bson::Bson::String("Value 1".to_string()));
        document.insert("key_2".to_string(), bson::Bson::String("Value 2".to_string()));
        assert!(collection.insert(&document, None).is_ok());

        let mut second_document = bson::Document::new();
        second_document.insert("key_1".to_string(), bson::Bson::String("Value 3".to_string()));
        assert!(collection.insert(&second_document, None).is_ok());

        let query = bson::Document::new();

        // Count the documents in the collection
        assert_eq!(2, collection.count(&query, None).unwrap());

        // Find the documents
        assert_eq!(
            collection.find(&document, None).unwrap().next().unwrap().unwrap().get("key_1").unwrap().to_json(),
            bson::Bson::String("Value 1".to_string()).to_json()
        );
        let mut found_document = collection.find(&second_document, None).unwrap().next().unwrap().unwrap();
        assert_eq!(
            found_document.get("key_1").unwrap().to_json(),
            bson::Bson::String("Value 3".to_string()).to_json()
        );

        // Update the second document
        found_document.insert("key_1".to_string(), bson::Bson::String("Value 4".to_string()));
        assert!(collection.save(&found_document, None).is_ok());

        // Reload and check value
        let found_document = collection.find(&found_document, None).unwrap().next().unwrap().unwrap();
        assert_eq!(
            found_document.get("key_1").unwrap().to_json(),
            bson::Bson::String("Value 4".to_string()).to_json()
        );

        // Remove one
        assert!(collection.remove(&found_document, None).is_ok());

        // Count again
        assert_eq!(1, collection.count(&query, None).unwrap());

        // Find the document and see if it has the keys we expect
        {
            let mut cursor = collection.find(&query, None).unwrap();
            let next_document = cursor.next().unwrap().unwrap();
            assert!(next_document.contains_key("key_1"));
            assert!(next_document.contains_key("key_2"));
        }

        // Find the document with fields set
        {
            let mut fields = bson::Document::new();
            fields.insert("key_1".to_string(), bson::Bson::Boolean(true));
            let options = super::super::CommandAndFindOptions {
                query_flags: flags::Flags::new(),
                skip:        0,
                limit:       0,
                batch_size:  0,
                fields:      Some(fields),
                read_prefs:  None
            };

            // Query a couple of times to make sure the C driver keeps
            // access to the fields bson object.
            for _ in 0..5 {
                collection.find(&query, Some(&options)).unwrap();
            }

            let mut cursor = collection.find(&query, Some(&options)).unwrap();
            let next_document = cursor.next().unwrap().unwrap();
            assert!(next_document.contains_key("key_1"));
            assert!(!next_document.contains_key("key_2"));
        }

        // Drop collection
        collection.drop().unwrap();
        assert_eq!(0, collection.count(&query, None).unwrap());
    }

    #[test]
    fn test_insert_failure() {
        let uri        = Uri::new("mongodb://localhost:27018/"); // There should be no mongo server here
        let pool       = ClientPool::new(uri, None);
        let client     = pool.pop();
        let collection = client.get_collection("rust_driver_test", "items");
        let document   = bson::Document::new();

        let result = collection.insert(&document, None);
        assert!(result.is_err());
        assert_eq!(
            "MongoError (BsoncError: Failed to connect to target host: localhost:27018)",
            format!("{:?}", result.err().unwrap())
        );
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use askama::Template;

use crate::{CommandFailed, CommandQApp, CommandRequest, CommandResponse, CommandSuccess, Task};

#[derive(Template)]
#[template(path = "index.html")]
struct Index {
    queued_tasks: Vec<TaskTemplateObject>,
    running_tasks: Vec<TaskTemplateObject>,
}

struct TaskTemplateObject {
    id: String,
    path: String,
    process: String,
    tries: usize,
    last_attempt: String,
}

impl From<Task> for TaskTemplateObject {
    fn from(task: Task) -> Self {
        TaskTemplateObject {
            id: task.id,
            path: task.command.path,
            process: format!("{} {:?}", task.command.program, task.command.args),
            tries: task.tries,
            last_attempt: task
                .last_attempt
                .and_then(|last_attempt| last_attempt.elapsed().ok())
                .map(|last_attempt_elapsed| {
                    format!("{} ago", humantime::format_duration(last_attempt_elapsed))
                })
                .unwrap_or("None".to_string()),
        }
    }
}

// TODO implement a more generic template to html trait https://github.com/djc/askama/blob/main/askama_actix/src/lib.rs#L33
#[get("/")]
async fn index(app: web::Data<Arc<CommandQApp>>) -> impl Responder {
    let queued_tasks = app.queue.queued().into_iter().map(|t| t.into()).collect();
    let running_tasks = app.queue.running().into_iter().map(|t| t.into()).collect();
    let html_body = Index {
        queued_tasks,
        running_tasks,
    }
    .render()
    .unwrap();
    HttpResponse::Ok().content_type("text/html").body(html_body)
}
<commit_msg>cmd-queue: start of queue command<commit_after>use std::sync::Arc;

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use askama::Template;
use serde::{Deserialize, Serialize};

use crate::{CommandFailed, CommandQApp, CommandRequest, CommandResponse, CommandSuccess, Task};

#[derive(Template)]
#[template(path = "index.html")]
struct Index {
    queued_tasks: Vec<TaskTemplateObject>,
    running_tasks: Vec<TaskTemplateObject>,
}

struct TaskTemplateObject {
    id: String,
    path: String,
    process: String,
    tries: usize,
    last_attempt: String,
}

impl From<Task> for TaskTemplateObject {
    fn from(task: Task) -> Self {
        TaskTemplateObject {
            id: task.id,
            path: task.command.path,
            process: format!("{} {:?}", task.command.program, task.command.args),
            tries: task.tries,
            last_attempt: task
                .last_attempt
                .and_then(|last_attempt| last_attempt.elapsed().ok())
                .map(|last_attempt_elapsed| {
                    format!("{} ago", humantime::format_duration(last_attempt_elapsed))
                })
                .unwrap_or("None".to_string()),
        }
    }
}

// TODO implement a more generic template to html trait https://github.com/djc/askama/blob/main/askama_actix/src/lib.rs#L33
#[get("/")]
async fn index(app: web::Data<Arc<CommandQApp>>) -> impl Responder {
    let queued_tasks = app.queue.queued().into_iter().map(|t| t.into()).collect();
    let running_tasks = app.queue.running().into_iter().map(|t| t.into()).collect();
    let html_body = Index {
        queued_tasks,
        running_tasks,
    }
    .render()
    .unwrap();
    HttpResponse::Ok().content_type("text/html").body(html_body)
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum HtmlFormCommandRequest {
    Ytdlp {
        path: String,
        url: String,
        prefix: Option<String>,
    },
    Raw {
        path: String,
        command: String,
    },
}

#[post("/html/api/commands")]
async fn html_queue_command(
    app: web::Data<Arc<CommandQApp>>,
    command: web::Form<HtmlFormCommandRequest>,
) -> impl Responder {
    match command.0 {
        HtmlFormCommandRequest::Ytdlp { path, url, prefix } => {}
        HtmlFormCommandRequest::Raw { path, command } => {}
    }

    // TODO replace with more specific component
    let queued_tasks = app.queue.queued().into_iter().map(|t| t.into()).collect();
    let running_tasks = app.queue.running().into_iter().map(|t| t.into()).collect();
    let html_body = Index {
        queued_tasks,
        running_tasks,
    }
    .render()
    .unwrap();
    HttpResponse::Ok().content_type("text/html").body(html_body)
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 Kubos Corporation
 *
 * 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.
 */

use super::*;
use failure::Error;
use kubos_app::query;

pub fn apps_test() -> Result<(), Error> {
    let mut passed = 0;
    let mut failed = 0;

    match get_apps() {
        Ok(()) => passed += 1,
        Err(_) => failed += 1,
    }

    info!("Applications Service Test Results: Passed - {}, Failed - {}", passed, failed);

    Ok(())
}

fn get_apps() -> Result<(), Error> {
    info!("Querying for active applications");

    let request = r#"{
        apps {
            active,
            app {
                name,
                version,
                author
            }
        }
    }"#;

    match query(
        &ServiceConfig::new("app-service")?,
        request,
        Some(Duration::from_secs(1)),
    ) {
        Ok(msg) => {
            info!("App query result: {:?}", msg);
            Ok(())
        }
        Err(err) => {
            error!("App service query failed: {}", err);
            bail!("App service query failed")
        }
    }
}
<commit_msg>Updating app service query<commit_after>/*
 * Copyright (C) 2018 Kubos Corporation
 *
 * 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.
 */

use super::*;
use failure::Error;
use kubos_app::query;

pub fn apps_test() -> Result<(), Error> {
    let mut passed = 0;
    let mut failed = 0;

    match get_apps() {
        Ok(()) => passed += 1,
        Err(_) => failed += 1,
    }

    info!("Applications Service Test Results: Passed - {}, Failed - {}", passed, failed);

    Ok(())
}

fn get_apps() -> Result<(), Error> {
    info!("Querying for active applications");

    let request = r#"{
        registeredApps {
            active,
            app {
                name,
                version,
                author
            }
        },
        appStatus {
            name,
            version,
            runLevel,
            startTime,
            running
        }
    }"#;

    match query(
        &ServiceConfig::new("app-service")?,
        request,
        Some(Duration::from_secs(1)),
    ) {
        Ok(msg) => {
            info!("App query result: {:?}", msg);
            Ok(())
        }
        Err(err) => {
            error!("App service query failed: {}", err);
            bail!("App service query failed")
        }
    }
}
<|endoftext|>


<commit_before>import std::cmp::fuzzy_eq;
import Num = num::num;

pure fn Mat4<T:copy fuzzy_eq Num>(m11: T, m12: T, m13: T, m14: T,
                                     m21: T, m22: T, m23: T, m24: T,
                                     m31: T, m32: T, m33: T, m34: T,
                                     m41: T, m42: T, m43: T, m44: T)
                                  -> Matrix4<T> {

    Matrix4 {
        m11: m11, m12: m12, m13: m13, m14: m14,
        m21: m21, m22: m22, m23: m23, m24: m24,
        m31: m31, m32: m32, m33: m33, m34: m34,
        m41: m41, m42: m42, m43: m43, m44: m44
    }
}

struct Matrix4<T:copy fuzzy_eq Num> {
    let m11: T; let m12: T; let m13: T; let m14: T;
    let m21: T; let m22: T; let m23: T; let m24: T;
    let m31: T; let m32: T; let m33: T; let m34: T;
    let m41: T; let m42: T; let m43: T; let m44: T;

    pure fn fuzzy_eq(&&other: Matrix4<T>) -> bool {
        self.m11.fuzzy_eq(other.m11) && self.m12.fuzzy_eq(other.m12) &&
        self.m13.fuzzy_eq(other.m13) && self.m14.fuzzy_eq(other.m14) &&
        self.m21.fuzzy_eq(other.m21) && self.m22.fuzzy_eq(other.m22) &&
        self.m23.fuzzy_eq(other.m23) && self.m24.fuzzy_eq(other.m24) &&
        self.m31.fuzzy_eq(other.m31) && self.m32.fuzzy_eq(other.m32) &&
        self.m33.fuzzy_eq(other.m33) && self.m34.fuzzy_eq(other.m34) &&
        self.m41.fuzzy_eq(other.m41) && self.m42.fuzzy_eq(other.m42) &&
        self.m43.fuzzy_eq(other.m43) && self.m44.fuzzy_eq(other.m44)
    }

    pure fn mul(&&m: Matrix4<T>) -> Matrix4<T> {
        return Mat4(m.m11.mul(self.m11).add(m.m12.mul(self.m21))
                                          .add(m.m13.mul(self.m31))
                                          .add(m.m14.mul(self.m41)),
                       m.m11.mul(self.m12).add(m.m12.mul(self.m22))
                                          .add(m.m13.mul(self.m32))
                                          .add(m.m14.mul(self.m42)),
                       m.m11.mul(self.m13).add(m.m12.mul(self.m23))
                                          .add(m.m13.mul(self.m33))
                                          .add(m.m14.mul(self.m43)),
                       m.m11.mul(self.m14).add(m.m12.mul(self.m24))
                                          .add(m.m13.mul(self.m34))
                                          .add(m.m14.mul(self.m44)),
                       m.m21.mul(self.m11).add(m.m22.mul(self.m21))
                                          .add(m.m23.mul(self.m31))
                                          .add(m.m24.mul(self.m41)),
                       m.m21.mul(self.m12).add(m.m22.mul(self.m22))
                                          .add(m.m23.mul(self.m32))
                                          .add(m.m24.mul(self.m42)),
                       m.m21.mul(self.m13).add(m.m22.mul(self.m23))
                                          .add(m.m23.mul(self.m33))
                                          .add(m.m24.mul(self.m43)),
                       m.m21.mul(self.m14).add(m.m22.mul(self.m24))
                                          .add(m.m23.mul(self.m34))
                                          .add(m.m24.mul(self.m44)),
                       m.m31.mul(self.m11).add(m.m32.mul(self.m21))
                                          .add(m.m33.mul(self.m31))
                                          .add(m.m34.mul(self.m41)),
                       m.m31.mul(self.m12).add(m.m32.mul(self.m22))
                                          .add(m.m33.mul(self.m32))
                                          .add(m.m34.mul(self.m42)),
                       m.m31.mul(self.m13).add(m.m32.mul(self.m23))
                                          .add(m.m33.mul(self.m33))
                                          .add(m.m34.mul(self.m43)),
                       m.m31.mul(self.m14).add(m.m32.mul(self.m24))
                                          .add(m.m33.mul(self.m34))
                                          .add(m.m34.mul(self.m44)),
                       m.m41.mul(self.m11).add(m.m42.mul(self.m21))
                                          .add(m.m43.mul(self.m31))
                                          .add(m.m44.mul(self.m41)),
                       m.m41.mul(self.m12).add(m.m42.mul(self.m22))
                                          .add(m.m43.mul(self.m32))
                                          .add(m.m44.mul(self.m42)),
                       m.m41.mul(self.m13).add(m.m42.mul(self.m23))
                                          .add(m.m43.mul(self.m33))
                                          .add(m.m44.mul(self.m43)),
                       m.m41.mul(self.m14).add(m.m42.mul(self.m24))
                                          .add(m.m43.mul(self.m34))
                                          .add(m.m44.mul(self.m44)));
    }

    pure fn mul_s(&&x: T) -> Matrix4<T> {
        return Mat4(self.m11.mul(x), self.m12.mul(x), self.m13.mul(x), self.m14.mul(x),
                       self.m21.mul(x), self.m22.mul(x), self.m23.mul(x), self.m24.mul(x),
                       self.m31.mul(x), self.m32.mul(x), self.m33.mul(x), self.m34.mul(x),
                       self.m41.mul(x), self.m42.mul(x), self.m43.mul(x), self.m44.mul(x));
    }

    pure fn scale(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        return Mat4(self.m11.mul(x), self.m12,        self.m13,        self.m14,
                       self.m21,        self.m22.mul(y), self.m23,        self.m24,
                       self.m31,        self.m32,        self.m33.mul(z), self.m34,
                       self.m41,        self.m42,        self.m43,        self.m44);
    }

    pure fn to_array() -> ~[T] {
        return ~[
            self.m11, self.m12, self.m13, self.m14,
            self.m21, self.m22, self.m23, self.m24,
            self.m31, self.m32, self.m33, self.m34,
            self.m41, self.m42, self.m43, self.m44
        ];
    }

    pure fn translate(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        let _0 = x.from_int(0);
        let _1 = x.from_int(1);
        let matrix = Mat4(_1, _0, _0, _0,
                             _0, _1, _0, _0,
                             _0, _0, _1, _0,
                              x,  y,  z, _1);

        return self.mul(matrix);
    }
}

fn ortho<T:copy fuzzy_eq Num>(+left: T, +right: T, +bottom: T, +top: T, +near: T, +far: T)
                           -> Matrix4<T> {

    let two = left.from_int(2);
    let one = left.from_int(1);
    let zero = left.from_int(0);
    let minus_two = left.from_int(-2);

    let tx = right.add(left).div(right.sub(left)).neg();
    let ty = top.add(bottom).div(top.sub(bottom)).neg();
    let tz = far.add(near).div(far.sub(near)).neg();

    return Mat4(two.div(right.sub(left)), zero, zero, zero,
                   zero, two.div(top.sub(bottom)), zero, zero,
                   zero, zero, minus_two.div(far.sub(near)), zero,
                   tx, ty, tz, one);
}

fn identity<T:copy fuzzy_eq Num>(_0: T) -> Matrix4<T> {
    let _1 = _0.from_int(1);
    return Mat4(_1, _0, _0, _0,
                   _0, _1, _0, _0,
                   _0, _0, _1, _0,
                   _0, _0, _0, _1);
}

#[test]
fn test_ortho() {
    let (left, right, bottom, top) = (0.0, 1.0, 0.1, 1.0);
    let (near, far) = (-1.0, 1.0);
    let result = ortho(left, right, bottom, top, near, far);
    let expected = Mat4(2.0,  0.0,         0.0,  0.0,
                           0.0,  2.22222222,  0.0,  0.0,
                           0.0,  0.0,         -1.0, 0.0,
                           -1.0, -1.22222222, -0.0, 1.0);
    #debug("result=%? expected=%?", result, expected);
    assert result.fuzzy_eq(expected);
}

<commit_msg>Revert "Temporary fix to avoid a compiler bug", the compiler was fixed<commit_after>import std::cmp::fuzzy_eq;
import Num = num::num;

pure fn Matrix4<T:copy fuzzy_eq Num>(m11: T, m12: T, m13: T, m14: T,
                                     m21: T, m22: T, m23: T, m24: T,
                                     m31: T, m32: T, m33: T, m34: T,
                                     m41: T, m42: T, m43: T, m44: T)
                                  -> Matrix4<T> {

    Matrix4 {
        m11: m11, m12: m12, m13: m13, m14: m14,
        m21: m21, m22: m22, m23: m23, m24: m24,
        m31: m31, m32: m32, m33: m33, m34: m34,
        m41: m41, m42: m42, m43: m43, m44: m44
    }
}

struct Matrix4<T:copy fuzzy_eq Num> {
    let m11: T; let m12: T; let m13: T; let m14: T;
    let m21: T; let m22: T; let m23: T; let m24: T;
    let m31: T; let m32: T; let m33: T; let m34: T;
    let m41: T; let m42: T; let m43: T; let m44: T;

    pure fn fuzzy_eq(&&other: Matrix4<T>) -> bool {
        self.m11.fuzzy_eq(other.m11) && self.m12.fuzzy_eq(other.m12) &&
        self.m13.fuzzy_eq(other.m13) && self.m14.fuzzy_eq(other.m14) &&
        self.m21.fuzzy_eq(other.m21) && self.m22.fuzzy_eq(other.m22) &&
        self.m23.fuzzy_eq(other.m23) && self.m24.fuzzy_eq(other.m24) &&
        self.m31.fuzzy_eq(other.m31) && self.m32.fuzzy_eq(other.m32) &&
        self.m33.fuzzy_eq(other.m33) && self.m34.fuzzy_eq(other.m34) &&
        self.m41.fuzzy_eq(other.m41) && self.m42.fuzzy_eq(other.m42) &&
        self.m43.fuzzy_eq(other.m43) && self.m44.fuzzy_eq(other.m44)
    }

    pure fn mul(&&m: Matrix4<T>) -> Matrix4<T> {
        return Matrix4(m.m11.mul(self.m11).add(m.m12.mul(self.m21))
                                          .add(m.m13.mul(self.m31))
                                          .add(m.m14.mul(self.m41)),
                       m.m11.mul(self.m12).add(m.m12.mul(self.m22))
                                          .add(m.m13.mul(self.m32))
                                          .add(m.m14.mul(self.m42)),
                       m.m11.mul(self.m13).add(m.m12.mul(self.m23))
                                          .add(m.m13.mul(self.m33))
                                          .add(m.m14.mul(self.m43)),
                       m.m11.mul(self.m14).add(m.m12.mul(self.m24))
                                          .add(m.m13.mul(self.m34))
                                          .add(m.m14.mul(self.m44)),
                       m.m21.mul(self.m11).add(m.m22.mul(self.m21))
                                          .add(m.m23.mul(self.m31))
                                          .add(m.m24.mul(self.m41)),
                       m.m21.mul(self.m12).add(m.m22.mul(self.m22))
                                          .add(m.m23.mul(self.m32))
                                          .add(m.m24.mul(self.m42)),
                       m.m21.mul(self.m13).add(m.m22.mul(self.m23))
                                          .add(m.m23.mul(self.m33))
                                          .add(m.m24.mul(self.m43)),
                       m.m21.mul(self.m14).add(m.m22.mul(self.m24))
                                          .add(m.m23.mul(self.m34))
                                          .add(m.m24.mul(self.m44)),
                       m.m31.mul(self.m11).add(m.m32.mul(self.m21))
                                          .add(m.m33.mul(self.m31))
                                          .add(m.m34.mul(self.m41)),
                       m.m31.mul(self.m12).add(m.m32.mul(self.m22))
                                          .add(m.m33.mul(self.m32))
                                          .add(m.m34.mul(self.m42)),
                       m.m31.mul(self.m13).add(m.m32.mul(self.m23))
                                          .add(m.m33.mul(self.m33))
                                          .add(m.m34.mul(self.m43)),
                       m.m31.mul(self.m14).add(m.m32.mul(self.m24))
                                          .add(m.m33.mul(self.m34))
                                          .add(m.m34.mul(self.m44)),
                       m.m41.mul(self.m11).add(m.m42.mul(self.m21))
                                          .add(m.m43.mul(self.m31))
                                          .add(m.m44.mul(self.m41)),
                       m.m41.mul(self.m12).add(m.m42.mul(self.m22))
                                          .add(m.m43.mul(self.m32))
                                          .add(m.m44.mul(self.m42)),
                       m.m41.mul(self.m13).add(m.m42.mul(self.m23))
                                          .add(m.m43.mul(self.m33))
                                          .add(m.m44.mul(self.m43)),
                       m.m41.mul(self.m14).add(m.m42.mul(self.m24))
                                          .add(m.m43.mul(self.m34))
                                          .add(m.m44.mul(self.m44)));
    }

    pure fn mul_s(&&x: T) -> Matrix4<T> {
        return Matrix4(self.m11.mul(x), self.m12.mul(x), self.m13.mul(x), self.m14.mul(x),
                       self.m21.mul(x), self.m22.mul(x), self.m23.mul(x), self.m24.mul(x),
                       self.m31.mul(x), self.m32.mul(x), self.m33.mul(x), self.m34.mul(x),
                       self.m41.mul(x), self.m42.mul(x), self.m43.mul(x), self.m44.mul(x));
    }

    pure fn scale(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        return Matrix4(self.m11.mul(x), self.m12,        self.m13,        self.m14,
                       self.m21,        self.m22.mul(y), self.m23,        self.m24,
                       self.m31,        self.m32,        self.m33.mul(z), self.m34,
                       self.m41,        self.m42,        self.m43,        self.m44);
    }

    pure fn to_array() -> ~[T] {
        return ~[
            self.m11, self.m12, self.m13, self.m14,
            self.m21, self.m22, self.m23, self.m24,
            self.m31, self.m32, self.m33, self.m34,
            self.m41, self.m42, self.m43, self.m44
        ];
    }

    pure fn translate(&&x: T, &&y: T, &&z: T) -> Matrix4<T> {
        let _0 = x.from_int(0);
        let _1 = x.from_int(1);
        let matrix = Matrix4(_1, _0, _0, _0,
                             _0, _1, _0, _0,
                             _0, _0, _1, _0,
                              x,  y,  z, _1);

        return self.mul(matrix);
    }
}

fn ortho<T:copy fuzzy_eq Num>(+left: T, +right: T, +bottom: T, +top: T, +near: T, +far: T)
                           -> Matrix4<T> {

    let two = left.from_int(2);
    let one = left.from_int(1);
    let zero = left.from_int(0);
    let minus_two = left.from_int(-2);

    let tx = right.add(left).div(right.sub(left)).neg();
    let ty = top.add(bottom).div(top.sub(bottom)).neg();
    let tz = far.add(near).div(far.sub(near)).neg();

    return Matrix4(two.div(right.sub(left)), zero, zero, zero,
                   zero, two.div(top.sub(bottom)), zero, zero,
                   zero, zero, minus_two.div(far.sub(near)), zero,
                   tx, ty, tz, one);
}

fn identity<T:copy fuzzy_eq Num>(_0: T) -> Matrix4<T> {
    let _1 = _0.from_int(1);
    return Matrix4(_1, _0, _0, _0,
                   _0, _1, _0, _0,
                   _0, _0, _1, _0,
                   _0, _0, _0, _1);
}

#[test]
fn test_ortho() {
    let (left, right, bottom, top) = (0.0, 1.0, 0.1, 1.0);
    let (near, far) = (-1.0, 1.0);
    let result = ortho(left, right, bottom, top, near, far);
    let expected = Matrix4(2.0,  0.0,         0.0,  0.0,
                           0.0,  2.22222222,  0.0,  0.0,
                           0.0,  0.0,         -1.0, 0.0,
                           -1.0, -1.22222222, -0.0, 1.0);
    #debug("result=%? expected=%?", result, expected);
    assert result.fuzzy_eq(expected);
}

<|endoftext|>


<commit_before>use value::Value;

type Id = String;

// a table with a single unique key K
type KV<K, V> = Hashtable<K, V>;

// every record in this struct represents a table
// the tables are named by their fields, separated by underscores
// eg view_kind is a table with two fields: view and kind
type Program = {
    // all state lives in a view of some kind
    view_kind: KV<Id, ViewKind>,

    // views have fields which are globally unique
    field_view: KV<Id, Id>,
    // some fields have constraints on how they can be queried
    field_kind: KV<Id, FieldKind>,

    // a table has an insert view and a remove view
    table_insert: KV<Id, Id>,
    table_remove: KV<Id, Id>,

    // sources uniquely identify a copy of a child view inside a parent join or union
    // eg a parent join can have multiple copies of some child view, with different source ids
    parent_source_child: KV<(Id, Id), Id>,
    // every view also has an implicit "constant" source
    // anywhere a source-field pair is expected, you can instead use "constant"-id
    constant_value: KV<Id, Value>,

    // constraints belong to a join view
    // the left and right fields are compared using the operation
    constraint_parent: KV<Id, Id>,
    constraint_leftsource_leftfield: KV<Id, (Id, Id)>,
    constraint_operation: KV<Id, ConstraintOperation>,
    constraint_rightsource_rightfield: KV<Id, (Id, Id)>,

    // aggregates group an "inner" source by the rows of an "outer" source
    // the groups are reduced by joining against a "primitive" source
    // the grouping is determined by binding inner fields to outer fields or constants
    aggregate_innerfield_source_sourcefield: KV<(Id, Id), (Id, Id)>,
    // inner fields and constants can also be used as arguments to the aggregate function
    aggregate_primitivefield_source_sourcefield: KV<(Id, Id), (Id, Id)>,

    // views produce output by binding fields from sources
    // each join field is bound exactly once
    join_joinfield_source_sourcefield: KV<(Id, Id), (Id, Id)>,
    // each union field is bound exactly once per source
    union_unionfield_source_sourcefield: KV<(Id, Id, Id), Id>,
    // each aggregate field is bound exactly once and cannot bind "outer"
    aggregate_aggregatefield_source_sourcefield: KV<(Id, Id), (Id, Id)>,

    // things that live in an ordered list are sorted by some f64
    thing_order: KV<Id, f64>,

    // things can have human readable names
    thing_name: KV<Id, String>,

    // TODO primitives cannot represent limit
    // TODO want multiple primitives per aggregate?
    // TODO sort order for aggregates?
}

enum ViewKind {
    Table, // a view which can depend on the past
    Join, // take the product of multiple views and filter the results
    Union, // take the union of multiple views
    Aggregate, // group one view by the contents of another and run some primitive on the groups
    Primitive, // a built-in function, represented as a view with one or more non-Data fields
}

enum FieldKind {
    Data, // a normal field
    ScalarInput, // a field that must be constrained to a single scalar value
    VectorInput, // a field that must be constrained to a single vector value (in an aggregate)
}

enum ConstraintOperation {
    Equal,
    NotEqual,
    LessThan,
    LessThanOrEqual,
    GreaterThan,
    GreaterThanOrEqual,
}<commit_msg>Handle sort, limit and multiple reducers<commit_after>use value::Value;

type Id = String;

// a table with a single unique key K
type KV<K, V> = Hashtable<K, V>;

// every record in this struct represents a table
// the tables are named by their fields, separated by underscores
// eg view_kind is a table with two fields: view and kind
type Program = {
    // all state lives in a view of some kind
    view_kind: KV<Id, ViewKind>,

    // views have fields which are globally unique
    field_view: KV<Id, Id>,
    // some fields have constraints on how they can be queried
    field_kind: KV<Id, FieldKind>,

    // a table has an insert view and a remove view
    table_insert: KV<Id, Id>,
    table_remove: KV<Id, Id>,

    // sources uniquely identify a copy of a child view inside a parent join or union
    // eg a parent join can have multiple copies of some child view, with different source ids
    parent_source_child: KV<(Id, Id), Id>,
    // every view also has an implicit "constant" source
    // anywhere a source-field pair is expected, you can instead use "constant"-id
    constant_value: KV<Id, Value>,

    // constraints belong to a join view
    // the left and right fields are compared using the operation
    constraint_parent: KV<Id, Id>,
    constraint_leftsource_leftfield: KV<Id, (Id, Id)>,
    constraint_operation: KV<Id, ConstraintOperation>,
    constraint_rightsource_rightfield: KV<Id, (Id, Id)>,

    // aggregates group an "inner" source by the rows of an "outer" source
    // the grouping is determined by binding inner fields to outer fields or constants
    aggregate_innerfield_groupsource_groupfield: KV<(Id, Id), (Id, Id)>,
    // before aggregation the groups are sorted
    aggregate_innerfield_order: KV<(Id, Id), f64>,
    // groups may be limited by an inner field or constant
    aggregate_fromsource_fromfield: KV<Id, (Id, Id)>,
    aggregate_tosource_tofield: KV<Id, (Id, Id)>,
    // the groups are reduced by binding against reducer sources
    // constants and inner fields which are bound to outer fields may both be used as ScalarInput arguments
    // inner fields which are not bound to outer fields may be used as VectorInput arguments
    aggregate_reducersource_reducerfield_argumentsource_argumentfield: KV<(Id, Id, Id), (Id, Id)>,

    // views produce output by binding fields from sources
    // each join field is bound exactly once
    // each union field is bound exactly once per source
    // each aggregate field is bound exactly once and can only bind constants, inner fields or reducer outputs
    view_outputfield_inputsource_inputfield: KV<(Id, Id), (Id, Id)>,

    // things that live in an ordered list are sorted by some f64
    // ties are broken by Id
    thing_order: KV<Id, f64>,

    // things can have human readable names
    thing_name: KV<Id, String>,
}

enum ViewKind {
    Table, // a view which can depend on the past
    Join, // take the product of multiple views and filter the results
    Union, // take the union of multiple views
    Aggregate, // group one view by the contents of another and run reducing functions on the groups
    Primitive, // a built-in function, represented as a view with one or more non-Data fields
}

enum FieldKind {
    Output, // a normal field
    ScalarInput, // a field that must be constrained to a single scalar value
    VectorInput, // a field that must be constrained to a single vector value (in an aggregate)
}

enum ConstraintOperation {
    Equal,
    NotEqual,
    LessThan,
    LessThanOrEqual,
    GreaterThan,
    GreaterThanOrEqual,
}<|endoftext|>


<commit_before>//! varlink_parser crate for parsing [varlink](http://varlink.org) interface definition files.
//!
//! # Examples
//!
//! ```rust
//! use varlink_parser::Varlink;
//! let v = Varlink::from_string("
//! ## The Varlink Service Interface is provided by every varlink service. It
//! ## describes the service and the interfaces it implements.
//! interface org.varlink.service
//!
//! ## Get a list of all the interfaces a service provides and information
//! ## about the implementation.
//! method GetInfo() -> (
//! vendor: string,
//! product: string,
//! version: string,
//! url: string,
//! interfaces: []string
//! )
//!
//! ## Get the description of an interface that is implemented by this service.
//! method GetInterfaceDescription(interface: string) -> (description: string)
//!
//! ## The requested interface was not found.
//! error InterfaceNotFound (interface: string)
//!
//! ## The requested method was not found
//! error MethodNotFound (method: string)
//!
//! ## The interface defines the requested method, but the service does not
//! ## implement it.
//! error MethodNotImplemented (method: string)
//!
//! ## One of the passed parameters is invalid.
//! error InvalidParameter (parameter: string)
//! ").unwrap();
//!    assert_eq!(v.interface.name, "org.varlink.service");
//! ```

extern crate bytes;
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate itertools;

use self::varlink_grammar::VInterface;
use failure::{Backtrace, Context, Fail, ResultExt};
use itertools::Itertools;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::fmt::{self, Display};

#[cfg(test)]
mod test;

mod varlink_grammar {
    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

#[derive(Debug)]
pub struct Error {
    inner: Context<ErrorKind>,
}

#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
    #[fail(display = "Parse error")]
    Peg,
    #[fail(display = "Interface definition error: '{}'", _0)]
    InterfaceDefinition(String),
}

impl Fail for Error {
    fn cause(&self) -> Option<&Fail> {
        self.inner.cause()
    }

    fn backtrace(&self) -> Option<&Backtrace> {
        self.inner.backtrace()
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(&self.inner, f)
    }
}

impl Error {
    pub fn kind(&self) -> ErrorKind {
        self.inner.get_context().clone()
    }
}

impl From<ErrorKind> for Error {
    fn from(kind: ErrorKind) -> Error {
        Error {
            inner: Context::new(kind),
        }
    }
}

impl From<Context<ErrorKind>> for Error {
    fn from(inner: Context<ErrorKind>) -> Error {
        Error { inner }
    }
}

pub type Result<T> = std::result::Result<T, Error>;

pub enum VType<'a> {
    Bool,
    Int,
    Float,
    String,
    Object,
    Typename(&'a str),
    Struct(Box<VStruct<'a>>),
    Enum(Box<VEnum<'a>>),
}

pub enum VTypeExt<'a> {
    Array(Box<VTypeExt<'a>>),
    Dict(Box<VTypeExt<'a>>),
    Option(Box<VTypeExt<'a>>),
    Plain(VType<'a>),
}

pub struct Argument<'a> {
    pub name: &'a str,
    pub vtype: VTypeExt<'a>,
}

pub struct VStruct<'a> {
    pub elts: Vec<Argument<'a>>,
}

pub struct VEnum<'a> {
    pub elts: Vec<&'a str>,
}

pub struct VError<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub parm: VStruct<'a>,
}

pub enum VStructOrEnum<'a> {
    VStruct(Box<VStruct<'a>>),
    VEnum(Box<VEnum<'a>>),
}

pub struct Typedef<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub elt: VStructOrEnum<'a>,
}

pub struct Method<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub input: VStruct<'a>,
    pub output: VStruct<'a>,
}

enum MethodOrTypedefOrError<'a> {
    Error(VError<'a>),
    Typedef(Typedef<'a>),
    Method(Method<'a>),
}

pub struct Interface<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub methods: BTreeMap<&'a str, Method<'a>>,
    pub typedefs: BTreeMap<&'a str, Typedef<'a>>,
    pub errors: BTreeMap<&'a str, VError<'a>>,
    pub error: HashSet<String>,
}

macro_rules! printVTypeExt {
    ($s:ident, $f:ident, $t:expr) => {{
        write!($f, "{}", $t)?;
    }};
}

impl<'a> fmt::Display for VTypeExt<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &VTypeExt::Plain(VType::Bool) => printVTypeExt!(self, f, "bool"),
            &VTypeExt::Plain(VType::Int) => printVTypeExt!(self, f, "int"),
            &VTypeExt::Plain(VType::Float) => printVTypeExt!(self, f, "float"),
            &VTypeExt::Plain(VType::String) => printVTypeExt!(self, f, "string"),
            &VTypeExt::Plain(VType::Object) => printVTypeExt!(self, f, "object"),
            &VTypeExt::Plain(VType::Typename(ref v)) => printVTypeExt!(self, f, v),
            &VTypeExt::Plain(VType::Struct(ref v)) => printVTypeExt!(self, f, v),
            &VTypeExt::Plain(VType::Enum(ref v)) => printVTypeExt!(self, f, v),
            &VTypeExt::Array(ref v) => write!(f, "[]{}", v)?,
            &VTypeExt::Dict(ref v) => write!(f, "[dict]{}", v)?,
            &VTypeExt::Option(ref v) => write!(f, "?{}", v)?,
        }
        Ok(())
    }
}

impl<'a> fmt::Display for VStructOrEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VStructOrEnum::VStruct(ref v) => write!(f, "{}", v)?,
            VStructOrEnum::VEnum(ref v) => write!(f, "{}", v)?,
        }
        Ok(())
    }
}

impl<'a> fmt::Display for Argument<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.name, self.vtype)?;
        Ok(())
    }
}

impl<'a> fmt::Display for VStruct<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for VEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for Interface<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.doc.len() > 0 {
            write!(f, "{}\n", self.doc)?;
        }
        write!(f, "interface {}\n", self.name)?;

        for t in self.typedefs.values() {
            write!(f, "\n")?;
            if t.doc.len() > 0 {
                write!(f, "{}\n", t.doc)?;
            }
            write!(f, "type {} {}\n", t.name, t.elt)?;
        }

        for m in self.methods.values() {
            write!(f, "\n")?;
            if m.doc.len() > 0 {
                write!(f, "{}\n", m.doc)?;
            }
            write!(f, "method {}{} -> {}\n", m.name, m.input, m.output)?;
        }

        for e in self.errors.values() {
            write!(f, "\n")?;
            if e.doc.len() > 0 {
                write!(f, "{}\n", e.doc)?;
            }
            write!(f, "error {} {}\n", e.name, e.parm)?;
        }
        Ok(())
    }
}

pub fn trim_doc(s: &str) -> &str {
    s.trim_matches(&[
        ' ', '\n', '\r', '\u{00A0}', '\u{FEFF}', '\u{1680}', '\u{180E}', '\u{2000}', '\u{2001}',
        '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}',
        '\u{2009}', '\u{200A}', '\u{202F}', '\u{205F}', '\u{3000}', '\u{2028}', '\u{2029}',
    ] as &[_])
}

impl<'a> Interface<'a> {
    fn from_token(n: &'a str, mt: Vec<MethodOrTypedefOrError<'a>>, doc: &'a str) -> Interface<'a> {
        let mut i = Interface {
            name: n,
            doc: doc,
            methods: BTreeMap::new(),
            typedefs: BTreeMap::new(),
            errors: BTreeMap::new(),
            error: HashSet::new(),
        };

        for o in mt {
            match o {
                MethodOrTypedefOrError::Method(m) => {
                    if let Some(d) = i.methods.insert(m.name, m) {
                        i.error.insert(format!(
                            "Interface `{}`: multiple definitions of type `{}`!",
                            i.name, d.name
                        ));
                    };
                }
                MethodOrTypedefOrError::Typedef(t) => {
                    if let Some(d) = i.typedefs.insert(t.name, t) {
                        i.error.insert(format!(
                            "Interface `{}`: multiple definitions of type `{}`!",
                            i.name, d.name
                        ));
                    };
                }
                MethodOrTypedefOrError::Error(e) => {
                    if let Some(d) = i.errors.insert(e.name, e) {
                        i.error.insert(format!(
                            "Interface `{}`: multiple definitions of error `{}`!",
                            i.name, d.name
                        ));
                    };
                }
            };
        }
        if i.methods.len() == 0 {
            i.error
                .insert(format!("Interface `{}`: no method defined!", i.name));
        }

        i
    }
}

pub struct Varlink<'a> {
    pub string: &'a str,
    pub interface: Interface<'a>,
}

impl<'a> Varlink<'a> {
    pub fn from_string<S: ?Sized + AsRef<str>>(s: &'a S) -> Result<Self> {
        let s = s.as_ref();
        let iface = VInterface(s).context(ErrorKind::Peg)?;

        if iface.error.len() != 0 {
            Err(ErrorKind::InterfaceDefinition(iface.error.into_iter().sorted().join("\n")).into())
        } else {
            Ok(Varlink {
                string: s,
                interface: iface,
            })
        }
    }
}
<commit_msg>make trim_doc private<commit_after>//! varlink_parser crate for parsing [varlink](http://varlink.org) interface definition files.
//!
//! # Examples
//!
//! ```rust
//! use varlink_parser::Varlink;
//! let v = Varlink::from_string("
//! ## The Varlink Service Interface is provided by every varlink service. It
//! ## describes the service and the interfaces it implements.
//! interface org.varlink.service
//!
//! ## Get a list of all the interfaces a service provides and information
//! ## about the implementation.
//! method GetInfo() -> (
//! vendor: string,
//! product: string,
//! version: string,
//! url: string,
//! interfaces: []string
//! )
//!
//! ## Get the description of an interface that is implemented by this service.
//! method GetInterfaceDescription(interface: string) -> (description: string)
//!
//! ## The requested interface was not found.
//! error InterfaceNotFound (interface: string)
//!
//! ## The requested method was not found
//! error MethodNotFound (method: string)
//!
//! ## The interface defines the requested method, but the service does not
//! ## implement it.
//! error MethodNotImplemented (method: string)
//!
//! ## One of the passed parameters is invalid.
//! error InvalidParameter (parameter: string)
//! ").unwrap();
//!    assert_eq!(v.interface.name, "org.varlink.service");
//! ```

extern crate bytes;
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate itertools;

use self::varlink_grammar::VInterface;
use failure::{Backtrace, Context, Fail, ResultExt};
use itertools::Itertools;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::fmt::{self, Display};

#[cfg(test)]
mod test;

mod varlink_grammar {
    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

#[derive(Debug)]
pub struct Error {
    inner: Context<ErrorKind>,
}

#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
    #[fail(display = "Parse error")]
    Peg,
    #[fail(display = "Interface definition error: '{}'", _0)]
    InterfaceDefinition(String),
}

impl Fail for Error {
    fn cause(&self) -> Option<&Fail> {
        self.inner.cause()
    }

    fn backtrace(&self) -> Option<&Backtrace> {
        self.inner.backtrace()
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(&self.inner, f)
    }
}

impl Error {
    pub fn kind(&self) -> ErrorKind {
        self.inner.get_context().clone()
    }
}

impl From<ErrorKind> for Error {
    fn from(kind: ErrorKind) -> Error {
        Error {
            inner: Context::new(kind),
        }
    }
}

impl From<Context<ErrorKind>> for Error {
    fn from(inner: Context<ErrorKind>) -> Error {
        Error { inner }
    }
}

pub type Result<T> = std::result::Result<T, Error>;

pub enum VType<'a> {
    Bool,
    Int,
    Float,
    String,
    Object,
    Typename(&'a str),
    Struct(Box<VStruct<'a>>),
    Enum(Box<VEnum<'a>>),
}

pub enum VTypeExt<'a> {
    Array(Box<VTypeExt<'a>>),
    Dict(Box<VTypeExt<'a>>),
    Option(Box<VTypeExt<'a>>),
    Plain(VType<'a>),
}

pub struct Argument<'a> {
    pub name: &'a str,
    pub vtype: VTypeExt<'a>,
}

pub struct VStruct<'a> {
    pub elts: Vec<Argument<'a>>,
}

pub struct VEnum<'a> {
    pub elts: Vec<&'a str>,
}

pub struct VError<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub parm: VStruct<'a>,
}

pub enum VStructOrEnum<'a> {
    VStruct(Box<VStruct<'a>>),
    VEnum(Box<VEnum<'a>>),
}

pub struct Typedef<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub elt: VStructOrEnum<'a>,
}

pub struct Method<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub input: VStruct<'a>,
    pub output: VStruct<'a>,
}

enum MethodOrTypedefOrError<'a> {
    Error(VError<'a>),
    Typedef(Typedef<'a>),
    Method(Method<'a>),
}

pub struct Interface<'a> {
    pub name: &'a str,
    pub doc: &'a str,
    pub methods: BTreeMap<&'a str, Method<'a>>,
    pub typedefs: BTreeMap<&'a str, Typedef<'a>>,
    pub errors: BTreeMap<&'a str, VError<'a>>,
    pub error: HashSet<String>,
}

macro_rules! printVTypeExt {
    ($s:ident, $f:ident, $t:expr) => {{
        write!($f, "{}", $t)?;
    }};
}

impl<'a> fmt::Display for VTypeExt<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &VTypeExt::Plain(VType::Bool) => printVTypeExt!(self, f, "bool"),
            &VTypeExt::Plain(VType::Int) => printVTypeExt!(self, f, "int"),
            &VTypeExt::Plain(VType::Float) => printVTypeExt!(self, f, "float"),
            &VTypeExt::Plain(VType::String) => printVTypeExt!(self, f, "string"),
            &VTypeExt::Plain(VType::Object) => printVTypeExt!(self, f, "object"),
            &VTypeExt::Plain(VType::Typename(ref v)) => printVTypeExt!(self, f, v),
            &VTypeExt::Plain(VType::Struct(ref v)) => printVTypeExt!(self, f, v),
            &VTypeExt::Plain(VType::Enum(ref v)) => printVTypeExt!(self, f, v),
            &VTypeExt::Array(ref v) => write!(f, "[]{}", v)?,
            &VTypeExt::Dict(ref v) => write!(f, "[dict]{}", v)?,
            &VTypeExt::Option(ref v) => write!(f, "?{}", v)?,
        }
        Ok(())
    }
}

impl<'a> fmt::Display for VStructOrEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VStructOrEnum::VStruct(ref v) => write!(f, "{}", v)?,
            VStructOrEnum::VEnum(ref v) => write!(f, "{}", v)?,
        }
        Ok(())
    }
}

impl<'a> fmt::Display for Argument<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.name, self.vtype)?;
        Ok(())
    }
}

impl<'a> fmt::Display for VStruct<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for VEnum<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        let mut iter = self.elts.iter();
        if let Some(fst) = iter.next() {
            write!(f, "{}", fst)?;
            for elt in iter {
                write!(f, ", {}", elt)?;
            }
        }
        write!(f, ")")
    }
}

impl<'a> fmt::Display for Interface<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.doc.len() > 0 {
            write!(f, "{}\n", self.doc)?;
        }
        write!(f, "interface {}\n", self.name)?;

        for t in self.typedefs.values() {
            write!(f, "\n")?;
            if t.doc.len() > 0 {
                write!(f, "{}\n", t.doc)?;
            }
            write!(f, "type {} {}\n", t.name, t.elt)?;
        }

        for m in self.methods.values() {
            write!(f, "\n")?;
            if m.doc.len() > 0 {
                write!(f, "{}\n", m.doc)?;
            }
            write!(f, "method {}{} -> {}\n", m.name, m.input, m.output)?;
        }

        for e in self.errors.values() {
            write!(f, "\n")?;
            if e.doc.len() > 0 {
                write!(f, "{}\n", e.doc)?;
            }
            write!(f, "error {} {}\n", e.name, e.parm)?;
        }
        Ok(())
    }
}

fn trim_doc(s: &str) -> &str {
    s.trim_matches(&[
        ' ', '\n', '\r', '\u{00A0}', '\u{FEFF}', '\u{1680}', '\u{180E}', '\u{2000}', '\u{2001}',
        '\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}',
        '\u{2009}', '\u{200A}', '\u{202F}', '\u{205F}', '\u{3000}', '\u{2028}', '\u{2029}',
    ] as &[_])
}

impl<'a> Interface<'a> {
    fn from_token(n: &'a str, mt: Vec<MethodOrTypedefOrError<'a>>, doc: &'a str) -> Interface<'a> {
        let mut i = Interface {
            name: n,
            doc: doc,
            methods: BTreeMap::new(),
            typedefs: BTreeMap::new(),
            errors: BTreeMap::new(),
            error: HashSet::new(),
        };

        for o in mt {
            match o {
                MethodOrTypedefOrError::Method(m) => {
                    if let Some(d) = i.methods.insert(m.name, m) {
                        i.error.insert(format!(
                            "Interface `{}`: multiple definitions of type `{}`!",
                            i.name, d.name
                        ));
                    };
                }
                MethodOrTypedefOrError::Typedef(t) => {
                    if let Some(d) = i.typedefs.insert(t.name, t) {
                        i.error.insert(format!(
                            "Interface `{}`: multiple definitions of type `{}`!",
                            i.name, d.name
                        ));
                    };
                }
                MethodOrTypedefOrError::Error(e) => {
                    if let Some(d) = i.errors.insert(e.name, e) {
                        i.error.insert(format!(
                            "Interface `{}`: multiple definitions of error `{}`!",
                            i.name, d.name
                        ));
                    };
                }
            };
        }
        if i.methods.len() == 0 {
            i.error
                .insert(format!("Interface `{}`: no method defined!", i.name));
        }

        i
    }
}

pub struct Varlink<'a> {
    pub string: &'a str,
    pub interface: Interface<'a>,
}

impl<'a> Varlink<'a> {
    pub fn from_string<S: ?Sized + AsRef<str>>(s: &'a S) -> Result<Self> {
        let s = s.as_ref();
        let iface = VInterface(s).context(ErrorKind::Peg)?;

        if iface.error.len() != 0 {
            Err(ErrorKind::InterfaceDefinition(iface.error.into_iter().sorted().join("\n")).into())
        } else {
            Ok(Varlink {
                string: s,
                interface: iface,
            })
        }
    }
}
<|endoftext|>


<commit_before>//! # Integration tests.
//!
//! These tests are run on our executable to make sure that all the
//! command-line options work correctly.

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};

static TEST_ID: AtomicUsize = ATOMIC_USIZE_INIT;

/// This code is inspired by the `WorkDir` pattern that BurntSushi uses to
/// test CLI tools like `ripgrep` and `xsv`.
struct WorkDir {
    bin: PathBuf,
    dir: PathBuf,
}

impl WorkDir {
    fn new(test_name: &str) -> WorkDir {
        let mut bin_dir = env::current_exe().unwrap().parent().unwrap().to_path_buf();
        if bin_dir.ends_with("deps") {
            bin_dir.pop();
        }
        let id = TEST_ID.fetch_add(1, Ordering::SeqCst);
        let dir = bin_dir.join("vobsub2png-tests")
            .join(test_name)
            .join(format!("{}", id));
        if dir.exists() {
            fs::remove_dir_all(&dir).unwrap();
        }
        fs::create_dir_all(&dir).unwrap();
        WorkDir {
            bin: bin_dir.join("vobsub2png"),
            dir: dir,
        }
    }

    fn cmd(&self) -> process::Command {
        let mut cmd = process::Command::new(&self.bin);
        cmd.current_dir(&self.dir);
        cmd
    }

    fn fixture<P: AsRef<Path>>(&self, path: P) -> PathBuf {
        fs::canonicalize(env::current_dir().unwrap().join(path)).unwrap()
    }

    fn expect_path<P: AsRef<Path>>(&self, path: P) {
        let path = self.dir.join(path);
        assert!(path.exists(), format!("{} exists", path.display()));
    }
}

#[test]
fn generates_png_and_json_files() {
    let workdir = WorkDir::new("generates_png_and_json_files");
    let status = workdir.cmd()
        .arg("-o")
        .arg("out")
        .arg(workdir.fixture("../fixtures/example.idx"))
        .status()
        .expect("could not run command");
    assert!(status.success());
    workdir.expect_path("out/index.json");
    workdir.expect_path("out/0000.png");
    workdir.expect_path("out/0001.png");
}

#[test]
fn defaults_out_dir_based_on_input_name() {
    let workdir = WorkDir::new("generates_png_and_json_files");
    let status = workdir.cmd()
        .arg(workdir.fixture("../fixtures/example.idx"))
        .status()
        .expect("could not run command");
    assert!(status.success());
    workdir.expect_path("example_subtitles/index.json");
    workdir.expect_path("example_subtitles/0000.png");
    workdir.expect_path("example_subtitles/0001.png");
}
<commit_msg>Improve error messages for test failures<commit_after>//! # Integration tests.
//!
//! These tests are run on our executable to make sure that all the
//! command-line options work correctly.

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};

static TEST_ID: AtomicUsize = ATOMIC_USIZE_INIT;

/// This code is inspired by the `WorkDir` pattern that BurntSushi uses to
/// test CLI tools like `ripgrep` and `xsv`.
struct WorkDir {
    bin: PathBuf,
    dir: PathBuf,
}

impl WorkDir {
    fn new(test_name: &str) -> WorkDir {
        let mut bin_dir = env::current_exe()
            .expect("Could not find executable")
            .parent()
            .expect("Could not find parent directory for executable")
            .to_path_buf();
        if bin_dir.ends_with("deps") {
            bin_dir.pop();
        }
        let id = TEST_ID.fetch_add(1, Ordering::SeqCst);
        let dir = bin_dir.join("vobsub2png-tests")
            .join(test_name)
            .join(format!("{}", id));
        if dir.exists() {
            fs::remove_dir_all(&dir)
                .expect("Could not remove test output directory");
        }
        fs::create_dir_all(&dir)
            .expect("Could not create test output directory");
        WorkDir {
            bin: bin_dir.join("vobsub2png"),
            dir: dir,
        }
    }

    fn cmd(&self) -> process::Command {
        let mut cmd = process::Command::new(&self.bin);
        cmd.current_dir(&self.dir);
        cmd
    }

    fn fixture<P: AsRef<Path>>(&self, path: P) -> PathBuf {
        let cwd = env::current_dir().expect("Could not get current dir");
        fs::canonicalize(cwd.join(path))
            .expect("Could not canonicalize path")
    }

    fn expect_path<P: AsRef<Path>>(&self, path: P) {
        let path = self.dir.join(path);
        assert!(path.exists(), format!("{} exists", path.display()));
    }
}

#[test]
fn generates_png_and_json_files() {
    let workdir = WorkDir::new("generates_png_and_json_files");
    let status = workdir.cmd()
        .arg("-o")
        .arg("out")
        .arg(workdir.fixture("../fixtures/example.idx"))
        .status()
        .expect("could not run command");
    assert!(status.success());
    workdir.expect_path("out/index.json");
    workdir.expect_path("out/0000.png");
    workdir.expect_path("out/0001.png");
}

#[test]
fn defaults_out_dir_based_on_input_name() {
    let workdir = WorkDir::new("generates_png_and_json_files");
    let status = workdir.cmd()
        .arg(workdir.fixture("../fixtures/example.idx"))
        .status()
        .expect("could not run command");
    assert!(status.success());
    workdir.expect_path("example_subtitles/index.json");
    workdir.expect_path("example_subtitles/0000.png");
    workdir.expect_path("example_subtitles/0001.png");
}
<|endoftext|>