<commit_before>use token::Token;
use logger::Logger;
use error::{Error, Result, Source};

use std::collections::HashMap;
use std::path::{Path,PathBuf};
use std::borrow::Cow;
use std::fs;
use std::io::Read;

use walkdir::WalkDir;
use rustc_serialize::base64::{self, ToBase64};
use mime_guess;

/// Resource Handler.
///
/// Its task is to make sure that some resource (image, link) is available
/// for the book and to list images used in Markdown files so they can be used for the book
#[derive(Debug)]
pub struct ResourceHandler<'r> {
    /// Maps an original url (e.g.) "foo/Readme.md" to a valid link
    /// (e.g.) chapter3.html
    links: HashMap<String, String>,
    map_images: bool,
    logger: &'r Logger,
    base64: bool,

    /// Maps an original (local) file name to a new file name. Allows to
    /// make sure all image files will be included in e.g. the Epub document.
    #[doc(hidden)]
    pub images: HashMap<String, String>,
}

impl<'r> ResourceHandler<'r> {
    /// Creates a new, empty Resource Handler
    pub fn new(logger: &'r Logger) -> ResourceHandler {
        ResourceHandler {
            links: HashMap::new(),
            images: HashMap::new(),
            map_images: false,
            base64: false,
            logger: logger,
        }
    }

    /// Turns on mapping for image files
    ///
    /// # Argument: an offset (should be book.root)
    pub fn set_images_mapping(&mut self, b: bool) {
        self.map_images = b;
    }

    /// Sets base64 mode for image mapping
    ///
    /// If set to true, instead of returning a destination file path,
    /// `map_image` will include the image as base64
    pub fn set_base64(&mut self, b: bool) {
        self.base64 = b;
    }

    /// Add a local image file and get the resulting transformed
    /// file name
    pub fn map_image<'a, S: Into<Cow<'a, str>>>(&'a mut self, source: &Source, file: S) -> Result<Cow<'a, str>> {
        // If image is not local, do nothing much
        let file = file.into();
        if !Self::is_local(file.as_ref()) {
            self.logger.warning(format!("Resources: book includes non-local image {}, which might cause problem for proper inclusion.", file));
            return Ok(file);
        }

        // Check exisence of the file
        if fs::metadata(file.as_ref()).is_err() {
            return Err(Error::file_not_found(source,
                                             "image",
                                             format!("{}", file)));
        }

        // if image mapping is not activated do nothing else
        if !self.map_images {
            return Ok(file);
        }

        // If this image has already been registered, returns it
        if self.images.contains_key(file.as_ref()) {
            return Ok(Cow::Borrowed(self.images.get(file.as_ref()).unwrap()));
        }

        // Else, create a new file name that has same extension
        // (or a base64 version of the file)
        let dest_file = if !(self.base64) {
            if let Some(extension) = Path::new(file.as_ref()).extension() {
                format!("images/image_{}.{}", self.images.len(), extension.to_string_lossy())
            } else {
                self.logger.warning(format!("Resources: book includes image {} which doesn't have an extension", file));
                format!("images/image_{}", self.images.len())
            }
        } else {
            let mut f = match fs::File::open(file.as_ref()) {
                Ok(f) => f,
                Err(_) => {
                    return Err(Error::file_not_found(source,
                                                     "image",
                                                     format!("{}", file)));
                }
            };
            let mut content: Vec<u8> = vec!();
            if f.read_to_end(&mut content).is_err() {
                self.logger.error(format!("Resources: could not read file {}", file));
                return Ok(file);
            }
            let base64 = content.to_base64(base64::STANDARD);
            match mime_guess::guess_mime_type_opt(file.as_ref()) {
                None => {
                    self.logger.error(format!("Resources: could not guess mime type of file {}", file));
                    return Ok(file);
                },
                Some(s) => format!("data:{};base64,{}", s.to_string(), base64)
            }
        };

        self.images.insert(file.into_owned(), dest_file.clone());
        Ok(Cow::Owned(dest_file))
    }

    /// Returns an iterator the the images files mapping
    #[doc(hidden)]
    pub fn images_mapping(&self) -> &HashMap<String,String> {
        &self.images
    }

    /// Add a match between an original file and a dest file
    pub fn add_link(&mut self, from: String, to: String) {
        self.links.insert(from, to);
    }

    /// Get a destination link from an original link
    pub fn get_link<'a>(&'a self, from: &'a str) -> &'a str {
        if let Some(link) = self.links.get(from) {
            link
        } else {
            self.logger.error(format!("Resources: could not find a in-book match for link {}", from));
            from
        }
    }

    /// Tell whether a file name is a local resource or net
    pub fn is_local(path: &str) -> bool{
        !path.contains("://") // todo: use better algorithm
    }

    /// Add a path offset to all linked urls and images src
    pub fn add_offset(link_offset: &Path, image_offset: &Path, ast: &mut [Token]) {
        if link_offset == Path::new("") && image_offset == Path::new("") {
            //nothing do to
            return;
        }
        for mut token in ast {
            match *token {
                Token::Link(ref mut url, _, ref mut v) => {
                    if ResourceHandler::is_local(url) {
                        let new_url = format!("{}", link_offset.join(&url).display());
                        *url = new_url;
                    }
                    Self::add_offset(link_offset, image_offset, v);
                },
                Token::Image(ref mut url, _, ref mut v) | Token::StandaloneImage(ref mut url, _, ref mut v) => {
                        if ResourceHandler::is_local(url) {
                            let new_url = format!("{}", image_offset.join(&url).display());
                            *url = new_url;
                        }
                        Self::add_offset(link_offset, image_offset, v);
                    },
                _ => {
                    if let Some(ref mut inner) = token.inner_mut() {
                            Self::add_offset(link_offset, image_offset, inner);
                    }
                }
            }
        }
    }
}

/// Get the list of all files, walking recursively in directories
///
/// # Arguments
/// - list: a list of files
/// - base: the path where to get them
///
/// # Returns
/// A list of files (relative to `base`), or an error.
pub fn get_files(list: Vec<String>, base: &str) -> Result<Vec<String>> {
    let mut out:Vec<String> = vec!();
    let base = Path::new(base);
    for path in list.into_iter() {
        let abs_path = base.join(&path);
        let res= fs::metadata(&abs_path);
        match res {
            Err(err) => return Err(Error::render(Source::empty(),
                                                 format!("error reading file {}: {}", abs_path.display(), err))),
            Ok(metadata) => {
                    if metadata.is_file() {
                        out.push(path);
                    } else if metadata.is_dir() {
                        let files = WalkDir::new(&abs_path)
                            .follow_links(true)
                            .into_iter()
                            .filter_map(|e| e.ok())
                            .filter(|e| e.file_type().is_file())
                            .map(|e| PathBuf::from(e.path().strip_prefix(base)
                                                   .unwrap()));
                        for file in files {
                            out.push(file.to_string_lossy().into_owned());
                        }
                    } else {
                        return Err(Error::render(Source::empty(),
                                                 format!("error in epub rendering: {} is neither a file nor a directory", &path)));
                    }
            }
        }
    }
    Ok(out)
}
<commit_msg>Allow resource_hander::add_link to take Into<String> instead of String<commit_after>use token::Token;
use logger::Logger;
use error::{Error, Result, Source};

use std::collections::HashMap;
use std::path::{Path,PathBuf};
use std::borrow::Cow;
use std::fs;
use std::io::Read;

use walkdir::WalkDir;
use rustc_serialize::base64::{self, ToBase64};
use mime_guess;

/// Resource Handler.
///
/// Its task is to make sure that some resource (image, link) is available
/// for the book and to list images used in Markdown files so they can be used for the book
#[derive(Debug)]
pub struct ResourceHandler<'r> {
    /// Maps an original url (e.g.) "foo/Readme.md" to a valid link
    /// (e.g.) chapter3.html
    links: HashMap<String, String>,
    map_images: bool,
    logger: &'r Logger,
    base64: bool,

    /// Maps an original (local) file name to a new file name. Allows to
    /// make sure all image files will be included in e.g. the Epub document.
    #[doc(hidden)]
    pub images: HashMap<String, String>,
}

impl<'r> ResourceHandler<'r> {
    /// Creates a new, empty Resource Handler
    pub fn new(logger: &'r Logger) -> ResourceHandler {
        ResourceHandler {
            links: HashMap::new(),
            images: HashMap::new(),
            map_images: false,
            base64: false,
            logger: logger,
        }
    }

    /// Turns on mapping for image files
    ///
    /// # Argument: an offset (should be book.root)
    pub fn set_images_mapping(&mut self, b: bool) {
        self.map_images = b;
    }

    /// Sets base64 mode for image mapping
    ///
    /// If set to true, instead of returning a destination file path,
    /// `map_image` will include the image as base64
    pub fn set_base64(&mut self, b: bool) {
        self.base64 = b;
    }

    /// Add a local image file and get the resulting transformed
    /// file name
    pub fn map_image<'a, S: Into<Cow<'a, str>>>(&'a mut self, source: &Source, file: S) -> Result<Cow<'a, str>> {
        // If image is not local, do nothing much
        let file = file.into();
        if !Self::is_local(file.as_ref()) {
            self.logger.warning(format!("Resources: book includes non-local image {}, which might cause problem for proper inclusion.", file));
            return Ok(file);
        }

        // Check exisence of the file
        if fs::metadata(file.as_ref()).is_err() {
            return Err(Error::file_not_found(source,
                                             "image",
                                             format!("{}", file)));
        }

        // if image mapping is not activated do nothing else
        if !self.map_images {
            return Ok(file);
        }

        // If this image has already been registered, returns it
        if self.images.contains_key(file.as_ref()) {
            return Ok(Cow::Borrowed(self.images.get(file.as_ref()).unwrap()));
        }

        // Else, create a new file name that has same extension
        // (or a base64 version of the file)
        let dest_file = if !(self.base64) {
            if let Some(extension) = Path::new(file.as_ref()).extension() {
                format!("images/image_{}.{}", self.images.len(), extension.to_string_lossy())
            } else {
                self.logger.warning(format!("Resources: book includes image {} which doesn't have an extension", file));
                format!("images/image_{}", self.images.len())
            }
        } else {
            let mut f = match fs::File::open(file.as_ref()) {
                Ok(f) => f,
                Err(_) => {
                    return Err(Error::file_not_found(source,
                                                     "image",
                                                     format!("{}", file)));
                }
            };
            let mut content: Vec<u8> = vec!();
            if f.read_to_end(&mut content).is_err() {
                self.logger.error(format!("Resources: could not read file {}", file));
                return Ok(file);
            }
            let base64 = content.to_base64(base64::STANDARD);
            match mime_guess::guess_mime_type_opt(file.as_ref()) {
                None => {
                    self.logger.error(format!("Resources: could not guess mime type of file {}", file));
                    return Ok(file);
                },
                Some(s) => format!("data:{};base64,{}", s.to_string(), base64)
            }
        };

        self.images.insert(file.into_owned(), dest_file.clone());
        Ok(Cow::Owned(dest_file))
    }

    /// Returns an iterator the the images files mapping
    #[doc(hidden)]
    pub fn images_mapping(&self) -> &HashMap<String,String> {
        &self.images
    }

    /// Add a match between an original file and a dest file
    pub fn add_link<S: Into<String>>(&mut self, from: S, to: S) {
        self.links.insert(from.into(), to.into());
    }

    /// Get a destination link from an original link
    pub fn get_link<'a>(&'a self, from: &'a str) -> &'a str {
        if let Some(link) = self.links.get(from) {
            link
        } else {
            self.logger.error(format!("Resources: could not find a in-book match for link {}", from));
            from
        }
    }

    /// Tell whether a file name is a local resource or net
    pub fn is_local(path: &str) -> bool{
        !path.contains("://") // todo: use better algorithm
    }

    /// Add a path offset to all linked urls and images src
    pub fn add_offset(link_offset: &Path, image_offset: &Path, ast: &mut [Token]) {
        if link_offset == Path::new("") && image_offset == Path::new("") {
            //nothing do to
            return;
        }
        for mut token in ast {
            match *token {
                Token::Link(ref mut url, _, ref mut v) => {
                    if ResourceHandler::is_local(url) {
                        let new_url = format!("{}", link_offset.join(&url).display());
                        *url = new_url;
                    }
                    Self::add_offset(link_offset, image_offset, v);
                },
                Token::Image(ref mut url, _, ref mut v) | Token::StandaloneImage(ref mut url, _, ref mut v) => {
                        if ResourceHandler::is_local(url) {
                            let new_url = format!("{}", image_offset.join(&url).display());
                            *url = new_url;
                        }
                        Self::add_offset(link_offset, image_offset, v);
                    },
                _ => {
                    if let Some(ref mut inner) = token.inner_mut() {
                            Self::add_offset(link_offset, image_offset, inner);
                    }
                }
            }
        }
    }
}

/// Get the list of all files, walking recursively in directories
///
/// # Arguments
/// - list: a list of files
/// - base: the path where to get them
///
/// # Returns
/// A list of files (relative to `base`), or an error.
pub fn get_files(list: Vec<String>, base: &str) -> Result<Vec<String>> {
    let mut out:Vec<String> = vec!();
    let base = Path::new(base);
    for path in list.into_iter() {
        let abs_path = base.join(&path);
        let res= fs::metadata(&abs_path);
        match res {
            Err(err) => return Err(Error::render(Source::empty(),
                                                 format!("error reading file {}: {}", abs_path.display(), err))),
            Ok(metadata) => {
                    if metadata.is_file() {
                        out.push(path);
                    } else if metadata.is_dir() {
                        let files = WalkDir::new(&abs_path)
                            .follow_links(true)
                            .into_iter()
                            .filter_map(|e| e.ok())
                            .filter(|e| e.file_type().is_file())
                            .map(|e| PathBuf::from(e.path().strip_prefix(base)
                                                   .unwrap()));
                        for file in files {
                            out.push(file.to_string_lossy().into_owned());
                        }
                    } else {
                        return Err(Error::render(Source::empty(),
                                                 format!("error in epub rendering: {} is neither a file nor a directory", &path)));
                    }
            }
        }
    }
    Ok(out)
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate errno;

use self::libc::{c_int};

use std::{ptr, mem};
use std::fmt;

use crate::libproc::helpers;

// See https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/msgbuf.h
const MAX_MSG_BSIZE : c_int = (1*1024*1024);
const MSG_MAGIC : c_int = 0x063061;

// See /usr/include/sys/msgbuf.h on your Mac.
#[repr(C)]
struct MessageBuffer {
    pub msg_magic : c_int,
    pub msg_size : c_int,
    pub msg_bufx : c_int,      // write pointer
    pub msg_bufr : c_int,      // read pointer
    pub msg_bufc : * mut u8     // buffer
}

impl Default for MessageBuffer {
    fn default() -> MessageBuffer {
        MessageBuffer {
            msg_magic : 0,
            msg_size : 0,
            msg_bufx : 0,
            msg_bufr : 0,
            msg_bufc : ptr::null_mut() as * mut u8
        }
    }
}

impl fmt::Debug for MessageBuffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MessageBuffer {{ magic: 0x{:x}, size: {}, bufx: {}}}", self.msg_magic, self.msg_size, self.msg_bufx)
    }
}

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_kmsgbuf(buffer : *mut MessageBuffer, buffersize : u32) -> c_int;
}

/// Get upto buffersize bytes from the the kernel message buffer - as used by dmesg
/// extern crate libproc;
/// extern crate libc;
///
/// use std::str;
/// use std::io::Write;
/// use libproc::libproc::kmesg_buffer;
///
/// fn main() {
///     if kmesg_buffer::am_root() {
///         match kmesg_buffer::kmsgbuf() {
///             Ok(message) => println!("{}", message),
///             Err(err) => writeln!(&mut std::io::stderr(), "Error: {}", err).unwrap()
///         }
///     } else {
///         writeln!(&mut std::io::stderr(), "Must be run as root").unwrap()
///     }
// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c
#[cfg(target_os = "macos")]
pub fn kmsgbuf() -> Result<String, String> {
    let mut message_buffer : MessageBuffer = Default::default();
    let ret: i32;

    unsafe {
        ret = proc_kmsgbuf(&mut message_buffer, mem::size_of::<MessageBuffer>() as u32);
    }

    if ret <= 0 {
        Err(helpers::get_errno_with_message(ret))
    } else
    {
        if message_buffer.msg_magic != MSG_MAGIC {
            println!("Message buffer: {:?}", message_buffer);
            Err(format!("The magic number 0x{:x} is incorrect", message_buffer.msg_magic))
        } else {
            // Avoid starting beyond the end of the buffer
            if message_buffer.msg_bufx >= MAX_MSG_BSIZE {
                message_buffer.msg_bufx = 0;
            }
            let mut output : Vec<u8> = Vec::new();

            // The message buffer is circular; start at the read pointer, and go to the write pointer - 1.
            unsafe {
                let mut ch : u8;
//                let newl : bool = false;
//                let skip : bool = false;
                let mut p : * mut u8 = message_buffer.msg_bufc.offset(message_buffer.msg_bufx as isize);
                let ep : * mut u8 = message_buffer.msg_bufc.offset((message_buffer.msg_bufx - 1) as isize);
//                let buf : [u8; 5];

                while p != ep {
                    // If at the end, then loop around to the start
                    // TODO should use actual size (from struct element) - not the max size??
                    if p == message_buffer.msg_bufc.offset(MAX_MSG_BSIZE as isize) {
                        p = message_buffer.msg_bufc;
                    }

                    ch = *p;

                    /* Skip "\n<.*>" syslog sequences.
                    if skip {
                        if ch == '>' {
                            newl = skip = false;
                        }
                        continue;
                    }

                    if newl && ch == '<' {
                        skip = true;
                        continue;
                    }

                    if ch == '\0' {
                        continue;
                    }

                    newl = ch == '\n';

//                    (void)vis(buf, ch, 0, 0);

                    if buf[1] == 0 {
                        output.append(buf[0]);
                    } else {
                        output.append("%s", buf);
                    }
                    */

                    output.push(ch);
                    p = p.offset(1);
                }

                Ok(String::from_utf8(output).unwrap())
            }
        }
    }
}

#[cfg(not(target_os = "macos"))]
pub fn kmsgbuf() -> Result<String, String> {
    unimplemented!()
}

/*
    Determine if the current user ID of this process is root
*/
pub fn am_root() -> bool {
    unsafe { libc::getuid() == 0 }
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;
    use super::kmsgbuf;
    use super::am_root;

    // If you want this test to actually test something, then you need to run as root 'sudo cargo test'
    #[test]
    fn kmessagebuffer_test() {
        if am_root() {
            match kmsgbuf() {
                Ok(buffer) => println!("Buffer: {:?}", buffer),
                Err(message) => assert!(false, message)
            }
        } else {
            writeln!(&mut io::stdout(), "test libproc::kmesg_buffer::kmessagebuffer_test ... skipped as it needs to be run as root").unwrap();
        }
    }
}<commit_msg>Implement am_root() for linux separately using geteuid - TBD if to use an unstable rust libc::geteuid() or directly an extern function.<commit_after>extern crate libc;
extern crate errno;

use self::libc::{c_int};

use std::{ptr, mem};
use std::fmt;

use crate::libproc::helpers;

// See https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/msgbuf.h
const MAX_MSG_BSIZE : c_int = (1*1024*1024);
const MSG_MAGIC : c_int = 0x063061;

// See /usr/include/sys/msgbuf.h on your Mac.
#[repr(C)]
struct MessageBuffer {
    pub msg_magic : c_int,
    pub msg_size : c_int,
    pub msg_bufx : c_int,      // write pointer
    pub msg_bufr : c_int,      // read pointer
    pub msg_bufc : * mut u8     // buffer
}

impl Default for MessageBuffer {
    fn default() -> MessageBuffer {
        MessageBuffer {
            msg_magic : 0,
            msg_size : 0,
            msg_bufx : 0,
            msg_bufr : 0,
            msg_bufc : ptr::null_mut() as * mut u8
        }
    }
}

impl fmt::Debug for MessageBuffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MessageBuffer {{ magic: 0x{:x}, size: {}, bufx: {}}}", self.msg_magic, self.msg_size, self.msg_bufx)
    }
}

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_kmsgbuf(buffer : *mut MessageBuffer, buffersize : u32) -> c_int;
}

/// Get upto buffersize bytes from the the kernel message buffer - as used by dmesg
/// extern crate libproc;
/// extern crate libc;
///
/// use std::str;
/// use std::io::Write;
/// use libproc::libproc::kmesg_buffer;
///
/// fn main() {
///     if kmesg_buffer::am_root() {
///         match kmesg_buffer::kmsgbuf() {
///             Ok(message) => println!("{}", message),
///             Err(err) => writeln!(&mut std::io::stderr(), "Error: {}", err).unwrap()
///         }
///     } else {
///         writeln!(&mut std::io::stderr(), "Must be run as root").unwrap()
///     }
// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c
#[cfg(target_os = "macos")]
pub fn kmsgbuf() -> Result<String, String> {
    let mut message_buffer : MessageBuffer = Default::default();
    let ret: i32;

    unsafe {
        ret = proc_kmsgbuf(&mut message_buffer, mem::size_of::<MessageBuffer>() as u32);
    }

    if ret <= 0 {
        Err(helpers::get_errno_with_message(ret))
    } else
    {
        if message_buffer.msg_magic != MSG_MAGIC {
            println!("Message buffer: {:?}", message_buffer);
            Err(format!("The magic number 0x{:x} is incorrect", message_buffer.msg_magic))
        } else {
            // Avoid starting beyond the end of the buffer
            if message_buffer.msg_bufx >= MAX_MSG_BSIZE {
                message_buffer.msg_bufx = 0;
            }
            let mut output : Vec<u8> = Vec::new();

            // The message buffer is circular; start at the read pointer, and go to the write pointer - 1.
            unsafe {
                let mut ch : u8;
//                let newl : bool = false;
//                let skip : bool = false;
                let mut p : * mut u8 = message_buffer.msg_bufc.offset(message_buffer.msg_bufx as isize);
                let ep : * mut u8 = message_buffer.msg_bufc.offset((message_buffer.msg_bufx - 1) as isize);
//                let buf : [u8; 5];

                while p != ep {
                    // If at the end, then loop around to the start
                    // TODO should use actual size (from struct element) - not the max size??
                    if p == message_buffer.msg_bufc.offset(MAX_MSG_BSIZE as isize) {
                        p = message_buffer.msg_bufc;
                    }

                    ch = *p;

                    /* Skip "\n<.*>" syslog sequences.
                    if skip {
                        if ch == '>' {
                            newl = skip = false;
                        }
                        continue;
                    }

                    if newl && ch == '<' {
                        skip = true;
                        continue;
                    }

                    if ch == '\0' {
                        continue;
                    }

                    newl = ch == '\n';

//                    (void)vis(buf, ch, 0, 0);

                    if buf[1] == 0 {
                        output.append(buf[0]);
                    } else {
                        output.append("%s", buf);
                    }
                    */

                    output.push(ch);
                    p = p.offset(1);
                }

                Ok(String::from_utf8(output).unwrap())
            }
        }
    }
}

#[cfg(not(target_os = "macos"))]
pub fn kmsgbuf() -> Result<String, String> {
    unimplemented!()
}

/// Determine if the current user ID of this process is root
///
/// ```
/// use libproc::libproc::kmesg_buffer::am_root;
///
/// if am_root() {
///     println!("With great power comes great responsibility");
/// }
/// ```
#[cfg(target_os = "macos")]
pub fn am_root() -> bool {
    // geteuid() is unstable still - wait for it or wrap this:
    // https://stackoverflow.com/questions/3214297/how-can-my-c-c-application-determine-if-the-root-user-is-executing-the-command
    unsafe { libc::getuid() == 0 }
}

#[cfg(target_os = "linux")]
pub fn am_root() -> bool {
    // when this becomes stable in rust libc then we can remove this function or combine for mac and linux
    unsafe { libc::geteuid() == 0 }
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;
    use super::kmsgbuf;
    use super::am_root;

    #[test]
    fn test_if_root() {
        // Assumes tests are NOT run as root!
        assert_eq!(false, am_root());
    }

    // If you want this test to actually test something, then you need to run as root 'sudo cargo test'
    #[test]
    fn kmessagebuffer_test() {
        if am_root() {
            match kmsgbuf() {
                Ok(buffer) => println!("Buffer: {:?}", buffer),
                Err(message) => assert!(false, message)
            }
        } else {
            writeln!(&mut io::stdout(), "test libproc::kmesg_buffer::kmessagebuffer_test ... skipped as it needs to be run as root").unwrap();
        }
    }
}<|endoftext|>


<commit_before>use std::io::{self, ErrorKind};

use mio::*;
use mio::tcp::*;
use slab;
use rpc::connection::Connection;

type Slab<T> = slab::Slab<T, Token>;

pub type ServerCallback = FnMut(&mut Vec<u8>, &mut Connection);

pub struct Server {
    // main socket for our server
    sock: TcpListener,

    // token of our server. we keep track of it here instead of doing `const SERVER = Token(0)`.
    token: Token,

    // a list of connections _accepted_ by our server
    conns: Slab<Connection>,

    // a list of events to process
    events: Events,

    callback: Box<ServerCallback>
}

impl Server {
    pub fn new(sock: TcpListener, callback: Box<ServerCallback>) -> Server {
        Server {
            sock: sock,

            // Give our server token a number much larger than our slab capacity. The slab used to
            // track an internal offset, but does not anymore.
            token: Token(10_000_000),

            // SERVER is Token(1), so start after that
            // we can deal with a max of 126 connections
            conns: Slab::with_capacity(128),

            // list of events from the poller that the server needs to process
            events: Events::with_capacity(1024),

            callback: callback,
        }
    }

    pub fn run(&mut self, poll: &mut Poll) -> io::Result<()> {

        try!(self.register(poll));

        info!("Server run loop starting...");
        loop {
            let cnt = try!(poll.poll(&mut self.events, None));

            let mut i = 0;

            trace!("processing events... cnt={}; len={}", cnt, self.events.len());

            // Iterate over the notifications. Each event provides the token
            // it was registered with (which usually represents, at least, the
            // handle that the event is about) as well as information about
            // what kind of event occurred (readable, writable, signal, etc.)
            while i < cnt {
                // TODO this would be nice if it would turn a Result type. trying to convert this
                // into a io::Result runs into a problem because .ok_or() expects std::Result and
                // not io::Result
                let event = self.events.get(i).expect("Failed to get event");

                trace!("event={:?}; idx={:?}", event, i);
                self.ready(poll, event.token(), event.kind());

                i += 1;
            }

            self.tick(poll);
        }
    }

    /// Register Server with the poller.
    ///
    /// This keeps the registration details neatly tucked away inside of our implementation.
    pub fn register(&mut self, poll: &mut Poll) -> io::Result<()> {
        poll.register(
            &self.sock,
            self.token,
            Ready::readable(),
            PollOpt::edge()
        ).or_else(|e| {
            error!("Failed to register server {:?}, {:?}", self.token, e);
            Err(e)
        })
    }

    fn tick(&mut self, poll: &mut Poll) {
        trace!("Handling end of tick");

        let mut reset_tokens = Vec::new();

        for c in self.conns.iter_mut() {
            if c.is_reset() {
                reset_tokens.push(c.token);
            } else if c.is_idle() {
                c.reregister(poll)
                    .unwrap_or_else(|e| {
                        warn!("Reregister failed {:?}", e);
                        c.mark_reset();
                        reset_tokens.push(c.token);
                    });
            }
        }

        for token in reset_tokens {
            match self.conns.remove(token) {
                Some(_c) => {
                    debug!("reset connection; token={:?}", token);
                }
                None => {
                    warn!("Unable to remove connection for {:?}", token);
                }
            }
        }
    }

    fn ready(&mut self, poll: &mut Poll, token: Token, event: Ready) {
        debug!("{:?} event = {:?}", token, event);
        if event.is_error() {
            warn!("Error event for {:?}", token);
            self.find_connection_by_token(token).mark_reset();
            return;
        }

        if event.is_hup() {
            trace!("Hup event for {:?}", token);
            self.find_connection_by_token(token).mark_reset();
            return;
        }

        // We never expect a write event for our `Server` token . A write event for any other token
        // should be handed off to that connection.
        if event.is_writable() {
            trace!("Write event for {:?}", token);
            assert!(self.token != token, "Received writable event for Server");

            let conn = self.find_connection_by_token(token);

            if conn.is_reset() {
                info!("{:?} has already been reset", token);
                return;
            }

            conn.writable()
                .unwrap_or_else(|e| {
                    warn!("Write event failed for {:?}, {:?}", token, e);
                    conn.mark_reset();
                });
        }

        // A read event for our `Server` token means we are establishing a new connection. A read
        // event for any other token should be handed off to that connection.
        if event.is_readable() {
            trace!("Read event for {:?}", token);
            if self.token == token {
                self.accept(poll);
            } else {

                if self.find_connection_by_token(token).is_reset() {
                    info!("{:?} has already been reset", token);
                    return;
                }

                self.readable(token)
                    .unwrap_or_else(|e| {
                        warn!("Read event failed for {:?}: {:?}", token, e);
                        self.find_connection_by_token(token).mark_reset();
                    });
            }
        }

        if self.token != token {
            self.find_connection_by_token(token).mark_idle();
        }
    }

    /// Accept a _new_ client connection.
    ///
    /// The server will keep track of the new connection and forward any events from the poller
    /// to this connection.
    fn accept(&mut self, poll: &mut Poll) {
        debug!("server accepting new socket");

        loop {
            // Log an error if there is no socket, but otherwise move on so we do not tear down the
            // entire server.
            let sock = match self.sock.accept() {
                Ok((sock, _)) => sock,
                Err(e) => {
                    if e.kind() == ErrorKind::WouldBlock {
                        debug!("accept encountered WouldBlock");
                    } else {
                        error!("Failed to accept new socket, {:?}", e);
                    }
                    return;
                }
            };

            let token = match self.conns.vacant_entry() {
                Some(entry) => {
                    debug!("registering {:?} with poller", entry.index());
                    let c = Connection::new(sock, entry.index());
                    entry.insert(c).index()
                }
                None => {
                    error!("Failed to insert connection into slab");
                    return;
                }
            };

            match self.find_connection_by_token(token).register(poll) {
                Ok(_) => {},
                Err(e) => {
                    error!("Failed to register {:?} connection with poller, {:?}", token, e);
                    self.conns.remove(token);
                }
            }
        }
    }

    /// Forward a readable event to an closure.
    ///
    /// Connections are identified by the token provided to us from the poller. Once a read has
    /// finished, send the message and corresponding connection to the closure
    fn readable(&mut self, token: Token) -> io::Result<()> {
        debug!("server conn readable; token={:?}", token);
        let connection = &mut self.conns[token]; //TODO: use the function below without conflict the ownership
        while let Some(mut message) = try!(connection.readable()) {
            (self.callback)(&mut message, connection);
        }
        Ok(())
    }

    /// Find a connection in the slab using the given token.
    fn find_connection_by_token<'a>(&'a mut self, token: Token) -> &'a mut Connection {
        &mut self.conns[token]
    }
}
<commit_msg>increase server clients<commit_after>use std::io::{self, ErrorKind};

use mio::*;
use mio::tcp::*;
use slab;
use rpc::connection::Connection;

type Slab<T> = slab::Slab<T, Token>;

pub type ServerCallback = FnMut(&mut Vec<u8>, &mut Connection);

pub struct Server {
    // main socket for our server
    sock: TcpListener,

    // token of our server. we keep track of it here instead of doing `const SERVER = Token(0)`.
    token: Token,

    // a list of connections _accepted_ by our server
    conns: Slab<Connection>,

    // a list of events to process
    events: Events,

    callback: Box<ServerCallback>
}

impl Server {
    pub fn new(sock: TcpListener, callback: Box<ServerCallback>) -> Server {
        Server {
            sock: sock,

            // Give our server token a number much larger than our slab capacity. The slab used to
            // track an internal offset, but does not anymore.
            token: Token(10_000_000),

            // SERVER is Token(1), so start after that
            // we can deal with a max of 126 connections
            conns: Slab::with_capacity(25535),

            // list of events from the poller that the server needs to process
            events: Events::with_capacity(1024),

            callback: callback,
        }
    }

    pub fn run(&mut self, poll: &mut Poll) -> io::Result<()> {

        try!(self.register(poll));

        info!("Server run loop starting...");
        loop {
            let cnt = try!(poll.poll(&mut self.events, None));

            let mut i = 0;

            trace!("processing events... cnt={}; len={}", cnt, self.events.len());

            // Iterate over the notifications. Each event provides the token
            // it was registered with (which usually represents, at least, the
            // handle that the event is about) as well as information about
            // what kind of event occurred (readable, writable, signal, etc.)
            while i < cnt {
                // TODO this would be nice if it would turn a Result type. trying to convert this
                // into a io::Result runs into a problem because .ok_or() expects std::Result and
                // not io::Result
                let event = self.events.get(i).expect("Failed to get event");

                trace!("event={:?}; idx={:?}", event, i);
                self.ready(poll, event.token(), event.kind());

                i += 1;
            }

            self.tick(poll);
        }
    }

    /// Register Server with the poller.
    ///
    /// This keeps the registration details neatly tucked away inside of our implementation.
    pub fn register(&mut self, poll: &mut Poll) -> io::Result<()> {
        poll.register(
            &self.sock,
            self.token,
            Ready::readable(),
            PollOpt::edge()
        ).or_else(|e| {
            error!("Failed to register server {:?}, {:?}", self.token, e);
            Err(e)
        })
    }

    fn tick(&mut self, poll: &mut Poll) {
        trace!("Handling end of tick");

        let mut reset_tokens = Vec::new();

        for c in self.conns.iter_mut() {
            if c.is_reset() {
                reset_tokens.push(c.token);
            } else if c.is_idle() {
                c.reregister(poll)
                    .unwrap_or_else(|e| {
                        warn!("Reregister failed {:?}", e);
                        c.mark_reset();
                        reset_tokens.push(c.token);
                    });
            }
        }

        for token in reset_tokens {
            match self.conns.remove(token) {
                Some(_c) => {
                    debug!("reset connection; token={:?}", token);
                }
                None => {
                    warn!("Unable to remove connection for {:?}", token);
                }
            }
        }
    }

    fn ready(&mut self, poll: &mut Poll, token: Token, event: Ready) {
        debug!("{:?} event = {:?}", token, event);
        if event.is_error() {
            warn!("Error event for {:?}", token);
            self.find_connection_by_token(token).mark_reset();
            return;
        }

        if event.is_hup() {
            trace!("Hup event for {:?}", token);
            self.find_connection_by_token(token).mark_reset();
            return;
        }

        // We never expect a write event for our `Server` token . A write event for any other token
        // should be handed off to that connection.
        if event.is_writable() {
            trace!("Write event for {:?}", token);
            assert!(self.token != token, "Received writable event for Server");

            let conn = self.find_connection_by_token(token);

            if conn.is_reset() {
                info!("{:?} has already been reset", token);
                return;
            }

            conn.writable()
                .unwrap_or_else(|e| {
                    warn!("Write event failed for {:?}, {:?}", token, e);
                    conn.mark_reset();
                });
        }

        // A read event for our `Server` token means we are establishing a new connection. A read
        // event for any other token should be handed off to that connection.
        if event.is_readable() {
            trace!("Read event for {:?}", token);
            if self.token == token {
                self.accept(poll);
            } else {

                if self.find_connection_by_token(token).is_reset() {
                    info!("{:?} has already been reset", token);
                    return;
                }

                self.readable(token)
                    .unwrap_or_else(|e| {
                        warn!("Read event failed for {:?}: {:?}", token, e);
                        self.find_connection_by_token(token).mark_reset();
                    });
            }
        }

        if self.token != token {
            self.find_connection_by_token(token).mark_idle();
        }
    }

    /// Accept a _new_ client connection.
    ///
    /// The server will keep track of the new connection and forward any events from the poller
    /// to this connection.
    fn accept(&mut self, poll: &mut Poll) {
        debug!("server accepting new socket");

        loop {
            // Log an error if there is no socket, but otherwise move on so we do not tear down the
            // entire server.
            let sock = match self.sock.accept() {
                Ok((sock, _)) => sock,
                Err(e) => {
                    if e.kind() == ErrorKind::WouldBlock {
                        debug!("accept encountered WouldBlock");
                    } else {
                        error!("Failed to accept new socket, {:?}", e);
                    }
                    return;
                }
            };

            let token = match self.conns.vacant_entry() {
                Some(entry) => {
                    debug!("registering {:?} with poller", entry.index());
                    let c = Connection::new(sock, entry.index());
                    entry.insert(c).index()
                }
                None => {
                    error!("Failed to insert connection into slab");
                    return;
                }
            };

            match self.find_connection_by_token(token).register(poll) {
                Ok(_) => {},
                Err(e) => {
                    error!("Failed to register {:?} connection with poller, {:?}", token, e);
                    self.conns.remove(token);
                }
            }
        }
    }

    /// Forward a readable event to an closure.
    ///
    /// Connections are identified by the token provided to us from the poller. Once a read has
    /// finished, send the message and corresponding connection to the closure
    fn readable(&mut self, token: Token) -> io::Result<()> {
        debug!("server conn readable; token={:?}", token);
        let connection = &mut self.conns[token]; //TODO: use the function below without conflict the ownership
        while let Some(mut message) = try!(connection.readable()) {
            (self.callback)(&mut message, connection);
        }
        Ok(())
    }

    /// Find a connection in the slab using the given token.
    fn find_connection_by_token<'a>(&'a mut self, token: Token) -> &'a mut Connection {
        &mut self.conns[token]
    }
}
<|endoftext|>


<commit_before>use std::num::NonZeroU8;
use std::{fmt, ops};

/// Interests used in registering.
///
/// Interests are used in [registering] [`event::Source`]s with [`Poll`], they
/// indicate what readiness should be monitored for. For example if a socket is
/// registered with [readable] interests and the socket becomes writable, no
/// event will be returned from a call to [`poll`].
///
/// The size of `Option<Interests>` should be identical to itself.
///
/// ```
/// use std::mem::size_of;
/// use mio::Interests;
///
/// assert_eq!(size_of::<Option<Interests>>(), size_of::<Interests>());
/// ```
///
/// [registering]: crate::Registry::register
/// [`event::Source`]: crate::event::Source
/// [`Poll`]: crate::Poll
/// [readable]: Interests::READABLE
/// [`poll`]: crate::Poll::poll
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Interests(NonZeroU8);

// These must be unique.
const READABLE: u8 = 0b0_001;
const WRITABLE: u8 = 0b0_010;
// The following are not available on all platforms.
#[cfg_attr(
    not(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    )),
    allow(dead_code)
)]
const AIO: u8 = 0b0_100;
#[cfg_attr(not(target_os = "freebsd"), allow(dead_code))]
const LIO: u8 = 0b1_000;

impl Interests {
    /// Returns a `Interests` set representing readable interests.
    pub const READABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(READABLE) });

    /// Returns a `Interests` set representing writable interests.
    pub const WRITABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(WRITABLE) });

    /// Returns a `Interests` set representing AIO completion interests.
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    ))]
    pub const AIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(AIO) });

    /// Returns a `Interests` set representing LIO completion interests.
    #[cfg(target_os = "freebsd")]
    pub const LIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(LIO) });

    /// Add together two `Interests`.
    ///
    /// This does the same thing as the `BitOr` implementation, but is a
    /// constant function.
    ///
    /// ```
    /// use mio::Interests;
    ///
    /// const INTERESTS: Interests = Interests::READABLE.and(Interests::WRITABLE);
    /// # fn silent_dead_code_warning(_: Interests) { }
    /// # silent_dead_code_warning(INTERESTS)
    /// ```
    pub const fn and(self, other: Interests) -> Interests {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }

    /// Returns true if the value includes readable readiness.
    pub fn is_readable(self) -> bool {
        (self.0.get() & READABLE) != 0
    }

    /// Returns true if the value includes writable readiness.
    pub fn is_writable(self) -> bool {
        (self.0.get() & WRITABLE) != 0
    }

    /// Returns true if `Interests` contains AIO readiness
    pub fn is_aio(self) -> bool {
        (self.0.get() & AIO) != 0
    }

    /// Returns true if `Interests` contains LIO readiness
    pub fn is_lio(self) -> bool {
        (self.0.get() & LIO) != 0
    }
}

impl ops::BitOr for Interests {
    type Output = Self;

    #[inline]
    fn bitor(self, other: Self) -> Self {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }
}

impl ops::BitOrAssign for Interests {
    #[inline]
    fn bitor_assign(&mut self, other: Self) {
        self.0 = (*self | other).0;
    }
}

impl fmt::Debug for Interests {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut one = false;
        if self.is_readable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "READABLE")?;
            one = true
        }
        if self.is_writable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "WRITABLE")?;
            one = true
        }
        #[cfg(any(
            target_os = "dragonfly",
            target_os = "freebsd",
            target_os = "ios",
            target_os = "macos"
        ))]
        {
            if self.is_aio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "AIO")?;
                one = true
            }
        }
        #[cfg(any(target_os = "freebsd"))]
        {
            if self.is_lio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "LIO")?;
                one = true
            }
        }
        debug_assert!(one, "printing empty interests");
        Ok(())
    }
}
<commit_msg>Make Interests::is_* methods constant<commit_after>use std::num::NonZeroU8;
use std::{fmt, ops};

/// Interests used in registering.
///
/// Interests are used in [registering] [`event::Source`]s with [`Poll`], they
/// indicate what readiness should be monitored for. For example if a socket is
/// registered with [readable] interests and the socket becomes writable, no
/// event will be returned from a call to [`poll`].
///
/// The size of `Option<Interests>` should be identical to itself.
///
/// ```
/// use std::mem::size_of;
/// use mio::Interests;
///
/// assert_eq!(size_of::<Option<Interests>>(), size_of::<Interests>());
/// ```
///
/// [registering]: crate::Registry::register
/// [`event::Source`]: crate::event::Source
/// [`Poll`]: crate::Poll
/// [readable]: Interests::READABLE
/// [`poll`]: crate::Poll::poll
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Interests(NonZeroU8);

// These must be unique.
const READABLE: u8 = 0b0_001;
const WRITABLE: u8 = 0b0_010;
// The following are not available on all platforms.
#[cfg_attr(
    not(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    )),
    allow(dead_code)
)]
const AIO: u8 = 0b0_100;
#[cfg_attr(not(target_os = "freebsd"), allow(dead_code))]
const LIO: u8 = 0b1_000;

impl Interests {
    /// Returns a `Interests` set representing readable interests.
    pub const READABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(READABLE) });

    /// Returns a `Interests` set representing writable interests.
    pub const WRITABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(WRITABLE) });

    /// Returns a `Interests` set representing AIO completion interests.
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    ))]
    pub const AIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(AIO) });

    /// Returns a `Interests` set representing LIO completion interests.
    #[cfg(target_os = "freebsd")]
    pub const LIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(LIO) });

    /// Add together two `Interests`.
    ///
    /// This does the same thing as the `BitOr` implementation, but is a
    /// constant function.
    ///
    /// ```
    /// use mio::Interests;
    ///
    /// const INTERESTS: Interests = Interests::READABLE.and(Interests::WRITABLE);
    /// # fn silent_dead_code_warning(_: Interests) { }
    /// # silent_dead_code_warning(INTERESTS)
    /// ```
    pub const fn and(self, other: Interests) -> Interests {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }

    /// Returns true if the value includes readable readiness.
    pub const fn is_readable(self) -> bool {
        (self.0.get() & READABLE) != 0
    }

    /// Returns true if the value includes writable readiness.
    pub const fn is_writable(self) -> bool {
        (self.0.get() & WRITABLE) != 0
    }

    /// Returns true if `Interests` contains AIO readiness
    pub const fn is_aio(self) -> bool {
        (self.0.get() & AIO) != 0
    }

    /// Returns true if `Interests` contains LIO readiness
    pub const fn is_lio(self) -> bool {
        (self.0.get() & LIO) != 0
    }
}

impl ops::BitOr for Interests {
    type Output = Self;

    #[inline]
    fn bitor(self, other: Self) -> Self {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }
}

impl ops::BitOrAssign for Interests {
    #[inline]
    fn bitor_assign(&mut self, other: Self) {
        self.0 = (*self | other).0;
    }
}

impl fmt::Debug for Interests {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut one = false;
        if self.is_readable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "READABLE")?;
            one = true
        }
        if self.is_writable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "WRITABLE")?;
            one = true
        }
        #[cfg(any(
            target_os = "dragonfly",
            target_os = "freebsd",
            target_os = "ios",
            target_os = "macos"
        ))]
        {
            if self.is_aio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "AIO")?;
                one = true
            }
        }
        #[cfg(any(target_os = "freebsd"))]
        {
            if self.is_lio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "LIO")?;
                one = true
            }
        }
        debug_assert!(one, "printing empty interests");
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::slice;
use std::ffi;

use htslib;

pub struct Record {
    pub inner: *mut htslib::vcf::bcf1_t,
    pub header: *mut htslib::vcf::bcf_hdr_t,
    buffer: *mut ::libc::c_void,
}

impl Record {
    pub fn new() -> Self {
        let inner = unsafe { htslib::vcf::bcf_init() };
        Record { inner: inner, header: ptr::null_mut(), buffer: ptr::null_mut() }
    }

    #[inline]
    fn inner(&self) -> htslib::vcf::bcf1_t {
        unsafe { *self.inner }
    }

    pub fn rid(&self) -> Option<u32> {
        match self.inner().rid {
            -1  => None,
            rid => Some(rid as u32)
        }
    }

    // 0-based position.
    pub fn pos(&self) -> u32 {
        self.inner().pos as u32
    }

    pub fn alleles(&self) -> Vec<&[u8]> {
        unsafe { htslib::vcf::bcf_unpack(self.inner, htslib::vcf::BCF_UN_STR) };
        let n = self.inner().n_allele as usize;
        let dec = self.inner().d;
        let alleles = unsafe { slice::from_raw_parts(dec.allele, n) };
        (0..n).map(|i| unsafe { ffi::CStr::from_ptr(alleles[i]).to_bytes() }).collect()
    }

    pub fn qual(&self) -> f32 {
        self.inner().qual
    }

    /// Get the value of the given info tag.
    pub fn info<'a>(&'a mut self, tag: &'a [u8]) -> Info {
        Info { record: self, tag: tag }
    }

    pub fn sample_count(&self) -> u32 {
        self.inner().n_fmt_n_sample & 0xffffff
    }

    /// Get the value of the given format tag for each sample.
    pub fn format<'a>(&'a mut self, tag: &'a [u8]) -> Format {
        Format::new(self, tag)
    }
}

impl Drop for Record {
    fn drop(&mut self) {
        if !self.buffer.is_null() {
            unsafe { ::libc::free(self.buffer) };
        }
        unsafe { htslib::vcf::bcf_destroy(self.inner) };
    }
}

pub struct Info<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
}

impl<'a> Info<'a> {
    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_info_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<&[i32], TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe { slice::from_raw_parts(self.record.buffer as *mut i32, n) }
        })
    }

    pub fn float(&mut self) -> Result<&[f32], TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe { slice::from_raw_parts(self.record.buffer as *mut f32, n) }
        })
    }

    pub fn flag(&mut self) -> Result<bool, TagError> {
        self.data(htslib::vcf::BCF_HT_FLAG).map(|(_, ret)| {
            ret == 1
        })
    }

    pub fn string(&mut self) -> Result<&[u8], TagError> {
        self.data(htslib::vcf::BCF_HT_STR).map(|(_, ret)| {
            unsafe { slice::from_raw_parts(self.record.buffer as *mut u8, ret as usize) }
        })
    }
}

// TODO implement format.
pub struct Format<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
    inner: *mut htslib::vcf::bcf_fmt_t,
}

impl<'a> Format<'a> {
    fn new(record: &'a mut Record, tag: &'a [u8]) -> Format<'a> {
        let inner = unsafe { htslib::vcf::bcf_get_fmt(
            record.header,
            record.inner,
            ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
        ) };
        Format { record: record, tag: tag, inner: inner }
    }

    fn values_per_sample(&self) -> usize {
        unsafe { (*self.inner).n as usize }
    }

    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_format_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<Vec<&[i32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts(self.record.buffer as *mut i32, n)
            }.chunks(self.values_per_sample()).collect()
        })
    }

    pub fn float(&mut self) -> Result<Vec<&[f32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts(self.record.buffer as *mut f32, n)
            }.chunks(self.values_per_sample()).collect()
        })
    }
}

pub enum TagError {
    UndefinedTag,
    UnexpectedType,
    MissingTag,
}
<commit_msg>Add method to obtain allele count.<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::slice;
use std::ffi;

use htslib;

pub struct Record {
    pub inner: *mut htslib::vcf::bcf1_t,
    pub header: *mut htslib::vcf::bcf_hdr_t,
    buffer: *mut ::libc::c_void,
}

impl Record {
    pub fn new() -> Self {
        let inner = unsafe { htslib::vcf::bcf_init() };
        Record { inner: inner, header: ptr::null_mut(), buffer: ptr::null_mut() }
    }

    #[inline]
    fn inner(&self) -> htslib::vcf::bcf1_t {
        unsafe { *self.inner }
    }

    pub fn rid(&self) -> Option<u32> {
        match self.inner().rid {
            -1  => None,
            rid => Some(rid as u32)
        }
    }

    // 0-based position.
    pub fn pos(&self) -> u32 {
        self.inner().pos as u32
    }

    pub fn alleles(&self) -> Vec<&[u8]> {
        unsafe { htslib::vcf::bcf_unpack(self.inner, htslib::vcf::BCF_UN_STR) };
        let n = self.inner().n_allele as usize;
        let dec = self.inner().d;
        let alleles = unsafe { slice::from_raw_parts(dec.allele, n) };
        (0..n).map(|i| unsafe { ffi::CStr::from_ptr(alleles[i]).to_bytes() }).collect()
    }

    pub fn qual(&self) -> f32 {
        self.inner().qual
    }

    /// Get the value of the given info tag.
    pub fn info<'a>(&'a mut self, tag: &'a [u8]) -> Info {
        Info { record: self, tag: tag }
    }

    pub fn sample_count(&self) -> u32 {
        self.inner().n_fmt_n_sample & 0xffffff
    }

    pub fn allele_count(&self) -> u16 {
        self.inner().n_allele
    }

    /// Get the value of the given format tag for each sample.
    pub fn format<'a>(&'a mut self, tag: &'a [u8]) -> Format {
        Format::new(self, tag)
    }
}

impl Drop for Record {
    fn drop(&mut self) {
        if !self.buffer.is_null() {
            unsafe { ::libc::free(self.buffer) };
        }
        unsafe { htslib::vcf::bcf_destroy(self.inner) };
    }
}

pub struct Info<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
}

impl<'a> Info<'a> {
    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_info_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<&[i32], TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe { slice::from_raw_parts(self.record.buffer as *mut i32, n) }
        })
    }

    pub fn float(&mut self) -> Result<&[f32], TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe { slice::from_raw_parts(self.record.buffer as *mut f32, n) }
        })
    }

    pub fn flag(&mut self) -> Result<bool, TagError> {
        self.data(htslib::vcf::BCF_HT_FLAG).map(|(_, ret)| {
            ret == 1
        })
    }

    pub fn string(&mut self) -> Result<&[u8], TagError> {
        self.data(htslib::vcf::BCF_HT_STR).map(|(_, ret)| {
            unsafe { slice::from_raw_parts(self.record.buffer as *mut u8, ret as usize) }
        })
    }
}

// TODO implement format.
pub struct Format<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
    inner: *mut htslib::vcf::bcf_fmt_t,
}

impl<'a> Format<'a> {
    fn new(record: &'a mut Record, tag: &'a [u8]) -> Format<'a> {
        let inner = unsafe { htslib::vcf::bcf_get_fmt(
            record.header,
            record.inner,
            ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
        ) };
        Format { record: record, tag: tag, inner: inner }
    }

    fn values_per_sample(&self) -> usize {
        unsafe { (*self.inner).n as usize }
    }

    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_format_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<Vec<&[i32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts(self.record.buffer as *mut i32, n)
            }.chunks(self.values_per_sample()).collect()
        })
    }

    pub fn float(&mut self) -> Result<Vec<&[f32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts(self.record.buffer as *mut f32, n)
            }.chunks(self.values_per_sample()).collect()
        })
    }
}

pub enum TagError {
    UndefinedTag,
    UnexpectedType,
    MissingTag,
}
<|endoftext|>


<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::slice;
use std::ffi;

use htslib;
use bcf::header::HeaderView;

pub struct Record {
    pub inner: *mut htslib::vcf::bcf1_t,
    pub header: *mut htslib::vcf::bcf_hdr_t,
    buffer: *mut ::libc::c_void,
}

impl Record {
    pub fn new() -> Self {
        let inner = unsafe { htslib::vcf::bcf_init() };
        Record { inner: inner, header: ptr::null_mut(), buffer: ptr::null_mut() }
    }

    pub fn translate(&mut self, header: HeaderView) {
        unsafe {
            htslib::vcf::bcf_translate(header.inner, self.header, self.inner);
        }
        self.header = header.inner;
    }

    #[inline]
    fn inner(&self) -> htslib::vcf::bcf1_t {
        unsafe { *self.inner }
    }

    pub fn rid(&self) -> Option<u32> {
        match self.inner().rid {
            -1  => None,
            rid => Some(rid as u32)
        }
    }

    // 0-based position.
    pub fn pos(&self) -> u32 {
        self.inner().pos as u32
    }

    pub fn alleles(&self) -> Vec<&[u8]> {
        unsafe { htslib::vcf::bcf_unpack(self.inner, htslib::vcf::BCF_UN_STR) };
        let n = self.inner().n_allele as usize;
        let dec = self.inner().d;
        let alleles = unsafe { slice::from_raw_parts(dec.allele, n) };
        (0..n).map(|i| unsafe { ffi::CStr::from_ptr(alleles[i]).to_bytes() }).collect()
    }

    pub fn qual(&self) -> f32 {
        self.inner().qual
    }

    /// Get the value of the given info tag.
    pub fn info<'a>(&'a mut self, tag: &'a [u8]) -> Info {
        Info { record: self, tag: tag }
    }

    pub fn sample_count(&self) -> u32 {
        self.inner().n_fmt_n_sample & 0xffffff
    }

    pub fn allele_count(&self) -> u16 {
        self.inner().n_allele
    }

    /// Get the value of the given format tag for each sample.
    pub fn format<'a>(&'a mut self, tag: &'a [u8]) -> Format {
        Format::new(self, tag)
    }

    /// Add an integer format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_integer(&mut self, tag: &[u8], data: &[i32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_INT)
    }

    /// Add a float format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_float(&mut self, tag: &[u8], data: &[f32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_REAL)
    }

    /// Add a format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    fn push_format<T>(&mut self, tag: &[u8], data: &[T], ht: i32) -> Result<(), ()> {
        assert!(data.len() > 0);
        unsafe {
            if htslib::vcf::bcf_update_format(
                self.header,
                self.inner,
                ffi::CString::new(tag).unwrap().as_ptr() as *mut i8,
                data.as_ptr() as *const ::libc::c_void,
                data.len() as i32,
                ht
            ) == 0 {
                Ok(())
            }
            else {
                Err(())
            }
        }
    }

    pub fn set_qual(&mut self, qual: f32) {
        self.inner().qual = qual;
    }
}

impl Drop for Record {
    fn drop(&mut self) {
        if !self.buffer.is_null() {
            unsafe { ::libc::free(self.buffer) };
        }
        unsafe { htslib::vcf::bcf_destroy(self.inner) };
    }
}

unsafe impl Send for Record {}

pub struct Info<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
}

impl<'a> Info<'a> {
    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_info_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<&mut [i32], TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut i32, n) }
        })
    }

    pub fn float(&mut self) -> Result<&mut [f32], TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut f32, n) }
        })
    }

    pub fn flag(&mut self) -> Result<bool, TagError> {
        self.data(htslib::vcf::BCF_HT_FLAG).map(|(_, ret)| {
            ret == 1
        })
    }

    pub fn string(&mut self) -> Result<&mut [u8], TagError> {
        self.data(htslib::vcf::BCF_HT_STR).map(|(_, ret)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut u8, ret as usize) }
        })
    }
}

// TODO implement format.
pub struct Format<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
    inner: *mut htslib::vcf::bcf_fmt_t,
}

impl<'a> Format<'a> {
    fn new(record: &'a mut Record, tag: &'a [u8]) -> Format<'a> {
        let inner = unsafe { htslib::vcf::bcf_get_fmt(
            record.header,
            record.inner,
            ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
        ) };
        Format { record: record, tag: tag, inner: inner }
    }

    fn values_per_sample(&self) -> usize {
        unsafe { (*self.inner).n as usize }
    }

    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_format_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<Vec<&mut [i32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut i32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }

    pub fn float(&mut self) -> Result<Vec<&mut [f32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut f32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }
}

pub enum TagError {
    UndefinedTag,
    UnexpectedType,
    MissingTag,
}
<commit_msg>Borrow header.<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::slice;
use std::ffi;

use htslib;
use bcf::header::HeaderView;

pub struct Record {
    pub inner: *mut htslib::vcf::bcf1_t,
    pub header: *mut htslib::vcf::bcf_hdr_t,
    buffer: *mut ::libc::c_void,
}

impl Record {
    pub fn new() -> Self {
        let inner = unsafe { htslib::vcf::bcf_init() };
        Record { inner: inner, header: ptr::null_mut(), buffer: ptr::null_mut() }
    }

    pub fn translate(&mut self, header: &HeaderView) {
        unsafe {
            htslib::vcf::bcf_translate(header.inner, self.header, self.inner);
        }
        self.header = header.inner;
    }

    #[inline]
    fn inner(&self) -> htslib::vcf::bcf1_t {
        unsafe { *self.inner }
    }

    pub fn rid(&self) -> Option<u32> {
        match self.inner().rid {
            -1  => None,
            rid => Some(rid as u32)
        }
    }

    // 0-based position.
    pub fn pos(&self) -> u32 {
        self.inner().pos as u32
    }

    pub fn alleles(&self) -> Vec<&[u8]> {
        unsafe { htslib::vcf::bcf_unpack(self.inner, htslib::vcf::BCF_UN_STR) };
        let n = self.inner().n_allele as usize;
        let dec = self.inner().d;
        let alleles = unsafe { slice::from_raw_parts(dec.allele, n) };
        (0..n).map(|i| unsafe { ffi::CStr::from_ptr(alleles[i]).to_bytes() }).collect()
    }

    pub fn qual(&self) -> f32 {
        self.inner().qual
    }

    /// Get the value of the given info tag.
    pub fn info<'a>(&'a mut self, tag: &'a [u8]) -> Info {
        Info { record: self, tag: tag }
    }

    pub fn sample_count(&self) -> u32 {
        self.inner().n_fmt_n_sample & 0xffffff
    }

    pub fn allele_count(&self) -> u16 {
        self.inner().n_allele
    }

    /// Get the value of the given format tag for each sample.
    pub fn format<'a>(&'a mut self, tag: &'a [u8]) -> Format {
        Format::new(self, tag)
    }

    /// Add an integer format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_integer(&mut self, tag: &[u8], data: &[i32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_INT)
    }

    /// Add a float format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_float(&mut self, tag: &[u8], data: &[f32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_REAL)
    }

    /// Add a format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    fn push_format<T>(&mut self, tag: &[u8], data: &[T], ht: i32) -> Result<(), ()> {
        assert!(data.len() > 0);
        unsafe {
            if htslib::vcf::bcf_update_format(
                self.header,
                self.inner,
                ffi::CString::new(tag).unwrap().as_ptr() as *mut i8,
                data.as_ptr() as *const ::libc::c_void,
                data.len() as i32,
                ht
            ) == 0 {
                Ok(())
            }
            else {
                Err(())
            }
        }
    }

    pub fn set_qual(&mut self, qual: f32) {
        self.inner().qual = qual;
    }
}

impl Drop for Record {
    fn drop(&mut self) {
        if !self.buffer.is_null() {
            unsafe { ::libc::free(self.buffer) };
        }
        unsafe { htslib::vcf::bcf_destroy(self.inner) };
    }
}

unsafe impl Send for Record {}

pub struct Info<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
}

impl<'a> Info<'a> {
    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_info_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<&mut [i32], TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut i32, n) }
        })
    }

    pub fn float(&mut self) -> Result<&mut [f32], TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut f32, n) }
        })
    }

    pub fn flag(&mut self) -> Result<bool, TagError> {
        self.data(htslib::vcf::BCF_HT_FLAG).map(|(_, ret)| {
            ret == 1
        })
    }

    pub fn string(&mut self) -> Result<&mut [u8], TagError> {
        self.data(htslib::vcf::BCF_HT_STR).map(|(_, ret)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut u8, ret as usize) }
        })
    }
}

// TODO implement format.
pub struct Format<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
    inner: *mut htslib::vcf::bcf_fmt_t,
}

impl<'a> Format<'a> {
    fn new(record: &'a mut Record, tag: &'a [u8]) -> Format<'a> {
        let inner = unsafe { htslib::vcf::bcf_get_fmt(
            record.header,
            record.inner,
            ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
        ) };
        Format { record: record, tag: tag, inner: inner }
    }

    fn values_per_sample(&self) -> usize {
        unsafe { (*self.inner).n as usize }
    }

    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_format_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<Vec<&mut [i32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut i32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }

    pub fn float(&mut self) -> Result<Vec<&mut [f32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut f32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }
}

pub enum TagError {
    UndefinedTag,
    UnexpectedType,
    MissingTag,
}
<|endoftext|>