<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

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

use std::io::{self, Error};
use std::os::unix::io::RawFd;

bitflags! {
    pub struct ControlOptions: i32 {
        /// Indicates an addition to the interest list.
        const EPOLL_CTL_ADD = libc::EPOLL_CTL_ADD;
        /// Indicates a modification of flags for an interest already in list.
        const EPOLL_CTL_MOD = libc::EPOLL_CTL_MOD;
        /// Indicates a removal of an interest from the list.
        const EPOLL_CTL_DEL = libc::EPOLL_CTL_DEL;
    }
}

bitflags! {
    pub struct Events: u32 {
        /// Sets the Edge Triggered behavior for the associated file descriptor.
        ///
        /// The default behavior for epoll is Level Triggered.
        const EPOLLET      = libc::EPOLLET as u32;
        /// The associated file is available for read operations.
        const EPOLLIN      = libc::EPOLLIN as u32;
        /// Error condition happened on the associated file descriptor.
        ///
        /// `wait` will always wait for this event; is not necessary to set it in events.
        const EPOLLERR     = libc::EPOLLERR as u32;
        /// Hang up happened on the associated file descriptor.
        ///
        /// `wait` will always wait for this event; it is not necessary to set it in events.
        /// Note that when reading from a channel such as a pipe or a stream socket, this event
        /// merely indicates that the peer closed its end of the channel. Subsequent reads from
        /// the channel will return 0 (end of file) only after all outstanding data in the
        /// channel has been consumed.
        const EPOLLHUP     = libc::EPOLLHUP as u32;
        /// The associated file is available for write operations.
        const EPOLLOUT     = libc::EPOLLOUT as u32;
        /// There is urgent data available for read operations.
        const EPOLLPRI     = libc::EPOLLPRI as u32;
        /// Stream socket peer closed connection, or shut down writing half of connection.
        ///
        /// This flag is especially useful for writing simple code to detect peer shutdown when
        /// using Edge Triggered monitoring.
        const EPOLLRDHUP   = libc::EPOLLRDHUP as u32;
        /// If `EPOLLONESHOT` and `EPOLLET` are clear and the process has the `CAP_BLOCK_SUSPEND`
        /// capability, ensure that the system does not enter "suspend" or "hibernate" while this
        /// event is pending or being processed.
        ///
        /// The event is considered as being "processed" from the time when it is returned by
        /// a call to `wait` until the next call to `wait` on the same `EpollInstance`
        /// descriptor, the closure of that file descriptor, the removal of the event file
        /// descriptor with `EPOLL_CTL_DEL`, or the clearing of `EPOLLWAKEUP` for the event file
        /// descriptor with `EPOLL_CTL_MOD`.
        const EPOLLWAKEUP  = libc::EPOLLWAKEUP as u32;
        /// Sets the one-shot behavior for the associated file descriptor.
        ///
        /// This means that after an event is pulled out with `wait` the associated file
        /// descriptor is internally disabled and no other events will be reported by the epoll
        /// interface.  The user must call `ctl` with `EPOLL_CTL_MOD` to rearm the file
        /// descriptor with a new event mask.
        const EPOLLONESHOT = libc::EPOLLONESHOT as u32;
    }
}

/// 'libc::epoll_event' equivalent.
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy)]
pub struct Event {
    pub events: u32,
    pub data: u64
}

impl Event {
    pub fn new(events: Events, data: u64) -> Event {
        Event { events: events.bits(), data: data }
    }
}

/// Creates a new epoll file descriptor.
///
/// If `cloexec` is true, `FD_CLOEXEC` will be set on the returned file descriptor.
///
/// ## Notes
///
/// * `epoll_create1()` is the underlying syscall.
pub fn create(cloexec: bool) -> io::Result<RawFd> {
    let flags = if cloexec { libc::FD_CLOEXEC } else { 0 };
    unsafe { cvt(libc::epoll_create1(flags)) }
}

/// Safe wrapper for `libc::epoll_ctl`
pub fn ctl(epfd: RawFd,
           op: ControlOptions,
           fd: RawFd,
           mut event: Event)
           -> io::Result<()>
{
    let e = &mut event as *mut _ as *mut libc::epoll_event;
    unsafe { try!(cvt(libc::epoll_ctl(epfd, op.bits, fd, e))) };
    Ok(())
}

/// Safe wrapper for `libc::epoll_wait`
///
/// ## Notes
///
/// * If `timeout` is negative, it will block until an event is received.
pub fn wait(epfd: RawFd,
            timeout: i32,
            buf: &mut [Event])
            -> io::Result<usize>
{
    let timeout = if timeout < -1 { -1 } else { timeout };
    let num_events = unsafe {
        try!(cvt(libc::epoll_wait(epfd,
                                  buf.as_mut_ptr() as *mut libc::epoll_event,
                                  buf.len() as i32,
                                  timeout))) as usize
    };
    Ok(num_events)
}

fn cvt(result: libc::c_int) -> io::Result<libc::c_int> {
    if result < 0 { Err(Error::last_os_error()) } else { Ok(result) }
}
<commit_msg>bug fix: libc::FD_CLOEXEC was incorectly used instead of libc::EPOLL_CLOEXEC<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

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

use std::io::{self, Error};
use std::os::unix::io::RawFd;

bitflags! {
    pub struct ControlOptions: i32 {
        /// Indicates an addition to the interest list.
        const EPOLL_CTL_ADD = libc::EPOLL_CTL_ADD;
        /// Indicates a modification of flags for an interest already in list.
        const EPOLL_CTL_MOD = libc::EPOLL_CTL_MOD;
        /// Indicates a removal of an interest from the list.
        const EPOLL_CTL_DEL = libc::EPOLL_CTL_DEL;
    }
}

bitflags! {
    pub struct Events: u32 {
        /// Sets the Edge Triggered behavior for the associated file descriptor.
        ///
        /// The default behavior for epoll is Level Triggered.
        const EPOLLET      = libc::EPOLLET as u32;
        /// The associated file is available for read operations.
        const EPOLLIN      = libc::EPOLLIN as u32;
        /// Error condition happened on the associated file descriptor.
        ///
        /// `wait` will always wait for this event; is not necessary to set it in events.
        const EPOLLERR     = libc::EPOLLERR as u32;
        /// Hang up happened on the associated file descriptor.
        ///
        /// `wait` will always wait for this event; it is not necessary to set it in events.
        /// Note that when reading from a channel such as a pipe or a stream socket, this event
        /// merely indicates that the peer closed its end of the channel. Subsequent reads from
        /// the channel will return 0 (end of file) only after all outstanding data in the
        /// channel has been consumed.
        const EPOLLHUP     = libc::EPOLLHUP as u32;
        /// The associated file is available for write operations.
        const EPOLLOUT     = libc::EPOLLOUT as u32;
        /// There is urgent data available for read operations.
        const EPOLLPRI     = libc::EPOLLPRI as u32;
        /// Stream socket peer closed connection, or shut down writing half of connection.
        ///
        /// This flag is especially useful for writing simple code to detect peer shutdown when
        /// using Edge Triggered monitoring.
        const EPOLLRDHUP   = libc::EPOLLRDHUP as u32;
        /// If `EPOLLONESHOT` and `EPOLLET` are clear and the process has the `CAP_BLOCK_SUSPEND`
        /// capability, ensure that the system does not enter "suspend" or "hibernate" while this
        /// event is pending or being processed.
        ///
        /// The event is considered as being "processed" from the time when it is returned by
        /// a call to `wait` until the next call to `wait` on the same `EpollInstance`
        /// descriptor, the closure of that file descriptor, the removal of the event file
        /// descriptor with `EPOLL_CTL_DEL`, or the clearing of `EPOLLWAKEUP` for the event file
        /// descriptor with `EPOLL_CTL_MOD`.
        const EPOLLWAKEUP  = libc::EPOLLWAKEUP as u32;
        /// Sets the one-shot behavior for the associated file descriptor.
        ///
        /// This means that after an event is pulled out with `wait` the associated file
        /// descriptor is internally disabled and no other events will be reported by the epoll
        /// interface.  The user must call `ctl` with `EPOLL_CTL_MOD` to rearm the file
        /// descriptor with a new event mask.
        const EPOLLONESHOT = libc::EPOLLONESHOT as u32;
    }
}

/// 'libc::epoll_event' equivalent.
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy)]
pub struct Event {
    pub events: u32,
    pub data: u64
}

impl Event {
    pub fn new(events: Events, data: u64) -> Event {
        Event { events: events.bits(), data: data }
    }
}

/// Creates a new epoll file descriptor.
///
/// If `cloexec` is true, `FD_CLOEXEC` will be set on the returned file descriptor.
///
/// ## Notes
///
/// * `epoll_create1()` is the underlying syscall.
pub fn create(cloexec: bool) -> io::Result<RawFd> {
    let flags = if cloexec { libc::EPOLL_CLOEXEC } else { 0 };
    unsafe { cvt(libc::epoll_create1(flags)) }
}

/// Safe wrapper for `libc::epoll_ctl`
pub fn ctl(epfd: RawFd,
           op: ControlOptions,
           fd: RawFd,
           mut event: Event)
           -> io::Result<()>
{
    let e = &mut event as *mut _ as *mut libc::epoll_event;
    unsafe { try!(cvt(libc::epoll_ctl(epfd, op.bits, fd, e))) };
    Ok(())
}

/// Safe wrapper for `libc::epoll_wait`
///
/// ## Notes
///
/// * If `timeout` is negative, it will block until an event is received.
pub fn wait(epfd: RawFd,
            timeout: i32,
            buf: &mut [Event])
            -> io::Result<usize>
{
    let timeout = if timeout < -1 { -1 } else { timeout };
    let num_events = unsafe {
        try!(cvt(libc::epoll_wait(epfd,
                                  buf.as_mut_ptr() as *mut libc::epoll_event,
                                  buf.len() as i32,
                                  timeout))) as usize
    };
    Ok(num_events)
}

fn cvt(result: libc::c_int) -> io::Result<libc::c_int> {
    if result < 0 { Err(Error::last_os_error()) } else { Ok(result) }
}
<|endoftext|>


<commit_before>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
#![feature(macro_rules)]

pub use vector::Vector;
pub use vector::VectorOperations;
pub use matrix::Matrix;

pub mod mat;

pub mod vector;
pub mod matrix_vector;
pub mod matrix;

pub mod attribute;
pub mod default;
mod pointer;
mod scalar;
<commit_msg>Make pointer mod public<commit_after>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
#![feature(macro_rules)]

pub use vector::Vector;
pub use vector::VectorOperations;
pub use matrix::Matrix;

pub mod mat;

pub mod vector;
pub mod matrix_vector;
pub mod matrix;

pub mod attribute;
pub mod default;
pub mod pointer;
mod scalar;
<|endoftext|>


<commit_before>//! ODBC types those representation is compatible with the ODBC C API.
//!
//! This layer has not been created using automatic code generation. It is incomplete, i.e. it does
//! not contain every symbol or constant defined in the ODBC C headers. Symbols which are
//! deprecated since ODBC 3 have been left out intentionally. While some extra type safety has been
//! added by grouping some of C's `#define` constants into `enum`-types it mostly offers the same
//! power (all) and safety guarantess(none) as the wrapped C-API.
//! ODBC 4.0 is still under development by Microsoft, so these symbols are deactivated by default
//! in the cargo.toml

mod sqlreturn;
pub use self::sqlreturn::*;
mod info_type;
pub use self::info_type::*;
mod fetch_orientation;
pub use self::fetch_orientation::*;
mod attributes;
pub use self::attributes::*;
use std::os::raw::{c_void, c_short, c_ushort, c_int};

//These types can never be instantiated in Rust code.
pub enum Obj {}
pub enum Env {}
pub enum Dbc {}
pub enum Stmt {}

pub type SQLHANDLE = *mut Obj;
pub type SQLHENV = *mut Env;

/// The connection handle references storage of all information about the connection to the data
/// source, including status, transaction state, and error information.
pub type SQLHDBC = *mut Dbc;
pub type SQLHSTMT = *mut Stmt;

/// 16 Bit signed integer
pub type SQLSMALLINT = c_short;
pub type SQLUSMALLINT = c_ushort;
pub type SQLINTEGER = c_int;
pub type SQLPOINTER = *mut c_void;
pub type SQLCHAR = u8;

#[cfg(all(windows, target_pointer_width = "64"))]
pub type SQLLEN = i64;
#[cfg(not(windows))]
pub type SQLLEN = SQLINTEGER;

pub type SQLHWND = SQLPOINTER;

// flags for null-terminated string
pub const SQL_NTS: SQLSMALLINT = -3;
pub const SQL_NTSL: SQLINTEGER = -3;

/// Maximum message length
pub const SQL_MAX_MESSAGE_LENGTH: SQLSMALLINT = 512;
pub const SQL_SQLSTATE_SIZE: usize = 5;

// Special SQLGetData indicator values
pub const SQL_NULL_DATA: SQLLEN = -1;
pub const SQL_NO_TOTAL: SQLLEN = -4;

/// SQL Data Types
#[repr(i16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SqlDataType {
    SQL_UNKNOWN_TYPE = 0, // also called SQL_VARIANT_TYPE since odbc 4.0
    SQL_CHAR = 1,
    SQL_NUMERIC = 2,
    SQL_DECIMAL = 3,
    SQL_INTEGER = 4,
    SQL_SMALLINT = 5,
    SQL_FLOAT = 6,
    SQL_REAL = 7,
    SQL_DOUBLE = 8,
    SQL_DATETIME = 9,
    SQL_VARCHAR = 12,
    #[cfg(feature = "odbc_version_4")]
    SQL_UDT = 17,
    #[cfg(feature = "odbc_version_4")]
    SQL_ROW = 19,
    #[cfg(feature = "odbc_version_4")]
    SQL_ARRAY = 50,
    #[cfg(feature = "odbc_version_4")]
    SQL_MULTISET = 55,
}
pub use self::SqlDataType::*;

#[repr(i16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SqlCDataType {
    SQL_C_CHAR = 1,
}
pub use self::SqlCDataType::*;

/// Represented in C headers as SQLSMALLINT
#[repr(i16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum HandleType {
    SQL_HANDLE_ENV = 1,
    SQL_HANDLE_DBC = 2,
    SQL_HANDLE_STMT = 3,
    SQL_HANDLE_DESC = 4,
}
pub use self::HandleType::*;

/// Options for `SQLDriverConnect`
#[repr(u16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SqlDriverConnectOption {
    SQL_DRIVER_NOPROMPT = 0,
    SQL_DRIVER_COMPLETE = 1,
    SQL_DRIVER_PROMPT = 2,
    SQL_DRIVER_COMPLETE_REQUIRED = 3,
}
pub use self::SqlDriverConnectOption::*;

#[cfg_attr(windows, link(name="odbc32"))]
#[cfg_attr(not(windows), link(name="odbc"))]
extern "C" {
    /// Allocates an environment, connection, statement, or descriptor handle.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLAllocHandle(handle_type: HandleType,
                          input_handle: SQLHANDLE,
                          output_Handle: *mut SQLHANDLE)
                          -> SQLRETURN;

    /// Frees resources associated with a specific environment, connection, statement, or
    /// descriptor handle.
    ///
    /// If `SQL_ERRQR` is returned the handle is still valid.
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLFreeHandle(handle_type: HandleType, handle: SQLHANDLE) -> SQLRETURN;

    /// Sets attributes that govern aspects of environments
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLSetEnvAttr(environment_handle: SQLHENV,
                         attribute: EnvironmentAttribute,
                         value: SQLPOINTER,
                         string_length: SQLINTEGER)
                         -> SQLRETURN;

    /// Closes the connection associated with a specific connection handle.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLDisconnect(connection_handle: SQLHDBC) -> SQLRETURN;

    pub fn SQLGetDiagRec(handle_type: HandleType,
                         handle: SQLHANDLE,
                         RecNumber: SQLSMALLINT,
                         state: *mut SQLCHAR,
                         native_error_ptr: *mut SQLINTEGER,
                         message_text: *mut SQLCHAR,
                         buffer_length: SQLSMALLINT,
                         text_length_ptr: *mut SQLSMALLINT)
                         -> SQLRETURN;

    pub fn SQLExecDirect(statement_handle: SQLHSTMT,
                         statement_text: *const SQLCHAR,
                         text_length: SQLINTEGER)
                         -> SQLRETURN;

    pub fn SQLNumResultCols(statement_handle: SQLHSTMT,
                            column_count_ptr: *mut SQLSMALLINT)
                            -> SQLRETURN;

    // Can be used since odbc version 3.8 to stream results
    pub fn SQLGetData(statement_handle: SQLHSTMT,
                      col_or_param_num: SQLUSMALLINT,
                      target_type: SqlCDataType,
                      target_value_ptr: SQLPOINTER,
                      buffer_length: SQLLEN,
                      str_len_or_ind_ptr: *mut SQLLEN)
                      -> SQLRETURN;

    /// SQLFetch fetches the next rowset of data from the result set and returns data for all bound
    /// columns.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, `SQL_NO_DATA` or
    /// `SQL_STILL_EXECUTING`
    pub fn SQLFetch(statement_handle: SQLHSTMT) -> SQLRETURN;

    /// Returns general information about the driver and data source associated with a connection
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLGetInfo(connection_handle: SQLHDBC,
                      info_type: InfoType,
                      info_value_ptr: SQLPOINTER,
                      buffer_length: SQLSMALLINT,
                      string_length_ptr: *mut SQLSMALLINT)
                      -> SQLRETURN;

    /// SQLConnect establishes connections to a driver and a data source.
    ///
    /// The connection handle references storage of all information about the connection to the
    /// data source, including status, transaction state, and error information.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or
    /// `SQL_STILL_EXECUTING`
    pub fn SQLConnect(connection_handle: SQLHDBC,
                      server_name: *const SQLCHAR,
                      name_length_1: SQLSMALLINT,
                      user_name: *const SQLCHAR,
                      name_length_2: SQLSMALLINT,
                      authentication: *const SQLCHAR,
                      name_length_3: SQLSMALLINT)
                      -> SQLRETURN;

    /// Returns the list of table, catalog, or schema names, and table types, stored in a specific
    /// data source. The driver returns the information as a result set
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or
    /// `SQL_STILL_EXECUTING`
    pub fn SQLTables(statement_handle: SQLHSTMT,
                     catalog_name: *const SQLCHAR,
                     name_length_1: SQLSMALLINT,
                     schema_name: *const SQLCHAR,
                     name_length_2: SQLSMALLINT,
                     table_name: *const SQLCHAR,
                     name_length_3: SQLSMALLINT,
                     TableType: *const SQLCHAR,
                     name_length_4: SQLSMALLINT)
                     -> SQLRETURN;

    /// Returns information about a data source. This function is implemented only by the Driver
    /// Manager.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or `SQL_NO_DATA`
    pub fn SQLDataSources(environment_handle: SQLHENV,
                          direction: FetchOrientation,
                          server_name: *mut SQLCHAR,
                          buffer_length_1: SQLSMALLINT,
                          name_length_1: *mut SQLSMALLINT,
                          description: *mut SQLCHAR,
                          buffer_length_2: SQLSMALLINT,
                          name_length_2: *mut SQLSMALLINT)
                          -> SQLRETURN;

    /// An alternative to `SQLConnect`. It supports data sources that require more connection
    /// information than the three arguments in `SQLConnect`, dialog boxes to prompt the user for
    /// all connection information, and data sources that are not defined in the system information
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, `SQL_NO_DATA`,
    /// or `SQL_STILL_EXECUTING`
    pub fn SQLDriverConnect(connection_handle: SQLHDBC,
                            window_handle: SQLHWND,
                            in_connection_string: *const SQLCHAR,
                            string_length_1: SQLSMALLINT,
                            out_connection_string: *mut SQLCHAR,
                            buffer_length: SQLSMALLINT,
                            string_length_2: *mut SQLSMALLINT,
                            DriverCompletion: SqlDriverConnectOption)
                            -> SQLRETURN;

    /// Lists driver descriptions and driver attribute keywords. This function is implemented only
    /// by the Driver Manager.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or `SQL_NO_DATA`
    pub fn SQLDrivers(henv: SQLHENV,
                      direction: FetchOrientation,
                      driver_desc: *mut SQLCHAR,
                      driver_desc_max: SQLSMALLINT,
                      out_driver_desc: *mut SQLSMALLINT,
                      driver_attributes: *mut SQLCHAR,
                      drvr_attr_max: SQLSMALLINT,
                      out_drvr_attr: *mut SQLSMALLINT)
                      -> SQLRETURN;

    /// Closes a cursor that has been opened on a statement and discards pending results.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR` or `SQL_INVALID_HANDLE`
    pub fn SQLCloseCursor(hstmt: SQLHSTMT) -> SQLRETURN;
}
<commit_msg>defines SQLLEN for 32 Windows as SQLINTEGER<commit_after>//! ODBC types those representation is compatible with the ODBC C API.
//!
//! This layer has not been created using automatic code generation. It is incomplete, i.e. it does
//! not contain every symbol or constant defined in the ODBC C headers. Symbols which are
//! deprecated since ODBC 3 have been left out intentionally. While some extra type safety has been
//! added by grouping some of C's `#define` constants into `enum`-types it mostly offers the same
//! power (all) and safety guarantess(none) as the wrapped C-API.
//! ODBC 4.0 is still under development by Microsoft, so these symbols are deactivated by default
//! in the cargo.toml

mod sqlreturn;
pub use self::sqlreturn::*;
mod info_type;
pub use self::info_type::*;
mod fetch_orientation;
pub use self::fetch_orientation::*;
mod attributes;
pub use self::attributes::*;
use std::os::raw::{c_void, c_short, c_ushort, c_int};

//These types can never be instantiated in Rust code.
pub enum Obj {}
pub enum Env {}
pub enum Dbc {}
pub enum Stmt {}

pub type SQLHANDLE = *mut Obj;
pub type SQLHENV = *mut Env;

/// The connection handle references storage of all information about the connection to the data
/// source, including status, transaction state, and error information.
pub type SQLHDBC = *mut Dbc;
pub type SQLHSTMT = *mut Stmt;

/// 16 Bit signed integer
pub type SQLSMALLINT = c_short;
pub type SQLUSMALLINT = c_ushort;
pub type SQLINTEGER = c_int;
pub type SQLPOINTER = *mut c_void;
pub type SQLCHAR = u8;

#[cfg(all(windows, target_pointer_width = "64"))]
pub type SQLLEN = i64;
#[cfg(all(windows, target_pointer_width = "32"))]
pub type SQLLEN = SQLINTEGER;
#[cfg(not(windows))]
pub type SQLLEN = SQLINTEGER;

pub type SQLHWND = SQLPOINTER;

// flags for null-terminated string
pub const SQL_NTS: SQLSMALLINT = -3;
pub const SQL_NTSL: SQLINTEGER = -3;

/// Maximum message length
pub const SQL_MAX_MESSAGE_LENGTH: SQLSMALLINT = 512;
pub const SQL_SQLSTATE_SIZE: usize = 5;

// Special SQLGetData indicator values
pub const SQL_NULL_DATA: SQLLEN = -1;
pub const SQL_NO_TOTAL: SQLLEN = -4;

/// SQL Data Types
#[repr(i16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SqlDataType {
    SQL_UNKNOWN_TYPE = 0, // also called SQL_VARIANT_TYPE since odbc 4.0
    SQL_CHAR = 1,
    SQL_NUMERIC = 2,
    SQL_DECIMAL = 3,
    SQL_INTEGER = 4,
    SQL_SMALLINT = 5,
    SQL_FLOAT = 6,
    SQL_REAL = 7,
    SQL_DOUBLE = 8,
    SQL_DATETIME = 9,
    SQL_VARCHAR = 12,
    #[cfg(feature = "odbc_version_4")]
    SQL_UDT = 17,
    #[cfg(feature = "odbc_version_4")]
    SQL_ROW = 19,
    #[cfg(feature = "odbc_version_4")]
    SQL_ARRAY = 50,
    #[cfg(feature = "odbc_version_4")]
    SQL_MULTISET = 55,
}
pub use self::SqlDataType::*;

#[repr(i16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SqlCDataType {
    SQL_C_CHAR = 1,
}
pub use self::SqlCDataType::*;

/// Represented in C headers as SQLSMALLINT
#[repr(i16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum HandleType {
    SQL_HANDLE_ENV = 1,
    SQL_HANDLE_DBC = 2,
    SQL_HANDLE_STMT = 3,
    SQL_HANDLE_DESC = 4,
}
pub use self::HandleType::*;

/// Options for `SQLDriverConnect`
#[repr(u16)]
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SqlDriverConnectOption {
    SQL_DRIVER_NOPROMPT = 0,
    SQL_DRIVER_COMPLETE = 1,
    SQL_DRIVER_PROMPT = 2,
    SQL_DRIVER_COMPLETE_REQUIRED = 3,
}
pub use self::SqlDriverConnectOption::*;

#[cfg_attr(windows, link(name="odbc32"))]
#[cfg_attr(not(windows), link(name="odbc"))]
extern "C" {
    /// Allocates an environment, connection, statement, or descriptor handle.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLAllocHandle(handle_type: HandleType,
                          input_handle: SQLHANDLE,
                          output_Handle: *mut SQLHANDLE)
                          -> SQLRETURN;

    /// Frees resources associated with a specific environment, connection, statement, or
    /// descriptor handle.
    ///
    /// If `SQL_ERRQR` is returned the handle is still valid.
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLFreeHandle(handle_type: HandleType, handle: SQLHANDLE) -> SQLRETURN;

    /// Sets attributes that govern aspects of environments
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLSetEnvAttr(environment_handle: SQLHENV,
                         attribute: EnvironmentAttribute,
                         value: SQLPOINTER,
                         string_length: SQLINTEGER)
                         -> SQLRETURN;

    /// Closes the connection associated with a specific connection handle.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLDisconnect(connection_handle: SQLHDBC) -> SQLRETURN;

    pub fn SQLGetDiagRec(handle_type: HandleType,
                         handle: SQLHANDLE,
                         RecNumber: SQLSMALLINT,
                         state: *mut SQLCHAR,
                         native_error_ptr: *mut SQLINTEGER,
                         message_text: *mut SQLCHAR,
                         buffer_length: SQLSMALLINT,
                         text_length_ptr: *mut SQLSMALLINT)
                         -> SQLRETURN;

    pub fn SQLExecDirect(statement_handle: SQLHSTMT,
                         statement_text: *const SQLCHAR,
                         text_length: SQLINTEGER)
                         -> SQLRETURN;

    pub fn SQLNumResultCols(statement_handle: SQLHSTMT,
                            column_count_ptr: *mut SQLSMALLINT)
                            -> SQLRETURN;

    // Can be used since odbc version 3.8 to stream results
    pub fn SQLGetData(statement_handle: SQLHSTMT,
                      col_or_param_num: SQLUSMALLINT,
                      target_type: SqlCDataType,
                      target_value_ptr: SQLPOINTER,
                      buffer_length: SQLLEN,
                      str_len_or_ind_ptr: *mut SQLLEN)
                      -> SQLRETURN;

    /// SQLFetch fetches the next rowset of data from the result set and returns data for all bound
    /// columns.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, `SQL_NO_DATA` or
    /// `SQL_STILL_EXECUTING`
    pub fn SQLFetch(statement_handle: SQLHSTMT) -> SQLRETURN;

    /// Returns general information about the driver and data source associated with a connection
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, or `SQL_INVALID_HANDLE`
    pub fn SQLGetInfo(connection_handle: SQLHDBC,
                      info_type: InfoType,
                      info_value_ptr: SQLPOINTER,
                      buffer_length: SQLSMALLINT,
                      string_length_ptr: *mut SQLSMALLINT)
                      -> SQLRETURN;

    /// SQLConnect establishes connections to a driver and a data source.
    ///
    /// The connection handle references storage of all information about the connection to the
    /// data source, including status, transaction state, and error information.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or
    /// `SQL_STILL_EXECUTING`
    pub fn SQLConnect(connection_handle: SQLHDBC,
                      server_name: *const SQLCHAR,
                      name_length_1: SQLSMALLINT,
                      user_name: *const SQLCHAR,
                      name_length_2: SQLSMALLINT,
                      authentication: *const SQLCHAR,
                      name_length_3: SQLSMALLINT)
                      -> SQLRETURN;

    /// Returns the list of table, catalog, or schema names, and table types, stored in a specific
    /// data source. The driver returns the information as a result set
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or
    /// `SQL_STILL_EXECUTING`
    pub fn SQLTables(statement_handle: SQLHSTMT,
                     catalog_name: *const SQLCHAR,
                     name_length_1: SQLSMALLINT,
                     schema_name: *const SQLCHAR,
                     name_length_2: SQLSMALLINT,
                     table_name: *const SQLCHAR,
                     name_length_3: SQLSMALLINT,
                     TableType: *const SQLCHAR,
                     name_length_4: SQLSMALLINT)
                     -> SQLRETURN;

    /// Returns information about a data source. This function is implemented only by the Driver
    /// Manager.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or `SQL_NO_DATA`
    pub fn SQLDataSources(environment_handle: SQLHENV,
                          direction: FetchOrientation,
                          server_name: *mut SQLCHAR,
                          buffer_length_1: SQLSMALLINT,
                          name_length_1: *mut SQLSMALLINT,
                          description: *mut SQLCHAR,
                          buffer_length_2: SQLSMALLINT,
                          name_length_2: *mut SQLSMALLINT)
                          -> SQLRETURN;

    /// An alternative to `SQLConnect`. It supports data sources that require more connection
    /// information than the three arguments in `SQLConnect`, dialog boxes to prompt the user for
    /// all connection information, and data sources that are not defined in the system information
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, `SQL_NO_DATA`,
    /// or `SQL_STILL_EXECUTING`
    pub fn SQLDriverConnect(connection_handle: SQLHDBC,
                            window_handle: SQLHWND,
                            in_connection_string: *const SQLCHAR,
                            string_length_1: SQLSMALLINT,
                            out_connection_string: *mut SQLCHAR,
                            buffer_length: SQLSMALLINT,
                            string_length_2: *mut SQLSMALLINT,
                            DriverCompletion: SqlDriverConnectOption)
                            -> SQLRETURN;

    /// Lists driver descriptions and driver attribute keywords. This function is implemented only
    /// by the Driver Manager.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR`, `SQL_INVALID_HANDLE`, or `SQL_NO_DATA`
    pub fn SQLDrivers(henv: SQLHENV,
                      direction: FetchOrientation,
                      driver_desc: *mut SQLCHAR,
                      driver_desc_max: SQLSMALLINT,
                      out_driver_desc: *mut SQLSMALLINT,
                      driver_attributes: *mut SQLCHAR,
                      drvr_attr_max: SQLSMALLINT,
                      out_drvr_attr: *mut SQLSMALLINT)
                      -> SQLRETURN;

    /// Closes a cursor that has been opened on a statement and discards pending results.
    ///
    /// # Returns
    /// `SQL_SUCCESS`, `SQL_SUCCESS_WITH_INFO`, `SQL_ERROR` or `SQL_INVALID_HANDLE`
    pub fn SQLCloseCursor(hstmt: SQLHSTMT) -> SQLRETURN;
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(old_io)]
#![feature(std_misc)]
#![feature(collections)]

extern crate "rustc-serialize" as rustc_serialize;
extern crate time;
extern crate uuid;
extern crate rand;

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::old_io::net::ip::{SocketAddr, ToSocketAddr};
use std::old_io::net::udp::UdpSocket;
use std::old_io::timer::Timer;
use std::str::FromStr;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::time::Duration;
use std::thread;

use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_serialize::json;
use uuid::Uuid;

mod member;
mod memberlist;

use member::{Member, MemberState, StateChange};
use memberlist::MemberList;

pub type ClusterEvent = (Vec<Member>, MemberEvent);

#[derive(Debug)]
pub enum MemberEvent {
    MemberJoined(Member),
    MemberWentUp(Member),
    MemberSuspectedDown(Member),
    MemberWentDown(Member),
}

pub struct Cluster {
    pub events: Receiver<ClusterEvent>,
    comm: Sender<InternalRequest>,
}

#[derive(Debug, Clone)]
struct EncSocketAddr(SocketAddr);

#[derive(RustcEncodable, RustcDecodable, Debug, Clone)]
enum Request {
    Ping,
    Ack,
    PingRequest(EncSocketAddr),
    AckHost(Member),
}

#[derive(Debug, Clone)]
struct TargetedRequest {
    request: Request,
    target: SocketAddr,
}

#[derive(Debug, Clone)]
enum InternalRequest {
    AddSeed(SocketAddr),
    Respond(SocketAddr, Message),
}

struct State {
    host_key: Uuid,
    cluster_key: Vec<u8>,
    members: MemberList,
    seed_queue: Vec<SocketAddr>,
    pending_responses: Vec<(time::Tm, SocketAddr, Vec<StateChange>)>,
    state_changes: Vec<StateChange>,
    wait_list: HashMap<SocketAddr, Vec<SocketAddr>>,
    socket: UdpSocket,
}

#[derive(RustcEncodable, RustcDecodable, Debug, Clone)]
struct Message {
    sender: Uuid,
    cluster_key: Vec<u8>,
    request: Request,
    state_changes: Vec<StateChange>,
}

pub fn start_cluster<A: ToSocketAddr>(host_key: Uuid, cluster_key: &str, listen_addr: A) -> Cluster {
    let (event_tx, event_rx) = channel();
    let (request_tx, request_rx) = channel();
    let (internal_tx, internal_rx) = channel();
    let socket = UdpSocket::bind(listen_addr).unwrap();
    let cluster_key = cluster_key.as_bytes().to_vec();

    let timeout_tx = request_tx.clone();
    let worker_socket = socket.clone();
    thread::spawn(move || {
        let me = Member::myself(host_key.clone());

        let mut state = State {
            host_key: host_key,
            cluster_key: cluster_key,
            members: MemberList::new(me.clone()),
            seed_queue: Vec::new(),
            pending_responses: Vec::new(),
            state_changes: vec![StateChange::new(me)],
            wait_list: HashMap::new(),
            socket: worker_socket,
        };

        let mut timer = Timer::new().unwrap();
        let periodic = timer.periodic(Duration::seconds(1));

        loop {
            select!(
                _ = periodic.recv() => {
                    enqueue_seed_nodes(&state.seed_queue, &timeout_tx);
                    enqueue_random_ping(&mut state, &timeout_tx);
                },
                request = request_rx.recv() => {
                    prune_timed_out_responses(&mut state, &event_tx, &timeout_tx);
                    process_request(&mut state, request.unwrap());
                },
                internal_message = internal_rx.recv() => {
                    process_internal_request(&mut state, internal_message.unwrap(), &event_tx, &timeout_tx);
                }
            );
        }
    });

    let listener_tx = internal_tx.clone();
    let mut listener_socket = socket.clone();
    thread::spawn(move || {
        loop {
            let mut buf = [0; 512];

            match listener_socket.recv_from(&mut buf) {
                Ok((size, src_addr)) => {
                    let message = json::decode(&*String::from_utf8_lossy(&buf[..size]));
                    listener_tx.send(InternalRequest::Respond(src_addr, message.unwrap())).unwrap();
                },
                Err(e) => {
                    println!("Error while reading: {:?}", e);
                }
            };
        }
    });

    return Cluster { events: event_rx, comm: internal_tx };
}

impl Cluster {
    pub fn add_seed_node<A: ToSocketAddr>(&self, addr: A) {
        self.comm.send(InternalRequest::AddSeed(addr.to_socket_addr().unwrap())).unwrap();
    }
}

fn process_request(state: &mut State, request: TargetedRequest) {
    use Request::*;

    let timeout = time::now_utc() + Duration::seconds(3);
    let should_add_pending = match request.request { Ping => true, _ => false };
    let message = build_message(&state.host_key, &state.cluster_key, request.request, state.state_changes.clone());

    if should_add_pending {
        state.pending_responses.push((timeout, request.target.clone(), message.state_changes.clone()));
    }

    let encoded = json::encode(&message).unwrap();

    assert!(encoded.len() < 512);

    state.socket.send_to(encoded.as_bytes(), request.target).unwrap();
}

fn build_message(sender: &Uuid, cluster_key: &Vec<u8>, request: Request, state_changes: Vec<StateChange>) -> Message {
    let mut message = Message {
        sender: sender.clone(),
        cluster_key: cluster_key.clone(),
        request: request.clone(),
        state_changes: Vec::new(),
    };

    for i in range(0, state_changes.len() + 1) {
        message = Message {
            sender: sender.clone(),
            cluster_key: cluster_key.clone(),
            request: request.clone(),
            state_changes: (&state_changes[..i]).iter().cloned().collect(),
        };

        let encoded = json::encode(&message).unwrap();
        if encoded.len() >= 512 {
            return message;
        }
    }

    return message;
}

fn enqueue_seed_nodes(seed_nodes: &[SocketAddr], tx: &Sender<TargetedRequest>) {
    for seed_node in seed_nodes {
        tx.send(TargetedRequest { request: Request::Ping, target: seed_node.clone() }).unwrap();
    }
}

fn enqueue_random_ping(state: &mut State, tx: &Sender<TargetedRequest>) {
    if let Some(member) = state.members.next_random_member() {
        tx.send(TargetedRequest { request: Request::Ping, target: member.remote_host().unwrap() }).unwrap();
    }
}

fn prune_timed_out_responses(state: &mut State, event_tx: &Sender<ClusterEvent>, request_tx: &Sender<TargetedRequest>) {
    let now = time::now_utc();

    let (remaining, expired): (Vec<_>, Vec<_>) = state.pending_responses
        .iter()
        .cloned()
        .partition(| &(t, _, _) | t < now);

    let expired_hosts: HashSet<SocketAddr> = expired
        .iter()
        .map(| &(_, a, _) | a)
        .collect();

    state.pending_responses = remaining;

    let (suspect, down) = state.members.time_out_nodes(expired_hosts);

    enqueue_state_change(state, down.as_slice());
    enqueue_state_change(state, suspect.as_slice());

    for member in suspect {
        send_ping_requests(state, &member, request_tx);
        send_member_event(&state.members, event_tx, MemberEvent::MemberSuspectedDown(member.clone()));
    }

    for member in down {
        send_member_event(&state.members, event_tx, MemberEvent::MemberWentDown(member.clone()));
    }
}

fn send_ping_requests(state: &State, target: &Member, request_tx: &Sender<TargetedRequest>) {
    if let Some(target_host) = target.remote_host() {
        for relay in state.members.hosts_for_indirect_ping(&target_host) {
            request_tx.send(TargetedRequest {
                request: Request::PingRequest(EncSocketAddr::from_addr(&target_host)),
                target: relay,
            }).unwrap();
        }
    }
}

fn send_member_event(members: &MemberList, event_tx: &Sender<ClusterEvent>, event: MemberEvent) {
    use MemberEvent::*;

    match event {
        MemberJoined(_) => {},
        MemberWentUp(ref m) => assert_eq!(m.state(), MemberState::Alive),
        MemberWentDown(ref m) => assert_eq!(m.state(), MemberState::Down),
        MemberSuspectedDown(ref m) => assert_eq!(m.state(), MemberState::Suspect),
    };

    event_tx.send((members.to_vec(), event)).unwrap();
}

fn process_internal_request(state: &mut State, message: InternalRequest, event_tx: &Sender<ClusterEvent>, request_tx: &Sender<TargetedRequest>) {
    use InternalRequest::*;
    use Request::*;

    match message {
        AddSeed(addr) => {
            state.seed_queue.push(addr);
        },
        Respond(src_addr, message) => {
            if message.cluster_key != state.cluster_key {
                println!("ERROR: Mismatching cluster keys, ignoring message");
            }
            else {
                apply_state_changes(state, message.state_changes, src_addr, event_tx);
                remove_potential_seed(state, src_addr);

                ensure_node_is_member(state, src_addr, event_tx, message.sender);

                let response = match message.request {
                    Ping => Some(TargetedRequest { request: Ack, target: src_addr }),
                    Ack => {
                        ack_response(state, src_addr);
                        mark_node_alive(state, src_addr, event_tx, request_tx);
                        None
                    },
                    PingRequest(dest_addr) => {
                        let EncSocketAddr(dest_addr) = dest_addr;
                        add_to_wait_list(state, &dest_addr, &src_addr);
                        Some(TargetedRequest { request: Ping, target: dest_addr })
                    },
                    AckHost(member) => {
                        ack_response(state, member.remote_host().unwrap());
                        mark_node_alive(state, member.remote_host().unwrap(), event_tx, request_tx);
                        None
                    }
                };

                match response {
                    Some(response) => request_tx.send(response).unwrap(),
                    None => (),
                };
            }
        },
    };
}

fn add_to_wait_list(state: &mut State, wait_addr: &SocketAddr, notify_addr: &SocketAddr) {
    match state.wait_list.entry(*wait_addr) {
        Entry::Occupied(mut entry) => { entry.get_mut().push(notify_addr.clone()); },
        Entry::Vacant(entry) => { entry.insert(vec![notify_addr.clone()]); }
    };
}

fn remove_potential_seed(state: &mut State, src_addr: SocketAddr) {
    state.seed_queue.retain(|&addr| addr != src_addr)
}

fn ack_response(state: &mut State, src_addr: SocketAddr) {
    let mut to_remove = Vec::new();

    for &(ref t, ref addr, ref state_changes) in state.pending_responses.iter() {
        if src_addr != *addr {
            continue;
        }

        to_remove.push((t.clone(), addr.clone(), state_changes.clone()));

        state.state_changes
            .retain(|os| !state_changes.iter().any(| is | is.member().host_key() == os.member().host_key()))
    }

    state.pending_responses.retain(|op| !to_remove.iter().any(|ip| ip == op));
}

fn ensure_node_is_member(state: &mut State, src_addr: SocketAddr, event_tx: &Sender<ClusterEvent>, sender: Uuid) {
    if state.members.has_member(&src_addr) {
        return;
    }

    let new_member = Member::new(sender, src_addr, 0, MemberState::Alive);

    state.members.add_member(new_member.clone());
    enqueue_state_change(state, &[new_member.clone()]);
    send_member_event(&state.members, event_tx, MemberEvent::MemberJoined(new_member));
}

fn mark_node_alive(state: &mut State, src_addr: SocketAddr, event_tx: &Sender<ClusterEvent>, request_tx: &Sender<TargetedRequest>) {
    if let Some(member) = state.members.mark_node_alive(&src_addr) {
        match state.wait_list.get_mut(&src_addr) {
            Some(mut wait_list) => {
                for remote in wait_list.drain() {
                    request_tx.send(TargetedRequest { request: Request::AckHost(member.clone()), target: remote }).unwrap();
                }
            },
            None => ()
        };

        enqueue_state_change(state, &[member.clone()]);
        send_member_event(&state.members, event_tx, MemberEvent::MemberWentUp(member.clone()));
    }
}

fn apply_state_changes(state: &mut State, state_changes: Vec<StateChange>, from: SocketAddr, event_tx: &Sender<ClusterEvent>) {
    let (new, changed) = state.members.apply_state_changes(state_changes, &from);

    enqueue_state_change(state, new.as_slice());
    enqueue_state_change(state, changed.as_slice());

    for member in new {

        send_member_event(&state.members, event_tx, MemberEvent::MemberJoined(member));
    }

    for member in changed {
        send_member_event(&state.members, event_tx, determine_member_event(member));
    }
}

fn determine_member_event(member: Member) -> MemberEvent {
    use member::MemberState::*;
    use MemberEvent::*;

    return match member.state() {
        Alive => MemberWentUp(member),
        Suspect => MemberSuspectedDown(member),
        Down => MemberWentDown(member),
    };
}

fn enqueue_state_change(state: &mut State, members: &[Member]) {
    for member in members {
        for state_change in &mut state.state_changes {
            if state_change.member().host_key() == member.host_key() {
                state_change.update(member.clone());
                return;
            }
        }

        state.state_changes.push(StateChange::new(member.clone()));
    }
}

impl Decodable for EncSocketAddr {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        match d.read_str() {
            Ok(s) => match FromStr::from_str(s.as_slice()) {
                Ok(addr) => Ok(EncSocketAddr(addr)),
                Err(e) => Err(d.error(format!("{:?}", e).as_slice())),
            },
            Err(e) => Err(e),
        }
    }
}

impl Encodable for EncSocketAddr {
    fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
        let &EncSocketAddr(addr) = self;
        format!("{}", addr).encode(e)
    }
}

impl EncSocketAddr {
    fn from_addr(addr: &SocketAddr) -> Self {
        EncSocketAddr(addr.clone())
    }
}
<commit_msg>Move event/request senders into cluster state<commit_after>#![feature(core)]
#![feature(old_io)]
#![feature(std_misc)]
#![feature(collections)]

extern crate "rustc-serialize" as rustc_serialize;
extern crate time;
extern crate uuid;
extern crate rand;

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::old_io::net::ip::{SocketAddr, ToSocketAddr};
use std::old_io::net::udp::UdpSocket;
use std::old_io::timer::Timer;
use std::str::FromStr;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::time::Duration;
use std::thread;

use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_serialize::json;
use uuid::Uuid;

mod member;
mod memberlist;

use member::{Member, MemberState, StateChange};
use memberlist::MemberList;

pub type ClusterEvent = (Vec<Member>, MemberEvent);

#[derive(Debug)]
pub enum MemberEvent {
    MemberJoined(Member),
    MemberWentUp(Member),
    MemberSuspectedDown(Member),
    MemberWentDown(Member),
}

pub struct Cluster {
    pub events: Receiver<ClusterEvent>,
    comm: Sender<InternalRequest>,
}

#[derive(Debug, Clone)]
struct EncSocketAddr(SocketAddr);

#[derive(RustcEncodable, RustcDecodable, Debug, Clone)]
enum Request {
    Ping,
    Ack,
    PingRequest(EncSocketAddr),
    AckHost(Member),
}

#[derive(Debug, Clone)]
struct TargetedRequest {
    request: Request,
    target: SocketAddr,
}

#[derive(Debug, Clone)]
enum InternalRequest {
    AddSeed(SocketAddr),
    Respond(SocketAddr, Message),
}

struct State {
    host_key: Uuid,
    cluster_key: Vec<u8>,
    members: MemberList,
    seed_queue: Vec<SocketAddr>,
    pending_responses: Vec<(time::Tm, SocketAddr, Vec<StateChange>)>,
    state_changes: Vec<StateChange>,
    wait_list: HashMap<SocketAddr, Vec<SocketAddr>>,
    socket: UdpSocket,
    request_tx: Sender<TargetedRequest>,
    event_tx: Sender<ClusterEvent>,
}

#[derive(RustcEncodable, RustcDecodable, Debug, Clone)]
struct Message {
    sender: Uuid,
    cluster_key: Vec<u8>,
    request: Request,
    state_changes: Vec<StateChange>,
}

pub fn start_cluster<A: ToSocketAddr>(host_key: Uuid, cluster_key: &str, listen_addr: A) -> Cluster {
    let (event_tx, event_rx) = channel();
    let (request_tx, request_rx) = channel();
    let (internal_tx, internal_rx) = channel();
    let socket = UdpSocket::bind(listen_addr).unwrap();
    let cluster_key = cluster_key.as_bytes().to_vec();

    let timeout_tx = request_tx.clone();
    let worker_socket = socket.clone();
    thread::spawn(move || {
        let me = Member::myself(host_key.clone());

        let mut state = State {
            host_key: host_key,
            cluster_key: cluster_key,
            members: MemberList::new(me.clone()),
            seed_queue: Vec::new(),
            pending_responses: Vec::new(),
            state_changes: vec![StateChange::new(me)],
            wait_list: HashMap::new(),
            socket: worker_socket,
            request_tx: timeout_tx,
            event_tx: event_tx,
        };

        let mut timer = Timer::new().unwrap();
        let periodic = timer.periodic(Duration::seconds(1));

        loop {
            select!(
                _ = periodic.recv() => {
                    enqueue_seed_nodes(&state.seed_queue, &state.request_tx);
                    enqueue_random_ping(&mut state);
                },
                request = request_rx.recv() => {
                    prune_timed_out_responses(&mut state);
                    process_request(&mut state, request.unwrap());
                },
                internal_message = internal_rx.recv() => {
                    process_internal_request(&mut state, internal_message.unwrap());
                }
            );
        }
    });

    let listener_tx = internal_tx.clone();
    let mut listener_socket = socket.clone();
    thread::spawn(move || {
        loop {
            let mut buf = [0; 512];

            match listener_socket.recv_from(&mut buf) {
                Ok((size, src_addr)) => {
                    let message = json::decode(&*String::from_utf8_lossy(&buf[..size]));
                    listener_tx.send(InternalRequest::Respond(src_addr, message.unwrap())).unwrap();
                },
                Err(e) => {
                    println!("Error while reading: {:?}", e);
                }
            };
        }
    });

    return Cluster { events: event_rx, comm: internal_tx };
}

impl Cluster {
    pub fn add_seed_node<A: ToSocketAddr>(&self, addr: A) {
        self.comm.send(InternalRequest::AddSeed(addr.to_socket_addr().unwrap())).unwrap();
    }
}

fn process_request(state: &mut State, request: TargetedRequest) {
    use Request::*;

    let timeout = time::now_utc() + Duration::seconds(3);
    let should_add_pending = match request.request { Ping => true, _ => false };
    let message = build_message(&state.host_key, &state.cluster_key, request.request, state.state_changes.clone());

    if should_add_pending {
        state.pending_responses.push((timeout, request.target.clone(), message.state_changes.clone()));
    }

    let encoded = json::encode(&message).unwrap();

    assert!(encoded.len() < 512);

    state.socket.send_to(encoded.as_bytes(), request.target).unwrap();
}

fn build_message(sender: &Uuid, cluster_key: &Vec<u8>, request: Request, state_changes: Vec<StateChange>) -> Message {
    let mut message = Message {
        sender: sender.clone(),
        cluster_key: cluster_key.clone(),
        request: request.clone(),
        state_changes: Vec::new(),
    };

    for i in range(0, state_changes.len() + 1) {
        message = Message {
            sender: sender.clone(),
            cluster_key: cluster_key.clone(),
            request: request.clone(),
            state_changes: (&state_changes[..i]).iter().cloned().collect(),
        };

        let encoded = json::encode(&message).unwrap();
        if encoded.len() >= 512 {
            return message;
        }
    }

    return message;
}

fn enqueue_seed_nodes(seed_nodes: &[SocketAddr], tx: &Sender<TargetedRequest>) {
    for seed_node in seed_nodes {
        tx.send(TargetedRequest { request: Request::Ping, target: seed_node.clone() }).unwrap();
    }
}

fn enqueue_random_ping(state: &mut State) {
    if let Some(member) = state.members.next_random_member() {
        state.request_tx.send(TargetedRequest { request: Request::Ping, target: member.remote_host().unwrap() }).unwrap();
    }
}

fn prune_timed_out_responses(state: &mut State) {
    let now = time::now_utc();

    let (remaining, expired): (Vec<_>, Vec<_>) = state.pending_responses
        .iter()
        .cloned()
        .partition(| &(t, _, _) | t < now);

    let expired_hosts: HashSet<SocketAddr> = expired
        .iter()
        .map(| &(_, a, _) | a)
        .collect();

    state.pending_responses = remaining;

    let (suspect, down) = state.members.time_out_nodes(expired_hosts);

    enqueue_state_change(state, down.as_slice());
    enqueue_state_change(state, suspect.as_slice());

    for member in suspect {
        send_ping_requests(state, &member);
        send_member_event(&state.members, &state.event_tx, MemberEvent::MemberSuspectedDown(member.clone()));
    }

    for member in down {
        send_member_event(&state.members, &state.event_tx, MemberEvent::MemberWentDown(member.clone()));
    }
}

fn send_ping_requests(state: &State, target: &Member) {
    if let Some(target_host) = target.remote_host() {
        for relay in state.members.hosts_for_indirect_ping(&target_host) {
            state.request_tx.send(TargetedRequest {
                request: Request::PingRequest(EncSocketAddr::from_addr(&target_host)),
                target: relay,
            }).unwrap();
        }
    }
}

fn send_member_event(members: &MemberList, event_tx: &Sender<ClusterEvent>, event: MemberEvent) {
    use MemberEvent::*;

    match event {
        MemberJoined(_) => {},
        MemberWentUp(ref m) => assert_eq!(m.state(), MemberState::Alive),
        MemberWentDown(ref m) => assert_eq!(m.state(), MemberState::Down),
        MemberSuspectedDown(ref m) => assert_eq!(m.state(), MemberState::Suspect),
    };

    event_tx.send((members.to_vec(), event)).unwrap();
}

fn process_internal_request(state: &mut State, message: InternalRequest) {
    use InternalRequest::*;
    use Request::*;

    match message {
        AddSeed(addr) => {
            state.seed_queue.push(addr);
        },
        Respond(src_addr, message) => {
            if message.cluster_key != state.cluster_key {
                println!("ERROR: Mismatching cluster keys, ignoring message");
            }
            else {
                apply_state_changes(state, message.state_changes, src_addr);
                remove_potential_seed(state, src_addr);

                ensure_node_is_member(state, src_addr, message.sender);

                let response = match message.request {
                    Ping => Some(TargetedRequest { request: Ack, target: src_addr }),
                    Ack => {
                        ack_response(state, src_addr);
                        mark_node_alive(state, src_addr);
                        None
                    },
                    PingRequest(dest_addr) => {
                        let EncSocketAddr(dest_addr) = dest_addr;
                        add_to_wait_list(state, &dest_addr, &src_addr);
                        Some(TargetedRequest { request: Ping, target: dest_addr })
                    },
                    AckHost(member) => {
                        ack_response(state, member.remote_host().unwrap());
                        mark_node_alive(state, member.remote_host().unwrap());
                        None
                    }
                };

                match response {
                    Some(response) => state.request_tx.send(response).unwrap(),
                    None => (),
                };
            }
        },
    };
}

fn add_to_wait_list(state: &mut State, wait_addr: &SocketAddr, notify_addr: &SocketAddr) {
    match state.wait_list.entry(*wait_addr) {
        Entry::Occupied(mut entry) => { entry.get_mut().push(notify_addr.clone()); },
        Entry::Vacant(entry) => { entry.insert(vec![notify_addr.clone()]); }
    };
}

fn remove_potential_seed(state: &mut State, src_addr: SocketAddr) {
    state.seed_queue.retain(|&addr| addr != src_addr)
}

fn ack_response(state: &mut State, src_addr: SocketAddr) {
    let mut to_remove = Vec::new();

    for &(ref t, ref addr, ref state_changes) in state.pending_responses.iter() {
        if src_addr != *addr {
            continue;
        }

        to_remove.push((t.clone(), addr.clone(), state_changes.clone()));

        state.state_changes
            .retain(|os| !state_changes.iter().any(| is | is.member().host_key() == os.member().host_key()))
    }

    state.pending_responses.retain(|op| !to_remove.iter().any(|ip| ip == op));
}

fn ensure_node_is_member(state: &mut State, src_addr: SocketAddr, sender: Uuid) {
    if state.members.has_member(&src_addr) {
        return;
    }

    let new_member = Member::new(sender, src_addr, 0, MemberState::Alive);

    state.members.add_member(new_member.clone());
    enqueue_state_change(state, &[new_member.clone()]);
    send_member_event(&state.members, &state.event_tx, MemberEvent::MemberJoined(new_member));
}

fn mark_node_alive(state: &mut State, src_addr: SocketAddr) {
    if let Some(member) = state.members.mark_node_alive(&src_addr) {
        match state.wait_list.get_mut(&src_addr) {
            Some(mut wait_list) => {
                for remote in wait_list.drain() {
                    state.request_tx.send(TargetedRequest { request: Request::AckHost(member.clone()), target: remote }).unwrap();
                }
            },
            None => ()
        };

        enqueue_state_change(state, &[member.clone()]);
        send_member_event(&state.members, &state.event_tx, MemberEvent::MemberWentUp(member.clone()));
    }
}

fn apply_state_changes(state: &mut State, state_changes: Vec<StateChange>, from: SocketAddr) {
    let (new, changed) = state.members.apply_state_changes(state_changes, &from);

    enqueue_state_change(state, new.as_slice());
    enqueue_state_change(state, changed.as_slice());

    for member in new {
        send_member_event(&state.members, &state.event_tx, MemberEvent::MemberJoined(member));
    }

    for member in changed {
        send_member_event(&state.members, &state.event_tx, determine_member_event(member));
    }
}

fn determine_member_event(member: Member) -> MemberEvent {
    use member::MemberState::*;
    use MemberEvent::*;

    return match member.state() {
        Alive => MemberWentUp(member),
        Suspect => MemberSuspectedDown(member),
        Down => MemberWentDown(member),
    };
}

fn enqueue_state_change(state: &mut State, members: &[Member]) {
    for member in members {
        for state_change in &mut state.state_changes {
            if state_change.member().host_key() == member.host_key() {
                state_change.update(member.clone());
                return;
            }
        }

        state.state_changes.push(StateChange::new(member.clone()));
    }
}

impl Decodable for EncSocketAddr {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        match d.read_str() {
            Ok(s) => match FromStr::from_str(s.as_slice()) {
                Ok(addr) => Ok(EncSocketAddr(addr)),
                Err(e) => Err(d.error(format!("{:?}", e).as_slice())),
            },
            Err(e) => Err(e),
        }
    }
}

impl Encodable for EncSocketAddr {
    fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
        let &EncSocketAddr(addr) = self;
        format!("{}", addr).encode(e)
    }
}

impl EncSocketAddr {
    fn from_addr(addr: &SocketAddr) -> Self {
        EncSocketAddr(addr.clone())
    }
}
<|endoftext|>


<commit_before>#[crate_id = "fragments#0.1-pre"];

#[comment = "A simple template library for Rust"];
#[license = "MIT"];
#[crate_type = "lib"];
#[crate_type = "rlib"];

extern crate collections;

use std::fmt;
use std::from_str::FromStr;
use std::io::BufReader;
use std::vec_ng::Vec;
use collections::hashmap::{HashMap, HashSet};

type ParserState = Option<fn(&mut Buffer) -> ParserResult>;

struct ParserResult {
	token: Token,
	next_parser: ParserState
}

#[deriving(Eq, TotalEq, Show)]
enum Token {
	String(~str),
	Placeholder(~str),
	Conditional(~str, bool, Vec<Token>)
}

///A string template with placeholders and conditional content.
///
///Placeholders are written as `[[:label]]`, where `label` becomes the name of the placeholder.
///The label is then used to insert content: `my_template.insert(~"label", my_content);`.
///The assigned content for a placeholder can be anything that implements `Show`.
///Even other templates may be inserted, which allows a more atomic structure.
///
///Conditional segments are surrounded by `[[?label]]...[[/]]`, where `label` becomes the name of the condition,
///and they are used to display content depending on whether its label exists in the `conditions` set.
///`[[/]]` marks the end of a block and may contain other characters after the `/`, which may be useful for labeling the end mark.
///Conditions can be made negative by writing `[[?!label]]...[[/]]`, which makes the content visible if the label
///is missing from the `conditions` set.
///
///Any character can be escaped by writing `\` before it. It can be used like this: `\[[[:label1]], [[:label2]]]`
///which will result in `[content1, content2]`, since the first `[` will be ignored by the parser and just added to the
///rest of the content.
pub struct Template {
	///Content for the placeholders
	content: HashMap<~str, ~fmt::Show>,
	///Conditional switches
	conditions: HashSet<~str>,
	priv tokens: Vec<Token>
}

impl Template {
	#[inline]
	pub fn from_buffer(b: &mut Buffer) -> Template {
		Template {
			content: HashMap::new(),
			conditions: HashSet::new(),
			tokens: parse_block(b)
		}
	}

	///Convenience method for inserting content.
	#[inline]
	pub fn insert<T: fmt::Show + Send>(&mut self, placeholder: ~str, item: ~T) {
		self.content.insert(placeholder, item as ~fmt::Show);
	}

	///Convenience method for setting a condition.
	#[inline]
	pub fn set(&mut self, condition: ~str, value: bool) {
		if value {
			self.conditions.insert(condition);
		} else {
			self.conditions.remove(&condition);
		}
	}

	fn format_tokens(&self, tokens: &Vec<Token>, f: &mut fmt::Formatter) -> fmt::Result {
		for token in tokens.iter() {
			let res = match token {
				&String(ref s) => f.buf.write_str(*s),

				&Placeholder(ref k) => {
					match self.content.find(k) {
						Some(value) => value.fmt(f),
						None => Ok(())
					}
				},

				&Conditional(ref k, expected, ref tokens) => {
					if self.conditions.contains(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				}
			};

			match res {
				Err(e) => return Err(e),
				_ => {}
			}
		}

		Ok(())
	}
}

impl FromStr for Template {
	///Creates a new `Template` from a string.
	fn from_str(s: &str) -> Option<Template> {
		Some(Template::from_buffer(&mut BufReader::new(s.as_bytes())))
	}
}

impl fmt::Show for Template {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		self.format_tokens(&self.tokens, f)
	}
}

fn parse_block(b: &mut Buffer) -> Vec<Token> {
	let mut tokens = vec!();
	let mut parser = parse_string;

	loop {
		let ParserResult{token: token, next_parser: next} = parser(b);

		tokens.push(token);

		match next {
			Some(p) => parser = p,
			None => break
		}
	}

	tokens
}

fn parse_string(b: &mut Buffer) -> ParserResult {
	let mut content = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						content.push_char(c);
					},
					_ => break
				}
			},
			Ok('[') => {
				match b.read_char() {
					Ok('[') => {
						match b.read_char() {
							Ok(':') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_placeholder)
							},
							Ok('?') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_conditional)
							},
							Ok('/') => {
								skip_to_token_end(b);

								return ParserResult{
									token: String(content),
									next_parser: None
								}
							},
							_ => break
						}
					},
					Ok(c) => {
						content.push_char('[');
						content.push_char(c);
					},
					Err(_) => content.push_char('[')
				}
			},
			Ok(c) => content.push_char(c),
			Err(_) => break
		}
	}

	ParserResult{
		token: String(content),
		next_parser: None
	}
}

fn skip_to_token_end(b: &mut Buffer) {
	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(_) => {},
					_ => break
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => break,
					_ => {}
				}
			},
			Err(_) => break,
			_ => {}
		}
	}
}

fn parse_placeholder(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					_ => break
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Placeholder(label),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(_) => label.push_char(']')
				}
			},
			Ok(c) => label.push_char(c),
			Err(_) => break
		}
	}

	ParserResult{
		token: Placeholder(label),
		next_parser: None
	}
}

fn parse_conditional(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";
	let mut expected = true;
	let mut expected_set = false;

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					_ => break
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Conditional(label, expected, parse_block(b)),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(_) => label.push_char(']')
				}
			},
			Ok('!') => {
				if label.len() == 0 && !expected_set {
					expected = false;
					expected_set = true;
				} else {
					label.push_char('!');
				}
			},
			Ok(c) => {
				label.push_char(c)
			},
			Err(_) => break
		}
	}

	ParserResult{
		token: Conditional(label, expected, vec!()),
		next_parser: None
	}
}

#[cfg(test)]
mod test {
	use super::{Template, Placeholder, String};

	#[test]
	fn basic_tokens() {
		let template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! This is a "));
		assert_eq!(template.tokens.get(3), &Placeholder(~"something"));
		assert_eq!(template.tokens.get(4), &String(~" template."));
	}

	#[test]
	fn strange_tokens() {
		let template: Template = from_str("Hello, [[[:name]]]! This is a [[[[:something]] template.").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
	}

	#[test]
	fn escaped_tokens() {
		let template: Template = from_str("Hello, [[:name]]! Write placeholders like \\[[:this]] and escape them like \\\\\\[[:this]]").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! Write placeholders like [[:this]] and escape them like \\[[:this]]"));
	}

	#[test]
	fn replacement() {
		let mut template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		template.insert(~"name", ~("Peter"));
		template.insert(~"something", ~("nice"));
		assert_eq!(template.to_str(), ~"Hello, Peter! This is a nice template.");
	}

	#[test]
	fn templates_in_templates() {
		let mut template1: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		let mut template2: ~Template = ~from_str("really [[:something]]").unwrap();
		template1.insert(~"name", ~("Peter"));
		template2.insert(~"something", ~("nice"));

		template1.insert(~"something", template2);

		assert_eq!(template1.to_str(), ~"Hello, Peter! This is a really nice template.");
	}

	#[test]
	fn conditional() {
		let mut template: Template = from_str("Hello, [[:name]]![[?condition]] The condition is true.[[/condition]]").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn conditional_switch() {
		let mut template: Template = from_str("Hello, [[:name]]! The condition is [[?condition]]true[[/condition]][[?!condition]]false[[/condition]].").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is false.");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}
}<commit_msg>The parser will now fail on syntax errors and IO errors<commit_after>#[crate_id = "fragments#0.1-pre"];

#[comment = "A simple template library for Rust"];
#[license = "MIT"];
#[crate_type = "lib"];
#[crate_type = "rlib"];

extern crate collections;

use std::fmt;
use std::from_str::FromStr;
use std::io::{BufReader, IoError, EndOfFile};
use std::vec_ng::Vec;
use collections::hashmap::{HashMap, HashSet};

type ParserState = Option<fn(&mut Buffer) -> ParserResult>;

struct ParserResult {
	token: Token,
	next_parser: ParserState
}

#[deriving(Eq, TotalEq, Show)]
enum Token {
	String(~str),
	Placeholder(~str),
	Conditional(~str, bool, Vec<Token>)
}

///A string template with placeholders and conditional content.
///
///Placeholders are written as `[[:label]]`, where `label` becomes the name of the placeholder.
///The label is then used to insert content: `my_template.insert(~"label", my_content);`.
///The assigned content for a placeholder can be anything that implements `Show`.
///Even other templates may be inserted, which allows a more atomic structure.
///
///Conditional segments are surrounded by `[[?label]]...[[/]]`, where `label` becomes the name of the condition,
///and they are used to display content depending on whether its label exists in the `conditions` set.
///`[[/]]` marks the end of a block and may contain other characters after the `/`, which may be useful for labeling the end mark.
///Conditions can be made negative by writing `[[?!label]]...[[/]]`, which makes the content visible if the label
///is missing from the `conditions` set.
///
///Any character can be escaped by writing `\` before it. It can be used like this: `\[[[:label1]], [[:label2]]]`
///which will result in `[content1, content2]`, since the first `[` will be ignored by the parser and just added to the
///rest of the content.
pub struct Template {
	///Content for the placeholders
	content: HashMap<~str, ~fmt::Show>,
	///Conditional switches
	conditions: HashSet<~str>,
	priv tokens: Vec<Token>
}

impl Template {
	#[inline]
	pub fn from_buffer(b: &mut Buffer) -> Template {
		Template {
			content: HashMap::new(),
			conditions: HashSet::new(),
			tokens: parse_block(b)
		}
	}

	///Convenience method for inserting content.
	#[inline]
	pub fn insert<T: fmt::Show + Send>(&mut self, placeholder: ~str, item: ~T) {
		self.content.insert(placeholder, item as ~fmt::Show);
	}

	///Convenience method for setting a condition.
	#[inline]
	pub fn set(&mut self, condition: ~str, value: bool) {
		if value {
			self.conditions.insert(condition);
		} else {
			self.conditions.remove(&condition);
		}
	}

	fn format_tokens(&self, tokens: &Vec<Token>, f: &mut fmt::Formatter) -> fmt::Result {
		for token in tokens.iter() {
			let res = match token {
				&String(ref s) => f.buf.write_str(*s),

				&Placeholder(ref k) => {
					match self.content.find(k) {
						Some(value) => value.fmt(f),
						None => Ok(())
					}
				},

				&Conditional(ref k, expected, ref tokens) => {
					if self.conditions.contains(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				}
			};

			match res {
				Err(e) => return Err(e),
				_ => {}
			}
		}

		Ok(())
	}
}

impl FromStr for Template {
	///Creates a new `Template` from a string.
	fn from_str(s: &str) -> Option<Template> {
		Some(Template::from_buffer(&mut BufReader::new(s.as_bytes())))
	}
}

impl fmt::Show for Template {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		self.format_tokens(&self.tokens, f)
	}
}

fn parse_block(b: &mut Buffer) -> Vec<Token> {
	let mut tokens = vec!();
	let mut parser = parse_string;

	loop {
		let ParserResult{token: token, next_parser: next} = parser(b);

		tokens.push(token);

		match next {
			Some(p) => parser = p,
			None => break
		}
	}

	tokens
}

fn parse_string(b: &mut Buffer) -> ParserResult {
	let mut content = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok('[') => {
				match b.read_char() {
					Ok('[') => {
						match b.read_char() {
							Ok(':') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_placeholder)
							},
							Ok('?') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_conditional)
							},
							Ok('/') => {
								skip_to_token_end(b);

								return ParserResult{
									token: String(content),
									next_parser: None
								}
							},
							Ok(c) => fail!("Unknown token type: '{}'", c),
							Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
							Err(e) => fail!("{}", e)
						}
					},
					Ok(c) => {
						content.push_char('[');
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => content.push_char('['),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => content.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: String(content),
		next_parser: None
	}
}

fn skip_to_token_end(b: &mut Buffer) {
	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(_) => {},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => break,
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e),
					_ => {}
				}
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e),
			_ => {}
		}
	}
}

fn parse_placeholder(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Placeholder(label),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => label.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Placeholder(label),
		next_parser: None
	}
}

fn parse_conditional(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";
	let mut expected = true;
	let mut expected_set = false;

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Conditional(label, expected, parse_block(b)),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok('!') => {
				if label.len() == 0 && !expected_set {
					expected = false;
					expected_set = true;
				} else {
					label.push_char('!');
				}
			},
			Ok(c) => {
				label.push_char(c)
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Conditional(label, expected, vec!()),
		next_parser: None
	}
}

#[cfg(test)]
mod test {
	use super::{Template, Placeholder, String};

	#[test]
	fn basic_tokens() {
		let template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! This is a "));
		assert_eq!(template.tokens.get(3), &Placeholder(~"something"));
		assert_eq!(template.tokens.get(4), &String(~" template."));
	}

	#[test]
	#[should_fail]
	fn strange_tokens() {
		let _: Template = from_str("Hello, [[[:name]]]! This is a [[[[:something]] template.").unwrap();
	}

	#[test]
	fn escaped_tokens() {
		let template: Template = from_str("Hello, [[:name]]! Write placeholders like \\[[:this]] and escape them like \\\\\\[[:this]]").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! Write placeholders like [[:this]] and escape them like \\[[:this]]"));
	}

	#[test]
	fn replacement() {
		let mut template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		template.insert(~"name", ~("Peter"));
		template.insert(~"something", ~("nice"));
		assert_eq!(template.to_str(), ~"Hello, Peter! This is a nice template.");
	}

	#[test]
	fn templates_in_templates() {
		let mut template1: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		let mut template2: ~Template = ~from_str("really [[:something]]").unwrap();
		template1.insert(~"name", ~("Peter"));
		template2.insert(~"something", ~("nice"));

		template1.insert(~"something", template2);

		assert_eq!(template1.to_str(), ~"Hello, Peter! This is a really nice template.");
	}

	#[test]
	fn conditional() {
		let mut template: Template = from_str("Hello, [[:name]]![[?condition]] The condition is true.[[/condition]]").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn conditional_switch() {
		let mut template: Template = from_str("Hello, [[:name]]! The condition is [[?condition]]true[[/condition]][[?!condition]]false[[/condition]].").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is false.");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}
}<|endoftext|>


<commit_before>//! Load and render SVG images into Cairo surfaces.
//!
//! This crate can load SVG images and render them to Cairo surfaces,
//! using a mixture of SVG's [static mode] and [secure static mode].
//! Librsvg does not do animation nor scripting, and can load
//! references to external data only in some situations; see below.
//!
//! Librsvg supports reading [SVG 1.1] data, and is gradually adding
//! support for features in [SVG 2].  Librsvg also supports SVGZ
//! files, which are just an SVG stream compressed with the GZIP
//! algorithm.
//!
//! # Basic usage
//!
//! * Create a [`Loader`] struct.
//! * Get an [`SvgHandle`] from the [`Loader`].
//! * Create a [`CairoRenderer`] for the [`SvgHandle`] and render to a Cairo context.
//!
//! You can put the following in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! librsvg = { git="https://gitlab.gnome.org/GNOME/librsvg" }
//! cairo-rs = "0.8.0"
//! glib = "0.9.0"                                # only if you need streams
//! gio = { version="0.8.1", features=["v2_50"] } # likewise
//! ```
//!
//! # Example
//!
//! ```
//!
//! const WIDTH: i32 = 640;
//! const HEIGHT: i32 = 480;
//!
//! fn main() {
//!     // Loading from a file
//!
//!     let handle = librsvg::Loader::new().read_path("example.svg").unwrap();
//!
//!     let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, WIDTH, HEIGHT).unwrap();
//!     let cr = cairo::Context::new(&surface).expect("Failed to create a cairo context");
//!
//!     let renderer = librsvg::CairoRenderer::new(&handle);
//!     renderer.render_document(
//!         &cr,
//!         &cairo::Rectangle {
//!             x: 0.0,
//!             y: 0.0,
//!             width: f64::from(WIDTH),
//!             height: f64::from(HEIGHT),
//!         },
//!     ).unwrap();
//!
//!     // Loading from a static SVG asset
//!
//!     let bytes = glib::Bytes::from_static(
//!         br#"<?xml version="1.0" encoding="UTF-8"?>
//!             <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
//!                 <rect id="foo" x="10" y="10" width="30" height="30"/>
//!             </svg>
//!         "#
//!     );
//!     let stream = gio::MemoryInputStream::from_bytes(&bytes);
//!
//!     let handle = librsvg::Loader::new().read_stream(
//!         &stream,
//!         None::<&gio::File>,          // no base file as this document has no references
//!         None::<&gio::Cancellable>,   // no cancellable
//!     ).unwrap();
//! }
//! ```
//!
//! # The "base file" and resolving references to external files
//!
//! When you load an SVG, librsvg needs to know the location of the "base file"
//! for it.  This is so that librsvg can determine the location of referenced
//! entities.  For example, say you have an SVG in <filename>/foo/bar/foo.svg</filename>
//! and that it has an image element like this:
//!
//! ```xml
//! <image href="resources/foo.png" .../>
//! ```
//!
//! In this case, librsvg needs to know the location of the toplevel
//! `/foo/bar/foo.svg` so that it can generate the appropriate
//! reference to `/foo/bar/resources/foo.png`.
//!
//! ## Security and locations of referenced files
//!
//! When processing an SVG, librsvg will only load referenced files if
//! they are in the same directory as the base file, or in a
//! subdirectory of it.  That is, if the base file is
//! `/foo/bar/baz.svg`, then librsvg will only try to load referenced
//! files (from SVG's `<image>` element, for example, or from content
//! included through XML entities) if those files are in `/foo/bar/*`
//! or in `/foo/bar/*/.../*`.  This is so that malicious SVG documents
//! cannot include files that are in a directory above.
//!
//! The full set of rules for deciding which URLs may be loaded is as follows;
//! they are applied in order.  A referenced URL will not be loaded as soon as
//! one of these rules fails:
//!
//! 1. All `data:` URLs may be loaded.  These are sometimes used to
//! include raster image data, encoded as base-64, directly in an SVG
//! file.
//!
//! 2. All other URL schemes in references require a base URL.  For
//! example, this means that if you load an SVG with [`Loader::read_stream`]
//! without providing a `base_file`, then any referenced files will not
//! be allowed (e.g. raster images to be loaded from other files will
//! not work).
//!
//! 3. If referenced URLs are absolute, rather than relative, then
//! they must have the same scheme as the base URL.  For example, if
//! the base URL has a "`file`" scheme, then all URL references inside
//! the SVG must also have the "`file`" scheme, or be relative
//! references which will be resolved against the base URL.
//!
//! 4. If referenced URLs have a "`resource`" scheme, that is, if they
//! are included into your binary program with GLib's resource
//! mechanism, they are allowed to be loaded (provided that the base
//! URL is also a "`resource`", per the previous rule).
//!
//! 5. Otherwise, non-`file` schemes are not allowed.  For example,
//! librsvg will not load `http` resources, to keep malicious SVG data
//! from "phoning home".
//!
//! 6. A relative URL must resolve to the same directory as the base
//! URL, or to one of its subdirectories.  Librsvg will canonicalize
//! filenames, by removing "`..`" path components and resolving symbolic
//! links, to decide whether files meet these conditions.
//!
//! [static mode]: https://www.w3.org/TR/SVG2/conform.html#static-mode
//! [secure static mode]: https://www.w3.org/TR/SVG2/conform.html#secure-static-mode
//! [SVG 1.1]: https://www.w3.org/TR/SVG11/
//! [SVG 2]: https://www.w3.org/TR/SVG2/

#![allow(rustdoc::private_intra_doc_links)]
#![allow(clippy::clone_on_ref_ptr)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
#![allow(clippy::too_many_arguments)]
#![warn(nonstandard_style, rust_2018_idioms, unused)]
// Some lints no longer exist
#![warn(renamed_and_removed_lints)]
// Standalone lints
#![warn(trivial_casts, trivial_numeric_casts)]
// The public API is exported here
pub use crate::api::*;

pub use crate::color::Color;

pub use crate::rect::{IRect, Rect};

#[macro_use]
pub mod log;

#[macro_use]
mod parsers;

#[macro_use]
mod coord_units;

#[macro_use]
mod float_eq_cairo;

#[macro_use]
mod node;

#[macro_use]
mod property_macros;

#[macro_use]
mod util;

mod accept_language;
mod angle;
mod api;
mod aspect_ratio;
mod bbox;
pub mod c_api;
mod color;
mod cond;
mod css;
mod dasharray;
mod document;
mod dpi;
mod drawing_ctx;
mod element;
mod error;
mod filter;
mod filter_func;
pub mod filters;
mod font_props;
mod gradient;
mod handle;
mod href;
mod image;
mod io;
mod iri;
mod layout;
mod length;
mod limits;
mod marker;
mod paint_server;
mod path_builder;
mod path_parser;
mod pattern;
mod properties;
mod property_defs;
mod rect;
mod session;
mod shapes;
mod space;
mod structure;
mod style;
pub mod surface_utils;
mod text;
mod transform;
mod unit_interval;
mod url_resolver;
mod viewbox;
mod xml;

#[doc(hidden)]
pub mod bench_only {
    pub use crate::path_builder::PathBuilder;
    pub use crate::path_parser::Lexer;
}

#[doc(hidden)]
pub mod doctest_only {
    pub use crate::aspect_ratio::AspectRatio;
    pub use crate::error::AttributeResultExt;
    pub use crate::error::ElementError;
    pub use crate::error::ValueErrorKind;
    pub use crate::href::is_href;
    pub use crate::href::set_href;
    pub use crate::length::{Both, CssLength, Horizontal, Length, LengthUnit, ULength, Vertical};
    pub use crate::parsers::{Parse, ParseValue};
}

#[doc(hidden)]
pub mod rsvg_convert_only {
    pub use crate::aspect_ratio::AspectRatio;
    pub use crate::c_api::handle::PathOrUrl;
    pub use crate::c_api::sizing::LegacySize;
    pub use crate::dpi::Dpi;
    pub use crate::error::ParseError;
    pub use crate::length::{
        CssLength, Horizontal, Length, Normalize, NormalizeParams, ULength, Validate, Vertical,
    };
    pub use crate::parsers::{Parse, ParseValue};
    pub use crate::rect::Rect;
    pub use crate::viewbox::ViewBox;
}
<commit_msg>clippy: allow deriving PartialEq without deriving Eq<commit_after>//! Load and render SVG images into Cairo surfaces.
//!
//! This crate can load SVG images and render them to Cairo surfaces,
//! using a mixture of SVG's [static mode] and [secure static mode].
//! Librsvg does not do animation nor scripting, and can load
//! references to external data only in some situations; see below.
//!
//! Librsvg supports reading [SVG 1.1] data, and is gradually adding
//! support for features in [SVG 2].  Librsvg also supports SVGZ
//! files, which are just an SVG stream compressed with the GZIP
//! algorithm.
//!
//! # Basic usage
//!
//! * Create a [`Loader`] struct.
//! * Get an [`SvgHandle`] from the [`Loader`].
//! * Create a [`CairoRenderer`] for the [`SvgHandle`] and render to a Cairo context.
//!
//! You can put the following in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! librsvg = { git="https://gitlab.gnome.org/GNOME/librsvg" }
//! cairo-rs = "0.8.0"
//! glib = "0.9.0"                                # only if you need streams
//! gio = { version="0.8.1", features=["v2_50"] } # likewise
//! ```
//!
//! # Example
//!
//! ```
//!
//! const WIDTH: i32 = 640;
//! const HEIGHT: i32 = 480;
//!
//! fn main() {
//!     // Loading from a file
//!
//!     let handle = librsvg::Loader::new().read_path("example.svg").unwrap();
//!
//!     let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, WIDTH, HEIGHT).unwrap();
//!     let cr = cairo::Context::new(&surface).expect("Failed to create a cairo context");
//!
//!     let renderer = librsvg::CairoRenderer::new(&handle);
//!     renderer.render_document(
//!         &cr,
//!         &cairo::Rectangle {
//!             x: 0.0,
//!             y: 0.0,
//!             width: f64::from(WIDTH),
//!             height: f64::from(HEIGHT),
//!         },
//!     ).unwrap();
//!
//!     // Loading from a static SVG asset
//!
//!     let bytes = glib::Bytes::from_static(
//!         br#"<?xml version="1.0" encoding="UTF-8"?>
//!             <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
//!                 <rect id="foo" x="10" y="10" width="30" height="30"/>
//!             </svg>
//!         "#
//!     );
//!     let stream = gio::MemoryInputStream::from_bytes(&bytes);
//!
//!     let handle = librsvg::Loader::new().read_stream(
//!         &stream,
//!         None::<&gio::File>,          // no base file as this document has no references
//!         None::<&gio::Cancellable>,   // no cancellable
//!     ).unwrap();
//! }
//! ```
//!
//! # The "base file" and resolving references to external files
//!
//! When you load an SVG, librsvg needs to know the location of the "base file"
//! for it.  This is so that librsvg can determine the location of referenced
//! entities.  For example, say you have an SVG in <filename>/foo/bar/foo.svg</filename>
//! and that it has an image element like this:
//!
//! ```xml
//! <image href="resources/foo.png" .../>
//! ```
//!
//! In this case, librsvg needs to know the location of the toplevel
//! `/foo/bar/foo.svg` so that it can generate the appropriate
//! reference to `/foo/bar/resources/foo.png`.
//!
//! ## Security and locations of referenced files
//!
//! When processing an SVG, librsvg will only load referenced files if
//! they are in the same directory as the base file, or in a
//! subdirectory of it.  That is, if the base file is
//! `/foo/bar/baz.svg`, then librsvg will only try to load referenced
//! files (from SVG's `<image>` element, for example, or from content
//! included through XML entities) if those files are in `/foo/bar/*`
//! or in `/foo/bar/*/.../*`.  This is so that malicious SVG documents
//! cannot include files that are in a directory above.
//!
//! The full set of rules for deciding which URLs may be loaded is as follows;
//! they are applied in order.  A referenced URL will not be loaded as soon as
//! one of these rules fails:
//!
//! 1. All `data:` URLs may be loaded.  These are sometimes used to
//! include raster image data, encoded as base-64, directly in an SVG
//! file.
//!
//! 2. All other URL schemes in references require a base URL.  For
//! example, this means that if you load an SVG with [`Loader::read_stream`]
//! without providing a `base_file`, then any referenced files will not
//! be allowed (e.g. raster images to be loaded from other files will
//! not work).
//!
//! 3. If referenced URLs are absolute, rather than relative, then
//! they must have the same scheme as the base URL.  For example, if
//! the base URL has a "`file`" scheme, then all URL references inside
//! the SVG must also have the "`file`" scheme, or be relative
//! references which will be resolved against the base URL.
//!
//! 4. If referenced URLs have a "`resource`" scheme, that is, if they
//! are included into your binary program with GLib's resource
//! mechanism, they are allowed to be loaded (provided that the base
//! URL is also a "`resource`", per the previous rule).
//!
//! 5. Otherwise, non-`file` schemes are not allowed.  For example,
//! librsvg will not load `http` resources, to keep malicious SVG data
//! from "phoning home".
//!
//! 6. A relative URL must resolve to the same directory as the base
//! URL, or to one of its subdirectories.  Librsvg will canonicalize
//! filenames, by removing "`..`" path components and resolving symbolic
//! links, to decide whether files meet these conditions.
//!
//! [static mode]: https://www.w3.org/TR/SVG2/conform.html#static-mode
//! [secure static mode]: https://www.w3.org/TR/SVG2/conform.html#secure-static-mode
//! [SVG 1.1]: https://www.w3.org/TR/SVG11/
//! [SVG 2]: https://www.w3.org/TR/SVG2/

#![allow(rustdoc::private_intra_doc_links)]
#![allow(clippy::clone_on_ref_ptr)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::derive_partial_eq_without_eq)]
#![warn(nonstandard_style, rust_2018_idioms, unused)]
// Some lints no longer exist
#![warn(renamed_and_removed_lints)]
// Standalone lints
#![warn(trivial_casts, trivial_numeric_casts)]
// The public API is exported here
pub use crate::api::*;

pub use crate::color::Color;

pub use crate::rect::{IRect, Rect};

#[macro_use]
pub mod log;

#[macro_use]
mod parsers;

#[macro_use]
mod coord_units;

#[macro_use]
mod float_eq_cairo;

#[macro_use]
mod node;

#[macro_use]
mod property_macros;

#[macro_use]
mod util;

mod accept_language;
mod angle;
mod api;
mod aspect_ratio;
mod bbox;
pub mod c_api;
mod color;
mod cond;
mod css;
mod dasharray;
mod document;
mod dpi;
mod drawing_ctx;
mod element;
mod error;
mod filter;
mod filter_func;
pub mod filters;
mod font_props;
mod gradient;
mod handle;
mod href;
mod image;
mod io;
mod iri;
mod layout;
mod length;
mod limits;
mod marker;
mod paint_server;
mod path_builder;
mod path_parser;
mod pattern;
mod properties;
mod property_defs;
mod rect;
mod session;
mod shapes;
mod space;
mod structure;
mod style;
pub mod surface_utils;
mod text;
mod transform;
mod unit_interval;
mod url_resolver;
mod viewbox;
mod xml;

#[doc(hidden)]
pub mod bench_only {
    pub use crate::path_builder::PathBuilder;
    pub use crate::path_parser::Lexer;
}

#[doc(hidden)]
pub mod doctest_only {
    pub use crate::aspect_ratio::AspectRatio;
    pub use crate::error::AttributeResultExt;
    pub use crate::error::ElementError;
    pub use crate::error::ValueErrorKind;
    pub use crate::href::is_href;
    pub use crate::href::set_href;
    pub use crate::length::{Both, CssLength, Horizontal, Length, LengthUnit, ULength, Vertical};
    pub use crate::parsers::{Parse, ParseValue};
}

#[doc(hidden)]
pub mod rsvg_convert_only {
    pub use crate::aspect_ratio::AspectRatio;
    pub use crate::c_api::handle::PathOrUrl;
    pub use crate::c_api::sizing::LegacySize;
    pub use crate::dpi::Dpi;
    pub use crate::error::ParseError;
    pub use crate::length::{
        CssLength, Horizontal, Length, Normalize, NormalizeParams, ULength, Validate, Vertical,
    };
    pub use crate::parsers::{Parse, ParseValue};
    pub use crate::rect::Rect;
    pub use crate::viewbox::ViewBox;
}
<|endoftext|>