<commit_before>#![doc(html_root_url="https://sfackler.github.io/doc")]
#![feature(if_let)]
extern crate r2d2;
extern crate postgres;

use std::cell::RefCell;
use std::collections::LruCache;
use std::default::Default;
use std::fmt;
use std::mem;
use std::rc::Rc;
use postgres::{IntoConnectParams, SslMode};
use postgres::error::{PostgresConnectError, PostgresError};
use postgres::types::ToSql;

pub enum Error {
    ConnectError(PostgresConnectError),
    OtherError(PostgresError),
}

impl fmt::Show for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConnectError(ref e) => write!(fmt, "{}", e),
            OtherError(ref e) => write!(fmt, "{}", e),
        }
    }
}

pub struct PostgresPoolManager {
    params: Result<postgres::ConnectParams, PostgresConnectError>,
    ssl_mode: SslMode,
}

impl PostgresPoolManager {
    pub fn new<T: IntoConnectParams>(params: T, ssl_mode: SslMode) -> PostgresPoolManager {
        PostgresPoolManager {
            params: params.into_connect_params(),
            ssl_mode: ssl_mode,
        }
    }
}

impl r2d2::PoolManager<postgres::Connection, Error> for PostgresPoolManager {
    fn connect(&self) -> Result<postgres::Connection, Error> {
        match self.params {
            Ok(ref p) => {
                postgres::Connection::connect(p.clone(), &self.ssl_mode).map_err(ConnectError)
            }
            Err(ref e) => Err(ConnectError(e.clone()))
        }
    }

    fn is_valid(&self, conn: &mut postgres::Connection) -> Result<(), Error> {
        conn.batch_execute("").map_err(OtherError)
    }

    fn has_broken(&self, conn: &mut postgres::Connection) -> bool {
        conn.is_desynchronized()
    }
}

pub struct Config {
    pub statement_pool_size: uint,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            statement_pool_size: 10,
        }
    }
}

pub struct StatementCachingManager {
    manager: PostgresPoolManager,
    config: Config,
}

impl StatementCachingManager {
    pub fn new<T>(params: T, ssl_mode: SslMode, config: Config) -> StatementCachingManager
            where T: IntoConnectParams {
        StatementCachingManager {
            manager: PostgresPoolManager::new(params, ssl_mode),
            config: config
        }
    }
}

impl r2d2::PoolManager<Connection, Error> for StatementCachingManager {
    fn connect(&self) -> Result<Connection, Error> {
        let cache = box RefCell::new(LruCache::<String, postgres::Statement<'static>>::new(
                self.config.statement_pool_size));
        Ok(Connection {
            conn: box try!(self.manager.connect()),
            stmts: unsafe { mem::transmute(cache) },
        })
    }

    fn is_valid(&self, conn: &mut Connection) -> Result<(), Error> {
        self.manager.is_valid(&mut *conn.conn)
    }

    fn has_broken(&self, conn: &mut Connection) -> bool {
        self.manager.has_broken(&mut *conn.conn)
    }
}

pub trait GenericConnection {
    /// Like `postgres::Connection::prepare`.
    fn prepare<'a>(&'a self, query: &str) -> postgres::Result<Rc<postgres::Statement<'a>>>;

    /// Like `postgres::Connection::execute`.
    fn execute(&self, query: &str, params: &[&ToSql]) -> postgres::Result<uint> {
        self.prepare(query).and_then(|s| s.execute(params))
    }

    /// Like `postgres::Connection::prepare_copy_in`.
    fn prepare_copy_in<'a>(&'a self, table: &str, columns: &[&str])
                           -> postgres::Result<postgres::CopyInStatement<'a>>;

    /// Like `postgres::Connection::transaction`.
    fn transaction<'a>(&'a self) -> postgres::Result<Transaction<'a>>;

    /// Like `postgres::Connection::batch_execute`.
    fn batch_execute(&self, query: &str) -> postgres::Result<()>;
}

pub struct Connection {
    conn: Box<postgres::Connection>,
    stmts: *mut (),
}

impl Drop for Connection {
    fn drop(&mut self) {
        let _: Box<RefCell<LruCache<String, Rc<postgres::Statement<'static>>>>> =
            unsafe { mem::transmute(self.stmts) };
    }
}

impl Connection {
    fn get_cache<'a>(&'a self) -> &'a RefCell<LruCache<String, Rc<postgres::Statement<'a>>>> {
        unsafe { mem::transmute(self.stmts) }
    }
}

impl GenericConnection for Connection {
    fn prepare<'a>(&'a self, query: &str) -> postgres::Result<Rc<postgres::Statement<'a>>> {
        let query = query.into_string();
        let mut stmts = self.get_cache().borrow_mut();

        if let Some(stmt) = stmts.get(&query) {
            return Ok(stmt.clone());
        }

        let stmt = Rc::new(try!(self.conn.prepare(query[])));
        stmts.put(query, stmt.clone());
        Ok(stmt)
    }

    fn prepare_copy_in<'a>(&'a self, table: &str, columns: &[&str])
                           -> postgres::Result<postgres::CopyInStatement<'a>> {
        self.conn.prepare_copy_in(table, columns)
    }

    fn transaction<'a>(&'a self) -> postgres::Result<Transaction<'a>> {
        Ok(Transaction {
            conn: self,
            trans: try!(self.conn.transaction())
        })
    }

    fn batch_execute(&self, query: &str) -> postgres::Result<()> {
        self.conn.batch_execute(query)
    }
}

pub struct Transaction<'a> {
    conn: &'a Connection,
    trans: postgres::Transaction<'a>
}

impl<'a> GenericConnection for Transaction<'a> {
    fn prepare<'a>(&'a self, query: &str) -> postgres::Result<Rc<postgres::Statement<'a>>> {
        let query = query.into_string();
        let mut stmts = self.conn.get_cache().borrow_mut();

        if let Some(stmt) = stmts.get(&query) {
            return Ok(stmt.clone());
        }

        Ok(Rc::new(try!(self.trans.prepare(query[]))))
    }

    fn prepare_copy_in<'a>(&'a self, table: &str, columns: &[&str])
                           -> postgres::Result<postgres::CopyInStatement<'a>> {
        self.trans.prepare_copy_in(table, columns)
    }

    fn transaction<'a>(&'a self) -> postgres::Result<Transaction<'a>> {
        Ok(Transaction {
            conn: self.conn,
            trans: try!(self.trans.transaction())
        })
    }

    fn batch_execute(&self, query: &str) -> postgres::Result<()> {
        self.trans.batch_execute(query)
    }
}
<commit_msg>Update for postgres changes<commit_after>#![doc(html_root_url="https://sfackler.github.io/doc")]
#![feature(if_let)]
extern crate r2d2;
extern crate postgres;

use std::cell::RefCell;
use std::collections::LruCache;
use std::default::Default;
use std::fmt;
use std::mem;
use std::rc::Rc;
use postgres::{IntoConnectParams, SslMode};
use postgres::types::ToSql;

pub enum Error {
    Connect(postgres::ConnectError),
    Other(postgres::Error),
}

impl fmt::Show for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Connect(ref e) => write!(fmt, "{}", e),
            Error::Other(ref e) => write!(fmt, "{}", e),
        }
    }
}

pub struct PostgresPoolManager {
    params: Result<postgres::ConnectParams, postgres::ConnectError>,
    ssl_mode: SslMode,
}

impl PostgresPoolManager {
    pub fn new<T: IntoConnectParams>(params: T, ssl_mode: SslMode) -> PostgresPoolManager {
        PostgresPoolManager {
            params: params.into_connect_params(),
            ssl_mode: ssl_mode,
        }
    }
}

impl r2d2::PoolManager<postgres::Connection, Error> for PostgresPoolManager {
    fn connect(&self) -> Result<postgres::Connection, Error> {
        match self.params {
            Ok(ref p) => {
                postgres::Connection::connect(p.clone(), &self.ssl_mode).map_err(Error::Connect)
            }
            Err(ref e) => Err(Error::Connect(e.clone()))
        }
    }

    fn is_valid(&self, conn: &mut postgres::Connection) -> Result<(), Error> {
        conn.batch_execute("").map_err(Error::Other)
    }

    fn has_broken(&self, conn: &mut postgres::Connection) -> bool {
        conn.is_desynchronized()
    }
}

pub struct Config {
    pub statement_pool_size: uint,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            statement_pool_size: 10,
        }
    }
}

pub struct StatementCachingManager {
    manager: PostgresPoolManager,
    config: Config,
}

impl StatementCachingManager {
    pub fn new<T>(params: T, ssl_mode: SslMode, config: Config) -> StatementCachingManager
            where T: IntoConnectParams {
        StatementCachingManager {
            manager: PostgresPoolManager::new(params, ssl_mode),
            config: config
        }
    }
}

impl r2d2::PoolManager<Connection, Error> for StatementCachingManager {
    fn connect(&self) -> Result<Connection, Error> {
        let cache = box RefCell::new(LruCache::<String, postgres::Statement<'static>>::new(
                self.config.statement_pool_size));
        Ok(Connection {
            conn: box try!(self.manager.connect()),
            stmts: unsafe { mem::transmute(cache) },
        })
    }

    fn is_valid(&self, conn: &mut Connection) -> Result<(), Error> {
        self.manager.is_valid(&mut *conn.conn)
    }

    fn has_broken(&self, conn: &mut Connection) -> bool {
        self.manager.has_broken(&mut *conn.conn)
    }
}

pub trait GenericConnection {
    /// Like `postgres::Connection::prepare`.
    fn prepare<'a>(&'a self, query: &str) -> postgres::Result<Rc<postgres::Statement<'a>>>;

    /// Like `postgres::Connection::execute`.
    fn execute(&self, query: &str, params: &[&ToSql]) -> postgres::Result<uint> {
        self.prepare(query).and_then(|s| s.execute(params))
    }

    /// Like `postgres::Connection::prepare_copy_in`.
    fn prepare_copy_in<'a>(&'a self, table: &str, columns: &[&str])
                           -> postgres::Result<postgres::CopyInStatement<'a>>;

    /// Like `postgres::Connection::transaction`.
    fn transaction<'a>(&'a self) -> postgres::Result<Transaction<'a>>;

    /// Like `postgres::Connection::batch_execute`.
    fn batch_execute(&self, query: &str) -> postgres::Result<()>;
}

pub struct Connection {
    conn: Box<postgres::Connection>,
    stmts: *mut (),
}

impl Drop for Connection {
    fn drop(&mut self) {
        let _: Box<RefCell<LruCache<String, Rc<postgres::Statement<'static>>>>> =
            unsafe { mem::transmute(self.stmts) };
    }
}

impl Connection {
    fn get_cache<'a>(&'a self) -> &'a RefCell<LruCache<String, Rc<postgres::Statement<'a>>>> {
        unsafe { mem::transmute(self.stmts) }
    }
}

impl GenericConnection for Connection {
    fn prepare<'a>(&'a self, query: &str) -> postgres::Result<Rc<postgres::Statement<'a>>> {
        let query = query.into_string();
        let mut stmts = self.get_cache().borrow_mut();

        if let Some(stmt) = stmts.get(&query) {
            return Ok(stmt.clone());
        }

        let stmt = Rc::new(try!(self.conn.prepare(query[])));
        stmts.put(query, stmt.clone());
        Ok(stmt)
    }

    fn prepare_copy_in<'a>(&'a self, table: &str, columns: &[&str])
                           -> postgres::Result<postgres::CopyInStatement<'a>> {
        self.conn.prepare_copy_in(table, columns)
    }

    fn transaction<'a>(&'a self) -> postgres::Result<Transaction<'a>> {
        Ok(Transaction {
            conn: self,
            trans: try!(self.conn.transaction())
        })
    }

    fn batch_execute(&self, query: &str) -> postgres::Result<()> {
        self.conn.batch_execute(query)
    }
}

pub struct Transaction<'a> {
    conn: &'a Connection,
    trans: postgres::Transaction<'a>
}

impl<'a> GenericConnection for Transaction<'a> {
    fn prepare<'a>(&'a self, query: &str) -> postgres::Result<Rc<postgres::Statement<'a>>> {
        let query = query.into_string();
        let mut stmts = self.conn.get_cache().borrow_mut();

        if let Some(stmt) = stmts.get(&query) {
            return Ok(stmt.clone());
        }

        Ok(Rc::new(try!(self.trans.prepare(query[]))))
    }

    fn prepare_copy_in<'a>(&'a self, table: &str, columns: &[&str])
                           -> postgres::Result<postgres::CopyInStatement<'a>> {
        self.trans.prepare_copy_in(table, columns)
    }

    fn transaction<'a>(&'a self) -> postgres::Result<Transaction<'a>> {
        Ok(Transaction {
            conn: self.conn,
            trans: try!(self.trans.transaction())
        })
    }

    fn batch_execute(&self, query: &str) -> postgres::Result<()> {
        self.trans.batch_execute(query)
    }
}
<|endoftext|>


<commit_before>#![doc(html_logo_url = "https://zzeroo.github.io/share/zzeroo-logo.png",
       html_favicon_url = "https://zzeroo.github.io/share/favicon.ico",
html_root_url = "https://zzeroo.com/")]
//! This is an 'hopefully' safe Rust interface for the [libmodbus](http://libmodbus.org/) C library from
//! http://libmodbus.org/.
//!
//! [libmodbus](http://libmodbus.org/) is a library to send/receive data with a device which respects the Modbus
//! protocol.
//! This library contains various backends to communicate over different networks (eg. serial in RTU mode or Ethernet
//! in TCP/IPv6).
//! The http://www.modbus.org site provides documentation about the protocol at http://www.modbus.org/specs.php.
//!
//! libmodbus provides an abstraction of the lower communication layers and offers the same API on all supported
//! platforms.
//!
//! This documentation presents an overview of libmodbus concepts, describes how libmodbus abstracts Modbus
//! communication with
//! different hardware and platforms and provides a reference manual for the functions provided by the libmodbus
//! library.
//!
//! **This project hosts the original libmodbus documentation, used here, as well.**

//! Please have a look at: [libmodbus/libmodbus.html](../libmodbus/libmodbus.html)
//!
//! ## Contexts
//!
//! The Modbus protocol contains many variants (eg. serial RTU or Ehternet TCP), to ease the implementation of a
//! variant, the library was designed to use a backend for each variant.
//!
//! **libmodbus-rs provides traits and matching implementations for these variants.** See
//! [ModbusRTU](trait.ModbusRTU.html) (trait), [Modbus::new_rtu()](struct.Modbus.html#method.new_rtu) (implementation)
//! or [ModbusTCP](trait.ModbusTCP.html) (trait).
//!
//! The backends are also a convenient way to fulfill other requirements (eg. real-time operations). Each backend
//! offers a specific function to create a new modbus_t context.
//! The modbus_t context is an opaque structure containing all necessary information to establish a connection with
//! others Modbus devices according to the selected variant.
//!
//! You can choose the best context for your needs among:
//!
//! * [RTU Context](trait.ModbusRTU.html)
//! * [TCP (IPv4) Context](trait.ModbusTCP.html)
//! * [TCP PI (IPv4 and IPv6) Context](trait.ModbusTCPPI.html)
//!
//! ### [RTU Context](trait.ModbusRTU.html)
//!
//! The RTU backend (Remote Terminal Unit) is used in serial communication and makes use of a compact, binary
//! representation of the data for protocol communication.
//! The RTU format follows the commands/data with a cyclic redundancy check checksum as an error check mechanism to
//! ensure the reliability of data.
//! Modbus RTU is the most common implementation available for Modbus. A Modbus RTU message must be transmitted
//! continuously without inter-character hesitations
//! (extract from Wikipedia, Modbus, http://en.wikipedia.org/wiki/Modbus (as of Mar. 13, 2011, 20:51 GMT).
//!
//! The Modbus RTU framing calls a slave, a device/service which handle Modbus requests, and a master, a client which
//! send requests. The communication is always initiated by the master.
//!
//! Many Modbus devices can be connected together on the same physical link so before sending a message, you must set
//! the slave (receiver) with modbus_set_slave(3).
//! If you’re running a slave, its slave number will be used to filter received messages.
//!
//! The libmodbus implementation of RTU isn’t time based as stated in original Modbus specification,
//! instead all bytes are sent as fast as possible and a response or an indication is considered complete when all
//! expected characters have been received.
//! This implementation offers very fast communication but you must take care to set a response timeout of slaves less
//! than response timeout of master
//! (ortherwise other slaves may ignore master requests when one of the slave is not responding).
//!
//! * Create a Modbus RTU context
//!     - [`new_rtu()`](struct.Modbus.html#method.new_rtu)
//!
//! * Set the serial mode
//! - [`rtu_get_serial_mode()`](struct.Modbus.html#method.rtu_get_serial_mode),
//! [`rtu_set_serial_mode()`](struct.Modbus.html#method.rtu_set_serial_mode),
//! [`rtu_get_rts()`](struct.Modbus.html#method.rtu_get_rts), [`rtu_set_rts()`](struct.Modbus.html#method.rtu_set_rts),
//! [`rtu_set_custom_rts()`](struct.Modbus.html#method.rtu_set_custom_rts),
//! [`rtu_get_rts_delay()`](struct.Modbus.html#method.rtu_get_rts_delay),
//! [`rtu_set_rts_delay()`](struct.Modbus.html#method.rtu_set_rts_delay)
//!
//! ### [TCP (IPv4) Context](trait.ModbusTCP.html)
//! The TCP backend implements a Modbus variant used for communications over TCP/IPv4 networks.
//! It does not require a checksum calculation as lower layer takes care of the same.
//!
//! * Create a Modbus TCP context
//!     - [`new_tcp()`](struct.Modbus.html#method.new_tcp)
//!
//! ### [TCP PI (IPv4 and IPv6) Context](trait.ModbusTCPPI.html)
//! The TCP PI (Protocol Independent) backend implements a Modbus variant used for communications over TCP IPv4 and
//! IPv6 networks.
//! It does not require a checksum calculation as lower layer takes care of the same.
//!
//! Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname resolution but it consumes about 1Kb of
//! additional memory.
//!
//! * Create a Modbus TCP context
//!     - [`new_tcp_pi()`](struct.Modbus.html#method.new_tcp_pi)
//!
//! ### Common
//!
//! Common methods to modify or change the current modbus context. Some of these function are not nessesary in Rust (e.g. because the Drop trait and so on)
//!
//! * Free libmodbus context
//!     - [`free()`](struct.Modbus.html#method.free) **It should normaly not nessesary to call this function.**

//!     Memory handling under Rust controlls the [`Drop trait`](https://doc.rust-lang.org/std/ops/trait.Drop.html) in "the book" you find [more information about](https://doc.rust-lang.org/book/ffi.html#destructors).
//! * Set slave ID
//!     - [`set_slave()`](struct.Modbus.html#method.set_slave)
//! * Enable debug mode
//!     - [`set_debug()`](struct.Modbus.html#method.set_debug)
//! * Timeout settings
//!     - [`get_byte_timeout()`](struct.Modbus.html#method.get_byte_timeout) [`set_byte_timeout()`](struct.Modbus.html#method.set_byte_timeout) [`get_response_timeout()`](struct.Modbus.html#method.get_response_timeout) [`set_response_timeout()`](struct.Modbus.html#method.set_response_timeout)
//! * Error recovery mod
//!     - [`set_error_recovery()`](struct.Modbus.html#method.set_error_recovery)
//! * Setter/getter of internal socket
//!     - [`set_socket()`](struct.Modbus.html#method.set_socket) [`get_socket()`](struct.Modbus.html#method.get_socket)
//! * Information about header
//!     - [`get_header_length()`](struct.Modbus.html#method.get_header_length)
//!
//! ### Connection
//!
//! ### [`Client`](trait.ModbusClient.html)
//!
//! The Modbus protocol defines different data types and functions to read and write them from/to remote devices.
//! The following functions are used by the clients to send Modbus requests:
//!
//! * Read data
//! - [`read_bits()`](struct.Modbus.html#method.read_bits),
//! [`read_input_bits()`](struct.Modbus.html#method.read_input_bits),
//! [`read_registers()`](struct.Modbus.html#method.read_registers),
//! [`read_input_registers()`](struct.Modbus.html#method.read_input_registers),
//! [`report_slave_id()`](struct.Modbus.html#method.report_slave_id)
//! * Write data
//! - [`write_bit()`](struct.Modbus.html#method.write_bit),
//! [`write_register()`](struct.Modbus.html#method.write_register),
//! [`write_bits()`](struct.Modbus.html#method.write_bits),
//! [`write_registers()`](struct.Modbus.html#method.write_registers)
//! * Write and read data
//!     - [`write_and_read_registers()`](struct.Modbus.html#method.write_and_read_registers)
//! * Raw requests
//! - [`send_raw_request()`](struct.Modbus.html#method.send_raw_request),
//! [`receive_confirmation()`](struct.Modbus.html#method.receive_confirmation)
//! * Reply an exception
//!     - [`reply_exception()`](struct.Modbus.html#method.reply_exception)
//!
//! ### [`Server`](trait.ModbusServer.html)
//!
//! The server is waiting for request from clients and must answer when it is concerned by the request.
//!
//! In TCP , you must not use the usual [`connect()`](struct.Modbus.html#method.connect) to establish the connection
//! but a pair of accept/listen calls
//!
//! * [`tcp_listen()`](struct.Modbus.html#method.tcp_listen), [`tcp_accept()`](struct.Modbus.html#method.tcp_accept),
//! [`tcp_pi_listen`()](struct.Modbus.html#method.tcp_pi_listen),
//! [`tcp_pi_accept`()](struct.Modbus.html#method.tcp_pi_accept)
//!
//! then the data can be received with
//!
//! * [`receive()`](struct.Modbus.html#method.receive)
//!
//! and a response can be send with
//!
//! * [`reply()`](struct.Modbus.html#method.reply), [`reply_exception()`](struct.Modbus.html#method.reply_exception)
//!
//! To handle the mapping of your Modbus data, you must use a [`ModbusMapping`](struct.ModbusMapping.html) struct:
//! [`ModbusMapping::new()`](struct.ModbusMapping.html#method.new)
//!

// `error_chain!` can recurse deeply(3)
#![recursion_limit = "1024"]

extern crate libc;
extern crate libmodbus_sys;
#[macro_use]
extern crate error_chain;

mod enums;
mod modbus_client;
mod modbus_mapping;
mod modbus_rtu;
mod modbus_server;
mod modbus_tcp_pi;
mod modbus_tcp;
mod modbus;
pub mod errors;

pub use self::enums::{ErrorRecoveryMode, Exception, FunctionCode};
pub use self::modbus_client::ModbusClient;
pub use self::modbus_mapping::ModbusMapping;
pub use self::modbus_rtu::{ModbusRTU, RequestToSendMode, SerialMode};
pub use self::modbus_server::ModbusServer;
pub use self::modbus_tcp_pi::ModbusTCPPI;
pub use self::modbus_tcp::ModbusTCP;
pub use self::modbus::Modbus;
pub use self::modbus::{set_bits_from_byte, set_bits_from_bytes, get_byte_from_bits,
    get_float_abcd, set_float_abcd, get_float_badc, set_float_badc,
    get_float_cdab, set_float_cdab, get_float_dcba, set_float_dcba,
};

pub use libmodbus_sys::MODBUS_MAX_PDU_LENGTH;
pub use libmodbus_sys::MODBUS_MAX_ADU_LENGTH;
pub use libmodbus_sys::MODBUS_ENOBASE;
pub use libmodbus_sys::MODBUS_TCP_DEFAULT_PORT;
pub use libmodbus_sys::MODBUS_TCP_SLAVE;
pub use libmodbus_sys::MODBUS_TCP_MAX_ADU_LENGTH;
pub use libmodbus_sys::MODBUS_RTU_MAX_ADU_LENGTH;
<commit_msg>little cosmetic<commit_after>#![doc(html_logo_url = "https://zzeroo.github.io/share/zzeroo-logo.png",
       html_favicon_url = "https://zzeroo.github.io/share/favicon.ico",
html_root_url = "https://zzeroo.com/")]
//! This is an 'hopefully' safe Rust interface for the [libmodbus](http://libmodbus.org/) C library from
//! http://libmodbus.org/.
//!
//! [libmodbus](http://libmodbus.org/) is a library to send/receive data with a device which respects the Modbus
//! protocol.
//! This library contains various backends to communicate over different networks (eg. serial in RTU mode or Ethernet
//! in TCP/IPv6).
//! The http://www.modbus.org site provides documentation about the protocol at http://www.modbus.org/specs.php.
//!
//! libmodbus provides an abstraction of the lower communication layers and offers the same API on all supported
//! platforms.
//!
//! This documentation presents an overview of libmodbus concepts, describes how libmodbus abstracts Modbus
//! communication with
//! different hardware and platforms and provides a reference manual for the functions provided by the libmodbus
//! library.
//!
//! **This project hosts the original libmodbus documentation, used here, as well.**

//! Please have a look at: [libmodbus/libmodbus.html](../libmodbus/libmodbus.html)
//!
//! ## Contexts
//!
//! The Modbus protocol contains many variants (eg. serial RTU or Ehternet TCP), to ease the implementation of a
//! variant, the library was designed to use a backend for each variant.
//!
//! **libmodbus-rs provides traits and matching implementations for these variants.** See
//! [ModbusRTU](trait.ModbusRTU.html) (trait), [Modbus::new_rtu()](struct.Modbus.html#method.new_rtu) (implementation)
//! or [ModbusTCP](trait.ModbusTCP.html) (trait).
//!
//! The backends are also a convenient way to fulfill other requirements (eg. real-time operations). Each backend
//! offers a specific function to create a new modbus_t context.
//! The modbus_t context is an opaque structure containing all necessary information to establish a connection with
//! others Modbus devices according to the selected variant.
//!
//! You can choose the best context for your needs among:
//!
//! * [RTU Context](trait.ModbusRTU.html)
//! * [TCP (IPv4) Context](trait.ModbusTCP.html)
//! * [TCP PI (IPv4 and IPv6) Context](trait.ModbusTCPPI.html)
//!
//! ### [RTU Context](trait.ModbusRTU.html)
//!
//! The RTU backend (Remote Terminal Unit) is used in serial communication and makes use of a compact, binary
//! representation of the data for protocol communication.
//! The RTU format follows the commands/data with a cyclic redundancy check checksum as an error check mechanism to
//! ensure the reliability of data.
//! Modbus RTU is the most common implementation available for Modbus. A Modbus RTU message must be transmitted
//! continuously without inter-character hesitations
//! (extract from Wikipedia, Modbus, http://en.wikipedia.org/wiki/Modbus (as of Mar. 13, 2011, 20:51 GMT).
//!
//! The Modbus RTU framing calls a slave, a device/service which handle Modbus requests, and a master, a client which
//! send requests. The communication is always initiated by the master.
//!
//! Many Modbus devices can be connected together on the same physical link so before sending a message, you must set
//! the slave (receiver) with modbus_set_slave(3).
//! If you’re running a slave, its slave number will be used to filter received messages.
//!
//! The libmodbus implementation of RTU isn’t time based as stated in original Modbus specification,
//! instead all bytes are sent as fast as possible and a response or an indication is considered complete when all
//! expected characters have been received.
//! This implementation offers very fast communication but you must take care to set a response timeout of slaves less
//! than response timeout of master
//! (ortherwise other slaves may ignore master requests when one of the slave is not responding).
//!
//! * Create a Modbus RTU context
//!     - [`new_rtu()`](struct.Modbus.html#method.new_rtu)
//!
//! * Set the serial mode
//! - [`rtu_get_serial_mode()`](struct.Modbus.html#method.rtu_get_serial_mode),
//! [`rtu_set_serial_mode()`](struct.Modbus.html#method.rtu_set_serial_mode),
//! [`rtu_get_rts()`](struct.Modbus.html#method.rtu_get_rts), [`rtu_set_rts()`](struct.Modbus.html#method.rtu_set_rts),
//! [`rtu_set_custom_rts()`](struct.Modbus.html#method.rtu_set_custom_rts),
//! [`rtu_get_rts_delay()`](struct.Modbus.html#method.rtu_get_rts_delay),
//! [`rtu_set_rts_delay()`](struct.Modbus.html#method.rtu_set_rts_delay)
//!
//! ### [TCP (IPv4) Context](trait.ModbusTCP.html)
//! The TCP backend implements a Modbus variant used for communications over TCP/IPv4 networks.
//! It does not require a checksum calculation as lower layer takes care of the same.
//!
//! * Create a Modbus TCP context
//!     - [`new_tcp()`](struct.Modbus.html#method.new_tcp)
//!
//! ### [TCP PI (IPv4 and IPv6) Context](trait.ModbusTCPPI.html)
//! The TCP PI (Protocol Independent) backend implements a Modbus variant used for communications over TCP IPv4 and
//! IPv6 networks.
//! It does not require a checksum calculation as lower layer takes care of the same.
//!
//! Contrary to the TCP IPv4 only backend, the TCP PI backend offers hostname resolution but it consumes about 1Kb of
//! additional memory.
//!
//! * Create a Modbus TCP context
//!     - [`new_tcp_pi()`](struct.Modbus.html#method.new_tcp_pi)
//!
//! ### Common
//!
//! Common methods to modify or change the current modbus context. Some of these function are not nessesary in Rust (e.g. because the Drop trait and so on)
//!
//! * Free libmodbus context
//!     - [`free()`](struct.Modbus.html#method.free) **It should normaly not nessesary to call this function.**

//!     Memory handling under Rust controlls the [`Drop trait`](https://doc.rust-lang.org/std/ops/trait.Drop.html) in "the book" you find [more information about](https://doc.rust-lang.org/book/ffi.html#destructors).
//! * Set slave ID
//!     - [`set_slave()`](struct.Modbus.html#method.set_slave)
//! * Enable debug mode
//!     - [`set_debug()`](struct.Modbus.html#method.set_debug)
//! * Timeout settings
//!     - [`get_byte_timeout()`](struct.Modbus.html#method.get_byte_timeout) [`set_byte_timeout()`](struct.Modbus.html#method.set_byte_timeout) [`get_response_timeout()`](struct.Modbus.html#method.get_response_timeout) [`set_response_timeout()`](struct.Modbus.html#method.set_response_timeout)
//! * Error recovery mod
//!     - [`set_error_recovery()`](struct.Modbus.html#method.set_error_recovery)
//! * Setter/getter of internal socket
//!     - [`set_socket()`](struct.Modbus.html#method.set_socket) [`get_socket()`](struct.Modbus.html#method.get_socket)
//! * Information about header
//!     - [`get_header_length()`](struct.Modbus.html#method.get_header_length)
//!
//! ### Connection
//!
//! ### [`Client`](trait.ModbusClient.html)
//!
//! The Modbus protocol defines different data types and functions to read and write them from/to remote devices.
//! The following functions are used by the clients to send Modbus requests:
//!
//! * Read data
//! - [`read_bits()`](struct.Modbus.html#method.read_bits),
//! [`read_input_bits()`](struct.Modbus.html#method.read_input_bits),
//! [`read_registers()`](struct.Modbus.html#method.read_registers),
//! [`read_input_registers()`](struct.Modbus.html#method.read_input_registers),
//! [`report_slave_id()`](struct.Modbus.html#method.report_slave_id)
//! * Write data
//! - [`write_bit()`](struct.Modbus.html#method.write_bit),
//! [`write_register()`](struct.Modbus.html#method.write_register),
//! [`write_bits()`](struct.Modbus.html#method.write_bits),
//! [`write_registers()`](struct.Modbus.html#method.write_registers)
//! * Write and read data
//!     - [`write_and_read_registers()`](struct.Modbus.html#method.write_and_read_registers)
//! * Raw requests
//! - [`send_raw_request()`](struct.Modbus.html#method.send_raw_request),
//! [`receive_confirmation()`](struct.Modbus.html#method.receive_confirmation)
//! * Reply an exception
//!     - [`reply_exception()`](struct.Modbus.html#method.reply_exception)
//!
//! ### [`Server`](trait.ModbusServer.html)
//!
//! The server is waiting for request from clients and must answer when it is concerned by the request.
//!
//! In TCP , you must not use the usual [`connect()`](struct.Modbus.html#method.connect) to establish the connection
//! but a pair of accept/listen calls
//!
//! * [`tcp_listen()`](struct.Modbus.html#method.tcp_listen), [`tcp_accept()`](struct.Modbus.html#method.tcp_accept),
//! [`tcp_pi_listen`()](struct.Modbus.html#method.tcp_pi_listen),
//! [`tcp_pi_accept`()](struct.Modbus.html#method.tcp_pi_accept)
//!
//! then the data can be received with
//!
//! * [`receive()`](struct.Modbus.html#method.receive)
//!
//! and a response can be send with
//!
//! * [`reply()`](struct.Modbus.html#method.reply), [`reply_exception()`](struct.Modbus.html#method.reply_exception)
//!
//! To handle the mapping of your Modbus data, you must use a [`ModbusMapping`](struct.ModbusMapping.html) struct:
//! [`ModbusMapping::new()`](struct.ModbusMapping.html#method.new)
//!

// `error_chain!` can recurse deeply(3)
#![recursion_limit = "1024"]

extern crate libc;
extern crate libmodbus_sys;
#[macro_use] extern crate error_chain;

mod enums;
mod modbus_client;
mod modbus_mapping;
mod modbus_rtu;
mod modbus_server;
mod modbus_tcp_pi;
mod modbus_tcp;
mod modbus;
pub mod errors;

pub use self::enums::{ErrorRecoveryMode, Exception, FunctionCode};
pub use self::modbus_client::ModbusClient;
pub use self::modbus_mapping::ModbusMapping;
pub use self::modbus_rtu::{ModbusRTU, RequestToSendMode, SerialMode};
pub use self::modbus_server::ModbusServer;
pub use self::modbus_tcp_pi::ModbusTCPPI;
pub use self::modbus_tcp::ModbusTCP;
pub use self::modbus::Modbus;
pub use self::modbus::{set_bits_from_byte, set_bits_from_bytes, get_byte_from_bits,
    get_float_abcd, set_float_abcd, get_float_badc, set_float_badc,
    get_float_cdab, set_float_cdab, get_float_dcba, set_float_dcba,
};

pub use libmodbus_sys::MODBUS_MAX_PDU_LENGTH;
pub use libmodbus_sys::MODBUS_MAX_ADU_LENGTH;
pub use libmodbus_sys::MODBUS_ENOBASE;
pub use libmodbus_sys::MODBUS_TCP_DEFAULT_PORT;
pub use libmodbus_sys::MODBUS_TCP_SLAVE;
pub use libmodbus_sys::MODBUS_TCP_MAX_ADU_LENGTH;
pub use libmodbus_sys::MODBUS_RTU_MAX_ADU_LENGTH;
<|endoftext|>


<commit_before>use super::PlayerAction;
use super::state::{PlayerState, PlayerStatus};

use ao;
use earwax::Earwax;
use pandora::Pandora;

use std::error::Error;
use std::thread;
use std::thread::JoinHandle;
use std::sync::{Arc, Mutex, Condvar};
use std::sync::mpsc::{channel, Sender, Receiver};

/// This function starts the event and player thread.
pub fn spawn_player(
    pandora: &Arc<Pandora>,
    main_state: &Arc<Mutex<PlayerState>>,
    main_sender: Sender<PlayerStatus>,
    main_receiver: Receiver<PlayerAction>) -> JoinHandle<()> {

    // The Condvar used for pausing the player thread.
    let main_pause_pair: Arc<(Mutex<bool>, Condvar)> = Arc::new((Mutex::new(false), Condvar::new()));

    // Sender and receiver for communication between the event thread and
    // the player thread.
    let (event_sender, event_receiver) = channel();

    // Thread is dedicated to receive the events from the main thread and
    // forward player events to the player thread.
    let state = main_state.clone();
    let pause_pair = main_pause_pair.clone();
    let receiver = main_receiver;
    let sender = main_sender.clone();

    let event_handle = {
        thread::Builder::new().name("event".to_string()).spawn(move || {
            while let Ok(action) = receiver.recv() {
                match action {
                    PlayerAction::Pause => {
                        let &(ref lock, _) = &*pause_pair;
                        let mut paused = lock.lock().unwrap();
                        *paused = true;
                    },
                    PlayerAction::Unpause => {
                        let &(ref lock, ref cvar) = &*pause_pair;
                        let mut paused = lock.lock().unwrap();
                        *paused = false;
                        cvar.notify_one();
                    },

                    PlayerAction::Report => {
                        sender.send(state.lock().unwrap().status().clone()).unwrap();
                    },

                    PlayerAction::Exit => {
                        event_sender.send(PlayerAction::Exit).unwrap();
                        break;
                    },

                    action => {
                        event_sender.send(action).unwrap();
                    }
                }
            }
        }).unwrap()
    };

    // The 'player' thread runs while the Player is in scope. It plays the given station
    // and takes care of fetching the tracks. All the events this thread receives are
    // the events forwarded from the 'event' thread.
    //
    //
    let pandora = pandora.clone();
    let state = main_state.clone();
    let pause_pair = main_pause_pair.clone();
    let sender = main_sender.clone();

    thread::Builder::new().name("player".to_string()).spawn(move || {
        /// Sets current player status and sends status event to main thread.
        let set_status = |status: PlayerStatus| {
            state.lock().unwrap().set_status(status.clone());
            sender.send(status).unwrap();
        };

        /// This macro tries the given Result, and if an Err is given sends the error using
        /// the sender in the current context and executes the specified action on Error.
        macro_rules! try_or {
            ($e:expr, $action:expr) => {
                match $e {
                    Ok(val) => val,
                    Err(err) => {
                        set_status(PlayerStatus::Error(err.description().to_owned()));
                        $action;
                    }
                }
            }
        }

        let driver = try_or!(ao::Driver::new(), return);

        let mut current_station = None;
        'main_loop: loop {

            // Stand-by, waiting to play a station.
            if current_station.is_none() {
                set_status(PlayerStatus::Standby);
                while let Ok(action) = event_receiver.recv() {
                    match action {
                        PlayerAction::Play(new_station) => {
                            current_station = Some(new_station);
                            break;
                        },
                        PlayerAction::Exit => break 'main_loop,
                        _ => (),
                    }
                }
            }

            // Playing a station.
            if let Some(ref station) = current_station.clone() {
                state.lock().unwrap().set_station(station.clone());
                set_status(PlayerStatus::Started(station.clone()));

                'station_loop: loop {
                    set_status(PlayerStatus::Fetching(station.clone()));
                    let tracklist = try_or!(pandora.stations().playlist(station).list(), continue);

                    for track in tracklist {
                        if track.is_ad() { continue; }
                        if let Some(ref audio) = track.track_audio {
                            if let Ok(mut earwax) = Earwax::new(&audio.high_quality.audio_url) {
                                // TODO: Format should replicate earwax format.
                                let format = ao::Format::new();
                                let device = try_or!(ao::Device::new(&driver, &format, None), continue);
                                let duration = earwax.info().duration.seconds();

                                state.lock().unwrap().set_track(track.clone());
                                set_status(PlayerStatus::Playing(track.clone()));

                                while let Some(chunk) = earwax.spit() {
                                    state.lock().unwrap().set_progress(chunk.time.seconds(), duration);

                                    // Pauses.
                                    let &(ref lock, ref cvar) = &*pause_pair;
                                    let mut paused = lock.lock().unwrap();
                                    while *paused {
                                        set_status(PlayerStatus::Paused(track.clone()));
                                        paused = cvar.wait(paused).unwrap();
                                        set_status(PlayerStatus::Playing(track.clone()));
                                    }

                                    // Message handler.
                                    if let Ok(action) = event_receiver.try_recv() {
                                        match action {
                                            PlayerAction::Play(new_station) => {
                                                current_station = Some(new_station);
                                                set_status(PlayerStatus::Finished(track.clone()));
                                                break 'station_loop;
                                            },
                                            PlayerAction::Stop => {
                                                current_station = None;
                                                set_status(PlayerStatus::Finished(track.clone()));
                                                break 'station_loop;
                                            },

                                            PlayerAction::Skip => break,

                                            PlayerAction::Exit => {
                                                set_status(PlayerStatus::Finished(track.clone()));
                                                set_status(PlayerStatus::Stopped(station.clone()));
                                                break 'main_loop;
                                            }
                                            _ => (),
                                        }
                                    }

                                    // Plays chunk.
                                    device.play(chunk.data);
                                }
                                set_status(PlayerStatus::Finished(track.clone()));
                            }
                        }
                    }
                }
                state.lock().unwrap().clear_info();
                set_status(PlayerStatus::Stopped(station.clone()));
            }
        } // 'main-loop
        *state.lock().unwrap() = PlayerState::new();
        set_status(PlayerStatus::Shutdown);
        event_handle.join().unwrap();
    }).unwrap()
}
<commit_msg>Typos<commit_after>use super::PlayerAction;
use super::state::{PlayerState, PlayerStatus};

use ao;
use earwax::Earwax;
use pandora::Pandora;

use std::error::Error;
use std::thread;
use std::thread::JoinHandle;
use std::sync::{Arc, Mutex, Condvar};
use std::sync::mpsc::{channel, Sender, Receiver};

/// This function starts the event and player thread.
pub fn spawn_player(
    pandora: &Arc<Pandora>,
    main_state: &Arc<Mutex<PlayerState>>,
    main_sender: Sender<PlayerStatus>,
    main_receiver: Receiver<PlayerAction>) -> JoinHandle<()> {

    // The Condvar used for pausing the player thread.
    let main_pause_pair: Arc<(Mutex<bool>, Condvar)> = Arc::new((Mutex::new(false), Condvar::new()));

    // Sender and receiver for communication between the event thread and
    // the player thread.
    let (event_sender, event_receiver) = channel();

    // Thread is dedicated to receive the events from the main thread and
    // forward player events to the player thread.
    let state = main_state.clone();
    let pause_pair = main_pause_pair.clone();
    let receiver = main_receiver;
    let sender = main_sender.clone();

    let event_handle = {
        thread::Builder::new().name("event".to_string()).spawn(move || {
            while let Ok(action) = receiver.recv() {
                match action {
                    PlayerAction::Pause => {
                        let &(ref lock, _) = &*pause_pair;
                        let mut paused = lock.lock().unwrap();
                        *paused = true;
                    },
                    PlayerAction::Unpause => {
                        let &(ref lock, ref cvar) = &*pause_pair;
                        let mut paused = lock.lock().unwrap();
                        *paused = false;
                        cvar.notify_one();
                    },

                    PlayerAction::Report => {
                        sender.send(state.lock().unwrap().status().clone()).unwrap();
                    },

                    PlayerAction::Exit => {
                        event_sender.send(PlayerAction::Exit).unwrap();
                        break;
                    },

                    action => {
                        event_sender.send(action).unwrap();
                    }
                }
            }
        }).unwrap()
    };

    // The 'player' thread runs while the Player is in scope. It plays the given station
    // and takes care of fetching the tracks. All the events this thread receives are
    // the events forwarded from the 'event' thread.
    let pandora = pandora.clone();
    let state = main_state.clone();
    let pause_pair = main_pause_pair.clone();
    let sender = main_sender.clone();

    thread::Builder::new().name("player".to_string()).spawn(move || {
        /// Sets current player status and sends status event to main thread.
        let set_status = |status: PlayerStatus| {
            state.lock().unwrap().set_status(status.clone());
            sender.send(status).unwrap();
        };

        /// This macro tries the given Result, and if an Err is given sends the error using
        /// the sender in the current context and executes the specified action on Error.
        macro_rules! try_or {
            ($e:expr, $action:expr) => {
                match $e {
                    Ok(val) => val,
                    Err(err) => {
                        set_status(PlayerStatus::Error(err.description().to_owned()));
                        $action;
                    }
                }
            }
        }

        // TODO: Correct handling of event thread if we return here.
        let driver = try_or!(ao::Driver::new(), return);

        let mut current_station = None;
        'main_loop: loop {

            // Stand-by, waiting to play a station.
            if current_station.is_none() {
                set_status(PlayerStatus::Standby);
                while let Ok(action) = event_receiver.recv() {
                    match action {
                        PlayerAction::Play(new_station) => {
                            current_station = Some(new_station);
                            break;
                        },
                        PlayerAction::Exit => break 'main_loop,
                        _ => (),
                    }
                }
            }

            // Playing a station.
            if let Some(ref station) = current_station.clone() {
                state.lock().unwrap().set_station(station.clone());
                set_status(PlayerStatus::Started(station.clone()));

                'station_loop: loop {
                    set_status(PlayerStatus::Fetching(station.clone()));
                    let tracklist = try_or!(pandora.stations().playlist(station).list(), continue);

                    for track in tracklist {
                        if track.is_ad() { continue; }
                        if let Some(ref audio) = track.track_audio {
                            if let Ok(mut earwax) = Earwax::new(&audio.high_quality.audio_url) {
                                // TODO: Format should replicate earwax format.
                                let format = ao::Format::new();
                                let device = try_or!(ao::Device::new(&driver, &format, None), continue);
                                let duration = earwax.info().duration.seconds();

                                state.lock().unwrap().set_track(track.clone());
                                set_status(PlayerStatus::Playing(track.clone()));

                                while let Some(chunk) = earwax.spit() {
                                    state.lock().unwrap().set_progress(chunk.time.seconds(), duration);

                                    // Pauses.
                                    let &(ref lock, ref cvar) = &*pause_pair;
                                    let mut paused = lock.lock().unwrap();
                                    while *paused {
                                        set_status(PlayerStatus::Paused(track.clone()));
                                        paused = cvar.wait(paused).unwrap();
                                        set_status(PlayerStatus::Playing(track.clone()));
                                    }

                                    // Message handler.
                                    if let Ok(action) = event_receiver.try_recv() {
                                        match action {
                                            PlayerAction::Play(new_station) => {
                                                current_station = Some(new_station);
                                                set_status(PlayerStatus::Finished(track.clone()));
                                                break 'station_loop;
                                            },
                                            PlayerAction::Stop => {
                                                current_station = None;
                                                set_status(PlayerStatus::Finished(track.clone()));
                                                break 'station_loop;
                                            },

                                            PlayerAction::Skip => break,

                                            PlayerAction::Exit => {
                                                set_status(PlayerStatus::Finished(track.clone()));
                                                set_status(PlayerStatus::Stopped(station.clone()));
                                                break 'main_loop;
                                            }
                                            _ => (),
                                        }
                                    }

                                    // Plays chunk.
                                    device.play(chunk.data);
                                }
                                set_status(PlayerStatus::Finished(track.clone()));
                            }
                        }
                    }
                }
                state.lock().unwrap().clear_info();
                set_status(PlayerStatus::Stopped(station.clone()));
            }
        } // 'main-loop
        *state.lock().unwrap() = PlayerState::new();
        set_status(PlayerStatus::Shutdown);
        event_handle.join().unwrap();
    }).unwrap()
}
<|endoftext|>


<commit_before>#![feature(collections, convert, libc)]
#![allow(raw_pointer_derive)]
#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

extern crate libc;

use std::ffi::CString;
use std::fmt;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim", kind = "static")]
    #[link(name = "uv", kind = "static")]
    #[link(name = "msgpack", kind = "static")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "vterm", kind = "static")]
    #[link(name = "util")]
    #[link(name = "intl")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim", kind = "static")]
    #[link(name = "uv", kind = "static")]
    #[link(name = "msgpack", kind = "static")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "vterm", kind = "static")]
    #[link(name = "util")]
    extern{}
}

mod ffi;

pub enum Object {
    Buffer(ffi::C_Buffer),
    Window(ffi::C_Window),
    Tabpage(ffi::C_Tabpage),
    Boolean(ffi::C_Boolean),
    Integer(ffi::C_Integer),
    Float(ffi::C_Float),
    String(String),
    Array(Array),
    Dictionary(ffi::C_Dictionary)
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Buffer(ref obj) => write!(f, "Buffer({:?})", obj),
            Object::Window(ref obj) => write!(f, "Window({:?})", obj),
            Object::Tabpage(ref obj) => write!(f, "Tabpage({:?})", obj),
            Object::Boolean(ref obj) => write!(f, "Boolean({:?})", obj),
            Object::Integer(ref obj) => write!(f, "Integer({:?})", obj),
            Object::Float(ref obj) => write!(f, "Float({:?})", obj),
            Object::String(ref obj) => write!(f, "String({:?})", obj),
            Object::Array(ref arr) => {
                write!(f, "Array(").ok();
                let obj = arr.unwrap_value();
                for i in 0..obj.size {
                    let inner_obj_opt = unsafe { c_object_to_object(obj.items.offset(i as isize)) };
                    if let Some(inner_obj) = inner_obj_opt {
                        write!(f, "{:?}", inner_obj).ok();
                        if i + 1 < obj.size {
                            write!(f, ", ").ok();
                        }
                    } else {
                        write!(f, "Nil ").ok();
                    }
                }
                write!(f, ")")
            },
            Object::Dictionary(ref obj) => write!(f, "Dictionary(Length: {:?})", obj.size)
        }
    }
}

unsafe fn c_object_to_object(obj: *mut ffi::C_Object) -> Option<Object> {
    match (*obj).object_type {
        ffi::ObjectType::BufferType =>
            Some(Object::Buffer((*(obj as *mut ffi::C_Object_Buffer)).data)),
        ffi::ObjectType::WindowType =>
            Some(Object::Window((*(obj as *mut ffi::C_Object_Window)).data)),
        ffi::ObjectType::TabpageType =>
            Some(Object::Tabpage((*(obj as *mut ffi::C_Object_Tabpage)).data)),
        ffi::ObjectType::NilType =>
            None,
        ffi::ObjectType::BooleanType =>
            Some(Object::Boolean((*(obj as *mut ffi::C_Object_Boolean)).data)),
        ffi::ObjectType::IntegerType =>
            Some(Object::Integer((*(obj as *mut ffi::C_Object_Integer)).data)),
        ffi::ObjectType::FloatType =>
            Some(Object::Float((*(obj as *mut ffi::C_Object_Float)).data)),
        ffi::ObjectType::StringType => {
            let vim_str: ffi::C_String = (*(obj as *mut ffi::C_Object_String)).data;
            let v = Vec::from_raw_buf(vim_str.data as *const u8, vim_str.size as usize);
            Some(Object::String(String::from_utf8_unchecked(v)))
        },
        ffi::ObjectType::ArrayType =>
            Some(Object::Array(Array::wrap_value((*(obj as *mut ffi::C_Object_Array)).data))),
        ffi::ObjectType::DictionaryType =>
            Some(Object::Dictionary((*(obj as *mut ffi::C_Object_Dictionary)).data))
    }
}

pub fn main_setup(args: &Vec<String>) -> i32 {
    let args_vec_cstr: Vec<CString> = args.iter().map(|s| {
        let s_ref: &str = s.as_ref();
        CString::new(s_ref).unwrap()
    }).collect();
    let args_vec_ptr: Vec<*const ffi::c_char> = args_vec_cstr.iter().map(|s| s.as_ptr()).collect();
    unsafe { ffi::nvim_main_setup(args_vec_ptr.len() as i32, args_vec_ptr.as_ptr()) }
}

pub fn main_loop() -> i32 {
    unsafe { ffi::nvim_main_loop() }
}

pub fn channel_from_fds(read_fd: i32, write_fd: i32) -> u64 {
    unsafe { ffi::channel_from_fds(read_fd, write_fd) }
}

pub fn serialize_message(id: u64, method: &'static str, args: &Array) -> String {
    unsafe {
        let buf = ffi::vim_msgpack_new();
        let vim_str = ffi::C_String {data: method.as_ptr() as *const i8, size: method.len() as u64};
        ffi::vim_serialize_request(id, vim_str, args.unwrap_value(), buf);
        let v = Vec::from_raw_buf((*buf).data as *const u8, (*buf).size as usize);
        ffi::vim_msgpack_free(buf);
        String::from_utf8_unchecked(v)
    }
}

pub fn deserialize_message(message: &String) -> Array {
    let message_ref: &str = message.as_ref();
    let s = ffi::C_String {
        data: message_ref.as_ptr() as *const i8,
        size: message.len() as u64
    };
    let mut arr_raw = ffi::C_Array {
        items: ::std::ptr::null_mut(),
        size: 0,
        capacity: 0
    };
    unsafe { ffi::vim_msgpack_parse(s, &mut arr_raw) };
    Array::wrap_value(arr_raw)
}

pub struct Array {
    value: ffi::C_Array,
    is_owned: bool,
}

impl Array {
    pub fn new() -> Array {
        Array {
            value: ffi::C_Array {
                items: ::std::ptr::null_mut(),
                size: 0,
                capacity: 0
            },
            is_owned: true
        }
    }

    pub fn add_buffer(&mut self, val: ffi::C_Buffer) {
        unsafe { ffi::vim_array_add_buffer(val, &mut self.value) }
    }

    pub fn add_window(&mut self, val: ffi::C_Window) {
        unsafe { ffi::vim_array_add_window(val, &mut self.value) }
    }

    pub fn add_tabpage(&mut self, val: ffi::C_Tabpage) {
        unsafe { ffi::vim_array_add_tabpage(val, &mut self.value) }
    }

    pub fn add_nil(&mut self) {
        unsafe { ffi::vim_array_add_nil(&mut self.value) }
    }

    pub fn add_boolean(&mut self, val: ffi::C_Boolean) {
        unsafe { ffi::vim_array_add_boolean(val, &mut self.value) }
    }

    pub fn add_integer(&mut self, val: ffi::C_Integer) {
        unsafe { ffi::vim_array_add_integer(val, &mut self.value) }
    }

    pub fn add_float(&mut self, val: ffi::C_Float) {
        unsafe { ffi::vim_array_add_float(val, &mut self.value) }
    }

    pub fn add_string(&mut self, val: &str) {
        unsafe {
            // we need to copy the string into memory not managed by Rust,
            // so api_free_array can clear it
            let ptr = ffi::malloc(val.len() as u64);
            ::std::ptr::copy(ptr, val.as_ptr() as *const ffi::c_void, val.len());
            let vim_str = ffi::C_String {data: ptr as *const i8, size: val.len() as u64};
            ffi::vim_array_add_string(vim_str, &mut self.value)
        }
    }

    pub fn add_array(&mut self, val: &Array) {
        unsafe { ffi::vim_array_add_array(val.unwrap_value(), &mut self.value) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        unsafe { ffi::vim_array_add_dictionary(val, &mut self.value) }
    }

    pub fn get(&self, index: u64) -> Option<Object> {
        if index >= self.len() {
            return None;
        }
        unsafe { c_object_to_object(self.value.items.offset(index as isize)) }
    }

    pub fn len(&self) -> u64 {
        self.value.size
    }

    #[doc(hidden)]
    pub fn unwrap_value(&self) -> ffi::C_Array {
        self.value
    }

    #[doc(hidden)]
    pub fn wrap_value(c_array: ffi::C_Array) -> Array {
        Array {
            value: c_array,
            is_owned: false
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        if self.is_owned {
            unsafe { ffi::api_free_array(self.value) };
        }
    }
}

impl fmt::Debug for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Array(").ok();
        for i in 0..self.len() {
            if let Some(obj) = self.get(i) {
                write!(f, "{:?}", obj).ok();
            } else {
                write!(f, "Nil").ok();
            }
            if i + 1 < self.len() {
                write!(f, ", ").ok();
            }
        }
        write!(f, ")")
    }
}

#[test]
fn test_request() {
    let mut args = Array::new();
    args.add_integer(80);
    args.add_integer(24);
    args.add_string("hello");

    let msg = serialize_message(1, "test", &args);
    let arr = deserialize_message(&msg);
    println!("LENGTH: {}", arr.len());
    for i in 0..arr.len() {
        println!("{:?}", arr.get(i));
    }
}
<commit_msg>Fix type cast<commit_after>#![feature(collections, convert, libc)]
#![allow(raw_pointer_derive)]
#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

extern crate libc;

use std::ffi::CString;
use std::fmt;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim", kind = "static")]
    #[link(name = "uv", kind = "static")]
    #[link(name = "msgpack", kind = "static")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "vterm", kind = "static")]
    #[link(name = "util")]
    #[link(name = "intl")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim", kind = "static")]
    #[link(name = "uv", kind = "static")]
    #[link(name = "msgpack", kind = "static")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "vterm", kind = "static")]
    #[link(name = "util")]
    extern{}
}

mod ffi;

pub enum Object {
    Buffer(ffi::C_Buffer),
    Window(ffi::C_Window),
    Tabpage(ffi::C_Tabpage),
    Boolean(ffi::C_Boolean),
    Integer(ffi::C_Integer),
    Float(ffi::C_Float),
    String(String),
    Array(Array),
    Dictionary(ffi::C_Dictionary)
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Buffer(ref obj) => write!(f, "Buffer({:?})", obj),
            Object::Window(ref obj) => write!(f, "Window({:?})", obj),
            Object::Tabpage(ref obj) => write!(f, "Tabpage({:?})", obj),
            Object::Boolean(ref obj) => write!(f, "Boolean({:?})", obj),
            Object::Integer(ref obj) => write!(f, "Integer({:?})", obj),
            Object::Float(ref obj) => write!(f, "Float({:?})", obj),
            Object::String(ref obj) => write!(f, "String({:?})", obj),
            Object::Array(ref arr) => {
                write!(f, "Array(").ok();
                let obj = arr.unwrap_value();
                for i in 0..obj.size {
                    let inner_obj_opt = unsafe { c_object_to_object(obj.items.offset(i as isize)) };
                    if let Some(inner_obj) = inner_obj_opt {
                        write!(f, "{:?}", inner_obj).ok();
                        if i + 1 < obj.size {
                            write!(f, ", ").ok();
                        }
                    } else {
                        write!(f, "Nil ").ok();
                    }
                }
                write!(f, ")")
            },
            Object::Dictionary(ref obj) => write!(f, "Dictionary(Length: {:?})", obj.size)
        }
    }
}

unsafe fn c_object_to_object(obj: *mut ffi::C_Object) -> Option<Object> {
    match (*obj).object_type {
        ffi::ObjectType::BufferType =>
            Some(Object::Buffer((*(obj as *mut ffi::C_Object_Buffer)).data)),
        ffi::ObjectType::WindowType =>
            Some(Object::Window((*(obj as *mut ffi::C_Object_Window)).data)),
        ffi::ObjectType::TabpageType =>
            Some(Object::Tabpage((*(obj as *mut ffi::C_Object_Tabpage)).data)),
        ffi::ObjectType::NilType =>
            None,
        ffi::ObjectType::BooleanType =>
            Some(Object::Boolean((*(obj as *mut ffi::C_Object_Boolean)).data)),
        ffi::ObjectType::IntegerType =>
            Some(Object::Integer((*(obj as *mut ffi::C_Object_Integer)).data)),
        ffi::ObjectType::FloatType =>
            Some(Object::Float((*(obj as *mut ffi::C_Object_Float)).data)),
        ffi::ObjectType::StringType => {
            let vim_str: ffi::C_String = (*(obj as *mut ffi::C_Object_String)).data;
            let v = Vec::from_raw_buf(vim_str.data as *const u8, vim_str.size as usize);
            Some(Object::String(String::from_utf8_unchecked(v)))
        },
        ffi::ObjectType::ArrayType =>
            Some(Object::Array(Array::wrap_value((*(obj as *mut ffi::C_Object_Array)).data))),
        ffi::ObjectType::DictionaryType =>
            Some(Object::Dictionary((*(obj as *mut ffi::C_Object_Dictionary)).data))
    }
}

pub fn main_setup(args: &Vec<String>) -> i32 {
    let args_vec_cstr: Vec<CString> = args.iter().map(|s| {
        let s_ref: &str = s.as_ref();
        CString::new(s_ref).unwrap()
    }).collect();
    let args_vec_ptr: Vec<*const ffi::c_char> = args_vec_cstr.iter().map(|s| s.as_ptr()).collect();
    unsafe { ffi::nvim_main_setup(args_vec_ptr.len() as i32, args_vec_ptr.as_ptr()) }
}

pub fn main_loop() -> i32 {
    unsafe { ffi::nvim_main_loop() }
}

pub fn channel_from_fds(read_fd: i32, write_fd: i32) -> u64 {
    unsafe { ffi::channel_from_fds(read_fd, write_fd) }
}

pub fn serialize_message(id: u64, method: &'static str, args: &Array) -> String {
    unsafe {
        let buf = ffi::vim_msgpack_new();
        let vim_str = ffi::C_String {data: method.as_ptr() as *const i8, size: method.len() as u64};
        ffi::vim_serialize_request(id, vim_str, args.unwrap_value(), buf);
        let v = Vec::from_raw_buf((*buf).data as *const u8, (*buf).size as usize);
        ffi::vim_msgpack_free(buf);
        String::from_utf8_unchecked(v)
    }
}

pub fn deserialize_message(message: &String) -> Array {
    let message_ref: &str = message.as_ref();
    let s = ffi::C_String {
        data: message_ref.as_ptr() as *const i8,
        size: message.len() as u64
    };
    let mut arr_raw = ffi::C_Array {
        items: ::std::ptr::null_mut(),
        size: 0,
        capacity: 0
    };
    unsafe { ffi::vim_msgpack_parse(s, &mut arr_raw) };
    Array::wrap_value(arr_raw)
}

pub struct Array {
    value: ffi::C_Array,
    is_owned: bool,
}

impl Array {
    pub fn new() -> Array {
        Array {
            value: ffi::C_Array {
                items: ::std::ptr::null_mut(),
                size: 0,
                capacity: 0
            },
            is_owned: true
        }
    }

    pub fn add_buffer(&mut self, val: ffi::C_Buffer) {
        unsafe { ffi::vim_array_add_buffer(val, &mut self.value) }
    }

    pub fn add_window(&mut self, val: ffi::C_Window) {
        unsafe { ffi::vim_array_add_window(val, &mut self.value) }
    }

    pub fn add_tabpage(&mut self, val: ffi::C_Tabpage) {
        unsafe { ffi::vim_array_add_tabpage(val, &mut self.value) }
    }

    pub fn add_nil(&mut self) {
        unsafe { ffi::vim_array_add_nil(&mut self.value) }
    }

    pub fn add_boolean(&mut self, val: ffi::C_Boolean) {
        unsafe { ffi::vim_array_add_boolean(val, &mut self.value) }
    }

    pub fn add_integer(&mut self, val: ffi::C_Integer) {
        unsafe { ffi::vim_array_add_integer(val, &mut self.value) }
    }

    pub fn add_float(&mut self, val: ffi::C_Float) {
        unsafe { ffi::vim_array_add_float(val, &mut self.value) }
    }

    pub fn add_string(&mut self, val: &str) {
        unsafe {
            // we need to copy the string into memory not managed by Rust,
            // so api_free_array can clear it
            let ptr = ffi::malloc(val.len() as u64);
            ::std::ptr::copy(ptr, val.as_ptr() as *mut ffi::c_void, val.len());
            let vim_str = ffi::C_String {data: ptr as *const i8, size: val.len() as u64};
            ffi::vim_array_add_string(vim_str, &mut self.value)
        }
    }

    pub fn add_array(&mut self, val: &Array) {
        unsafe { ffi::vim_array_add_array(val.unwrap_value(), &mut self.value) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        unsafe { ffi::vim_array_add_dictionary(val, &mut self.value) }
    }

    pub fn get(&self, index: u64) -> Option<Object> {
        if index >= self.len() {
            return None;
        }
        unsafe { c_object_to_object(self.value.items.offset(index as isize)) }
    }

    pub fn len(&self) -> u64 {
        self.value.size
    }

    #[doc(hidden)]
    pub fn unwrap_value(&self) -> ffi::C_Array {
        self.value
    }

    #[doc(hidden)]
    pub fn wrap_value(c_array: ffi::C_Array) -> Array {
        Array {
            value: c_array,
            is_owned: false
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        if self.is_owned {
            unsafe { ffi::api_free_array(self.value) };
        }
    }
}

impl fmt::Debug for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Array(").ok();
        for i in 0..self.len() {
            if let Some(obj) = self.get(i) {
                write!(f, "{:?}", obj).ok();
            } else {
                write!(f, "Nil").ok();
            }
            if i + 1 < self.len() {
                write!(f, ", ").ok();
            }
        }
        write!(f, ")")
    }
}

#[test]
fn test_request() {
    let mut args = Array::new();
    args.add_integer(80);
    args.add_integer(24);
    args.add_string("hello");

    let msg = serialize_message(1, "test", &args);
    let arr = deserialize_message(&msg);
    println!("LENGTH: {}", arr.len());
    for i in 0..arr.len() {
        println!("{:?}", arr.get(i));
    }
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#[macro_use]
extern crate log;
extern crate libc;

#[allow(non_camel_case_types)]
pub mod ffiairspy;

use std::mem;
use std::ptr;

#[derive(Debug)]
pub enum AirspyError {
    InvalidParam       = ffiairspy::AIRSPY_ERROR_INVALID_PARAM as isize,
    NotFound           = ffiairspy::AIRSPY_ERROR_NOT_FOUND as isize,
    Busy               = ffiairspy::AIRSPY_ERROR_BUSY as isize,
    NoMem              = ffiairspy::AIRSPY_ERROR_NO_MEM as isize,
    LibUsb             = ffiairspy::AIRSPY_ERROR_LIBUSB as isize,
    Thread             = ffiairspy::AIRSPY_ERROR_THREAD as isize,
    StreamingThreadErr = ffiairspy::AIRSPY_ERROR_STREAMING_THREAD_ERR as isize,
    StreamingStopped   = ffiairspy::AIRSPY_ERROR_STREAMING_STOPPED as isize,
    Other              = ffiairspy::AIRSPY_ERROR_OTHER as isize,
}

#[derive(Debug, Copy, Clone)]
pub enum AirspySamplerate {
    Sps10000000 = 10_000_000,
    Sps2500000  =  2_500_000,
}

#[derive(Debug)]
pub enum AirspySampleType {
    /// 2 * 32bit float per sample
    Float32IQ   = ffiairspy::AIRSPY_SAMPLE_FLOAT32_IQ as isize,
    /// 1 * 32bit float per sample
    Float32Real = ffiairspy::AIRSPY_SAMPLE_FLOAT32_REAL as isize,
    /// 2 * 16bit int per sample
    Int16IQ     = ffiairspy::AIRSPY_SAMPLE_INT16_IQ as isize,
    /// 1 * 16bit int per sample
    Int16Real   = ffiairspy::AIRSPY_SAMPLE_INT16_REAL as isize,
    /// 1 * 16bit unsigned int per sample
    Uint16Real  = ffiairspy::AIRSPY_SAMPLE_UINT16_REAL as isize,
}

#[repr(C)]
pub struct IQ<T> {
    i: T,
    q: T,
}

pub trait AirspySupportedType {
    fn get_sample_type() -> u32;
}

impl AirspySupportedType for IQ<f32> {
    fn get_sample_type() -> u32 {
        ffiairspy::AIRSPY_SAMPLE_FLOAT32_IQ
    }
}
impl AirspySupportedType for f32 {
    fn get_sample_type() -> u32 {
        ffiairspy::AIRSPY_SAMPLE_FLOAT32_REAL
    }
}
impl AirspySupportedType for i16 {
    fn get_sample_type() -> u32 {
        ffiairspy::AIRSPY_SAMPLE_INT16_REAL
    }
}
impl AirspySupportedType for IQ<i16> {
    fn get_sample_type() -> u32 {
        ffiairspy::AIRSPY_SAMPLE_INT16_IQ
    }
}
impl AirspySupportedType for u16 {
    fn get_sample_type() -> u32 {
        ffiairspy::AIRSPY_SAMPLE_UINT16_REAL
    }
}

pub struct Airspy<T: AirspySupportedType> {
    device: *mut ffiairspy::Struct_airspy_device,
    samples: Vec<T>,
}

impl<T: AirspySupportedType> Airspy<T> {
    pub fn new() -> Result<Airspy<T>, AirspyError> {
        let mut device = ptr::null_mut();

        let error = unsafe {ffiairspy::airspy_open(&mut device)};
        match error {
            ffiairspy::AIRSPY_SUCCESS => {
                let airspy = Airspy{device: device, samples: Vec::<T>::new()};

                match unsafe {ffiairspy::airspy_set_sample_type(device, T::get_sample_type())} {
                    ffiairspy::AIRSPY_SUCCESS => {
                        Ok(airspy)
                    },
                    err => {
                        // airspy_set_sample_type should only return SUCCESS
                        // however just in case panic here if for some reason it does anything else
                        panic!("airspy_set_sample_type returned error {}", err)
                    },
                }
            },
            ffiairspy::AIRSPY_ERROR_NO_MEM => Err(AirspyError::NoMem),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => panic!("airspy_open returned error ({:})", err),
        }
    }

    pub fn set_samplerate(&self, samplerate: AirspySamplerate) -> Result<(), AirspyError> {
        let mut supported_samplerates_count: u32 = 0;

        // get count of supported samplerates
        let mut error = unsafe {ffiairspy::airspy_get_samplerates(self.device, &mut supported_samplerates_count as *mut u32, 0)};
        // anything except SUCCESS is library failure, hence panic!
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        assert!(supported_samplerates_count == ffiairspy::AIRSPY_SAMPLERATE_END);

        // get list of supported samplerates
        let mut samplerates: Vec<u32> = vec![0_u32; supported_samplerates_count as usize];
        error = unsafe {ffiairspy::airspy_get_samplerates(self.device, mem::transmute::<&mut u32, *mut u32>(&mut samplerates[0]), supported_samplerates_count)};
        // supported_samplerates_count must be right because we just asked for it from library, there must be lib error if anything other than SUCCESS is returned
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        let sr = samplerate.clone() as u32;
        let mut index: Option<u32> = None;
        for (i, rate) in samplerates.iter().enumerate() {
            if *rate == sr {
                index = Some(i as u32);
                break;
            }
        }

        if index.is_some() {
            let error = unsafe {ffiairspy::airspy_set_samplerate(self.device, index.unwrap())};
            match error {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_samplerate returned error ({:})", err),
            }
        }
        else {
            panic!("samplerate {:?} not supported by libairspy, supported samplerates are {:?}", samplerate, samplerates);
        }
    }

    pub fn set_bias_tee(&self, onoff: bool) -> Result<(), AirspyError> {
        let c_onoff = match onoff {
            true => 1,
            false => 0,
        };

        match unsafe {ffiairspy::airspy_set_rf_bias(self.device, c_onoff)} {
            ffiairspy::AIRSPY_SUCCESS => Ok(()),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => {
                panic!("airspy_set_rf_bias returned error {}", err)
            }
        }
    }

    pub fn set_lna_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_lna_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_lna_gain returned error {}", err),
            }
        }
    }

    pub fn set_mixer_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_mixer_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_mixer_gain returned error {}", err)
            }
        }
    }

    pub fn set_vga_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_vga_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_vga_gain returned error {}", err),
            }
        }
    }

    pub fn set_frequency(&self, freq_hz: u32) -> Result<(), AirspyError> {
        // 24 MHz to 1.8 GHz
        if freq_hz < 24_000_000 || freq_hz > 1_8_00_000_000 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_freq(self.device, freq_hz)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_freq returned error {}", err),
            }
        }
    }

    pub fn start_rx(&self) -> Result<(), AirspyError> {
        match unsafe {ffiairspy::airspy_start_rx(self.device, Some(Self::receive), ptr::null_mut())} {
            ffiairspy::AIRSPY_SUCCESS => Ok(()),
            ffiairspy::AIRSPY_ERROR_BUSY => Err(AirspyError::Busy),
            ffiairspy::AIRSPY_ERROR_THREAD => Err(AirspyError::Thread),
            err => panic!("airspy_start_rx returned error {}", err),
        }
    }

    extern "C" fn receive(transfer: *mut ffiairspy::airspy_transfer) -> libc::c_int {
        info!("receive");
        0
    }
}

impl<T: AirspySupportedType> Drop for Airspy<T> {
    fn drop(&mut self) {
        unsafe{ffiairspy::airspy_close(self.device)};
        unsafe{ffiairspy::airspy_exit()};
    }
}
<commit_msg>make macro for "impl AirspySupportedType for ..", add generic rawbuf_to_vec(..) method to AirspySupportedType trait<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#[macro_use]
extern crate log;
extern crate libc;
use libc::{c_int, c_void};

#[allow(non_camel_case_types)]
pub mod ffiairspy;

use std::mem;
use std::ptr;
use std::slice;

#[derive(Debug)]
pub enum AirspyError {
    InvalidParam       = ffiairspy::AIRSPY_ERROR_INVALID_PARAM as isize,
    NotFound           = ffiairspy::AIRSPY_ERROR_NOT_FOUND as isize,
    Busy               = ffiairspy::AIRSPY_ERROR_BUSY as isize,
    NoMem              = ffiairspy::AIRSPY_ERROR_NO_MEM as isize,
    LibUsb             = ffiairspy::AIRSPY_ERROR_LIBUSB as isize,
    Thread             = ffiairspy::AIRSPY_ERROR_THREAD as isize,
    StreamingThreadErr = ffiairspy::AIRSPY_ERROR_STREAMING_THREAD_ERR as isize,
    StreamingStopped   = ffiairspy::AIRSPY_ERROR_STREAMING_STOPPED as isize,
    Other              = ffiairspy::AIRSPY_ERROR_OTHER as isize,
}

#[derive(Debug, Copy, Clone)]
pub enum AirspySamplerate {
    Sps10000000 = 10_000_000,
    Sps2500000  =  2_500_000,
}

#[derive(Debug)]
pub enum AirspySampleType {
    /// 2 * 32bit float per sample
    Float32IQ   = ffiairspy::AIRSPY_SAMPLE_FLOAT32_IQ as isize,
    /// 1 * 32bit float per sample
    Float32Real = ffiairspy::AIRSPY_SAMPLE_FLOAT32_REAL as isize,
    /// 2 * 16bit int per sample
    Int16IQ     = ffiairspy::AIRSPY_SAMPLE_INT16_IQ as isize,
    /// 1 * 16bit int per sample
    Int16Real   = ffiairspy::AIRSPY_SAMPLE_INT16_REAL as isize,
    /// 1 * 16bit unsigned int per sample
    Uint16Real  = ffiairspy::AIRSPY_SAMPLE_UINT16_REAL as isize,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct IQ<T> {
    i: T,
    q: T,
}

pub trait AirspySupportedType<T> {
    fn get_sample_type() -> u32;
    fn rawbuf_to_vec(rawbuf: *mut c_void, len: usize) -> Vec<T>;
}

macro_rules! impl_airspy_supported_type {
    ($typ:ty, $sample_type:expr) => {
        impl AirspySupportedType<$typ> for $typ {
            fn get_sample_type() -> u32 {
                $sample_type
            }

            fn rawbuf_to_vec(rawbuf: *mut c_void, len: usize) -> Vec<$typ> {
                let slice: &[$typ] = unsafe {
                        slice::from_raw_parts(rawbuf as *mut $typ, len)
                };
                slice.to_vec()
            }
        }
    };
}

impl_airspy_supported_type!(IQ<f32>, ffiairspy::AIRSPY_SAMPLE_FLOAT32_IQ);
impl_airspy_supported_type!(f32,     ffiairspy::AIRSPY_SAMPLE_FLOAT32_REAL);
impl_airspy_supported_type!(IQ<i16>, ffiairspy::AIRSPY_SAMPLE_INT16_IQ);
impl_airspy_supported_type!(i16,     ffiairspy::AIRSPY_SAMPLE_INT16_REAL);
impl_airspy_supported_type!(u16,     ffiairspy::AIRSPY_SAMPLE_UINT16_REAL);

pub struct Airspy<T: AirspySupportedType<T>> {
    device: *mut ffiairspy::Struct_airspy_device,
    samples: Vec<T>,
}

impl<T: AirspySupportedType<T>> Airspy<T> {

    fn context(&mut self) -> *mut c_void {
        self as *mut _ as *mut c_void
    }

    pub fn new() -> Result<Airspy<T>, AirspyError> {
        let mut device = ptr::null_mut();

        let error = unsafe {ffiairspy::airspy_open(&mut device)};
        match error {
            ffiairspy::AIRSPY_SUCCESS => {
                let airspy = Airspy{device: device, samples: Vec::<T>::new()};

                match unsafe {ffiairspy::airspy_set_sample_type(device, T::get_sample_type())} {
                    ffiairspy::AIRSPY_SUCCESS => {
                        Ok(airspy)
                    },
                    err => {
                        // airspy_set_sample_type should only return SUCCESS
                        // however just in case panic here if for some reason it does anything else
                        panic!("airspy_set_sample_type returned error {}", err)
                    },
                }
            },
            ffiairspy::AIRSPY_ERROR_NO_MEM => Err(AirspyError::NoMem),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => panic!("airspy_open returned error ({:})", err),
        }
    }

    pub fn set_samplerate(&self, samplerate: AirspySamplerate) -> Result<(), AirspyError> {
        let mut supported_samplerates_count: u32 = 0;

        // get count of supported samplerates
        let mut error = unsafe {ffiairspy::airspy_get_samplerates(self.device, &mut supported_samplerates_count as *mut u32, 0)};
        // anything except SUCCESS is library failure, hence panic!
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        assert!(supported_samplerates_count == ffiairspy::AIRSPY_SAMPLERATE_END);

        // get list of supported samplerates
        let mut samplerates: Vec<u32> = vec![0_u32; supported_samplerates_count as usize];
        error = unsafe {ffiairspy::airspy_get_samplerates(self.device, mem::transmute::<&mut u32, *mut u32>(&mut samplerates[0]), supported_samplerates_count)};
        // supported_samplerates_count must be right because we just asked for it from library, there must be lib error if anything other than SUCCESS is returned
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        let sr = samplerate.clone() as u32;
        let mut index: Option<u32> = None;
        for (i, rate) in samplerates.iter().enumerate() {
            if *rate == sr {
                index = Some(i as u32);
                break;
            }
        }

        if index.is_some() {
            let error = unsafe {ffiairspy::airspy_set_samplerate(self.device, index.unwrap())};
            match error {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_samplerate returned error ({:})", err),
            }
        }
        else {
            panic!("samplerate {:?} not supported by libairspy, supported samplerates are {:?}", samplerate, samplerates);
        }
    }

    pub fn set_bias_tee(&self, onoff: bool) -> Result<(), AirspyError> {
        let c_onoff = match onoff {
            true => 1,
            false => 0,
        };

        match unsafe {ffiairspy::airspy_set_rf_bias(self.device, c_onoff)} {
            ffiairspy::AIRSPY_SUCCESS => Ok(()),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => {
                panic!("airspy_set_rf_bias returned error {}", err)
            }
        }
    }

    pub fn set_lna_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_lna_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_lna_gain returned error {}", err),
            }
        }
    }

    pub fn set_mixer_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_mixer_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_mixer_gain returned error {}", err)
            }
        }
    }

    pub fn set_vga_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_vga_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_vga_gain returned error {}", err),
            }
        }
    }

    pub fn set_frequency(&self, freq_hz: u32) -> Result<(), AirspyError> {
        // 24 MHz to 1.8 GHz
        if freq_hz < 24_000_000 || freq_hz > 1_8_00_000_000 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_freq(self.device, freq_hz)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_freq returned error {}", err),
            }
        }
    }

    pub fn start_rx(&mut self) -> Result<(), AirspyError> {
        match unsafe {ffiairspy::airspy_start_rx(self.device, Some(Self::receive), self.context())} {
            ffiairspy::AIRSPY_SUCCESS => Ok(()),
            ffiairspy::AIRSPY_ERROR_BUSY => Err(AirspyError::Busy),
            ffiairspy::AIRSPY_ERROR_THREAD => Err(AirspyError::Thread),
            err => panic!("airspy_start_rx returned error {}", err),
        }
    }

    extern "C" fn receive(transfer: *mut ffiairspy::airspy_transfer) -> c_int {
        let transfer: &mut ffiairspy::airspy_transfer = unsafe {mem::transmute(transfer)};
        assert!(!transfer.ctx.is_null());
        info!("samplecount: {}", transfer.sample_count);
        assert!(transfer.sample_count == 131072 || transfer.sample_count == 65536);

        let selfclient : &mut Self = unsafe {mem::transmute(transfer.ctx)};
        selfclient.samples = T::rawbuf_to_vec(transfer.samples, transfer.sample_count as usize);

        0
    }
}

impl<T: AirspySupportedType<T>> Drop for Airspy<T> {
    fn drop(&mut self) {
        unsafe{ffiairspy::airspy_close(self.device)};
        unsafe{ffiairspy::airspy_exit()};
    }
}
<|endoftext|>