<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::error;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};

/// A parsed terminfo database entry.
#[derive(Debug)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String> ,
    /// Map of capability name to boolean value
    pub bools: HashMap<String, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<String, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<String, Vec<u8> >
}

/// A terminfo creation error.
#[derive(Debug)]
pub enum Error {
    /// TermUnset Indicates that the environment doesn't include enough information to find
    /// the terminfo entry.
    TermUnset,
    /// MalformedTerminfo indicates that parsing the terminfo entry failed.
    MalformedTerminfo(String),
    /// io::Error forwards any io::Errors encountered when finding or reading the terminfo entry.
    IoError(io::Error),
}

impl error::Error for Error {
    fn description(&self) -> &str { "failed to create TermInfo" }

    fn cause(&self) -> Option<&error::Error> {
        use self::Error::*;
        match self {
            &IoError(ref e) => Some(e),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;
        match self {
            &TermUnset => Ok(()),
            &MalformedTerminfo(ref e) => e.fmt(f),
            &IoError(ref e) => e.fmt(f),
        }
    }
}

impl TermInfo {
    /// Create a TermInfo based on current environment.
    pub fn from_env() -> Result<TermInfo, Error> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a TermInfo for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo, Error> {
        get_dbpath_for_term(name).ok_or_else(|| {
            Error::IoError(io::Error::new(io::ErrorKind::NotFound,
                                          "terminfo file not found"))
        }).and_then(|p| {
            TermInfo::from_path(&p)
        })
    }

    /// Parse the given TermInfo.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo, Error> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    fn _from_path(path: &Path) -> Result<TermInfo, Error> {
        let file = try!(File::open(path).map_err(|e| { Error::IoError(e) }));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false).map_err(|e| {
            Error::MalformedTerminfo(e)
        })
    }
}

pub mod searcher;

/// TermInfo format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold               => "bold",
        Attr::Dim                => "dim",
        Attr::Italic(true)       => "sitm",
        Attr::Italic(false)      => "ritm",
        Attr::Underline(true)    => "smul",
        Attr::Underline(false)   => "rmul",
        Attr::Blink              => "blink",
        Attr::Standout(true)     => "smso",
        Attr::Standout(false)    => "rmso",
        Attr::Reverse            => "rev",
        Attr::Secure             => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab"
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write+Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> io::Result<bool> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setaf", &[Param::Number(color as i32)]);
        }
        Ok(false)
    }

    fn bg(&mut self, color: color::Color) -> io::Result<bool> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setab", &[Param::Number(color as i32)]);
        }
        Ok(false)
    }

    fn attr(&mut self, attr: Attr) -> io::Result<bool> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.apply_cap(cap_for_attr(attr), &[]),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => {
                self.num_colors > 0
            }
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> io::Result<bool> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [
            "sg0", "sgr", "op"
        ].iter().filter_map(|cap| {
            self.ti.strings.get(*cap)
        }).next() {
            Some(op) => match expand(&op, &[], &mut Variables::new()) {
                Ok(cmd) => cmd,
                Err(_) => return Ok(false),
            },
            None => return Ok(false),
        };

        self.out.write_all(&cmd).map(|_|true)
    }

    fn cursor_up(&mut self) -> io::Result<bool> {
        self.apply_cap("cuu1", &[])
    }

    fn delete_line(&mut self) -> io::Result<bool> {
        self.apply_cap("dl", &[])
    }

    fn carriage_return(&mut self) -> io::Result<bool> {
        self.apply_cap("cr", &[])
    }

    fn get_ref<'a>(&'a self) -> &'a T { &self.out }

    fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }

    fn into_inner(self) -> T where Self: Sized { self.out }
}

impl<T: Write+Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf")
                 && terminfo.strings.contains_key("setab") {
                     terminfo.numbers.get("colors").map_or(0, |&n| n)
                 } else { 0 };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color-8
        } else { color }
    }

    fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> io::Result<bool> {
        if let Some(cmd) = self.ti.strings.get(cmd) {
            if let Ok(s) = expand(&cmd, params, &mut Variables::new()) {
                try!(self.out.write_all(&s));
                return Ok(true)
            }
        }
        Ok(false)
    }
}

impl<T: Write> Write for TerminfoTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.out.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
    }
}
<commit_msg>Return an error when expansion fails.<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::error;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};

/// A parsed terminfo database entry.
#[derive(Debug)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String> ,
    /// Map of capability name to boolean value
    pub bools: HashMap<String, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<String, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<String, Vec<u8> >
}

/// A terminfo creation error.
#[derive(Debug)]
pub enum Error {
    /// TermUnset Indicates that the environment doesn't include enough information to find
    /// the terminfo entry.
    TermUnset,
    /// MalformedTerminfo indicates that parsing the terminfo entry failed.
    MalformedTerminfo(String),
    /// io::Error forwards any io::Errors encountered when finding or reading the terminfo entry.
    IoError(io::Error),
}

impl error::Error for Error {
    fn description(&self) -> &str { "failed to create TermInfo" }

    fn cause(&self) -> Option<&error::Error> {
        use self::Error::*;
        match self {
            &IoError(ref e) => Some(e),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;
        match self {
            &TermUnset => Ok(()),
            &MalformedTerminfo(ref e) => e.fmt(f),
            &IoError(ref e) => e.fmt(f),
        }
    }
}

impl TermInfo {
    /// Create a TermInfo based on current environment.
    pub fn from_env() -> Result<TermInfo, Error> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a TermInfo for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo, Error> {
        get_dbpath_for_term(name).ok_or_else(|| {
            Error::IoError(io::Error::new(io::ErrorKind::NotFound,
                                          "terminfo file not found"))
        }).and_then(|p| {
            TermInfo::from_path(&p)
        })
    }

    /// Parse the given TermInfo.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo, Error> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    fn _from_path(path: &Path) -> Result<TermInfo, Error> {
        let file = try!(File::open(path).map_err(|e| { Error::IoError(e) }));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false).map_err(|e| {
            Error::MalformedTerminfo(e)
        })
    }
}

pub mod searcher;

/// TermInfo format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold               => "bold",
        Attr::Dim                => "dim",
        Attr::Italic(true)       => "sitm",
        Attr::Italic(false)      => "ritm",
        Attr::Underline(true)    => "smul",
        Attr::Underline(false)   => "rmul",
        Attr::Blink              => "blink",
        Attr::Standout(true)     => "smso",
        Attr::Standout(false)    => "rmso",
        Attr::Reverse            => "rev",
        Attr::Secure             => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab"
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write+Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> io::Result<bool> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setaf", &[Param::Number(color as i32)]);
        }
        Ok(false)
    }

    fn bg(&mut self, color: color::Color) -> io::Result<bool> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setab", &[Param::Number(color as i32)]);
        }
        Ok(false)
    }

    fn attr(&mut self, attr: Attr) -> io::Result<bool> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.apply_cap(cap_for_attr(attr), &[]),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => {
                self.num_colors > 0
            }
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> io::Result<bool> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [
            "sg0", "sgr", "op"
        ].iter().filter_map(|cap| {
            self.ti.strings.get(*cap)
        }).next() {
            Some(op) => match expand(&op, &[], &mut Variables::new()) {
                Ok(cmd) => cmd,
                Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e))
            },
            None => return Ok(false),
        };
        self.out.write_all(&cmd).and(Ok(true))
    }

    fn cursor_up(&mut self) -> io::Result<bool> {
        self.apply_cap("cuu1", &[])
    }

    fn delete_line(&mut self) -> io::Result<bool> {
        self.apply_cap("dl", &[])
    }

    fn carriage_return(&mut self) -> io::Result<bool> {
        self.apply_cap("cr", &[])
    }

    fn get_ref<'a>(&'a self) -> &'a T { &self.out }

    fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }

    fn into_inner(self) -> T where Self: Sized { self.out }
}

impl<T: Write+Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf")
                 && terminfo.strings.contains_key("setab") {
                     terminfo.numbers.get("colors").map_or(0, |&n| n)
                 } else { 0 };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color-8
        } else { color }
    }

    fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> io::Result<bool> {
        match self.ti.strings.get(cmd) {
            Some(cmd) => match expand(&cmd, params, &mut Variables::new()) {
                Ok(s) => self.out.write_all(&s).and(Ok(true)),
                Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            },
            None => Ok(false)
        }
    }
}

impl<T: Write> Write for TerminfoTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.out.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use Result;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};
use self::Error::*;

/// A parsed terminfo database entry.
#[derive(Debug, Clone)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String>,
    /// Map of capability name to boolean value
    pub bools: HashMap<String, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<String, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<String, Vec<u8>>,
}

impl TermInfo {
    /// Create a TermInfo based on current environment.
    pub fn from_env() -> Result<TermInfo> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(::Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a TermInfo for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo> {
        get_dbpath_for_term(name)
            .ok_or_else(|| ::Error::TerminfoEntryNotFound)
            .and_then(|p| TermInfo::from_path(&p))
    }

    /// Parse the given TermInfo.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    // (That is, this uses a &Path so that this function need not be instantiated
    // for every type
    // which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
    // might do this for
    // us. Alas. )
    fn _from_path(path: &Path) -> Result<TermInfo> {
        let file = try!(File::open(path).map_err(::Error::Io));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false)
    }
}

#[derive(Debug, Eq, PartialEq)]
/// An error from parsing a terminfo entry
pub enum Error {
    /// The "magic" number at the start of the file was wrong.
    ///
    /// It should be `0x11A`
    BadMagic(u16),
    /// The names in the file were not valid UTF-8.
    ///
    /// In theory these should only be ASCII, but to work with the Rust `str` type, we treat them
    /// as UTF-8. This is valid, except when a terminfo file decides to be invalid. This hasn't
    /// been encountered in the wild.
    NotUtf8(::std::str::Utf8Error),
    /// The names section of the file was empty
    ShortNames,
    /// More boolean parameters are present in the file than this crate knows how to interpret.
    TooManyBools,
    /// More number parameters are present in the file than this crate knows how to interpret.
    TooManyNumbers,
    /// More string parameters are present in the file than this crate knows how to interpret.
    TooManyStrings,
    /// The length of some field was not >= -1.
    InvalidLength,
    /// The names table was missing a trailing null terminator.
    NamesMissingNull,
    /// The strings table was missing a trailing null terminator.
    StringsMissingNull,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use std::error::Error;
        match *self {
            NotUtf8(e) => write!(f, "{}", e),
            BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v),
            _ => f.write_str(self.description()),
        }
    }
}

impl ::std::convert::From<::std::string::FromUtf8Error> for Error {
    fn from(v: ::std::string::FromUtf8Error) -> Self {
        NotUtf8(v.utf8_error())
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic(..) => "incorrect magic number at start of file",
            ShortNames => "no names exposed, need at least one",
            TooManyBools => "more boolean properties than libterm knows about",
            TooManyNumbers => "more number properties than libterm knows about",
            TooManyStrings => "more string properties than libterm knows about",
            InvalidLength => "invalid length field value, must be >= -1",
            NotUtf8(ref e) => e.description(),
            NamesMissingNull => "names table missing NUL terminator",
            StringsMissingNull => "string table missing NUL terminator",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            NotUtf8(ref e) => Some(e),
            _ => None,
        }
    }
}

pub mod searcher;

/// TermInfo format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
    mod names;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold => "bold",
        Attr::Dim => "dim",
        Attr::Italic(true) => "sitm",
        Attr::Italic(false) => "ritm",
        Attr::Underline(true) => "smul",
        Attr::Underline(false) => "rmul",
        Attr::Blink => "blink",
        Attr::Standout(true) => "smso",
        Attr::Standout(false) => "rmso",
        Attr::Reverse => "rev",
        Attr::Secure => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab",
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
#[derive(Clone, Debug)]
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setaf", &[Param::Number(color as i32)]);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.apply_cap("setab", &[Param::Number(color as i32)]);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.apply_cap(cap_for_attr(attr), &[]),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> Result<()> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [("sgr0", &[] as &[Param]), ("sgr", &[Param::Number(0)]), ("op", &[])]
                            .iter()
                            .filter_map(|&(cap, params)| {
                                self.ti.strings.get(cap).map(|c| (c, params))
                            })
                            .next() {
            Some((op, params)) => {
                match expand(op, params, &mut Variables::new()) {
                    Ok(cmd) => cmd,
                    Err(e) => return Err(e.into()),
                }
            }
            None => return Err(::Error::NotSupported),
        };
        try!(self.out.write_all(&cmd));
        Ok(())
    }

    fn supports_reset(&self) -> bool {
        ["sgr0", "sgr", "op"].iter().any(|&cap| self.ti.strings.get(cap).is_some())
    }

    fn supports_color(&self) -> bool {
        self.num_colors > 0 && self.supports_reset()
    }

    fn cursor_up(&mut self) -> Result<()> {
        self.apply_cap("cuu1", &[])
    }

    fn delete_line(&mut self) -> Result<()> {
        self.apply_cap("dl", &[])
    }

    fn carriage_return(&mut self) -> Result<()> {
        self.apply_cap("cr", &[])
    }

    fn get_ref(&self) -> &T {
        &self.out
    }

    fn get_mut(&mut self) -> &mut T {
        &mut self.out
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.out
    }
}

impl<T: Write + Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf") &&
                    terminfo.strings.contains_key("setab") {
            terminfo.numbers.get("colors").map_or(0, |&n| n)
        } else {
            0
        };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color - 8
        } else {
            color
        }
    }

    fn apply_cap(&mut self, cmd: &str, params: &[Param]) -> Result<()> {
        match self.ti.strings.get(cmd) {
            Some(cmd) => {
                match expand(&cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(self.out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }
}

impl<T: Write> Write for TerminfoTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.out.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
    }
}
<commit_msg>Factor out reset and apply_cap into TermInfo<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use Result;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};
use self::Error::*;

/// A parsed terminfo database entry.
#[derive(Debug, Clone)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String>,
    /// Map of capability name to boolean value
    pub bools: HashMap<String, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<String, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<String, Vec<u8>>,
}

impl TermInfo {
    /// Create a TermInfo based on current environment.
    pub fn from_env() -> Result<TermInfo> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(::Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a TermInfo for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo> {
        get_dbpath_for_term(name)
            .ok_or_else(|| ::Error::TerminfoEntryNotFound)
            .and_then(|p| TermInfo::from_path(&p))
    }

    /// Parse the given TermInfo.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    // (That is, this uses a &Path so that this function need not be instantiated
    // for every type
    // which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
    // might do this for
    // us. Alas. )
    fn _from_path(path: &Path) -> Result<TermInfo> {
        let file = try!(File::open(path).map_err(::Error::Io));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false)
    }

    /// Retrieve a capability `cmd` and expand it with `params`, writing result to `out`.
    pub fn apply_cap(&self, cmd: &str, params: &[Param], out: &mut io::Write) -> Result<()> {
        match self.strings.get(cmd) {
            Some(cmd) => {
                match expand(&cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }

    /// Write the reset string to `out`.
    pub fn reset(&self, out: &mut io::Write) -> Result<()> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [("sgr0", &[] as &[Param]), ("sgr", &[Param::Number(0)]), ("op", &[])]
                            .iter()
                            .filter_map(|&(cap, params)| {
                                self.strings.get(cap).map(|c| (c, params))
                            })
                            .next() {
            Some((op, params)) => {
                match expand(op, params, &mut Variables::new()) {
                    Ok(cmd) => cmd,
                    Err(e) => return Err(e.into()),
                }
            }
            None => return Err(::Error::NotSupported),
        };
        try!(out.write_all(&cmd));
        Ok(())
    }

}

#[derive(Debug, Eq, PartialEq)]
/// An error from parsing a terminfo entry
pub enum Error {
    /// The "magic" number at the start of the file was wrong.
    ///
    /// It should be `0x11A`
    BadMagic(u16),
    /// The names in the file were not valid UTF-8.
    ///
    /// In theory these should only be ASCII, but to work with the Rust `str` type, we treat them
    /// as UTF-8. This is valid, except when a terminfo file decides to be invalid. This hasn't
    /// been encountered in the wild.
    NotUtf8(::std::str::Utf8Error),
    /// The names section of the file was empty
    ShortNames,
    /// More boolean parameters are present in the file than this crate knows how to interpret.
    TooManyBools,
    /// More number parameters are present in the file than this crate knows how to interpret.
    TooManyNumbers,
    /// More string parameters are present in the file than this crate knows how to interpret.
    TooManyStrings,
    /// The length of some field was not >= -1.
    InvalidLength,
    /// The names table was missing a trailing null terminator.
    NamesMissingNull,
    /// The strings table was missing a trailing null terminator.
    StringsMissingNull,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use std::error::Error;
        match *self {
            NotUtf8(e) => write!(f, "{}", e),
            BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v),
            _ => f.write_str(self.description()),
        }
    }
}

impl ::std::convert::From<::std::string::FromUtf8Error> for Error {
    fn from(v: ::std::string::FromUtf8Error) -> Self {
        NotUtf8(v.utf8_error())
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic(..) => "incorrect magic number at start of file",
            ShortNames => "no names exposed, need at least one",
            TooManyBools => "more boolean properties than libterm knows about",
            TooManyNumbers => "more number properties than libterm knows about",
            TooManyStrings => "more string properties than libterm knows about",
            InvalidLength => "invalid length field value, must be >= -1",
            NotUtf8(ref e) => e.description(),
            NamesMissingNull => "names table missing NUL terminator",
            StringsMissingNull => "string table missing NUL terminator",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            NotUtf8(ref e) => Some(e),
            _ => None,
        }
    }
}

pub mod searcher;

/// TermInfo format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
    mod names;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold => "bold",
        Attr::Dim => "dim",
        Attr::Italic(true) => "sitm",
        Attr::Italic(false) => "ritm",
        Attr::Underline(true) => "smul",
        Attr::Underline(false) => "rmul",
        Attr::Blink => "blink",
        Attr::Standout(true) => "smso",
        Attr::Standout(false) => "rmso",
        Attr::Reverse => "rev",
        Attr::Secure => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab",
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
#[derive(Clone, Debug)]
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.ti.apply_cap("setaf", &[Param::Number(color as i32)], &mut self.out);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.ti.apply_cap("setab", &[Param::Number(color as i32)], &mut self.out);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.ti.apply_cap(cap_for_attr(attr), &[], &mut self.out),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.ti.reset(&mut self.out)
    }

    fn supports_reset(&self) -> bool {
        ["sgr0", "sgr", "op"].iter().any(|&cap| self.ti.strings.get(cap).is_some())
    }

    fn supports_color(&self) -> bool {
        self.num_colors > 0 && self.supports_reset()
    }

    fn cursor_up(&mut self) -> Result<()> {
        self.ti.apply_cap("cuu1", &[], &mut self.out)
    }

    fn delete_line(&mut self) -> Result<()> {
        self.ti.apply_cap("dl", &[], &mut self.out)
    }

    fn carriage_return(&mut self) -> Result<()> {
        self.ti.apply_cap("cr", &[], &mut self.out)
    }

    fn get_ref(&self) -> &T {
        &self.out
    }

    fn get_mut(&mut self) -> &mut T {
        &mut self.out
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.out
    }
}

impl<T: Write + Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf") &&
                    terminfo.strings.contains_key("setab") {
            terminfo.numbers.get("colors").map_or(0, |&n| n)
        } else {
            0
        };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color - 8
        } else {
            color
        }
    }
}

impl<T: Write> Write for TerminfoTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.out.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
    }
}
<|endoftext|>


<commit_before>use crate::cell::UnsafeCell;
use crate::collections::VecDeque;
use crate::ffi::c_void;
use crate::ops::{Deref, DerefMut, Drop};
use crate::ptr;
use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering};
use crate::sys::hermit::abi;

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completly fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
    queue: AtomicUsize,
    dequeue: AtomicUsize,
    data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
    dequeue: &'a AtomicUsize,
    data: &'a mut T,
}

impl<T> Spinlock<T> {
    pub const fn new(user_data: T) -> Spinlock<T> {
        Spinlock {
           queue: AtomicUsize::new(0),
           dequeue: AtomicUsize::new(1),
	   data: UnsafeCell::new(user_data),
        }
    }

    #[inline]
    fn obtain_lock(&self) {
        let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
        while self.dequeue.load(Ordering::SeqCst) != ticket {
            spin_loop_hint();
        }
    }

    #[inline]
    pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
        self.obtain_lock();
        SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
    }
}

impl<T: ?Sized + Default> Default for Spinlock<T> {
    fn default() -> Spinlock<T> {
        Spinlock::new(Default::default())
    }
}

impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        &*self.data
    }
}

impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut *self.data
    }
}

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
    /// The dropping of the SpinlockGuard will release the lock it was created from.
    fn drop(&mut self) {
        self.dequeue.fetch_add(1, Ordering::SeqCst);
    }
}

/// Realize a priority queue for tasks
struct PriorityQueue {
    queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
    prio_bitmap: u64,
}

impl PriorityQueue {
    pub const fn new() -> PriorityQueue {
        PriorityQueue {
            queues: [
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None,
            ],
            prio_bitmap: 0,
        }
    }

    /// Add a task id by its priority to the queue
    pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
        let i: usize = prio.into().into();
        self.prio_bitmap |= (1 << i) as u64;
        if let Some(queue) = &mut self.queues[i] {
            queue.push_back(id);
        } else {
            let mut queue = VecDeque::new();
            queue.push_back(id);
            self.queues[i] = Some(queue);
        }
    }

    fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
        if let Some(queue) = &mut self.queues[queue_index] {
            let id = queue.pop_front();

            if queue.is_empty() {
                self.prio_bitmap &= !(1 << queue_index as u64);
            }

            id
        } else {
            None
        }
    }

    /// Pop the task handle with the highest priority from the queue
    pub fn pop(&mut self) -> Option<abi::Tid> {
        for i in 0..abi::NO_PRIORITIES {
            if self.prio_bitmap & (1 << i) != 0 {
                return self.pop_from_queue(i);
            }
        }

        None
    }
}

struct MutexInner {
    locked: bool,
    blocked_task: PriorityQueue,
}

impl MutexInner {
    pub const fn new() -> MutexInner {
        MutexInner { locked: false, blocked_task: PriorityQueue::new() }
    }
}

pub struct Mutex {
    inner: Spinlock<MutexInner>,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex { inner: Spinlock::new(MutexInner::new()) }
    }

    #[inline]
    pub unsafe fn init(&mut self) {
        self.inner = Spinlock::new(MutexInner::new());
    }

    #[inline]
    pub unsafe fn lock(&self) {
        loop {
            let mut guard = self.inner.lock();
            if guard.locked == false {
                guard.locked = true;
                return;
            } else {
                let prio = abi::get_priority();
                let id = abi::getpid();

                guard.blocked_task.push(prio, id);
                abi::block_current_task();
                drop(guard);
                abi::yield_now();
            }
        }
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let mut guard = self.inner.lock();
        guard.locked = false;
        if let Some(tid) = guard.blocked_task.pop() {
            abi::wakeup_task(tid);
        }
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        let mut guard = self.inner.lock();
        if guard.locked == false {
            guard.locked = true;
        }
        guard.locked
    }

    #[inline]
    pub unsafe fn destroy(&self) {}
}

pub struct ReentrantMutex {
    inner: *const c_void,
}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: ptr::null() }
    }

    #[inline]
    pub unsafe fn init(&self) {
        let _ = abi::recmutex_init(&self.inner as *const *const c_void as *mut _);
    }

    #[inline]
    pub unsafe fn lock(&self) {
        let _ = abi::recmutex_lock(self.inner);
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        true
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let _ = abi::recmutex_unlock(self.inner);
    }

    #[inline]
    pub unsafe fn destroy(&self) {
        let _ = abi::recmutex_destroy(self.inner);
    }
}
<commit_msg>revise code to pass the format check<commit_after>use crate::cell::UnsafeCell;
use crate::collections::VecDeque;
use crate::ffi::c_void;
use crate::ops::{Deref, DerefMut, Drop};
use crate::ptr;
use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering};
use crate::sys::hermit::abi;

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completly fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
    queue: AtomicUsize,
    dequeue: AtomicUsize,
    data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
    dequeue: &'a AtomicUsize,
    data: &'a mut T,
}

impl<T> Spinlock<T> {
    pub const fn new(user_data: T) -> Spinlock<T> {
        Spinlock {
            queue: AtomicUsize::new(0),
            dequeue: AtomicUsize::new(1),
            data: UnsafeCell::new(user_data),
        }
    }

    #[inline]
    fn obtain_lock(&self) {
        let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
        while self.dequeue.load(Ordering::SeqCst) != ticket {
            spin_loop_hint();
        }
    }

    #[inline]
    pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
        self.obtain_lock();
        SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
    }
}

impl<T: ?Sized + Default> Default for Spinlock<T> {
    fn default() -> Spinlock<T> {
        Spinlock::new(Default::default())
    }
}

impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        &*self.data
    }
}

impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut *self.data
    }
}

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
    /// The dropping of the SpinlockGuard will release the lock it was created from.
    fn drop(&mut self) {
        self.dequeue.fetch_add(1, Ordering::SeqCst);
    }
}

/// Realize a priority queue for tasks
struct PriorityQueue {
    queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
    prio_bitmap: u64,
}

impl PriorityQueue {
    pub const fn new() -> PriorityQueue {
        PriorityQueue {
            queues: [
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None, None, None, None, None, None, None, None, None, None, None, None,
                None, None, None,
            ],
            prio_bitmap: 0,
        }
    }

    /// Add a task id by its priority to the queue
    pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
        let i: usize = prio.into().into();
        self.prio_bitmap |= (1 << i) as u64;
        if let Some(queue) = &mut self.queues[i] {
            queue.push_back(id);
        } else {
            let mut queue = VecDeque::new();
            queue.push_back(id);
            self.queues[i] = Some(queue);
        }
    }

    fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
        if let Some(queue) = &mut self.queues[queue_index] {
            let id = queue.pop_front();

            if queue.is_empty() {
                self.prio_bitmap &= !(1 << queue_index as u64);
            }

            id
        } else {
            None
        }
    }

    /// Pop the task handle with the highest priority from the queue
    pub fn pop(&mut self) -> Option<abi::Tid> {
        for i in 0..abi::NO_PRIORITIES {
            if self.prio_bitmap & (1 << i) != 0 {
                return self.pop_from_queue(i);
            }
        }

        None
    }
}

struct MutexInner {
    locked: bool,
    blocked_task: PriorityQueue,
}

impl MutexInner {
    pub const fn new() -> MutexInner {
        MutexInner { locked: false, blocked_task: PriorityQueue::new() }
    }
}

pub struct Mutex {
    inner: Spinlock<MutexInner>,
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
    pub const fn new() -> Mutex {
        Mutex { inner: Spinlock::new(MutexInner::new()) }
    }

    #[inline]
    pub unsafe fn init(&mut self) {
        self.inner = Spinlock::new(MutexInner::new());
    }

    #[inline]
    pub unsafe fn lock(&self) {
        loop {
            let mut guard = self.inner.lock();
            if guard.locked == false {
                guard.locked = true;
                return;
            } else {
                let prio = abi::get_priority();
                let id = abi::getpid();

                guard.blocked_task.push(prio, id);
                abi::block_current_task();
                drop(guard);
                abi::yield_now();
            }
        }
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let mut guard = self.inner.lock();
        guard.locked = false;
        if let Some(tid) = guard.blocked_task.pop() {
            abi::wakeup_task(tid);
        }
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        let mut guard = self.inner.lock();
        if guard.locked == false {
            guard.locked = true;
        }
        guard.locked
    }

    #[inline]
    pub unsafe fn destroy(&self) {}
}

pub struct ReentrantMutex {
    inner: *const c_void,
}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: ptr::null() }
    }

    #[inline]
    pub unsafe fn init(&self) {
        let _ = abi::recmutex_init(&self.inner as *const *const c_void as *mut _);
    }

    #[inline]
    pub unsafe fn lock(&self) {
        let _ = abi::recmutex_lock(self.inner);
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        true
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let _ = abi::recmutex_unlock(self.inner);
    }

    #[inline]
    pub unsafe fn destroy(&self) {
        let _ = abi::recmutex_destroy(self.inner);
    }
}
<|endoftext|>


<commit_before>use commands::*;
use database::models::{ToU64, NewTimeout};
use database::schema::timeouts;
use util::parse_duration_secs;

use lalafell::error::*;
use lalafell::commands::prelude::*;

use serenity::builder::CreateEmbed;
use serenity::model::channel::{Message, GuildChannel};
use serenity::model::misc::Mentionable;

use diesel::prelude::*;

use chrono::prelude::*;

use std::sync::Arc;

const USAGE: &str = "!timeout <who> <length>";

#[derive(Default)]
pub struct TimeoutCommand;

#[derive(Debug, Deserialize)]
pub struct Params {
  who: MentionOrId,
  length: Vec<String>
}

impl HasParams for TimeoutCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for TimeoutCommand {
  fn run(&self, _: &Context, message: &Message, guild: GuildId, channel: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;
    let member = guild.member(&message.author).chain_err(|| "could not get member")?;
    if !member.permissions().chain_err(|| "could not get permissions")?.manage_roles() {
      return Err(ExternalCommandFailure::default()
        .message(|e: CreateEmbed| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    let server_id = channel.read().guild_id;
    let who = params.who;

    let mut timeout_member = match guild.member(*who) {
      Ok(m) => m,
      Err(_) => return Err("That user is not in this guild.".into())
    };

    let guild = guild.find().chain_err(|| "could not find guild")?;

    let timeouts = ::bot::CONNECTION.with(|c| {
      use database::schema::timeouts::dsl;
      use diesel::expression::dsl::count;
      dsl::timeouts
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(server_id.to_u64())))
        .select(count(dsl::id))
        .first(c)
        .optional()
        .chain_err(|| "could not load timeouts")
    })?;
    if timeouts.unwrap_or(0) > 0 {
      return Err(format!("{} is already timed out.", who.mention()).into());
    }

    let role_id = match timeout::set_up_timeouts(&guild.read()) {
      Ok(r) => {
        if let Err(e) = timeout_member.add_role(r) {
          warn!("could not add user {} to timeout role: {}", who.0, e);
        }
        r
      },
      Err(e) => {
        warn!("could not set up timeouts for {}: {}", guild.read().id.0, e);
        return Err("Could not set up timeouts for this server. Do I have enough permissions?".into());
      }
    };

    let duration = match parse_duration_secs(&params.length.into_iter().collect::<String>()) {
      Ok(d) => d,
      Err(_) => return Err("Invalid time length. Try \"15m\" or \"3 hours\" for example.".into())
    };

    // TODO: spawn task to remove timeout as soon as it ends (versus 30s repeating task)

    let timeout_user = NewTimeout::new(who.0, server_id.0, role_id.0, duration as i32, Utc::now().timestamp());
    ::bot::CONNECTION.with(|c| ::diesel::insert_into(timeouts::table).values(&timeout_user).execute(c).chain_err(|| "could not insert timeout"))?;
    Ok(CommandSuccess::default())
  }
}
<commit_msg>refactor: use map_err<commit_after>use commands::*;
use database::models::{ToU64, NewTimeout};
use database::schema::timeouts;
use util::parse_duration_secs;

use lalafell::error::*;
use lalafell::commands::prelude::*;

use serenity::builder::CreateEmbed;
use serenity::model::channel::{Message, GuildChannel};
use serenity::model::misc::Mentionable;

use diesel::prelude::*;

use chrono::prelude::*;

use std::sync::Arc;

const USAGE: &str = "!timeout <who> <length>";

#[derive(Default)]
pub struct TimeoutCommand;

#[derive(Debug, Deserialize)]
pub struct Params {
  who: MentionOrId,
  length: Vec<String>
}

impl HasParams for TimeoutCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for TimeoutCommand {
  fn run(&self, _: &Context, message: &Message, guild: GuildId, channel: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;
    let member = guild.member(&message.author).chain_err(|| "could not get member")?;
    if !member.permissions().chain_err(|| "could not get permissions")?.manage_roles() {
      return Err(ExternalCommandFailure::default()
        .message(|e: CreateEmbed| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    let server_id = channel.read().guild_id;
    let who = params.who;

    let mut timeout_member = guild.member(*who).map_err(|_| into!(CommandFailure, "That user is not in this guild."))?;

    let guild = guild.find().chain_err(|| "could not find guild")?;

    let timeouts = ::bot::CONNECTION.with(|c| {
      use database::schema::timeouts::dsl;
      use diesel::expression::dsl::count;
      dsl::timeouts
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(server_id.to_u64())))
        .select(count(dsl::id))
        .first(c)
        .optional()
        .chain_err(|| "could not load timeouts")
    })?;
    if timeouts.unwrap_or(0) > 0 {
      return Err(format!("{} is already timed out.", who.mention()).into());
    }

    let role_id = match timeout::set_up_timeouts(&guild.read()) {
      Ok(r) => {
        if let Err(e) = timeout_member.add_role(r) {
          warn!("could not add user {} to timeout role: {}", who.0, e);
        }
        r
      },
      Err(e) => {
        warn!("could not set up timeouts for {}: {}", guild.read().id.0, e);
        return Err("Could not set up timeouts for this server. Do I have enough permissions?".into());
      }
    };

    let duration = match parse_duration_secs(&params.length.into_iter().collect::<String>()) {
      Ok(d) => d,
      Err(_) => return Err("Invalid time length. Try \"15m\" or \"3 hours\" for example.".into())
    };

    // TODO: spawn task to remove timeout as soon as it ends (versus 30s repeating task)

    let timeout_user = NewTimeout::new(who.0, server_id.0, role_id.0, duration as i32, Utc::now().timestamp());
    ::bot::CONNECTION.with(|c| ::diesel::insert_into(timeouts::table).values(&timeout_user).execute(c).chain_err(|| "could not insert timeout"))?;
    Ok(CommandSuccess::default())
  }
}
<|endoftext|>


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

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

use clap::{App, Arg};
use std::path::Path;
use std::result;
use std::str::FromStr;

use hcore::package::PackageIdent;
use url::Url;

/// The version of this library and program when built.
pub const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/VERSION"));

arg_enum!{
    #[derive(Debug)]
    pub enum RegistryType {
        Amazon,
        Docker
    }
}

/// A Docker-specific clap:App wrapper
#[derive(Clone)]
pub struct Cli<'a, 'b>
where
    'a: 'b,
{
    pub app: App<'a, 'b>,
}

impl<'a, 'b> Cli<'a, 'b> {
    pub fn new(name: &str, about: &'a str) -> Self {
        Cli {
            app: clap_app!(
            (name) =>
            (about: about)
            (version: VERSION)
            (author: "\nAuthors: The Habitat Maintainers <humans@habitat.sh>\n\n")
            (@arg IMAGE_NAME: --("image-name") -i +takes_value
                "Image name (default: \"{{pkg_origin}}/{{pkg_name}}\" supports: \
                 {{pkg_origin}}, {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})")
            ),
        }
    }

    pub fn add_base_packages_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("HAB_PKG")
                    .long("hab-pkg")
                    .value_name("HAB_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Habitat CLI package identifier (ex: acme/redis) or filepath to a Habitat \
                        artifact (ex: /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart) to \
                        install (default: core/hab)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_LAUNCHER_PKG")
                    .long("launcher-pkg")
                    .value_name("HAB_LAUNCHER_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Launcher package identifier (ex: core/hab-launcher) or filepath to a \
                        Habitat artifact (ex: \
                        /home/core-hab-launcher-6083-20171101045646-x86_64-linux.hart) to install \
                        (default: core/hab-launcher)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_SUP_PKG")
                    .long("sup-pkg")
                    .value_name("HAB_SUP_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Supervisor package identifier (ex: core/hab-sup) or filepath to a Habitat \
                        artifact (ex: \
                        /home/ore-hab-sup-0.39.1-20171118011657-x86_64-linux.hart) to install \
                        (default: core/hab-sup)",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_builder_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("BLDR_URL")
                    .long("url")
                    .short("u")
                    .value_name("BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install packages from Builder at the specified URL \
                        (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("CHANNEL")
                    .long("channel")
                    .short("c")
                    .value_name("CHANNEL")
                    .help(
                        "Install packages from the specified release channel (default: stable)",
                    ),
            )
            .arg(
                Arg::with_name("BASE_PKGS_BLDR_URL")
                    .long("base-pkgs-url")
                    .value_name("BASE_PKGS_BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install base packages from Builder at the specified URL \
                        (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("BASE_PKGS_CHANNEL")
                    .long("base-pkgs-channel")
                    .value_name("BASE_PKGS_CHANNEL")
                    .help(
                        "Install base packages from the specified release channel \
                        (default: stable)",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_image_customization_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("USER_ID")
                    .long("user-id")
                    .short("i")
                    .value_name("USER_ID")
                    .validator(valid_user_id)
                    .help(
                        "Specify the numeric ID of the service user and group (default: 42)",
                    ),
            )
            .arg(Arg::with_name("NON_ROOT").long("non-root").help(
                "Run the container as a non-root user (default: false)",
            ));
        Cli { app: app }
    }

    pub fn add_tagging_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("TAG_VERSION_RELEASE")
                    .long("tag-version-release")
                    .conflicts_with("NO_TAG_VERSION_RELEASE")
                    .help(
                        "Tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: yes)",
                    ),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION_RELEASE")
                    .long("no-tag-version-release")
                    .conflicts_with("TAG_VERSION_RELEASE")
                    .help(
                        "Do not tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: no)",
                    ),
            )
            .arg(
                Arg::with_name("TAG_VERSION")
                    .long("tag-version")
                    .conflicts_with("NO_TAG_VERSION")
                    .help("Tag image with :\"{{pkg_version}}\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION")
                    .long("no-tag-version")
                    .conflicts_with("TAG_VERSION")
                    .help("Do not tag image with :\"{{pkg_version}}\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_LATEST")
                    .long("tag-latest")
                    .conflicts_with("NO_TAG_LATEST")
                    .help("Tag image with :\"latest\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_LATEST")
                    .long("no-tag-latest")
                    .conflicts_with("TAG_LATEST")
                    .help("Do not tag image with :\"latest\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_CUSTOM")
                    .long("tag-custom")
                    .value_name("TAG_CUSTOM")
                    .help(
                        "Tag image with additional custom tag (supports: {{pkg_origin}}, \
                        {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_publishing_args(self) -> Self {
        let app = self.app.arg(Arg::with_name("PUSH_IMAGE")
                 .long("push-image")
                 .conflicts_with("NO_PUSH_IMAGE")
                 .requires_all(&["REGISTRY_USERNAME", "REGISTRY_PASSWORD"])
                 .help("Push image to remote registry (default: no)"))
            .arg(Arg::with_name("NO_PUSH_IMAGE")
                 .long("no-push-image")
                 .conflicts_with("PUSH_IMAGE")
                 .help("Do not push image to remote registry (default: yes)"))
            .arg(Arg::with_name("REGISTRY_USERNAME")
                 .long("username")
                 .short("U")
                 .value_name("REGISTRY_USERNAME")
                 .requires("REGISTRY_PASSWORD")
                 .help("Remote registry username, required for pushing image to remote registry"))
            .arg(Arg::with_name("REGISTRY_PASSWORD")
                 .long("password")
                 .short("P")
                 .value_name("REGISTRY_PASSWORD")
                 .requires("REGISTRY_USERNAME")
                 .help("Remote registry password, required for pushing image to remote registry"))
            .arg(Arg::with_name("REGISTRY_TYPE")
                 .possible_values(&RegistryType::variants())
                 .case_insensitive(true)
                 .long("registry-type")
                 .short("R")
                 .value_name("REGISTRY_TYPE")
                 .help("Remote registry type, Ex: Amazon, Docker (default: docker)"))
            .arg(Arg::with_name("REGISTRY_URL")
                 // This is not strictly a requirement but will keep someone from making a mistake
                 // when inputing an ECR URL
                 .requires("REGISTRY_TYPE")
                 .long("registry-url")
                 .short("G")
                 .value_name("REGISTRY_URL")
                 .help("Remote registry url"))
            // Cleanup
            .arg(Arg::with_name("RM_IMAGE")
                 .long("rm-image")
                 .help("Remove local image from engine after build and/or push (default: no)"));

        Cli { app: app }
    }
}

fn valid_ident_or_hart(val: String) -> result::Result<(), String> {
    if Path::new(&val).is_file() {
        Ok(())
    } else if val.ends_with(".hart") {
        Err(format!("Habitat artifact file: '{}' not found", &val))
    } else {
        match PackageIdent::from_str(&val) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{}", e)),
        }
    }
}

fn valid_url(val: String) -> result::Result<(), String> {
    match Url::parse(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("URL: '{}' is not valid", &val)),
    }
}

fn valid_user_id(val: String) -> result::Result<(), String> {
    match val.parse::<u32>() {
        Ok(_) => {
            // TODO (CM): need to filter out problematic
            // values (e.g., 0, existing user/group IDs)
            Ok(())
        }
        Err(_) => Err(format!("User ID: '{}' is not valid", &val)),
    }
}
<commit_msg>pkg-export-docker: Unique short for --user-id<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

use clap::{App, Arg};
use std::path::Path;
use std::result;
use std::str::FromStr;

use hcore::package::PackageIdent;
use url::Url;

/// The version of this library and program when built.
pub const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/VERSION"));

arg_enum!{
    #[derive(Debug)]
    pub enum RegistryType {
        Amazon,
        Docker
    }
}

/// A Docker-specific clap:App wrapper
#[derive(Clone)]
pub struct Cli<'a, 'b>
where
    'a: 'b,
{
    pub app: App<'a, 'b>,
}

impl<'a, 'b> Cli<'a, 'b> {
    pub fn new(name: &str, about: &'a str) -> Self {
        Cli {
            app: clap_app!(
            (name) =>
            (about: about)
            (version: VERSION)
            (author: "\nAuthors: The Habitat Maintainers <humans@habitat.sh>\n\n")
            (@arg IMAGE_NAME: --("image-name") -i +takes_value
                "Image name (default: \"{{pkg_origin}}/{{pkg_name}}\" supports: \
                 {{pkg_origin}}, {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})")
            ),
        }
    }

    pub fn add_base_packages_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("HAB_PKG")
                    .long("hab-pkg")
                    .value_name("HAB_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Habitat CLI package identifier (ex: acme/redis) or filepath to a Habitat \
                        artifact (ex: /home/acme-redis-3.0.7-21120102031201-x86_64-linux.hart) to \
                        install (default: core/hab)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_LAUNCHER_PKG")
                    .long("launcher-pkg")
                    .value_name("HAB_LAUNCHER_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Launcher package identifier (ex: core/hab-launcher) or filepath to a \
                        Habitat artifact (ex: \
                        /home/core-hab-launcher-6083-20171101045646-x86_64-linux.hart) to install \
                        (default: core/hab-launcher)",
                    ),
            )
            .arg(
                Arg::with_name("HAB_SUP_PKG")
                    .long("sup-pkg")
                    .value_name("HAB_SUP_PKG")
                    .validator(valid_ident_or_hart)
                    .help(
                        "Supervisor package identifier (ex: core/hab-sup) or filepath to a Habitat \
                        artifact (ex: \
                        /home/ore-hab-sup-0.39.1-20171118011657-x86_64-linux.hart) to install \
                        (default: core/hab-sup)",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_builder_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("BLDR_URL")
                    .long("url")
                    .short("u")
                    .value_name("BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install packages from Builder at the specified URL \
                        (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("CHANNEL")
                    .long("channel")
                    .short("c")
                    .value_name("CHANNEL")
                    .help(
                        "Install packages from the specified release channel (default: stable)",
                    ),
            )
            .arg(
                Arg::with_name("BASE_PKGS_BLDR_URL")
                    .long("base-pkgs-url")
                    .value_name("BASE_PKGS_BLDR_URL")
                    .validator(valid_url)
                    .help(
                        "Install base packages from Builder at the specified URL \
                        (default: https://bldr.habitat.sh)",
                    ),
            )
            .arg(
                Arg::with_name("BASE_PKGS_CHANNEL")
                    .long("base-pkgs-channel")
                    .value_name("BASE_PKGS_CHANNEL")
                    .help(
                        "Install base packages from the specified release channel \
                        (default: stable)",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_image_customization_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("USER_ID")
                    .long("user-id")
                    .short("s")
                    .value_name("USER_ID")
                    .validator(valid_user_id)
                    .help(
                        "Specify the numeric ID of the service user and group (default: 42)",
                    ),
            )
            .arg(Arg::with_name("NON_ROOT").long("non-root").help(
                "Run the container as a non-root user (default: false)",
            ));
        Cli { app: app }
    }

    pub fn add_tagging_args(self) -> Self {
        let app = self.app
            .arg(
                Arg::with_name("TAG_VERSION_RELEASE")
                    .long("tag-version-release")
                    .conflicts_with("NO_TAG_VERSION_RELEASE")
                    .help(
                        "Tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: yes)",
                    ),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION_RELEASE")
                    .long("no-tag-version-release")
                    .conflicts_with("TAG_VERSION_RELEASE")
                    .help(
                        "Do not tag image with :\"{{pkg_version}}-{{pkg_release}}\" (default: no)",
                    ),
            )
            .arg(
                Arg::with_name("TAG_VERSION")
                    .long("tag-version")
                    .conflicts_with("NO_TAG_VERSION")
                    .help("Tag image with :\"{{pkg_version}}\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_VERSION")
                    .long("no-tag-version")
                    .conflicts_with("TAG_VERSION")
                    .help("Do not tag image with :\"{{pkg_version}}\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_LATEST")
                    .long("tag-latest")
                    .conflicts_with("NO_TAG_LATEST")
                    .help("Tag image with :\"latest\" (default: yes)"),
            )
            .arg(
                Arg::with_name("NO_TAG_LATEST")
                    .long("no-tag-latest")
                    .conflicts_with("TAG_LATEST")
                    .help("Do not tag image with :\"latest\" (default: no)"),
            )
            .arg(
                Arg::with_name("TAG_CUSTOM")
                    .long("tag-custom")
                    .value_name("TAG_CUSTOM")
                    .help(
                        "Tag image with additional custom tag (supports: {{pkg_origin}}, \
                        {{pkg_name}}, {{pkg_version}}, {{pkg_release}}, {{channel}})",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_publishing_args(self) -> Self {
        let app = self.app.arg(Arg::with_name("PUSH_IMAGE")
                 .long("push-image")
                 .conflicts_with("NO_PUSH_IMAGE")
                 .requires_all(&["REGISTRY_USERNAME", "REGISTRY_PASSWORD"])
                 .help("Push image to remote registry (default: no)"))
            .arg(Arg::with_name("NO_PUSH_IMAGE")
                 .long("no-push-image")
                 .conflicts_with("PUSH_IMAGE")
                 .help("Do not push image to remote registry (default: yes)"))
            .arg(Arg::with_name("REGISTRY_USERNAME")
                 .long("username")
                 .short("U")
                 .value_name("REGISTRY_USERNAME")
                 .requires("REGISTRY_PASSWORD")
                 .help("Remote registry username, required for pushing image to remote registry"))
            .arg(Arg::with_name("REGISTRY_PASSWORD")
                 .long("password")
                 .short("P")
                 .value_name("REGISTRY_PASSWORD")
                 .requires("REGISTRY_USERNAME")
                 .help("Remote registry password, required for pushing image to remote registry"))
            .arg(Arg::with_name("REGISTRY_TYPE")
                 .possible_values(&RegistryType::variants())
                 .case_insensitive(true)
                 .long("registry-type")
                 .short("R")
                 .value_name("REGISTRY_TYPE")
                 .help("Remote registry type, Ex: Amazon, Docker (default: docker)"))
            .arg(Arg::with_name("REGISTRY_URL")
                 // This is not strictly a requirement but will keep someone from making a mistake
                 // when inputing an ECR URL
                 .requires("REGISTRY_TYPE")
                 .long("registry-url")
                 .short("G")
                 .value_name("REGISTRY_URL")
                 .help("Remote registry url"))
            // Cleanup
            .arg(Arg::with_name("RM_IMAGE")
                 .long("rm-image")
                 .help("Remove local image from engine after build and/or push (default: no)"));

        Cli { app: app }
    }
}

fn valid_ident_or_hart(val: String) -> result::Result<(), String> {
    if Path::new(&val).is_file() {
        Ok(())
    } else if val.ends_with(".hart") {
        Err(format!("Habitat artifact file: '{}' not found", &val))
    } else {
        match PackageIdent::from_str(&val) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{}", e)),
        }
    }
}

fn valid_url(val: String) -> result::Result<(), String> {
    match Url::parse(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("URL: '{}' is not valid", &val)),
    }
}

fn valid_user_id(val: String) -> result::Result<(), String> {
    match val.parse::<u32>() {
        Ok(_) => {
            // TODO (CM): need to filter out problematic
            // values (e.g., 0, existing user/group IDs)
            Ok(())
        }
        Err(_) => Err(format!("User ID: '{}' is not valid", &val)),
    }
}
<|endoftext|>


<commit_before>use libc::{c_int, time_t, size_t};
use data_structs::{JTM};
use jalali_bindings::*;
use std::ffi::CString;
use std::{mem, ptr};

pub struct JTMBuilder {
  pub tm_sec: i32,
  pub tm_min: i32,
  pub tm_hour: i32,
  pub tm_mday: i32,
  pub tm_mon: i32,
  pub tm_year: i32,
  pub tm_wday: i32,
  pub tm_yday: i32,
  pub tm_isdst: i32,
  pub tm_gmtoff: i64,
  pub tm_zone: String,
}

impl JTMBuilder {
  pub fn new() -> Self {
    JTMBuilder {
      tm_sec: 0,
      tm_min: 0,
      tm_hour: 0,
      tm_mday: 0,
      tm_mon: 0,
      tm_year: 0,
      tm_wday: 0,
      tm_yday: 0,
      tm_isdst: 0,
      tm_gmtoff: 0,
      tm_zone: "".to_string(),
    }
  }

  pub fn set_sec(&mut self, sec: i32) -> &mut Self {
    self.tm_sec = sec;
    self
  }

  pub fn set_min(&mut self, min: i32) -> &mut Self {
    self.tm_min = min;
    self
  }

  pub fn set_hour(&mut self, hour: i32) -> &mut Self {
    self.tm_hour = hour;
    self
  }

  pub fn set_mday(&mut self, mday: i32) -> &mut Self {
    self.tm_mday = mday;
    self
  }

  pub fn set_mon(&mut self, mon: i32) -> &mut Self {
    self.tm_mon = mon;
    self
  }

  pub fn set_year(&mut self, year: i32) -> &mut Self {
    self.tm_year = year;
    self
  }

  pub fn set_wday(&mut self, wday: i32) -> &mut Self {
    self.tm_wday = wday;
    self
  }

  pub fn set_yday(&mut self, yday: i32) -> &mut Self {
    self.tm_yday = yday;
    self
  }

  pub fn set_isdst(&mut self, isdst: i32) -> &mut Self {
    self.tm_isdst = isdst;
    self
  }

  pub fn set_gmtoff(&mut self, gmtoff: i64) -> &mut Self {
    self.tm_gmtoff = gmtoff;
    self
  }

  pub fn set_zone(&mut self, zone: String) -> &mut Self {
    self.tm_zone = zone;
    self
  }

  pub fn build(&self) -> JTM {
    JTM {
      tm_sec: self.tm_sec,
      tm_min: self.tm_min,
      tm_hour: self.tm_hour,
      tm_mday: self.tm_mday,
      tm_mon: self.tm_mon,
      tm_year: self.tm_year,
      tm_wday: self.tm_wday,
      tm_yday: self.tm_yday,
      tm_isdst: self.tm_isdst,
      tm_gmtoff: self.tm_gmtoff,
      tm_zone: CString::new(self.tm_zone.clone().into_bytes()).
                                                 unwrap().
                                                 into_raw(),
    }
  }
}

impl JTM {
  pub fn from_secs(secs: i64) -> Self {
    let mut result: Self;
    unsafe {
      result = mem::uninitialized();
      jlocaltime_r(&secs as *const time_t, &mut result);
    }
    result
  }

  pub fn from_days(days: i32) -> Self {
    let mut result: Self;
    unsafe {
      result = mem::uninitialized();
      jalali_get_date(days as c_int, &mut result);
    }
    result
  }

  pub fn from_secs_to_utc(secs: i64) -> Self {
    let mut result: Self;
    unsafe {
      result = mem::uninitialized();
      jgmtime_r(&secs as *const time_t, &mut result);
    }
    result
  }

  pub fn from_formatted_string(s: &String, format: &String) -> Result<Self,()> {
    let mut tmp_s = s.clone();
    let mut tmp_f = format.clone();
    tmp_f.push('\0');
    tmp_s.push('\0');
    let mut result: Self;
    let tmp;
    unsafe {
      result = mem::uninitialized();
      tmp = jstrptime(tmp_s.as_ptr() as *const i8,
                      tmp_f.as_ptr() as *const i8,
                      &mut result);
    }
    result.update();
    if tmp.is_null() {
      Err(())
    } else {
      Ok(result)
    }
  }

  pub fn get_timezone(&self) -> String {
    let mut tst = String::with_capacity(5);
    self.make_formated_string(&"%Z".to_string(), &mut tst);
    tst
  }

  pub fn create_date_from_days(&mut self) -> Result<(),()> {
    let tmp = unsafe {
      jalali_create_date_from_days(self)
    };
    match tmp {
      -1 => Err(()),
      _ => Ok(()),
    }
  }

  pub fn create_days_from_date(&mut self) -> Result<(),()> {
    let tmp = unsafe {
      jalali_create_days_from_date(self)
    };
    match tmp {
      -1 => Err(()),
      _ => Ok(()),
    }
  }

  pub fn get_days_since_epoch(&self) -> i32 {
    unsafe {
      jalali_get_diff(self)
    }
  }

  pub fn update(&mut self) {
    unsafe {
      jalali_update(self);
    }
  }

  pub fn show_time(&self) {
    unsafe {
      jalali_show_time(self);
    }
  }

  pub fn to_string(&self) -> String {
    let mut buff = Vec::<u8>::with_capacity(100);
    let result: String;
    unsafe {
      jasctime_r(self, buff.as_mut_ptr() as *mut i8);
      result = CString::from_raw(buff.as_mut_ptr() as *mut i8).into_string().unwrap();
    }
    mem::forget(buff);
    return result;
  }

  pub fn to_secs(&mut self) -> i64 {
    unsafe {
      jmktime(self)
    }
  }

  pub fn make_formated_string(&self, format: &String, buff: &mut String) -> usize {
    let mut b = String::with_capacity(buff.capacity()).into_bytes();
    let retval: usize;
    unsafe {
      let f = format.as_ptr() as *const i8;
      let max = b.capacity() as size_t;
      retval = jstrftime(b.as_mut_ptr() as *mut i8, max, f, self);
      let tmp = CString::from_raw(b.as_mut_ptr() as *mut i8).into_string().unwrap();
      ptr::copy_nonoverlapping(&tmp, buff, 1);
      mem::forget(tmp);
    }
    mem::forget(b);
    return retval;
  }
}
<commit_msg>write documentations for wrappers::jtm module<commit_after>//! This module holds the definitions of JTMBuilder and
//! JTM related bindings.

use libc::{c_int, time_t, size_t};
use data_structs::{JTM};
use jalali_bindings::*;
use std::ffi::CString;
use std::{mem, ptr};

/// This struct serves as a builder for JTM struct.
///
/// # Examples
///
/// ```
/// extern crate jalali;
///
/// jalali::JTMBuilder::new().set_sec(11).
///                           set_min(22).
///                           set_hour(33).
///                           set_mday(44).
///                           set_mon(55).
///                           set_year(66).
///                           set_wday(77).
///                           set_yday(88).
///                           set_isdst(1).
///                           set_gmtoff(99).
///                           set_zone("IRST".to_string()).
///                           build();
///
/// ```
pub struct JTMBuilder {
  pub tm_sec: i32,
  pub tm_min: i32,
  pub tm_hour: i32,
  pub tm_mday: i32,
  pub tm_mon: i32,
  pub tm_year: i32,
  pub tm_wday: i32,
  pub tm_yday: i32,
  pub tm_isdst: i32,
  pub tm_gmtoff: i64,
  pub tm_zone: String,
}

impl JTMBuilder {
  /// This function initializes a JTMBuilder struct.
  ///
  pub fn new() -> Self {
    JTMBuilder {
      tm_sec: 0,
      tm_min: 0,
      tm_hour: 0,
      tm_mday: 0,
      tm_mon: 0,
      tm_year: 0,
      tm_wday: 0,
      tm_yday: 0,
      tm_isdst: 0,
      tm_gmtoff: 0,
      tm_zone: "".to_string(),
    }
  }

  /// This method sets tm_sec of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `sec` - A 32 bit integer providing the value for *tm_sec*.
  ///
  pub fn set_sec(&mut self, sec: i32) -> &mut Self {
    self.tm_sec = sec;
    self
  }

  /// This method sets tm_min of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `min` - A 32 bit integer providing the value for *tm_min*.
  ///
  pub fn set_min(&mut self, min: i32) -> &mut Self {
    self.tm_min = min;
    self
  }

  /// This method sets tm_hour of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `hour` - A 32 bit integer providing the value for *tm_hour*.
  ///
  pub fn set_hour(&mut self, hour: i32) -> &mut Self {
    self.tm_hour = hour;
    self
  }

  /// This method sets tm_mday of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `mday` - A 32 bit integer providing the value for *tm_mday*.
  ///
  pub fn set_mday(&mut self, mday: i32) -> &mut Self {
    self.tm_mday = mday;
    self
  }

  /// This method sets tm_mon of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `mon` - A 32 bit integer providing the value for *tm_mon*.
  ///
  pub fn set_mon(&mut self, mon: i32) -> &mut Self {
    self.tm_mon = mon;
    self
  }

  /// This method sets tm_year of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `year` - A 32 bit integer providing the value for *tm_year*.
  ///
  pub fn set_year(&mut self, year: i32) -> &mut Self {
    self.tm_year = year;
    self
  }

  /// This method sets tm_wday of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `wday` - A 32 bit integer providing the value for *tm_wday*.
  ///
  pub fn set_wday(&mut self, wday: i32) -> &mut Self {
    self.tm_wday = wday;
    self
  }

  /// This method sets tm_yday of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `yday` - A 32 bit integer providing the value for *tm_yday*.
  ///
  pub fn set_yday(&mut self, yday: i32) -> &mut Self {
    self.tm_yday = yday;
    self
  }

  /// This method sets tm_isdst of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `isdst` - A 32 bit integer providing the value for *tm_isdst*.
  ///
  pub fn set_isdst(&mut self, isdst: i32) -> &mut Self {
    self.tm_isdst = isdst;
    self
  }

  /// This method sets tm_gmtoff of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `gmtoff` - A 64 bit integer providing the value for *tm_gmtoff*.
  ///
  pub fn set_gmtoff(&mut self, gmtoff: i64) -> &mut Self {
    self.tm_gmtoff = gmtoff;
    self
  }

  /// This method sets tm_zone of a JTMBuilder struct.
  ///
  /// # Arguments
  ///
  /// * `zone` - A String providing the value for *tm_zone*.
  ///
  pub fn set_zone(&mut self, zone: String) -> &mut Self {
    self.tm_zone = zone;
    self
  }

  /// This method builds a JTM and returns it.
  ///
  pub fn build(&self) -> JTM {
    JTM {
      tm_sec: self.tm_sec,
      tm_min: self.tm_min,
      tm_hour: self.tm_hour,
      tm_mday: self.tm_mday,
      tm_mon: self.tm_mon,
      tm_year: self.tm_year,
      tm_wday: self.tm_wday,
      tm_yday: self.tm_yday,
      tm_isdst: self.tm_isdst,
      tm_gmtoff: self.tm_gmtoff,
      tm_zone: CString::new(self.tm_zone.clone().into_bytes()).
                                                 unwrap().
                                                 into_raw(),
    }
  }
}

impl JTM {
  /// This function initializes a JTM from seconds passed since Epoch.
  ///
  /// # Arguments
  ///
  /// * `secs` - A 64 bit integer representing seconds passed since Epoch.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// jalali::JTM::from_secs(719425800);
  ///
  /// ```
  pub fn from_secs(secs: i64) -> Self {
    let mut result: Self;
    unsafe {
      result = mem::uninitialized();
      jlocaltime_r(&secs as *const time_t, &mut result);
    }
    result
  }

  /// This function initializes a JTM from days passed since Epoch.
  ///
  /// # Arguments
  ///
  /// * `days` - A 32 bit integer representing days passed since Epoch.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// jalali::JTM::from_days(8326);
  ///
  /// ```
  pub fn from_days(days: i32) -> Self {
    let mut result: Self;
    unsafe {
      result = mem::uninitialized();
      jalali_get_date(days as c_int, &mut result);
    }
    result
  }

  /// This function initializes a JTM from seconds passed since Epoch expressed in Coordinated Universal Time (UTC).
  ///
  /// # Arguments
  ///
  /// * `secs` - A 64 bit integer representing seconds passed since Epoch.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// jalali::JTM::from_days(8326);
  ///
  /// ```
  pub fn from_secs_to_utc(secs: i64) -> Self {
    let mut result: Self;
    unsafe {
      result = mem::uninitialized();
      jgmtime_r(&secs as *const time_t, &mut result);
    }
    result
  }

  /// This function initializes a JTM from a string with a special format.
  ///
  /// # Arguments
  ///
  /// * `s` - A string representing time.
  /// * `format` - A string representing format of *s* (see http://www.nongnu.org/jcal/jstrptime.html).
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let tmp1 = "26/07/1371 19:59:59".to_string();
  /// let tmp2 = "%d/%m/%Y %H:%M:%S".to_string();
  /// jalali::JTM::from_formatted_string(&tmp1, &tmp2).unwrap();
  ///
  /// ```
  pub fn from_formatted_string(s: &String, format: &String) -> Result<Self,()> {
    let mut tmp_s = s.clone();
    let mut tmp_f = format.clone();
    tmp_f.push('\0');
    tmp_s.push('\0');
    let mut result: Self;
    let tmp;
    unsafe {
      result = mem::uninitialized();
      tmp = jstrptime(tmp_s.as_ptr() as *const i8,
                      tmp_f.as_ptr() as *const i8,
                      &mut result);
    }
    result.update();
    if tmp.is_null() {
      Err(())
    } else {
      Ok(result)
    }
  }

  /// This method initializes a JTM from a string with a special format.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let tmp = jalali::JTM::from_days(8326);
  /// assert_eq!(tmp.get_timezone(), "IRST".to_string());
  ///
  /// ```
  pub fn get_timezone(&self) -> String {
    let mut tst = String::with_capacity(5);
    self.make_formated_string(&"%Z".to_string(), &mut tst);
    tst
  }

  /// This method alters tm_mon and tm_mday fields of the broken-down
  /// jalali time strucutre based on it's tm_yday field.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let mut j = jalali::JTMBuilder::new().set_yday(211).build();
  /// j.create_date_from_days().unwrap();
  /// assert_eq!(j.tm_mon, 6);
  /// assert_eq!(j.tm_mday, 26);
  ///
  /// ```
  pub fn create_date_from_days(&mut self) -> Result<(),()> {
    let tmp = unsafe {
      jalali_create_date_from_days(self)
    };
    match tmp {
      -1 => Err(()),
      _ => Ok(()),
    }
  }

  /// This method alters tm_yday field of the broken-down jalali
  /// time structure based on it's tm_mon and tm_mday fields.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let mut j = jalali::JTMBuilder::new().set_mday(26).set_mon(6).build();
  /// j.create_days_from_date().unwrap();
  /// assert_eq!(j.tm_yday, 211);
  ///
  /// ```
  pub fn create_days_from_date(&mut self) -> Result<(),()> {
    let tmp = unsafe {
      jalali_create_days_from_date(self)
    };
    match tmp {
      -1 => Err(()),
      _ => Ok(()),
    }
  }

  /// This method calculates the number of days passed since UTC
  /// Epoch based on a broken-down jalali time structure supplied to it.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let tmp = jalali::JTM::from_secs_to_utc(719425800);
  /// assert_eq!(tmp.get_days_since_epoch(), 8326);
  ///
  /// ```
  pub fn get_days_since_epoch(&self) -> i32 {
    unsafe {
      jalali_get_diff(self)
    }
  }

  /// This method updates tm_wday and tm_yday fields of the broken-down
  /// jalali time structure based on it's tm_year, tm_mon, tm_mday,
  /// tm_hour, tm_min and tm_sec fields. If structure members are outside
  /// their valid interval, they will be normalized (so that, for example,
  /// 40 Bahman is changed into 10 Esfand). tm_isdst, tm_gmtoff and tm_zone
  /// fields are set accordingly.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let mut j = jalali::JTMBuilder::new().set_sec(59).
  ///                                        set_min(59).
  ///                                        set_hour(19).
  ///                                        set_mday(26).
  ///                                        set_mon(6).
  ///                                        set_year(1371).
  ///                                        set_isdst(0).
  ///                                        set_gmtoff(12600).
  ///                                        set_zone("IRST".to_string()).
  ///                                        build();
  /// j.update();
  /// assert_eq!(j.tm_wday, 1);
  /// assert_eq!(j.tm_yday, 211);
  ///
  /// ```
  pub fn update(&mut self) {
    unsafe {
      jalali_update(self);
    }
  }

  /// This method writes a string representation of the struct
  /// to standard output.
  ///
  pub fn show_time(&self) {
    unsafe {
      jalali_show_time(self);
    }
  }

  /// This method returns a string representation of the struct.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let mut j = jalali::JTM::from_secs_to_utc(719425800);
  /// assert_eq!(j.to_string(), "Yek Meh 26 16:30:00 1371\n".to_string());
  ///
  /// ```
  pub fn to_string(&self) -> String {
    let mut buff = Vec::<u8>::with_capacity(100);
    let result: String;
    unsafe {
      jasctime_r(self, buff.as_mut_ptr() as *mut i8);
      result = CString::from_raw(buff.as_mut_ptr() as *mut i8).into_string().unwrap();
    }
    mem::forget(buff);
    return result;
  }

  /// This method converts a broken-down jalali time structure,
  /// expressed as local time, to calendar time representation.
  /// It ignores the values supplied by the caller in the tm_wday field.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let mut j = jalali::JTM::from_secs(719425800);
  /// assert_eq!(j.to_secs(), 719425800);
  ///
  /// ```
  pub fn to_secs(&mut self) -> i64 {
    unsafe {
      jmktime(self)
    }
  }

  /// This method returns a string representation of the struct
  /// based on a supplied *format*.
  /// It returns the number of characters placed in *buff*.
  /// If *buff* is not long enough, it returns max.
  ///
  /// # Arguments
  ///
  /// * `format` - A string representing format of *buff* (see http://www.nongnu.org/jcal/jstrftime.html).
  /// * `buff` - A string serving as a buffer. It must be long enough to hold the resulting string.
  ///
  /// # Examples
  ///
  /// ```
  /// extern crate jalali;
  ///
  /// let f = "%H:%M".to_string();
  /// let mut buffer = String::with_capacity(5);
  /// let mut j = jalali::JTM::from_secs(719425800);
  /// j.make_formated_string(&f, &mut buffer);
  /// assert_eq!(buffer, "20:0".to_string());
  ///
  /// ```
  pub fn make_formated_string(&self, format: &String, buff: &mut String) -> usize {
    let mut b = String::with_capacity(buff.capacity()).into_bytes();
    let retval: usize;
    unsafe {
      let f = format.as_ptr() as *const i8;
      let max = b.capacity() as size_t;
      retval = jstrftime(b.as_mut_ptr() as *mut i8, max, f, self);
      let tmp = CString::from_raw(b.as_mut_ptr() as *mut i8).into_string().unwrap();
      ptr::copy_nonoverlapping(&tmp, buff, 1);
      mem::forget(tmp);
    }
    mem::forget(b);
    return retval;
  }
}
<|endoftext|>