<commit_before>//! The interface for interacting with TMUX sessions. All the commands that are
/// built up during the parsing phase get matched to functions here. The
/// functions in this module all build up strings the get passed to a private
/// call. The private call converts them to CStrings and makes an "unsafe" system
/// call. All functions go through this `call` function as a common gateway to
/// system calls and can all be easily logged there.

pub mod config;

use libc::system;
use std::ffi::CString;
use std::process::{Command, ExitStatus, Output};
use std::io;

/// The program to call commands on.
static TMUX_NAME: &'static str = "tmux";

/// The gateway to calling any functions on tmux. Most public functions in this
/// module will be fed through this `call` function. This safely creates a new
/// thread to execute the command on. We say "Most" public functions will use
/// this as `attach` specificaly does not use it.
///
/// args: The command we will send to tmux on the host system for execution.
///
/// # Examples
///
/// ```
/// let _ = call(&["new-window", "-t", "muxed", "-c", "~/Projects/muxed/"]);
/// ```
fn call(args: &[&str]) -> Result<Output, io::Error> {
    //println!("{:?}", &args);
    Command::new(TMUX_NAME).args(args).output()
}

/// Attach is called as the last function in a set of commands. After the tmux
/// env has been setup by all previous commands this attaches the user to their
/// daemonized tmux session.
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
/// session_name: The active tmux session name.
pub fn attach(session_name: &String) -> () {
    let line = format!("{} attach -t '{}' {}", TMUX_NAME, session_name, ">/dev/null");
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe { system(system_call.as_ptr()); };
}

/// New session is the first call made in any sequence of commands. It initiates
/// a new tmux session with a default first window name.
/// *Why a randomized first window name?*
///   This is because the `new-session` tmux command does not support the `-c`
/// flag as `new-window` and `split-window` do. So if the first window requires
/// being changed to a default directory `send-keys` must be used after the
/// session and first window are opened. This means the first window is treated in
/// a different code path than any other window that is opened.
/// Instead I've opted to open the session with a randomized (no conflicts)
/// named window, and we close this window out before attaching the user. This
/// allows for us to treat all user defined windows the same.
///
/// session_name: The name of the session being created.
/// tmp_name: The named first window that we will close out.
///
/// # Examples
///
/// ```
/// use rand::random;
///
/// let session_name = "muxed".to_string();
/// let tmp_window_name = random::<u16>().to_string();
/// tmux::new_session(session_name, tmp_window_name);
/// ```
///
/// session_name: The active tmux session name.
/// tmp_name: The name for the temp initial window that is created with a new
/// session.
pub fn new_session(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new", "-d", "-s", session_name, "-n", window_name]);
}

/// Split window acts as the command to target named windows in a session to
/// send a make separate a single pane into two panes. Split window should target
/// a specific pane even if only a single pane exists.
///
/// # Examples
///
/// ```
/// let target = "muxed:cargo.0".to_string();
/// tmux::split_window(target, None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn split_window(target: &String) -> () {
    let _ = call(&["split-window", "-t", target]);
}

/// New window opens a new window in the named session with the provided window
/// name.
///
/// # Examples
///
/// ```
/// tmux::new_window("muxed".to_string(), "vim".to_string(), None);
/// ```
///
/// session_name: The active tmux session name.
/// window_name: The desired window name for the new window.
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn new_window(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new-window", "-t", session_name, "-n", window_name]);
}

/// The layout function will adjust the layout of a tmux window that already has
/// multiple panes. Currently it will only accept standard tmux layout types.
///
/// Possible layout options: even-horizontal, even-vertical, main-horizontal,
/// main-vertical, or tiled.
///
/// # Examples
///
/// ```
/// tmux::layout("muxed:cargo.0".to_string(), "main-vertical".to_string(), None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// layout: The predefined tmux named layout.
pub fn layout(target: &String, layout: &String) -> () {
    let _ = call(&["select-layout", "-t", target, layout]);
}

/// Send Keys executes literal key commands in specified target windows. This is
/// the execution method to perform actions like tailing logs, starting servers,
/// or opening editors.
///
/// `KPEnter` represent the keyboard press of the "Enter" key. So the execution
/// is a user input command followed by "enter"
///
/// # Examples
///
/// ```
/// tmux::send_keys("muxed:cargo.0".to_string(), "vim .".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// exec: The system command to be executed in a particular pane.
pub fn send_keys(target: &String, exec: &String) -> () {
    let _ = call(&["send-keys", "-t", target, exec, "KPEnter"]);
}

/// This is used to re-select the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_window("muxed:cargo.0".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
pub fn select_window(target: &String) -> () {
    let _ = call(&["select-window", "-t", target]);
}

/// This is used to re-select the top pane in the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_pane("muxed:cargo.top".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
pub fn select_pane(target: &String) -> () {
    let _ = call(&["select-pane", "-t", target]);
}

/// List Windows is used firgure out if a named session is already running.
///
/// # Examples
///
/// ```
/// tmux::has_session("muxed".to_string());
/// => ExitStatus
/// ```
///
/// target: A string represented by the {named_session}
pub fn has_session(target: &str) -> ExitStatus {
    let output = call(&["has-session", "-t", target]).expect("failed to see if the session existed");
    output.status
}

/// Read the tmux config and return a config object
///
/// # Examples
///
/// ```
/// tmux::get_config();
/// => "some-option false\npane-base-index 0"
/// ```
pub fn get_config() -> String {
    let output = call(&["start-server", ";", "show-options", "-g"]).expect("couldn't get tmux options");
    String::from_utf8_lossy(&output.stdout).to_string()
}
<commit_msg>docs(tmux/mod): Add backticks to vars<commit_after>//! The interface for interacting with TMUX sessions. All the commands that are
/// built up during the parsing phase get matched to functions here. The
/// functions in this module all build up strings the get passed to a private
/// call. The private call converts them to CStrings and makes an "unsafe" system
/// call. All functions go through this `call` function as a common gateway to
/// system calls and can all be easily logged there.

pub mod config;

use libc::system;
use std::ffi::CString;
use std::process::{Command, ExitStatus, Output};
use std::io;

/// The program to call commands on.
static TMUX_NAME: &'static str = "tmux";

/// The gateway to calling any functions on tmux. Most public functions in this
/// module will be fed through this `call` function. This safely creates a new
/// thread to execute the command on. We say "Most" public functions will use
/// this as `attach` specificaly does not use it.
///
/// args: The command we will send to tmux on the host system for execution.
///
/// # Examples
///
/// ```
/// let _ = call(&["new-window", "-t", "muxed", "-c", "~/Projects/muxed/"]);
/// ```
fn call(args: &[&str]) -> Result<Output, io::Error> {
    //println!("{:?}", &args);
    Command::new(TMUX_NAME).args(args).output()
}

/// Attach is called as the last function in a set of commands. After the tmux
/// env has been setup by all previous commands this attaches the user to their
/// daemonized tmux session.
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
/// `session_name: The active tmux session name.
pub fn attach(session_name: &String) -> () {
    let line = format!("{} attach -t '{}' {}", TMUX_NAME, session_name, ">/dev/null");
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe { system(system_call.as_ptr()); };
}

/// New session is the first call made in any sequence of commands. It initiates
/// a new tmux session with a default first window name.
/// *Why a randomized first window name?*
///   This is because the `new-session` tmux command does not support the `-c`
/// flag as `new-window` and `split-window` do. So if the first window requires
/// being changed to a default directory `send-keys` must be used after the
/// session and first window are opened. This means the first window is treated in
/// a different code path than any other window that is opened.
/// Instead I've opted to open the session with a randomized (no conflicts)
/// named window, and we close this window out before attaching the user. This
/// allows for us to treat all user defined windows the same.
///
/// `session_name`: The name of the session being created.
/// `tmp_name`: The named first window that we will close out.
///
/// # Examples
///
/// ```
/// use rand::random;
///
/// let session_name = "muxed".to_string();
/// let tmp_window_name = random::<u16>().to_string();
/// tmux::new_session(session_name, tmp_window_name);
/// ```
///
/// `session_name`: The active tmux session name.
/// `tmp_name`: The name for the temp initial window that is created with a new
/// session.
pub fn new_session(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new", "-d", "-s", session_name, "-n", window_name]);
}

/// Split window acts as the command to target named windows in a session to
/// send a make separate a single pane into two panes. Split window should target
/// a specific pane even if only a single pane exists.
///
/// # Examples
///
/// ```
/// let target = "muxed:cargo.0".to_string();
/// tmux::split_window(target, None);
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `root`: An `Option<String>` passed to the `-c` argument to change the current
/// directory.
pub fn split_window(target: &String) -> () {
    let _ = call(&["split-window", "-t", target]);
}

/// New window opens a new window in the named session with the provided window
/// name.
///
/// # Examples
///
/// ```
/// tmux::new_window("muxed".to_string(), "vim".to_string(), None);
/// ```
///
/// session_name: The active tmux session name.
/// window_name: The desired window name for the new window.
/// `root`: An `Option<String>` passed to the `-c` argument to change the current
/// directory.
pub fn new_window(session_name: &String, window_name: &String) -> () {
    let _ = call(&["new-window", "-t", session_name, "-n", window_name]);
}

/// The layout function will adjust the layout of a tmux window that already has
/// multiple panes. Currently it will only accept standard tmux layout types.
///
/// Possible layout options: even-horizontal, even-vertical, main-horizontal,
/// main-vertical, or tiled.
///
/// # Examples
///
/// ```
/// tmux::layout("muxed:cargo.0".to_string(), "main-vertical".to_string(), None);
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `layout`: The predefined tmux named layout.
pub fn layout(target: &String, layout: &String) -> () {
    let _ = call(&["select-layout", "-t", target, layout]);
}

/// Send Keys executes literal key commands in specified target windows. This is
/// the execution method to perform actions like tailing logs, starting servers,
/// or opening editors.
///
/// `KPEnter` represent the keyboard press of the "Enter" key. So the execution
/// is a user input command followed by "enter"
///
/// # Examples
///
/// ```
/// tmux::send_keys("muxed:cargo.0".to_string(), "vim .".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
/// `exec`: The system command to be executed in a particular pane.
pub fn send_keys(target: &String, exec: &String) -> () {
    let _ = call(&["send-keys", "-t", target, exec, "KPEnter"]);
}

/// This is used to re-select the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_window("muxed:cargo.0".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
pub fn select_window(target: &String) -> () {
    let _ = call(&["select-window", "-t", target]);
}

/// This is used to re-select the top pane in the first window before
/// attaching to the session.
///
/// # Examples
///
/// ```
/// tmux::select_pane("muxed:cargo.top".to_string());
/// ```
///
/// `target`: A string represented by the `{named_session}:{named_window}.{pane}`
pub fn select_pane(target: &String) -> () {
    let _ = call(&["select-pane", "-t", target]);
}

/// List Windows is used firgure out if a named session is already running.
///
/// # Examples
///
/// ```
/// tmux::has_session("muxed".to_string());
/// => ExitStatus
/// ```
///
/// `target`: A string represented by the `{named_session}`
pub fn has_session(target: &str) -> ExitStatus {
    let output = call(&["has-session", "-t", target]).expect("failed to see if the session existed");
    output.status
}

/// Read the tmux config and return a config object
///
/// # Examples
///
/// ```
/// tmux::get_config();
/// => "some-option false\npane-base-index 0"
/// ```
pub fn get_config() -> String {
    let output = call(&["start-server", ";", "show-options", "-g"]).expect("couldn't get tmux options");
    String::from_utf8_lossy(&output.stdout).to_string()
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Experimental BLAS (Basic Linear Algebra Subprograms) integration
//!
//! ***Requires crate feature `"rblas"`***
//!
//! Depends on crate [`rblas`], ([docs]).
//!
//! [`rblas`]: https://crates.io/crates/rblas/
//! [docs]: http://mikkyang.github.io/rust-blas/doc/rblas/
//!
//! ```
//! extern crate rblas;
//! extern crate ndarray;
//!
//! use rblas::Gemv;
//! use rblas::attribute::Transpose;
//!
//! use ndarray::{arr1, arr2};
//! use ndarray::blas::AsBlas;
//!
//! fn main() {
//!     // Gemv is the operation y = α a x + β y
//!     let alpha = 1.;
//!     let mut a = arr2(&[[1., 2., 3.],
//!                        [4., 5., 6.],
//!                        [7., 8., 9.]]);
//!     let x = [1., 0., 1.];
//!     let beta = 1.;
//!     let mut y = arr1(&[0., 0., 0.]);
//!
//!     Gemv::gemv(Transpose::NoTrans, &alpha, &a.blas(), &x[..],
//!                &beta, &mut y.blas());
//!
//!     assert_eq!(y, arr1(&[4., 10., 16.]));
//! }
//!
//! ```
//!
//! Use the methods in trait `AsBlas` to convert an array into a view that
//! implements rblas’ `Vector` or `Matrix` traits.
//!
//! Blas supports strided vectors and matrices; Matrices need to be contiguous
//! in their lowest dimension, so they will be copied into c-contiguous layout
//! automatically if needed. You should be able to use blocks sliced out
//! from a larger matrix without copying. Use the transpose flags in blas
//! instead of transposing with `ndarray`.
//!
//! Blas has its own error reporting system and will not panic on errors (that
//! I know), instead output its own error conditions, for example on dimension
//! mismatch in a matrix multiplication.
//!

use std::os::raw::{c_int};

use rblas::{
    Matrix,
    Vector,
};
use super::{
    ShapeError,
    zipsl,
};
use error::{from_kind, ErrorKind};
use imp_prelude::*;

/// ***Requires crate feature `"rblas"`***
pub struct BlasArrayView<'a, A: 'a, D>(ArrayView<'a, A, D>);
impl<'a, A, D: Copy> Copy for BlasArrayView<'a, A, D> { }
impl<'a, A, D: Clone> Clone for BlasArrayView<'a, A, D> {
    fn clone(&self) -> Self {
        BlasArrayView(self.0.clone())
    }
}

/// ***Requires crate feature `"rblas"`***
pub struct BlasArrayViewMut<'a, A: 'a, D>(ArrayViewMut<'a, A, D>);

impl<S, D> ArrayBase<S, D>
    where S: Data,
          D: Dimension
{
    fn size_check(&self) -> Result<(), ShapeError> {
        let max = c_int::max_value();
        for (&dim, &stride) in zipsl(self.shape(), self.strides()) {
            if dim > max as Ix || stride > max as Ixs {
                return Err(from_kind(ErrorKind::RangeLimited));
            }
        }
        Ok(())
    }

    fn contiguous_check(&self) -> Result<(), ShapeError> {
        // FIXME: handle transposed?
        if self.is_inner_contiguous() {
            Ok(())
        } else {
            Err(from_kind(ErrorKind::IncompatibleLayout))
        }
    }
}

impl<'a, A, D> Priv<ArrayView<'a, A, D>>
    where D: Dimension
{
    pub fn into_blas_view(self) -> Result<BlasArrayView<'a, A, D>, ShapeError> {
        let self_ = self.0;
        if self_.dim.ndim() > 1 {
            try!(self_.contiguous_check());
        }
        try!(self_.size_check());
        Ok(BlasArrayView(self_))
    }
}

impl<'a, A, D> ArrayViewMut<'a, A, D>
    where D: Dimension
{
    fn into_blas_view_mut(self) -> Result<BlasArrayViewMut<'a, A, D>, ShapeError> {
        if self.dim.ndim() > 1 {
            try!(self.contiguous_check());
        }
        try!(self.size_check());
        Ok(BlasArrayViewMut(self))
    }
}

/// Convert an array into a blas friendly wrapper.
///
/// Note that `blas` suppors four different element types: `f32`, `f64`,
/// `Complex<f32>`, and `Complex<f64>`.
///
/// ***Requires crate feature `"rblas"`***
pub trait AsBlas {
    /// Return an array view implementing Vector (1D) or Matrix (2D)
    /// traits.
    ///
    /// Elements are copied if needed to produce a contiguous matrix.

    /// The result is always mutable, due to the requirement of having write
    /// access to update the layout either way. Breaks sharing if the array is
    /// an `RcArray`.
    ///
    /// **Errors** if any dimension is larger than `c_int::MAX`.
    fn blas_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataOwned + DataMut,
              A: Clone;

    /// Equivalent to `.blas_checked().unwrap()`
    ///
    /// **Panics** if there was a an error in `.blas_checked()`.
    fn blas(&mut self) -> BlasArrayViewMut
        where S: DataOwned<Elem=A> + DataMut,
              A: Clone
    {
        self.blas_checked().unwrap()
    }

    /// Return a read-only array view implementing Vector (1D) or Matrix (2D)
    /// traits.
    ///
    /// The array must already be in a blas compatible layout: its innermost
    /// dimension must be contiguous.
    ///
    /// **Errors** if any dimension is larger than `c_int::MAX`.

    /// **Errors** if the inner dimension is not c-contiguous.
    ///
    /// Layout requirements may be loosened in the future.
    fn blas_view_checked(&self) -> Result<BlasArrayView, ShapeError>
        where S: Data;

    /// `bv` stands for **b**las **v**iew.
    ///
    /// Equivalent to `.blas_view_checked().unwrap()`
    ///
    /// **Panics** if there was a an error in `.blas_view_checked()`.
    fn bv(&self) -> BlasArrayView
        where S: Data,
    {
        self.blas_view_checked().unwrap()
    }

    /// Return a read-write array view implementing Vector (1D) or Matrix (2D)
    /// traits.
    ///
    /// The array must already be in a blas compatible layout: its innermost
    /// dimension must be contiguous.
    ///
    /// **Errors** if any dimension is larger than `c_int::MAX`.

    /// **Errors** if the inner dimension is not c-contiguous.
    ///
    /// Layout requirements may be loosened in the future.
    fn blas_view_mut_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataMut;

    /// `bvm` stands for **b**las **v**iew **m**ut.
    ///
    /// Equivalent to `.blas_view_mut_checked().unwrap()`
    ///
    /// **Panics** if there was a an error in `.blas_view_mut_checked()`.
    fn bvm(&mut self) -> BlasArrayViewMut
        where S: DataMut,
    {
        self.blas_view_mut_checked().unwrap()
    }
    /*

    /// Equivalent to `.blas_checked().unwrap()`, except elements
    /// are not copied to make the array contiguous: instead just
    /// dimensions and strides are adjusted, and elements end up in
    /// arbitrary location. Useful if the content of the array doesn't matter.
    ///
    /// **Panics** if there was a an error in `blas_checked`.
    fn blas_overwrite(&mut self) -> BlasArrayViewMut
        where S: DataMut;
        */
}

/// ***Requires crate feature `"rblas"`***
impl AsBlas for ArrayBase<S, D>
    where S: Data<Elem=A>,
          D: Dimension,
{
    fn blas_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataOwned + DataMut,
              A: Clone,
    {
        try!(self.size_check());
        match self.dim.ndim() {
            0 | 1 => { }
            2 => {
                if !self.is_inner_contiguous() {
                    self.ensure_standard_layout();
                }
            }
            _n => self.ensure_standard_layout(),
        }
        self.view_mut().into_blas_view_mut()
    }

    fn blas_view_checked(&self) -> Result<BlasArrayView, ShapeError>
        where S: Data
    {
        Priv(self.view()).into_blas_view()
    }

    fn blas_view_mut_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataMut,
    {
        self.view_mut().into_blas_view_mut()
    }

    /*
    fn blas_overwrite(&mut self) -> BlasArrayViewMut
        where S: DataMut,
    {
        self.size_check().unwrap();
        if self.dim.ndim() > 1 {
            self.force_standard_layout();
        }
        BlasArrayViewMut(self.view_mut())
    }
    */
}

/// **Panics** if `as_mut_ptr` is called on a read-only view.
impl<'a, A> Vector for BlasArrayView<'a, A, Ix> {
    fn len(&self) -> c_int {
        self.0.len() as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        panic!("ndarray: as_mut_ptr called on BlasArrayView (not mutable)");
    }

    // increment: stride
    fn inc(&self) -> c_int {
        self.0.strides as c_int
    }
}

impl<'a, A> Vector for BlasArrayViewMut<'a, A, Ix> {
    fn len(&self) -> c_int {
        self.0.len() as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        self.0.ptr
    }

    // increment: stride
    fn inc(&self) -> c_int {
        self.0.strides as c_int
    }
}

/// **Panics** if `as_mut_ptr` is called on a read-only view.
impl<'a, A> Matrix for BlasArrayView<'a, A, (Ix, Ix)> {
    fn rows(&self) -> c_int {
        self.0.dim().0 as c_int
    }

    fn cols(&self) -> c_int {
        self.0.dim().1 as c_int
    }

    // leading dimension == stride between each row
    fn lead_dim(&self) -> c_int {
        debug_assert!(self.cols() <= 1 || self.0.strides()[1] == 1);
        self.0.strides()[0] as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr as *const _
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        panic!("ndarray: as_mut_ptr called on BlasArrayView (not mutable)");
    }
}

impl<'a, A> Matrix for BlasArrayViewMut<'a, A, (Ix, Ix)> {
    fn rows(&self) -> c_int {
        self.0.dim().0 as c_int
    }

    fn cols(&self) -> c_int {
        self.0.dim().1 as c_int
    }

    // leading dimension == stride between each row
    fn lead_dim(&self) -> c_int {
        debug_assert!(self.cols() <= 1 || self.0.strides()[1] == 1);
        self.0.strides()[0] as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr as *const _
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        self.0.ptr
    }
}
<commit_msg>Put a deprecation notice on rblas -- it will move to its own crate<commit_after>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Experimental BLAS (Basic Linear Algebra Subprograms) integration
//!
//! ***Requires crate feature `"rblas"`***
//!
//! Depends on crate [`rblas`], ([docs]).
//!
//! [`rblas`]: https://crates.io/crates/rblas/
//! [docs]: http://mikkyang.github.io/rust-blas/doc/rblas/
//!
//! ```
//! extern crate rblas;
//! extern crate ndarray;
//!
//! use rblas::Gemv;
//! use rblas::attribute::Transpose;
//!
//! use ndarray::{arr1, arr2};
//! use ndarray::blas::AsBlas;
//!
//! fn main() {
//!     // Gemv is the operation y = α a x + β y
//!     let alpha = 1.;
//!     let mut a = arr2(&[[1., 2., 3.],
//!                        [4., 5., 6.],
//!                        [7., 8., 9.]]);
//!     let x = [1., 0., 1.];
//!     let beta = 1.;
//!     let mut y = arr1(&[0., 0., 0.]);
//!
//!     Gemv::gemv(Transpose::NoTrans, &alpha, &a.blas(), &x[..],
//!                &beta, &mut y.blas());
//!
//!     assert_eq!(y, arr1(&[4., 10., 16.]));
//! }
//!
//! ```
//!
//! Use the methods in trait `AsBlas` to convert an array into a view that
//! implements rblas’ `Vector` or `Matrix` traits.
//!
//! Blas supports strided vectors and matrices; Matrices need to be contiguous
//! in their lowest dimension, so they will be copied into c-contiguous layout
//! automatically if needed. You should be able to use blocks sliced out
//! from a larger matrix without copying. Use the transpose flags in blas
//! instead of transposing with `ndarray`.
//!
//! Blas has its own error reporting system and will not panic on errors (that
//! I know), instead output its own error conditions, for example on dimension
//! mismatch in a matrix multiplication.
//!
#![cfg_attr(has_deprecated, deprecated(note="`rblas` integration will move to its own crate."))]

use std::os::raw::{c_int};

use rblas::{
    Matrix,
    Vector,
};
use super::{
    ShapeError,
    zipsl,
};
use error::{from_kind, ErrorKind};
use imp_prelude::*;

/// ***Requires crate feature `"rblas"`***
pub struct BlasArrayView<'a, A: 'a, D>(ArrayView<'a, A, D>);
impl<'a, A, D: Copy> Copy for BlasArrayView<'a, A, D> { }
impl<'a, A, D: Clone> Clone for BlasArrayView<'a, A, D> {
    fn clone(&self) -> Self {
        BlasArrayView(self.0.clone())
    }
}

/// ***Requires crate feature `"rblas"`***
pub struct BlasArrayViewMut<'a, A: 'a, D>(ArrayViewMut<'a, A, D>);

impl<S, D> ArrayBase<S, D>
    where S: Data,
          D: Dimension
{
    fn size_check(&self) -> Result<(), ShapeError> {
        let max = c_int::max_value();
        for (&dim, &stride) in zipsl(self.shape(), self.strides()) {
            if dim > max as Ix || stride > max as Ixs {
                return Err(from_kind(ErrorKind::RangeLimited));
            }
        }
        Ok(())
    }

    fn contiguous_check(&self) -> Result<(), ShapeError> {
        // FIXME: handle transposed?
        if self.is_inner_contiguous() {
            Ok(())
        } else {
            Err(from_kind(ErrorKind::IncompatibleLayout))
        }
    }
}

impl<'a, A, D> Priv<ArrayView<'a, A, D>>
    where D: Dimension
{
    pub fn into_blas_view(self) -> Result<BlasArrayView<'a, A, D>, ShapeError> {
        let self_ = self.0;
        if self_.dim.ndim() > 1 {
            try!(self_.contiguous_check());
        }
        try!(self_.size_check());
        Ok(BlasArrayView(self_))
    }
}

impl<'a, A, D> ArrayViewMut<'a, A, D>
    where D: Dimension
{
    fn into_blas_view_mut(self) -> Result<BlasArrayViewMut<'a, A, D>, ShapeError> {
        if self.dim.ndim() > 1 {
            try!(self.contiguous_check());
        }
        try!(self.size_check());
        Ok(BlasArrayViewMut(self))
    }
}

/// Convert an array into a blas friendly wrapper.
///
/// Note that `blas` suppors four different element types: `f32`, `f64`,
/// `Complex<f32>`, and `Complex<f64>`.
///
/// ***Requires crate feature `"rblas"`***
pub trait AsBlas {
    /// Return an array view implementing Vector (1D) or Matrix (2D)
    /// traits.
    ///
    /// Elements are copied if needed to produce a contiguous matrix.

    /// The result is always mutable, due to the requirement of having write
    /// access to update the layout either way. Breaks sharing if the array is
    /// an `RcArray`.
    ///
    /// **Errors** if any dimension is larger than `c_int::MAX`.
    fn blas_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataOwned + DataMut,
              A: Clone;

    /// Equivalent to `.blas_checked().unwrap()`
    ///
    /// **Panics** if there was a an error in `.blas_checked()`.
    fn blas(&mut self) -> BlasArrayViewMut
        where S: DataOwned<Elem=A> + DataMut,
              A: Clone
    {
        self.blas_checked().unwrap()
    }

    /// Return a read-only array view implementing Vector (1D) or Matrix (2D)
    /// traits.
    ///
    /// The array must already be in a blas compatible layout: its innermost
    /// dimension must be contiguous.
    ///
    /// **Errors** if any dimension is larger than `c_int::MAX`.

    /// **Errors** if the inner dimension is not c-contiguous.
    ///
    /// Layout requirements may be loosened in the future.
    fn blas_view_checked(&self) -> Result<BlasArrayView, ShapeError>
        where S: Data;

    /// `bv` stands for **b**las **v**iew.
    ///
    /// Equivalent to `.blas_view_checked().unwrap()`
    ///
    /// **Panics** if there was a an error in `.blas_view_checked()`.
    fn bv(&self) -> BlasArrayView
        where S: Data,
    {
        self.blas_view_checked().unwrap()
    }

    /// Return a read-write array view implementing Vector (1D) or Matrix (2D)
    /// traits.
    ///
    /// The array must already be in a blas compatible layout: its innermost
    /// dimension must be contiguous.
    ///
    /// **Errors** if any dimension is larger than `c_int::MAX`.

    /// **Errors** if the inner dimension is not c-contiguous.
    ///
    /// Layout requirements may be loosened in the future.
    fn blas_view_mut_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataMut;

    /// `bvm` stands for **b**las **v**iew **m**ut.
    ///
    /// Equivalent to `.blas_view_mut_checked().unwrap()`
    ///
    /// **Panics** if there was a an error in `.blas_view_mut_checked()`.
    fn bvm(&mut self) -> BlasArrayViewMut
        where S: DataMut,
    {
        self.blas_view_mut_checked().unwrap()
    }
    /*

    /// Equivalent to `.blas_checked().unwrap()`, except elements
    /// are not copied to make the array contiguous: instead just
    /// dimensions and strides are adjusted, and elements end up in
    /// arbitrary location. Useful if the content of the array doesn't matter.
    ///
    /// **Panics** if there was a an error in `blas_checked`.
    fn blas_overwrite(&mut self) -> BlasArrayViewMut
        where S: DataMut;
        */
}

/// ***Requires crate feature `"rblas"`***
impl AsBlas for ArrayBase<S, D>
    where S: Data<Elem=A>,
          D: Dimension,
{
    fn blas_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataOwned + DataMut,
              A: Clone,
    {
        try!(self.size_check());
        match self.dim.ndim() {
            0 | 1 => { }
            2 => {
                if !self.is_inner_contiguous() {
                    self.ensure_standard_layout();
                }
            }
            _n => self.ensure_standard_layout(),
        }
        self.view_mut().into_blas_view_mut()
    }

    fn blas_view_checked(&self) -> Result<BlasArrayView, ShapeError>
        where S: Data
    {
        Priv(self.view()).into_blas_view()
    }

    fn blas_view_mut_checked(&mut self) -> Result<BlasArrayViewMut, ShapeError>
        where S: DataMut,
    {
        self.view_mut().into_blas_view_mut()
    }

    /*
    fn blas_overwrite(&mut self) -> BlasArrayViewMut
        where S: DataMut,
    {
        self.size_check().unwrap();
        if self.dim.ndim() > 1 {
            self.force_standard_layout();
        }
        BlasArrayViewMut(self.view_mut())
    }
    */
}

/// **Panics** if `as_mut_ptr` is called on a read-only view.
impl<'a, A> Vector for BlasArrayView<'a, A, Ix> {
    fn len(&self) -> c_int {
        self.0.len() as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        panic!("ndarray: as_mut_ptr called on BlasArrayView (not mutable)");
    }

    // increment: stride
    fn inc(&self) -> c_int {
        self.0.strides as c_int
    }
}

impl<'a, A> Vector for BlasArrayViewMut<'a, A, Ix> {
    fn len(&self) -> c_int {
        self.0.len() as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        self.0.ptr
    }

    // increment: stride
    fn inc(&self) -> c_int {
        self.0.strides as c_int
    }
}

/// **Panics** if `as_mut_ptr` is called on a read-only view.
impl<'a, A> Matrix for BlasArrayView<'a, A, (Ix, Ix)> {
    fn rows(&self) -> c_int {
        self.0.dim().0 as c_int
    }

    fn cols(&self) -> c_int {
        self.0.dim().1 as c_int
    }

    // leading dimension == stride between each row
    fn lead_dim(&self) -> c_int {
        debug_assert!(self.cols() <= 1 || self.0.strides()[1] == 1);
        self.0.strides()[0] as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr as *const _
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        panic!("ndarray: as_mut_ptr called on BlasArrayView (not mutable)");
    }
}

impl<'a, A> Matrix for BlasArrayViewMut<'a, A, (Ix, Ix)> {
    fn rows(&self) -> c_int {
        self.0.dim().0 as c_int
    }

    fn cols(&self) -> c_int {
        self.0.dim().1 as c_int
    }

    // leading dimension == stride between each row
    fn lead_dim(&self) -> c_int {
        debug_assert!(self.cols() <= 1 || self.0.strides()[1] == 1);
        self.0.strides()[0] as c_int
    }

    fn as_ptr(&self) -> *const A {
        self.0.ptr as *const _
    }

    fn as_mut_ptr(&mut self) -> *mut A {
        self.0.ptr
    }
}
<|endoftext|>


<commit_before>///! Busy handler (when the database is locked)
use std::time::Duration;
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;

use ffi;
use {Connection, InnerConnection, Result};

impl Connection {
    /// Set a busy handler that sleeps for a specified amount of time when a table is locked.
    /// The handler will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
    ///
    /// Calling this routine with an argument equal to zero turns off all busy handlers.
    //
    /// There can only be a single busy handler for a particular database connection at any given moment.
    /// If another busy handler was defined (using `busy_handler`) prior to calling this routine, that other busy handler is cleared.
    pub fn busy_timeout(&self, timeout: Duration) -> Result<()> {
        let ms = timeout
            .as_secs()
            .checked_mul(1000)
            .and_then(|t| t.checked_add(timeout.subsec_millis().into()))
            .expect("too big");
        self.db.borrow_mut().busy_timeout(ms as i32)
    }

    /// Register a callback to handle `SQLITE_BUSY` errors.
    ///
    /// If the busy callback is `None`, then `SQLITE_BUSY is returned immediately upon encountering the lock.`
    /// The argument to the busy handler callback is the number of times that the busy handler has been invoked previously for the same locking event.
    /// If the busy callback returns `false`, then no additional attempts are made to access the database and `SQLITE_BUSY` is returned to the application.
    /// If the callback returns `true`, then another attempt is made to access the database and the cycle repeats.
    ///
    /// There can only be a single busy handler defined for each database connection.
    /// Setting a new busy handler clears any previously set handler.
    /// Note that calling `busy_timeout()` or evaluating `PRAGMA busy_timeout=N` will change the busy handler and thus clear any previously set busy handler.
    pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> {
        unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int {
            let handler_fn: fn(i32) -> bool = mem::transmute(p_arg);
            if handler_fn(count) {
                1
            } else {
                0
            }
        }
        let mut c = self.db.borrow_mut();
        let r = match callback {
            Some(f) => unsafe {
                ffi::sqlite3_busy_handler(c.db(), Some(busy_handler_callback), mem::transmute(f))
            },
            None => unsafe { ffi::sqlite3_busy_handler(c.db(), None, ptr::null_mut()) },
        };
        c.decode_result(r)
    }
}

impl InnerConnection {
    fn busy_timeout(&mut self, timeout: c_int) -> Result<()> {
        let r = unsafe { ffi::sqlite3_busy_timeout(self.db, timeout) };
        self.decode_result(r)
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    use self::tempdir::TempDir;
    use std::sync::atomic::{AtomicBool, Ordering};
    use std::sync::mpsc::sync_channel;
    use std::time::Duration;
    use std::thread;

    use {Connection, Error, ErrorCode, TransactionBehavior};

    #[test]
    fn test_default_busy() {
        let temp_dir = TempDir::new("test_default_busy").unwrap();
        let path = temp_dir.path().join("test.db3");

        let mut db1 = Connection::open(&path).unwrap();
        let tx1 = db1
            .transaction_with_behavior(TransactionBehavior::Exclusive)
            .unwrap();
        let db2 = Connection::open(&path).unwrap();
        let r = db2.query_row("PRAGMA schema_version", &[], |_| unreachable!());
        match r.unwrap_err() {
            Error::SqliteFailure(err, _) => {
                assert_eq!(err.code, ErrorCode::DatabaseBusy);
            }
            err => panic!("Unexpected error {}", err),
        }
        tx1.rollback().unwrap();
    }

    #[test]
    fn test_busy_timeout() {
        let temp_dir = TempDir::new("test_busy_timeout").unwrap();
        let path = temp_dir.path().join("test.db3");

        let db2 = Connection::open(&path).unwrap();
        db2.busy_timeout(Duration::from_secs(1)).unwrap();

        let (rx, tx) = sync_channel(0);
        let child = thread::spawn(move || {
            let mut db1 = Connection::open(&path).unwrap();
            let tx1 = db1
                .transaction_with_behavior(TransactionBehavior::Exclusive)
                .unwrap();
            rx.send(1).unwrap();
            thread::sleep(Duration::from_millis(100));
            tx1.rollback().unwrap();
        });

        assert_eq!(tx.recv().unwrap(), 1);
        let _ =
            db2.query_row("PRAGMA schema_version", &[], |row| {
                row.get_checked::<_, i32>(0)
            }).expect("unexpected error");

        child.join().unwrap();
    }

    #[test]
    fn test_busy_handler() {
        lazy_static! {
            static ref CALLED: AtomicBool = AtomicBool::new(false);
        }
        fn busy_handler(_: i32) -> bool {
            CALLED.store(true, Ordering::Relaxed);
            thread::sleep(Duration::from_millis(100));
            true
        }

        let temp_dir = TempDir::new("test_busy_handler").unwrap();
        let path = temp_dir.path().join("test.db3");

        let db2 = Connection::open(&path).unwrap();
        db2.busy_handler(Some(busy_handler)).unwrap();

        let (rx, tx) = sync_channel(0);
        let child = thread::spawn(move || {
            let mut db1 = Connection::open(&path).unwrap();
            let tx1 = db1
                .transaction_with_behavior(TransactionBehavior::Exclusive)
                .unwrap();
            rx.send(1).unwrap();
            thread::sleep(Duration::from_millis(100));
            tx1.rollback().unwrap();
        });

        assert_eq!(tx.recv().unwrap(), 1);
        let _ =
            db2.query_row("PRAGMA schema_version", &[], |row| {
                row.get_checked::<_, i32>(0)
            }).expect("unexpected error");
        assert_eq!(CALLED.load(Ordering::Relaxed), true);

        child.join().unwrap();
    }
}
<commit_msg>[ci skip] Ignore unstable busy tests by default<commit_after>///! Busy handler (when the database is locked)
use std::time::Duration;
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;

use ffi;
use {Connection, InnerConnection, Result};

impl Connection {
    /// Set a busy handler that sleeps for a specified amount of time when a table is locked.
    /// The handler will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
    ///
    /// Calling this routine with an argument equal to zero turns off all busy handlers.
    //
    /// There can only be a single busy handler for a particular database connection at any given moment.
    /// If another busy handler was defined (using `busy_handler`) prior to calling this routine, that other busy handler is cleared.
    pub fn busy_timeout(&self, timeout: Duration) -> Result<()> {
        let ms = timeout
            .as_secs()
            .checked_mul(1000)
            .and_then(|t| t.checked_add(timeout.subsec_millis().into()))
            .expect("too big");
        self.db.borrow_mut().busy_timeout(ms as i32)
    }

    /// Register a callback to handle `SQLITE_BUSY` errors.
    ///
    /// If the busy callback is `None`, then `SQLITE_BUSY is returned immediately upon encountering the lock.`
    /// The argument to the busy handler callback is the number of times that the busy handler has been invoked previously for the same locking event.
    /// If the busy callback returns `false`, then no additional attempts are made to access the database and `SQLITE_BUSY` is returned to the application.
    /// If the callback returns `true`, then another attempt is made to access the database and the cycle repeats.
    ///
    /// There can only be a single busy handler defined for each database connection.
    /// Setting a new busy handler clears any previously set handler.
    /// Note that calling `busy_timeout()` or evaluating `PRAGMA busy_timeout=N` will change the busy handler and thus clear any previously set busy handler.
    pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> {
        unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int {
            let handler_fn: fn(i32) -> bool = mem::transmute(p_arg);
            if handler_fn(count) {
                1
            } else {
                0
            }
        }
        let mut c = self.db.borrow_mut();
        let r = match callback {
            Some(f) => unsafe {
                ffi::sqlite3_busy_handler(c.db(), Some(busy_handler_callback), mem::transmute(f))
            },
            None => unsafe { ffi::sqlite3_busy_handler(c.db(), None, ptr::null_mut()) },
        };
        c.decode_result(r)
    }
}

impl InnerConnection {
    fn busy_timeout(&mut self, timeout: c_int) -> Result<()> {
        let r = unsafe { ffi::sqlite3_busy_timeout(self.db, timeout) };
        self.decode_result(r)
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    use self::tempdir::TempDir;
    use std::sync::atomic::{AtomicBool, Ordering};
    use std::sync::mpsc::sync_channel;
    use std::time::Duration;
    use std::thread;

    use {Connection, Error, ErrorCode, TransactionBehavior};

    #[test]
    fn test_default_busy() {
        let temp_dir = TempDir::new("test_default_busy").unwrap();
        let path = temp_dir.path().join("test.db3");

        let mut db1 = Connection::open(&path).unwrap();
        let tx1 = db1
            .transaction_with_behavior(TransactionBehavior::Exclusive)
            .unwrap();
        let db2 = Connection::open(&path).unwrap();
        let r = db2.query_row("PRAGMA schema_version", &[], |_| unreachable!());
        match r.unwrap_err() {
            Error::SqliteFailure(err, _) => {
                assert_eq!(err.code, ErrorCode::DatabaseBusy);
            }
            err => panic!("Unexpected error {}", err),
        }
        tx1.rollback().unwrap();
    }

    #[test]
    #[ignore] // FIXME: unstable
    fn test_busy_timeout() {
        let temp_dir = TempDir::new("test_busy_timeout").unwrap();
        let path = temp_dir.path().join("test.db3");

        let db2 = Connection::open(&path).unwrap();
        db2.busy_timeout(Duration::from_secs(1)).unwrap();

        let (rx, tx) = sync_channel(0);
        let child = thread::spawn(move || {
            let mut db1 = Connection::open(&path).unwrap();
            let tx1 = db1
                .transaction_with_behavior(TransactionBehavior::Exclusive)
                .unwrap();
            rx.send(1).unwrap();
            thread::sleep(Duration::from_millis(100));
            tx1.rollback().unwrap();
        });

        assert_eq!(tx.recv().unwrap(), 1);
        let _ =
            db2.query_row("PRAGMA schema_version", &[], |row| {
                row.get_checked::<_, i32>(0)
            }).expect("unexpected error");

        child.join().unwrap();
    }

    #[test]
    #[ignore] // FIXME: unstable
    fn test_busy_handler() {
        lazy_static! {
            static ref CALLED: AtomicBool = AtomicBool::new(false);
        }
        fn busy_handler(_: i32) -> bool {
            CALLED.store(true, Ordering::Relaxed);
            thread::sleep(Duration::from_millis(100));
            true
        }

        let temp_dir = TempDir::new("test_busy_handler").unwrap();
        let path = temp_dir.path().join("test.db3");

        let db2 = Connection::open(&path).unwrap();
        db2.busy_handler(Some(busy_handler)).unwrap();

        let (rx, tx) = sync_channel(0);
        let child = thread::spawn(move || {
            let mut db1 = Connection::open(&path).unwrap();
            let tx1 = db1
                .transaction_with_behavior(TransactionBehavior::Exclusive)
                .unwrap();
            rx.send(1).unwrap();
            thread::sleep(Duration::from_millis(100));
            tx1.rollback().unwrap();
        });

        assert_eq!(tx.recv().unwrap(), 1);
        let _ =
            db2.query_row("PRAGMA schema_version", &[], |row| {
                row.get_checked::<_, i32>(0)
            }).expect("unexpected error");
        assert_eq!(CALLED.load(Ordering::Relaxed), true);

        child.join().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate rav1e;

use std::slice;
use std::sync::Arc;

use std::ffi::CStr;
use std::os::raw::c_char;
use std::os::raw::c_int;

pub struct RaFrame(Arc<rav1e::Frame>);

pub struct RaConfig {
    cfg: rav1e::Config,
    last_err: Option<rav1e::EncoderStatus>,
}

pub struct RaContext {
    ctx: rav1e::Context,
    last_err: Option<rav1e::EncoderStatus>,
}

type RaFrameType = rav1e::FrameType;

#[repr(C)]
pub struct RaPacket {
    pub data: *const u8,
    pub len: usize,
    pub number: usize,
    pub frame_type: RaFrameType,
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_default(
    width: usize,
    height: usize,
    bit_depth: usize,
    chroma_sampling: rav1e::ChromaSampling,
    timebase: rav1e::Ratio,
) -> *mut RaConfig {
    let cfg = rav1e::Config {
        frame_info: rav1e::FrameInfo {
            width,
            height,
            bit_depth,
            chroma_sampling,
        },
        timebase,
        enc: Default::default(),
    };

    let c = Box::new(RaConfig {
        cfg,
        last_err: None,
    });

    Box::into_raw(c)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_drop(cfg: *mut RaConfig) {
    let _ = Box::from_raw(cfg);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_parse(
    cfg: *mut RaConfig,
    key: *const c_char,
    value: *const c_char,
) -> c_int {
    let key = CStr::from_ptr(key).to_string_lossy();
    let value = CStr::from_ptr(value).to_string_lossy();

    (*cfg)
        .cfg
        .parse(&key, &value)
        .map(|_v| {
            (*cfg).last_err = None;
            0
        }).map_err(|e| (*cfg).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_context_new(cfg: *const RaConfig) -> *mut RaContext {
    let ctx = RaContext {
        ctx: (*cfg).cfg.new_context(),
        last_err: None,
    };

    Box::into_raw(Box::new(ctx))
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_context_drop(ctx: *mut RaContext) {
    let _ = Box::from_raw(ctx);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_new(ctx: *const RaContext) -> *mut RaFrame {
    let f = (*ctx).ctx.new_frame();
    let frame = Box::new(RaFrame(f));

    Box::into_raw(frame)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_drop(ctx: *mut RaFrame) {
    let _ = Arc::from_raw(ctx);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_send_frame(ctx: *mut RaContext, frame: *const RaFrame) -> c_int {
    let frame = if frame.is_null() {
        None
    } else {
        Some((*frame).0.clone())
    };

    (*ctx)
        .ctx
        .send_frame(frame)
        .map(|_v| {
            (*ctx).last_err = None;
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_receive_packet(
    ctx: *mut RaContext,
    pkt: *mut *mut RaPacket,
) -> c_int {
    (*ctx)
        .ctx
        .receive_packet()
        .map(|p| {
            (*ctx).last_err = None;
            let packet = RaPacket {
                data: p.data.as_ptr(),
                len: p.data.len(),
                number: p.number,
                frame_type: p.frame_type,
            };
            *pkt = Box::into_raw(Box::new(packet));
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern fn rav1e_packet_drop(pkt: *mut RaPacket) {
    let _ = Box::from_raw(pkt);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_fill_plane(
    frame: *mut RaFrame,
    plane: usize,
    data: *const u8,
    data_len: usize,
    stride: usize,
    bytewidth: usize,
) {
    let input = Arc::get_mut(&mut (*frame).0).unwrap();
    let data_slice = slice::from_raw_parts(data, data_len);

    input.planes[plane].copy_from_raw_u8(data_slice, stride, bytewidth);
}
<commit_msg>crav1e: wip: Update to the new API and test the documentation capabilities<commit_after>//! # C API for rav1e
//!
//! [rav1e](https://github.com/xiph/rav1e/) is an [AV1](https://aomediacodec.github.io/av1-spec/)
//! encoder written in [Rust](https://rust-lang.org)
//!
//! This is the C-compatible API to it

extern crate rav1e;

use std::slice;
use std::sync::Arc;

use std::ffi::CStr;
use std::os::raw::c_char;
use std::os::raw::c_int;

/// Raw video Frame
///
/// It can be allocated throught rav1e_frame_new(), populated using rav1e_frame_fill_plane()
/// and freed using rav1e_frame_drop().
pub struct RaFrame(Arc<rav1e::Frame>);

pub struct RaConfig {
    cfg: rav1e::Config,
    last_err: Option<rav1e::EncoderStatus>,
}

pub struct RaContext {
    ctx: rav1e::Context,
    last_err: Option<rav1e::EncoderStatus>,
}

type RaFrameType = rav1e::FrameType;

#[repr(C)]
pub struct RaPacket {
    pub data: *const u8,
    pub len: usize,
    pub number: u64,
    pub frame_type: RaFrameType,
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_default(
    width: usize,
    height: usize,
    bit_depth: usize,
    chroma_sampling: rav1e::ChromaSampling,
    timebase: rav1e::Ratio,
) -> *mut RaConfig {
    let cfg = rav1e::Config {
        frame_info: rav1e::FrameInfo {
            width,
            height,
            bit_depth,
            chroma_sampling,
        },
        timebase,
        enc: Default::default(),
    };

    let c = Box::new(RaConfig {
        cfg,
        last_err: None,
    });

    Box::into_raw(c)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_drop(cfg: *mut RaConfig) {
    let _ = Box::from_raw(cfg);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_config_parse(
    cfg: *mut RaConfig,
    key: *const c_char,
    value: *const c_char,
) -> c_int {
    let key = CStr::from_ptr(key).to_string_lossy();
    let value = CStr::from_ptr(value).to_string_lossy();

    (*cfg)
        .cfg
        .parse(&key, &value)
        .map(|_v| {
            (*cfg).last_err = None;
            0
        }).map_err(|e| (*cfg).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_context_new(cfg: *const RaConfig) -> *mut RaContext {
    let ctx = RaContext {
        ctx: (*cfg).cfg.new_context(),
        last_err: None,
    };

    Box::into_raw(Box::new(ctx))
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_context_drop(ctx: *mut RaContext) {
    let _ = Box::from_raw(ctx);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_new(ctx: *const RaContext) -> *mut RaFrame {
    let f = (*ctx).ctx.new_frame();
    let frame = Box::new(RaFrame(f));

    Box::into_raw(frame)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_drop(ctx: *mut RaFrame) {
    let _ = Arc::from_raw(ctx);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_send_frame(ctx: *mut RaContext, frame: *const RaFrame) -> c_int {
    let frame = if frame.is_null() {
        None
    } else {
        Some((*frame).0.clone())
    };

    (*ctx)
        .ctx
        .send_frame(frame)
        .map(|_v| {
            (*ctx).last_err = None;
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_receive_packet(
    ctx: *mut RaContext,
    pkt: *mut *mut RaPacket,
) -> c_int {
    (*ctx)
        .ctx
        .receive_packet()
        .map(|p| {
            (*ctx).last_err = None;
            let packet = RaPacket {
                data: p.data.as_ptr(),
                len: p.data.len(),
                number: p.number,
                frame_type: p.frame_type,
            };
            *pkt = Box::into_raw(Box::new(packet));
            0
        }).map_err(|e| (*ctx).last_err = Some(e))
        .unwrap_or(-1)
}

#[no_mangle]
pub unsafe extern fn rav1e_packet_drop(pkt: *mut RaPacket) {
    let _ = Box::from_raw(pkt);
}

#[no_mangle]
pub unsafe extern fn rav1e_container_sequence_header(ctx: *mut RaContext, len: *mut usize) -> *mut u8 {
    let buf = (*ctx).ctx.container_sequence_header();

    *len = buf.len();
    Box::into_raw(buf.into_boxed_slice()) as *mut u8
}

pub unsafe extern fn rav1e_container_sequence_header_drop(sequence: *mut u8) {
    let _ = Box::from_raw(sequence);
}

#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_fill_plane(
    frame: *mut RaFrame,
    plane: usize,
    data: *const u8,
    data_len: usize,
    stride: usize,
    bytewidth: usize,
) {
    let input = Arc::get_mut(&mut (*frame).0).unwrap();
    let data_slice = slice::from_raw_parts(data, data_len);

    input.planes[plane].copy_from_raw_u8(data_slice, stride, bytewidth);
}
<|endoftext|>


<commit_before>//! This module contains definition of table/row cells stuff

use std::io::{Write, Error};
use std::string::ToString;
use unicode_width::UnicodeWidthStr;
use term::{Attr, Terminal, color};
use super::format::Alignment;
use super::utils::print_align;

/// Represent a table cell containing a string.
///
/// Once created, a cell's content cannot be modified.
/// The cell would have to be replaced by another one
#[derive(Clone, Debug)]
pub struct Cell {
	content: Vec<String>,
	width: usize,
	align: Alignment,
	style: Vec<Attr>
}

impl Cell {
	/// Create a new `Cell` initialized with content from `string`.
	/// Text alignment in cell is configurable with the `align` argument
	pub fn new_align(string: &str, align: Alignment) -> Cell {
		let content: Vec<String> = string.lines().map(|ref x| x.to_string()).collect();
		let mut width = 0;
		for cont in &content {
			let l = UnicodeWidthStr::width(&cont[..]);
			if l > width {
				width = l;
			}
		}
		Cell {
			content: content,
			width: width,
			align: align,
			style: Vec::new()
		}
	}

	/// Create a new `Cell` initialized with content from `string`.
	/// By default, content is align to `LEFT`
	pub fn new(string: &str) -> Cell {
		Cell::new_align(string, Alignment::LEFT)
	}

	/// Set text alignment in the cell
	pub fn align(&mut self, align: Alignment) {
		self.align = align;
	}

	/// Add a style attribute to the cell
	pub fn style(&mut self, attr: Attr) {
		self.style.push(attr);
	}

	/// Add a style attribute to the cell. Can be chained
	pub fn with_style(mut self, attr: Attr) -> Cell {
		self.style(attr);
		self
	}

	/// Remove all style attributes and reset alignment to default (LEFT)
	pub fn reset_style(&mut self) {
		self.style.clear();
		self.align(Alignment::LEFT);
	}

	/// Set the cell's style by applying the given specifier string
	///
	/// # Style spec syntax
	///
	/// The syntax for the style specifier looks like this :
	/// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft
	///
	/// ### List of supported specifiers :
	///
	/// * **F** : **F**oreground (must be followed by a color specifier)
	/// * **B** : **B**ackground (must be followed by a color specifier)
	/// * **b** : **b**old
	/// * **i** : **i**talic
	/// * **u** : **u**nderline
	/// * **c** : Align **c**enter
	/// * **l** : Align **l**eft
	/// * **r** : Align **r**ight
	/// * **d** : **d**efault style
	///
	/// ### List of color specifiers :
	///
	/// * **r** : Red
	/// * **b** : Blue
	/// * **g** : Green
	/// * **y** : Yellow
	/// * **c** : Cyan
	/// * **m** : Magenta
	/// * **w** : White
	/// * **d** : Black
	///
	/// And capital letters are for **bright** colors.
	/// Eg :
	///
	/// * **R** : Bright Red
	/// * **B** : Bright Blue
	/// * ... and so on ...
	pub fn style_spec(mut self, spec: &str) -> Cell {
		self.reset_style();
		let mut foreground = false;
		let mut background = false;
		for c in spec.chars() {
			if foreground || background {
				let color = match c {
					'r' => color::RED,
					'R' => color::BRIGHT_RED,
					'b' => color::BLUE,
					'B' => color::BRIGHT_BLUE,
					'g' => color::GREEN,
					'G' => color::BRIGHT_GREEN,
					'y' => color::YELLOW,
					'Y' => color::BRIGHT_YELLOW,
					'c' => color::CYAN,
					'C' => color::BRIGHT_CYAN,
					'm' => color::MAGENTA,
					'M' => color::BRIGHT_MAGENTA,
					'w' => color::WHITE,
					'W' => color::BRIGHT_WHITE,
					'd' => color::BLACK,
					'D' => color::BRIGHT_BLACK,
					_ => { // Silently ignore unknown tags
							foreground = false;
							background = false;
							continue;
						 }
				};
				if foreground { self.style(Attr::ForegroundColor(color)); }
				else if background { self.style(Attr::BackgroundColor(color)); }
				foreground = false;
				background = false;
			}
			else {
				match c {
					'F' => foreground = true,
					'B' => background = true,
					'b' => self.style(Attr::Bold),
					'i' => self.style(Attr::Italic(true)),
					'u' => self.style(Attr::Underline(true)),
					'c' => self.align(Alignment::CENTER),
					'l' => self.align(Alignment::LEFT),
					'r' => self.align(Alignment::RIGHT),
					'd' => {/* Default : do nothing */}
					_ => {/* Silently ignore unknown tags */}
				}
			}
		}
		return self;
	}

	/// Return the height of the cell
	pub fn get_height(&self) -> usize {
		self.content.len()
	}

	/// Return the width of the cell
	pub fn get_width(&self) -> usize {
		self.width
	}

	/// Return a copy of the full string contained in the cell
	pub fn get_content(&self) -> String {
		self.content.join("\n")
	}

	/// Print a partial cell to `out`. Since the cell may be multi-lined,
	/// `idx` is the line index to print. `col_width` is the column width used to
	/// fill the cells with blanks so it fits in the table.
	/// If `ìdx` is higher than this cell's height, it will print empty content
	pub fn print<T: Write+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		let c = self.content.get(idx).map(|s| s.as_ref()).unwrap_or("");
		print_align(out, self.align, c, ' ', col_width, skip_right_fill)
	}

	/// Apply style then call `print` to print the cell into a terminal
	pub fn print_term<T: Terminal+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		for a in &self.style {
			match out.attr(a.clone()) {
				Ok(..) | Err(::term::Error::NotSupported) => (), // Ignore unsupported atrributes
				Err(e) => return Err(term_error_to_io_error(e))
			};
		}
		try!(self.print(out, idx, col_width, skip_right_fill));
		match out.reset() {
			Ok(..) | Err(::term::Error::NotSupported) => Ok(()),
			Err(e) => Err(term_error_to_io_error(e))
		}
	}
}

fn term_error_to_io_error(te: ::term::Error) -> Error {
    match te {
        ::term::Error::Io(why) => why,
        _ => Error::new(::std::io::ErrorKind::Other, te),
    }
}

impl <'a, T: ToString> From<&'a T> for Cell {
	fn from(f: &T) -> Cell {
		Cell::new(&f.to_string())
	}
}

impl ToString for Cell {
	fn to_string(&self) -> String {
		self.get_content()
	}
}

impl Default for Cell {
	/// Return a cell initialized with a single empty `String`, with LEFT alignment
	fn default() -> Cell {
		Cell {
			content: vec!["".to_string(); 1],
			width: 0,
			align: Alignment::LEFT,
			style: Vec::new()
		}
	}
}

/// This macro simplifies `Cell` creation
///
/// Support 2 syntax : With and without style specification.
/// # Syntax
/// ```text
/// cell!(value);
/// ```
/// or
///
/// ```text
/// cell!(spec:value);
/// ```
/// Value must implement the `std::string::ToString` trait
///
/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method
/// # Example
/// ```
/// # #[macro_use] extern crate prettytable;
/// # fn main() {
/// let cell = cell!("value");
/// // Do something with the cell
/// # drop(cell);
/// // Create a cell with style (Red foreground, Bold, aligned to left);
/// let styled = cell!(Frbl->"value");
/// # drop(styled);
/// # }
/// ```
#[macro_export]
macro_rules! cell {
	() => ($crate::cell::Cell::default());
	($value:expr) => ($crate::cell::Cell::new(&$value.to_string()));
	($style:ident -> $value:expr) => (cell!($value).style_spec(stringify!($style)));
}

#[cfg(test)]
mod tests {
	use cell::Cell;
	use utils::StringWriter;
	use format::Alignment;
	use term::{Attr, color};

	#[test]
	fn get_content() {
		let cell = Cell::new("test");
		assert_eq!(cell.get_content(), "test");
	}

	#[test]
	fn print_ascii() {
		let ascii_cell = Cell::new("hello");
		assert_eq!(ascii_cell.get_width(), 5);

		let mut out = StringWriter::new();
		let _ = ascii_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "hello     ");
	}

	#[test]
	fn print_unicode() {
		let unicode_cell = Cell::new("привет");
		assert_eq!(unicode_cell.get_width(), 6);

		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "привет    ");
	}

	#[test]
	fn print_cjk() {
		let unicode_cell = Cell::new("由系统自动更新");
		assert_eq!(unicode_cell.get_width(), 14);
		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 20, false);
		assert_eq!(out.as_string(), "由系统自动更新      ");
	}

	#[test]
	fn align_left() {
		let cell = Cell::new_align("test", Alignment::LEFT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "test      ");
	}

	#[test]
	fn align_center() {
		let cell = Cell::new_align("test", Alignment::CENTER);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "   test   ");
	}

	#[test]
	fn align_right() {
		let cell = Cell::new_align("test", Alignment::RIGHT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "      test");
	}

	#[test]
	fn style_spec() {
		let mut cell = Cell::new("test").style_spec("FrBBbuic");
		assert_eq!(cell.style.len(), 5);
		assert!(cell.style.contains(&Attr::Underline(true)));
		assert!(cell.style.contains(&Attr::Italic(true)));
		assert!(cell.style.contains(&Attr::Bold));
		assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE)));
		assert_eq!(cell.align, Alignment::CENTER);

		cell = cell.style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
		assert_eq!(cell.align, Alignment::RIGHT)
	}

	#[test]
	fn reset_style() {
		let mut cell = Cell::new("test").style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		cell.reset_style();
		assert_eq!(cell.style.len(), 0);
		assert_eq!(cell.align, Alignment::LEFT);
	}
}
<commit_msg>Ignore ColorOutOfRange term error<commit_after>//! This module contains definition of table/row cells stuff

use std::io::{Write, Error};
use std::string::ToString;
use unicode_width::UnicodeWidthStr;
use term::{Attr, Terminal, color};
use super::format::Alignment;
use super::utils::print_align;

/// Represent a table cell containing a string.
///
/// Once created, a cell's content cannot be modified.
/// The cell would have to be replaced by another one
#[derive(Clone, Debug)]
pub struct Cell {
	content: Vec<String>,
	width: usize,
	align: Alignment,
	style: Vec<Attr>
}

impl Cell {
	/// Create a new `Cell` initialized with content from `string`.
	/// Text alignment in cell is configurable with the `align` argument
	pub fn new_align(string: &str, align: Alignment) -> Cell {
		let content: Vec<String> = string.lines().map(|ref x| x.to_string()).collect();
		let mut width = 0;
		for cont in &content {
			let l = UnicodeWidthStr::width(&cont[..]);
			if l > width {
				width = l;
			}
		}
		Cell {
			content: content,
			width: width,
			align: align,
			style: Vec::new()
		}
	}

	/// Create a new `Cell` initialized with content from `string`.
	/// By default, content is align to `LEFT`
	pub fn new(string: &str) -> Cell {
		Cell::new_align(string, Alignment::LEFT)
	}

	/// Set text alignment in the cell
	pub fn align(&mut self, align: Alignment) {
		self.align = align;
	}

	/// Add a style attribute to the cell
	pub fn style(&mut self, attr: Attr) {
		self.style.push(attr);
	}

	/// Add a style attribute to the cell. Can be chained
	pub fn with_style(mut self, attr: Attr) -> Cell {
		self.style(attr);
		self
	}

	/// Remove all style attributes and reset alignment to default (LEFT)
	pub fn reset_style(&mut self) {
		self.style.clear();
		self.align(Alignment::LEFT);
	}

	/// Set the cell's style by applying the given specifier string
	///
	/// # Style spec syntax
	///
	/// The syntax for the style specifier looks like this :
	/// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft
	///
	/// ### List of supported specifiers :
	///
	/// * **F** : **F**oreground (must be followed by a color specifier)
	/// * **B** : **B**ackground (must be followed by a color specifier)
	/// * **b** : **b**old
	/// * **i** : **i**talic
	/// * **u** : **u**nderline
	/// * **c** : Align **c**enter
	/// * **l** : Align **l**eft
	/// * **r** : Align **r**ight
	/// * **d** : **d**efault style
	///
	/// ### List of color specifiers :
	///
	/// * **r** : Red
	/// * **b** : Blue
	/// * **g** : Green
	/// * **y** : Yellow
	/// * **c** : Cyan
	/// * **m** : Magenta
	/// * **w** : White
	/// * **d** : Black
	///
	/// And capital letters are for **bright** colors.
	/// Eg :
	///
	/// * **R** : Bright Red
	/// * **B** : Bright Blue
	/// * ... and so on ...
	pub fn style_spec(mut self, spec: &str) -> Cell {
		self.reset_style();
		let mut foreground = false;
		let mut background = false;
		for c in spec.chars() {
			if foreground || background {
				let color = match c {
					'r' => color::RED,
					'R' => color::BRIGHT_RED,
					'b' => color::BLUE,
					'B' => color::BRIGHT_BLUE,
					'g' => color::GREEN,
					'G' => color::BRIGHT_GREEN,
					'y' => color::YELLOW,
					'Y' => color::BRIGHT_YELLOW,
					'c' => color::CYAN,
					'C' => color::BRIGHT_CYAN,
					'm' => color::MAGENTA,
					'M' => color::BRIGHT_MAGENTA,
					'w' => color::WHITE,
					'W' => color::BRIGHT_WHITE,
					'd' => color::BLACK,
					'D' => color::BRIGHT_BLACK,
					_ => { // Silently ignore unknown tags
							foreground = false;
							background = false;
							continue;
						 }
				};
				if foreground { self.style(Attr::ForegroundColor(color)); }
				else if background { self.style(Attr::BackgroundColor(color)); }
				foreground = false;
				background = false;
			}
			else {
				match c {
					'F' => foreground = true,
					'B' => background = true,
					'b' => self.style(Attr::Bold),
					'i' => self.style(Attr::Italic(true)),
					'u' => self.style(Attr::Underline(true)),
					'c' => self.align(Alignment::CENTER),
					'l' => self.align(Alignment::LEFT),
					'r' => self.align(Alignment::RIGHT),
					'd' => {/* Default : do nothing */}
					_ => {/* Silently ignore unknown tags */}
				}
			}
		}
		return self;
	}

	/// Return the height of the cell
	pub fn get_height(&self) -> usize {
		self.content.len()
	}

	/// Return the width of the cell
	pub fn get_width(&self) -> usize {
		self.width
	}

	/// Return a copy of the full string contained in the cell
	pub fn get_content(&self) -> String {
		self.content.join("\n")
	}

	/// Print a partial cell to `out`. Since the cell may be multi-lined,
	/// `idx` is the line index to print. `col_width` is the column width used to
	/// fill the cells with blanks so it fits in the table.
	/// If `ìdx` is higher than this cell's height, it will print empty content
	pub fn print<T: Write+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		let c = self.content.get(idx).map(|s| s.as_ref()).unwrap_or("");
		print_align(out, self.align, c, ' ', col_width, skip_right_fill)
	}

	/// Apply style then call `print` to print the cell into a terminal
	pub fn print_term<T: Terminal+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		for a in &self.style {
			match out.attr(a.clone()) {
				Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => (), // Ignore unsupported atrributes
				Err(e) => return Err(term_error_to_io_error(e))
			};
		}
		try!(self.print(out, idx, col_width, skip_right_fill));
		match out.reset() {
			Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => Ok(()),
			Err(e) => Err(term_error_to_io_error(e))
		}
	}
}

fn term_error_to_io_error(te: ::term::Error) -> Error {
    match te {
        ::term::Error::Io(why) => why,
        _ => Error::new(::std::io::ErrorKind::Other, te),
    }
}

impl <'a, T: ToString> From<&'a T> for Cell {
	fn from(f: &T) -> Cell {
		Cell::new(&f.to_string())
	}
}

impl ToString for Cell {
	fn to_string(&self) -> String {
		self.get_content()
	}
}

impl Default for Cell {
	/// Return a cell initialized with a single empty `String`, with LEFT alignment
	fn default() -> Cell {
		Cell {
			content: vec!["".to_string(); 1],
			width: 0,
			align: Alignment::LEFT,
			style: Vec::new()
		}
	}
}

/// This macro simplifies `Cell` creation
///
/// Support 2 syntax : With and without style specification.
/// # Syntax
/// ```text
/// cell!(value);
/// ```
/// or
///
/// ```text
/// cell!(spec:value);
/// ```
/// Value must implement the `std::string::ToString` trait
///
/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method
/// # Example
/// ```
/// # #[macro_use] extern crate prettytable;
/// # fn main() {
/// let cell = cell!("value");
/// // Do something with the cell
/// # drop(cell);
/// // Create a cell with style (Red foreground, Bold, aligned to left);
/// let styled = cell!(Frbl->"value");
/// # drop(styled);
/// # }
/// ```
#[macro_export]
macro_rules! cell {
	() => ($crate::cell::Cell::default());
	($value:expr) => ($crate::cell::Cell::new(&$value.to_string()));
	($style:ident -> $value:expr) => (cell!($value).style_spec(stringify!($style)));
}

#[cfg(test)]
mod tests {
	use cell::Cell;
	use utils::StringWriter;
	use format::Alignment;
	use term::{Attr, color};

	#[test]
	fn get_content() {
		let cell = Cell::new("test");
		assert_eq!(cell.get_content(), "test");
	}

	#[test]
	fn print_ascii() {
		let ascii_cell = Cell::new("hello");
		assert_eq!(ascii_cell.get_width(), 5);

		let mut out = StringWriter::new();
		let _ = ascii_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "hello     ");
	}

	#[test]
	fn print_unicode() {
		let unicode_cell = Cell::new("привет");
		assert_eq!(unicode_cell.get_width(), 6);

		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "привет    ");
	}

	#[test]
	fn print_cjk() {
		let unicode_cell = Cell::new("由系统自动更新");
		assert_eq!(unicode_cell.get_width(), 14);
		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 20, false);
		assert_eq!(out.as_string(), "由系统自动更新      ");
	}

	#[test]
	fn align_left() {
		let cell = Cell::new_align("test", Alignment::LEFT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "test      ");
	}

	#[test]
	fn align_center() {
		let cell = Cell::new_align("test", Alignment::CENTER);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "   test   ");
	}

	#[test]
	fn align_right() {
		let cell = Cell::new_align("test", Alignment::RIGHT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "      test");
	}

	#[test]
	fn style_spec() {
		let mut cell = Cell::new("test").style_spec("FrBBbuic");
		assert_eq!(cell.style.len(), 5);
		assert!(cell.style.contains(&Attr::Underline(true)));
		assert!(cell.style.contains(&Attr::Italic(true)));
		assert!(cell.style.contains(&Attr::Bold));
		assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE)));
		assert_eq!(cell.align, Alignment::CENTER);

		cell = cell.style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
		assert_eq!(cell.align, Alignment::RIGHT)
	}

	#[test]
	fn reset_style() {
		let mut cell = Cell::new("test").style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		cell.reset_style();
		assert_eq!(cell.style.len(), 0);
		assert_eq!(cell.align, Alignment::LEFT);
	}
}
<|endoftext|>