<commit_before>use crate::inference::jsoninputerr::JsonInputErr;
use std::io::{BufReader, Bytes, Read};
use std::iter::Peekable;

#[derive(Debug, PartialEq)]
pub enum JsonToken {
    True,
    False,
    Null,
    Number(String),
    String(String),
    ObjectStart,
    ObjectEnd,
    ArrayStart,
    ArrayEnd,
    Comma,
    Colon,
}

pub struct JsonLexer<R: Read> {
    bytes: Peekable<Bytes<BufReader<R>>>, // TODO: Keep position info
    failed: bool,
}

impl<R: Read> JsonLexer<R> {
    pub fn new(source: R) -> Self {
        JsonLexer {
            bytes: BufReader::new(source).bytes().peekable(),
            failed: false,
        }
    }

    fn get_next_token(&mut self) -> Option<Result<JsonToken, JsonInputErr>> {
        loop {
            let byte = match self.bytes.peek() {
                Some(Ok(byte)) => *byte,
                Some(Err(_)) => return Some(Err(JsonInputErr::IoErr)),
                None => return None,
            };

            return Some(match byte {
                b' ' | b'\t' | b'\n' | b'\r' => {
                    self.bytes.next();
                    continue;
                }
                b'{' => self.skip_and_produce(JsonToken::ObjectStart),
                b'}' => self.skip_and_produce(JsonToken::ObjectEnd),
                b'[' => self.skip_and_produce(JsonToken::ArrayStart),
                b']' => self.skip_and_produce(JsonToken::ArrayEnd),
                b',' => self.skip_and_produce(JsonToken::Comma),
                b':' => self.skip_and_produce(JsonToken::Colon),
                b't' => self.match_token("true", JsonToken::True),
                b'f' => self.match_token("false", JsonToken::False),
                b'n' => self.match_token("null", JsonToken::Null),
                b'"' => self.match_string(),
                b'0'..=b'9' | b'-' => self.match_number(),
                _ => Err(JsonInputErr::InvalidJson),
            });
        }
    }

    fn skip_and_produce(&mut self, token: JsonToken) -> Result<JsonToken, JsonInputErr> {
        self.bytes.next();
        Ok(token)
    }

    fn expect_byte(&mut self) -> Result<u8, JsonInputErr> {
        match self.bytes.next() {
            Some(Ok(byte)) => Ok(byte),
            Some(Err(_)) => Err(JsonInputErr::IoErr),
            None => Err(JsonInputErr::UnexpectedEndOfInput),
        }
    }

    fn skip_byte(&mut self, target_byte: u8) -> Result<(), JsonInputErr> {
        let byte = self.expect_byte()?;
        if byte == target_byte {
            Ok(())
        } else {
            Err(JsonInputErr::InvalidJson)
        }
    }

    fn match_token(
        &mut self,
        target_str: &'static str,
        token: JsonToken,
    ) -> Result<JsonToken, JsonInputErr> {
        for target_byte in target_str.bytes() {
            self.skip_byte(target_byte)?;
        }
        Ok(token)
    }

    fn match_string(&mut self) -> Result<JsonToken, JsonInputErr> {
        self.skip_byte(b'"')?;
        let mut buffer = Vec::new();
        loop {
            let byte = self.expect_byte()?;

            if byte == b'"' {
                return Ok(JsonToken::String(
                    String::from_utf8(buffer).map_err(|_| JsonInputErr::InvalidUtf8)?,
                ));
            } else if byte == b'\\' {
                let escaped = self.expect_byte()?;

                match escaped {
                    b'"' => buffer.push(b'"'),
                    b'\\' => buffer.push(b'\\'),
                    b'/' => buffer.push(b'/'),
                    b'b' => buffer.push(8),  // backspace
                    b'f' => buffer.push(12), // form feed
                    b'n' => buffer.push(b'\n'),
                    b'r' => buffer.push(b'\r'),
                    b't' => buffer.push(b'\t'),
                    b'u' => {
                        let surrogate_offset: u32 = (0xD800 << 10) + 0xDC00 - 0x10000;

                        let mut codepoint = self.parse_codepoint()? as u32;
                        if (0xD800..=0xDFFF).contains(&codepoint) {
                            // first codepoint was the start of a surrogate pair
                            self.skip_byte(b'\\')?;
                            self.skip_byte(b'u')?;
                            let codepoint2 = self.parse_codepoint()? as u32;
                            codepoint = ((codepoint << 10) + codepoint2) - surrogate_offset;
                        };
                        let mut buf = [0u8; 4];
                        let encoded_bytes = std::char::from_u32(codepoint)
                            .unwrap_or(std::char::REPLACEMENT_CHARACTER)
                            .encode_utf8(&mut buf)
                            .bytes();
                        for encoded_byte in encoded_bytes {
                            buffer.push(encoded_byte);
                        }
                    }
                    _ => return Err(JsonInputErr::InvalidEscape(escaped)),
                };
            } else {
                buffer.push(byte)
            }
        }
    }

    // "ab03..." -> 0xab03
    fn parse_codepoint(&mut self) -> Result<u16, JsonInputErr> {
        let mut codepoint: u16 = 0;
        for _ in 0..4 {
            codepoint <<= 4;
            let byte2 = self.expect_byte()?;
            codepoint += match byte2 {
                b'0'..=b'9' => byte2 - b'0',
                b'a'..=b'f' => byte2 - b'a' + 10,
                b'A'..=b'F' => byte2 - b'A' + 10,
                _ => return Err(JsonInputErr::InvalidEscape(byte2)),
            } as u16;
        }
        Ok(codepoint)
    }

    fn match_number(&mut self) -> Result<JsonToken, JsonInputErr> {
        let mut buffer = Vec::new();
        loop {
            let byte = match self.bytes.peek() {
                Some(Ok(byte)) => *byte,
                Some(Err(_)) => return Err(JsonInputErr::IoErr),
                None => break,
            };

            match byte {
                b'0'..=b'9' | b'-' | b'+' | b'.' | b'e' | b'E' => {
                    buffer.push(byte);
                    self.bytes.next();
                }
                _ => break,
            }
        }
        // TODO: Actually parse numbers
        Ok(JsonToken::Number(
            String::from_utf8(buffer).map_err(|_err| JsonInputErr::InvalidUtf8)?,
        ))
    }
}

impl<R: Read> Iterator for JsonLexer<R> {
    type Item = Result<JsonToken, JsonInputErr>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.failed {
            return None;
        }

        let res = self.get_next_token();
        if let Some(Err(_)) = res {
            self.failed = true;
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::inference::jsoninputerr::JsonInputErr;
    use std::fmt::Debug;

    #[test]
    fn empty_input() {
        assert_eq!(tokens_from_str(""), Ok(vec![]));
        assert_eq!(tokens_from_str(" \t\r\n"), Ok(vec![]));
    }

    #[test]
    fn bare_number() {
        assert_eq!(
            tokens_from_str("123"),
            Ok(vec![JsonToken::Number("123".to_string())])
        );
    }

    #[test]
    fn object() {
        assert_eq!(
            tokens_from_str("{}"),
            Ok(vec![JsonToken::ObjectStart, JsonToken::ObjectEnd])
        );
        assert_eq!(
            tokens_from_str("  {  }  "),
            Ok(vec![JsonToken::ObjectStart, JsonToken::ObjectEnd])
        );
    }

    #[test]
    fn string() {
        assert_eq!(
            tokens_from_str(r#" "hello world" "#),
            Ok(vec![JsonToken::String("hello world".to_string())])
        );
    }

    #[test]
    fn escapes() {
        assert_eq!(
            tokens_from_str(r#" "foo\nbar" "#),
            Ok(vec![JsonToken::String("foo\nbar".to_string())])
        );

        assert_eq!(
            tokens_from_str(r#" "John says \"Hello\"" "#),
            Ok(vec![JsonToken::String(r#"John says "Hello""#.to_string())])
        );
    }

    #[test]
    fn unicode_escapes() {
        assert_eq!(
            tokens_from_str(r#" "\u00e6" "#),
            Ok(vec![JsonToken::String("æ".to_string())])
        );

        assert_eq!(
            tokens_from_str(r#" "\uD83D\uDE00" "#),
            Ok(vec![JsonToken::String("😀".to_string())])
        );

        assert_eq!(
            tokens_from_str(r#" "\uD83D" "#),
            Err(JsonInputErr::InvalidJson)
        );
    }

    #[test]
    fn number() {
        assert_eq!(
            tokens_from_str(r#" 14.5 "#),
            Ok(vec![JsonToken::Number("14.5".to_string())])
        );
    }

    #[test]
    fn complex() {
        assert_eq!(
            tokens_from_str(
                r#"
                {
                    "foo": [1, true]
                }
                "#
            ),
            Ok(vec![
                JsonToken::ObjectStart,
                JsonToken::String("foo".to_string()),
                JsonToken::Colon,
                JsonToken::ArrayStart,
                JsonToken::Number("1".to_string()),
                JsonToken::Comma,
                JsonToken::True,
                JsonToken::ArrayEnd,
                JsonToken::ObjectEnd
            ])
        );
    }

    #[test]
    fn invalid() {
        assert_eq!(tokens_from_str("foo"), Err(JsonInputErr::InvalidJson));
        assert_eq!(tokens_from_str(" [ foo ] "), Err(JsonInputErr::InvalidJson));
    }

    fn tokens_from_str(s: &'static str) -> Result<Vec<JsonToken>, JsonInputErr> {
        let collected: Vec<Result<JsonToken, JsonInputErr>> =
            JsonLexer::new(s.as_bytes()).collect();
        coalesce_err(collected)
    }

    fn coalesce_err<T: Debug, E: Debug>(vec: Vec<Result<T, E>>) -> Result<Vec<T>, E> {
        let error_count = vec.iter().filter(|res| res.is_err()).count();
        match error_count {
            0 => Ok(vec.into_iter().map(|res| res.unwrap()).collect()),
            1 => Err(vec
                .into_iter()
                .find(|res| res.is_err())
                .unwrap()
                .unwrap_err()),
            _ => panic!("More than one error: {:?}", vec),
        }
    }
}
<commit_msg>perf, lexer: Reuse buffer for string parsing<commit_after>use crate::inference::jsoninputerr::JsonInputErr;
use std::io::{BufReader, Bytes, Read};
use std::iter::Peekable;

#[derive(Debug, PartialEq)]
pub enum JsonToken {
    True,
    False,
    Null,
    Number(String),
    String(String),
    ObjectStart,
    ObjectEnd,
    ArrayStart,
    ArrayEnd,
    Comma,
    Colon,
}

pub struct JsonLexer<R: Read> {
    bytes: Peekable<Bytes<BufReader<R>>>, // TODO: Keep position info
    failed: bool,
    buffer: Vec<u8>,
}

impl<R: Read> JsonLexer<R> {
    pub fn new(source: R) -> Self {
        JsonLexer {
            bytes: BufReader::new(source).bytes().peekable(),
            failed: false,
            buffer: Vec::new(),
        }
    }

    fn get_next_token(&mut self) -> Option<Result<JsonToken, JsonInputErr>> {
        loop {
            let byte = match self.bytes.peek() {
                Some(Ok(byte)) => *byte,
                Some(Err(_)) => return Some(Err(JsonInputErr::IoErr)),
                None => return None,
            };

            return Some(match byte {
                b' ' | b'\t' | b'\n' | b'\r' => {
                    self.bytes.next();
                    continue;
                }
                b'{' => self.skip_and_produce(JsonToken::ObjectStart),
                b'}' => self.skip_and_produce(JsonToken::ObjectEnd),
                b'[' => self.skip_and_produce(JsonToken::ArrayStart),
                b']' => self.skip_and_produce(JsonToken::ArrayEnd),
                b',' => self.skip_and_produce(JsonToken::Comma),
                b':' => self.skip_and_produce(JsonToken::Colon),
                b't' => self.match_token("true", JsonToken::True),
                b'f' => self.match_token("false", JsonToken::False),
                b'n' => self.match_token("null", JsonToken::Null),
                b'"' => self.match_string(),
                b'0'..=b'9' | b'-' => self.match_number(),
                _ => Err(JsonInputErr::InvalidJson),
            });
        }
    }

    fn skip_and_produce(&mut self, token: JsonToken) -> Result<JsonToken, JsonInputErr> {
        self.bytes.next();
        Ok(token)
    }

    fn expect_byte(&mut self) -> Result<u8, JsonInputErr> {
        match self.bytes.next() {
            Some(Ok(byte)) => Ok(byte),
            Some(Err(_)) => Err(JsonInputErr::IoErr),
            None => Err(JsonInputErr::UnexpectedEndOfInput),
        }
    }

    fn skip_byte(&mut self, target_byte: u8) -> Result<(), JsonInputErr> {
        let byte = self.expect_byte()?;
        if byte == target_byte {
            Ok(())
        } else {
            Err(JsonInputErr::InvalidJson)
        }
    }

    fn match_token(
        &mut self,
        target_str: &'static str,
        token: JsonToken,
    ) -> Result<JsonToken, JsonInputErr> {
        for target_byte in target_str.bytes() {
            self.skip_byte(target_byte)?;
        }
        Ok(token)
    }

    fn match_string(&mut self) -> Result<JsonToken, JsonInputErr> {
        self.skip_byte(b'"')?;
        self.buffer.clear();
        loop {
            let byte = self.expect_byte()?;

            if byte == b'"' {
                return Ok(JsonToken::String(
                    String::from_utf8(self.buffer.clone())
                        .map_err(|_| JsonInputErr::InvalidUtf8)?,
                ));
            } else if byte == b'\\' {
                let escaped = self.expect_byte()?;

                match escaped {
                    b'"' => self.buffer.push(b'"'),
                    b'\\' => self.buffer.push(b'\\'),
                    b'/' => self.buffer.push(b'/'),
                    b'b' => self.buffer.push(8),  // backspace
                    b'f' => self.buffer.push(12), // form feed
                    b'n' => self.buffer.push(b'\n'),
                    b'r' => self.buffer.push(b'\r'),
                    b't' => self.buffer.push(b'\t'),
                    b'u' => {
                        let surrogate_offset: u32 = (0xD800 << 10) + 0xDC00 - 0x10000;

                        let mut codepoint = self.parse_codepoint()? as u32;
                        if (0xD800..=0xDFFF).contains(&codepoint) {
                            // first codepoint was the start of a surrogate pair
                            self.skip_byte(b'\\')?;
                            self.skip_byte(b'u')?;
                            let codepoint2 = self.parse_codepoint()? as u32;
                            codepoint = ((codepoint << 10) + codepoint2) - surrogate_offset;
                        };
                        let mut buf = [0u8; 4];
                        let encoded_bytes = std::char::from_u32(codepoint)
                            .unwrap_or(std::char::REPLACEMENT_CHARACTER)
                            .encode_utf8(&mut buf)
                            .bytes();
                        for encoded_byte in encoded_bytes {
                            self.buffer.push(encoded_byte);
                        }
                    }
                    _ => return Err(JsonInputErr::InvalidEscape(escaped)),
                };
            } else {
                self.buffer.push(byte)
            }
        }
    }

    // "ab03..." -> 0xab03
    fn parse_codepoint(&mut self) -> Result<u16, JsonInputErr> {
        let mut codepoint: u16 = 0;
        for _ in 0..4 {
            codepoint <<= 4;
            let byte2 = self.expect_byte()?;
            codepoint += match byte2 {
                b'0'..=b'9' => byte2 - b'0',
                b'a'..=b'f' => byte2 - b'a' + 10,
                b'A'..=b'F' => byte2 - b'A' + 10,
                _ => return Err(JsonInputErr::InvalidEscape(byte2)),
            } as u16;
        }
        Ok(codepoint)
    }

    fn match_number(&mut self) -> Result<JsonToken, JsonInputErr> {
        let mut buffer = Vec::new();
        loop {
            let byte = match self.bytes.peek() {
                Some(Ok(byte)) => *byte,
                Some(Err(_)) => return Err(JsonInputErr::IoErr),
                None => break,
            };

            match byte {
                b'0'..=b'9' | b'-' | b'+' | b'.' | b'e' | b'E' => {
                    buffer.push(byte);
                    self.bytes.next();
                }
                _ => break,
            }
        }
        // TODO: Actually parse numbers
        Ok(JsonToken::Number(
            String::from_utf8(buffer).map_err(|_err| JsonInputErr::InvalidUtf8)?,
        ))
    }
}

impl<R: Read> Iterator for JsonLexer<R> {
    type Item = Result<JsonToken, JsonInputErr>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.failed {
            return None;
        }

        let res = self.get_next_token();
        if let Some(Err(_)) = res {
            self.failed = true;
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::inference::jsoninputerr::JsonInputErr;
    use std::fmt::Debug;

    #[test]
    fn empty_input() {
        assert_eq!(tokens_from_str(""), Ok(vec![]));
        assert_eq!(tokens_from_str(" \t\r\n"), Ok(vec![]));
    }

    #[test]
    fn bare_number() {
        assert_eq!(
            tokens_from_str("123"),
            Ok(vec![JsonToken::Number("123".to_string())])
        );
    }

    #[test]
    fn object() {
        assert_eq!(
            tokens_from_str("{}"),
            Ok(vec![JsonToken::ObjectStart, JsonToken::ObjectEnd])
        );
        assert_eq!(
            tokens_from_str("  {  }  "),
            Ok(vec![JsonToken::ObjectStart, JsonToken::ObjectEnd])
        );
    }

    #[test]
    fn string() {
        assert_eq!(
            tokens_from_str(r#" "hello world" "#),
            Ok(vec![JsonToken::String("hello world".to_string())])
        );
    }

    #[test]
    fn escapes() {
        assert_eq!(
            tokens_from_str(r#" "foo\nbar" "#),
            Ok(vec![JsonToken::String("foo\nbar".to_string())])
        );

        assert_eq!(
            tokens_from_str(r#" "John says \"Hello\"" "#),
            Ok(vec![JsonToken::String(r#"John says "Hello""#.to_string())])
        );
    }

    #[test]
    fn unicode_escapes() {
        assert_eq!(
            tokens_from_str(r#" "\u00e6" "#),
            Ok(vec![JsonToken::String("æ".to_string())])
        );

        assert_eq!(
            tokens_from_str(r#" "\uD83D\uDE00" "#),
            Ok(vec![JsonToken::String("😀".to_string())])
        );

        assert_eq!(
            tokens_from_str(r#" "\uD83D" "#),
            Err(JsonInputErr::InvalidJson)
        );
    }

    #[test]
    fn number() {
        assert_eq!(
            tokens_from_str(r#" 14.5 "#),
            Ok(vec![JsonToken::Number("14.5".to_string())])
        );
    }

    #[test]
    fn complex() {
        assert_eq!(
            tokens_from_str(
                r#"
                {
                    "foo": [1, true]
                }
                "#
            ),
            Ok(vec![
                JsonToken::ObjectStart,
                JsonToken::String("foo".to_string()),
                JsonToken::Colon,
                JsonToken::ArrayStart,
                JsonToken::Number("1".to_string()),
                JsonToken::Comma,
                JsonToken::True,
                JsonToken::ArrayEnd,
                JsonToken::ObjectEnd
            ])
        );
    }

    #[test]
    fn invalid() {
        assert_eq!(tokens_from_str("foo"), Err(JsonInputErr::InvalidJson));
        assert_eq!(tokens_from_str(" [ foo ] "), Err(JsonInputErr::InvalidJson));
    }

    fn tokens_from_str(s: &'static str) -> Result<Vec<JsonToken>, JsonInputErr> {
        let collected: Vec<Result<JsonToken, JsonInputErr>> =
            JsonLexer::new(s.as_bytes()).collect();
        coalesce_err(collected)
    }

    fn coalesce_err<T: Debug, E: Debug>(vec: Vec<Result<T, E>>) -> Result<Vec<T>, E> {
        let error_count = vec.iter().filter(|res| res.is_err()).count();
        match error_count {
            0 => Ok(vec.into_iter().map(|res| res.unwrap()).collect()),
            1 => Err(vec
                .into_iter()
                .find(|res| res.is_err())
                .unwrap()
                .unwrap_err()),
            _ => panic!("More than one error: {:?}", vec),
        }
    }
}
<|endoftext|>


<commit_before>pub fn std_misc() {
    println!("***STD Misc***");

    threads();
    channels();
    paths();
    // file_io();

    println!("");
}

fn threads() {
    use std::thread;

    static NTHREADS: i32 = 10;

    // we are currently in the main thread
    // vector for children
    let mut children = vec![];

    for i in 0..NTHREADS {
        // spin up another thread
        children.push(thread::spawn(move || {
            println!("this is a thread number {}", i);
        }));
    }

    for child in children {
        // wait for the thread to finish, returns a result
        let _ = child.join();
    }
}

fn channels() {
    use std::sync::mpsc::{Sender, Receiver};
    use std::sync::mpsc;
    use std::thread;

    static NTHREADS: i32 = 3;

    // channels have two endpoints - Sender<T> and Receiver<T>
    // T is the type of the message to be transferred
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();

    for id in 0..NTHREADS {
        // the sender endpoint can be copied
        let thread_tx = tx.clone();

        // each thread will send its id via the channel
        thread::spawn(move || {
            // the thread takes ownership over thread_tx
            // each thread queues a message in the channel
            thread_tx.send(id).unwrap();

            // sending is a non-blocking operation
            // the thread will continue immediately after sending its message
            println!("thread {} finished", id);
        });
    }

    // collecting all messages
    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        // the recv method picks a message from the channel
        // recv will block the current thread if there are no messages available
        ids.push(rx.recv());
    }

    // show the order in which the messages were sent
    println!("{:?}", ids);
}

fn paths() {
    use std::path::Path;

    // create a Path from an &'static str
    let path = Path::new(".");

    // the display method returns a Showable structure
    let display = path.display();

    // join merges a path with a byte container using the OS specific
    // separator and returns the new path
    let new_path = path.join("a").join("b");

    // convert the path into a string slice
    match new_path.to_str() {
        None => panic!("new path is not a valid UTF-8 sequence"),
        Some(s) => println!("new path is {}", s),
    }
}

fn file_io() {
    /* open */
    {
        use std::error::Error;
        use std::fs::File;
        use std::io::prelude::*;
        use std::path::Path;

        // create a path to the desired file
        let path = Path::new("hello.txt");
        let display = path.display();

        // open the path in read-only mode, returns io::Result<File>
        let mut file = match File::open(&path) {
            // the description method of io::Error returns a string
            // that describes the error
            Err(why) => panic!("couldn't open {}: {}", display,
                               why.description()),
            Ok(file) => file,
        };

        // read the file contents into a string, returns io::Result<usize>
        let mut s = String::new();
        match file.read_to_string(&mut s) {
            Err(why) => panic!("couldn't read {}: {}", display,
                               why.description()),
            Ok(_) => println!("{} contains:\n{}", display, s),
        }

        // file goes out of scope and the "hello.txt" file gets closed
    }

    /* create */
    {
        static LOREM_IPSUM: &'static str =
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit,sed du eiusmod tempor incidiunt ut labore et dolore magna aliqua.";

        use std::error::Error;
        use std::io::prelude::*;
        use std::fs::File;
        use std::path::Path;

        let path = Path::new("lorem_ipsum.txt");
        let display = path.display();

        // Open a file in write-only mode, returns io::Result<File>
        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {}: {}",
                               display,
                               why.description()),
            Ok(file) => file,
        };

        // write the LOREM_IPSUM string to file, returns io::Result<()>
        match file.write_all(LOREM_IPSUM.as_bytes()) {
            Err(why) => panic!("couldn't write to {}: {}", display,
                               why.description()),
            Ok(_) => println!("successfully wrote to {}", display),
        }
    }
}
<commit_msg>[Rust] Rust by Example - STD Misc 2/2<commit_after>pub fn std_misc() {
    println!("***STD Misc***");

    threads();
    channels();
    paths();
    // file_io();
    // child_processes();
    // filesystem_operations();
    program_arguments();

    println!("");
}

fn threads() {
    use std::thread;

    static NTHREADS: i32 = 10;

    // we are currently in the main thread
    // vector for children
    let mut children = vec![];

    for i in 0..NTHREADS {
        // spin up another thread
        children.push(thread::spawn(move || {
            println!("this is a thread number {}", i);
        }));
    }

    for child in children {
        // wait for the thread to finish, returns a result
        let _ = child.join();
    }
}

fn channels() {
    use std::sync::mpsc::{Sender, Receiver};
    use std::sync::mpsc;
    use std::thread;

    static NTHREADS: i32 = 3;

    // channels have two endpoints - Sender<T> and Receiver<T>
    // T is the type of the message to be transferred
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();

    for id in 0..NTHREADS {
        // the sender endpoint can be copied
        let thread_tx = tx.clone();

        // each thread will send its id via the channel
        thread::spawn(move || {
            // the thread takes ownership over thread_tx
            // each thread queues a message in the channel
            thread_tx.send(id).unwrap();

            // sending is a non-blocking operation
            // the thread will continue immediately after sending its message
            println!("thread {} finished", id);
        });
    }

    // collecting all messages
    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        // the recv method picks a message from the channel
        // recv will block the current thread if there are no messages available
        ids.push(rx.recv());
    }

    // show the order in which the messages were sent
    println!("{:?}", ids);
}

fn paths() {
    use std::path::Path;

    // create a Path from an &'static str
    let path = Path::new(".");

    // the display method returns a Showable structure
    let display = path.display();

    // join merges a path with a byte container using the OS specific
    // separator and returns the new path
    let new_path = path.join("a").join("b");

    // convert the path into a string slice
    match new_path.to_str() {
        None => panic!("new path is not a valid UTF-8 sequence"),
        Some(s) => println!("new path is {}", s),
    }
}

fn file_io() {
    /* open */
    {
        use std::error::Error;
        use std::fs::File;
        use std::io::prelude::*;
        use std::path::Path;

        // create a path to the desired file
        let path = Path::new("hello.txt");
        let display = path.display();

        // open the path in read-only mode, returns io::Result<File>
        let mut file = match File::open(&path) {
            // the description method of io::Error returns a string
            // that describes the error
            Err(why) => panic!("couldn't open {}: {}", display,
                               why.description()),
            Ok(file) => file,
        };

        // read the file contents into a string, returns io::Result<usize>
        let mut s = String::new();
        match file.read_to_string(&mut s) {
            Err(why) => panic!("couldn't read {}: {}", display,
                               why.description()),
            Ok(_) => println!("{} contains:\n{}", display, s),
        }

        // file goes out of scope and the "hello.txt" file gets closed
    }

    /* create */
    {
        static LOREM_IPSUM: &'static str =
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit,sed du eiusmod tempor incidiunt ut labore et dolore magna aliqua.";

        use std::error::Error;
        use std::io::prelude::*;
        use std::fs::File;
        use std::path::Path;

        let path = Path::new("lorem_ipsum.txt");
        let display = path.display();

        // Open a file in write-only mode, returns io::Result<File>
        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {}: {}",
                               display,
                               why.description()),
            Ok(file) => file,
        };

        // write the LOREM_IPSUM string to file, returns io::Result<()>
        match file.write_all(LOREM_IPSUM.as_bytes()) {
            Err(why) => panic!("couldn't write to {}: {}", display,
                               why.description()),
            Ok(_) => println!("successfully wrote to {}", display),
        }
    }
}

fn child_processes() {
    {
        use std::process::Command;

        let output = Command::new("rustc")
            .arg("--version")
            .output().unwrap_or_else(|e| {
                panic!("Failed to execute a process: {}", e);
            });

        if output.status.success() {
            let s = String::from_utf8_lossy(&output.stdout);

            print!("rustc succeded and stdout was:\n{}", s);
        } else {
            let s = String::from_utf8_lossy(&output.stderr);

            print!("rustc failed and stderr was:\n{}", s);
        }
    }

    /* pipes */
    {
        use std::error::Error;
        use std::io::prelude::*;
        use std::process::{Command, Stdio};

        static PANGRAM: &'static str =
            "the quick brown fox jumped over the lazy dog\n";

        // spawn the wc command
        let process = match Command::new("wc")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn() {
                Err(why) => panic!("Couldn't spawn wc: {}", why.description()),
                Ok(process) => process,
            };

        // write a string to the stdin of wc
        // stdin is Option<ChildStdin>, we can directly unwrap it here
        match process.stdin.unwrap().write_all(PANGRAM.as_bytes()) {
            Err(why) => panic!("couldn't write to wc stdin: {}",
                               why.description()),
            Ok(_) => println!("sent pangram to wc"),
        }

        // stdin does not live after the above calls
        // it's dropped and the pipe is closed
        // otherwise wc wouldn't start processing the input we sent

        // stdout is Option<ChildStdout>
        let mut s = String::new();
        match process.stdout.unwrap().read_to_string(&mut s) {
            Err(why) => panic!("couldn't read wc stdout: {}",
                               why.description()),
            Ok(_) => print!("wc responded with:\n{}", s),
        }

    }

    /* wait */
    {
        use std::process::Command;

        let mut child = Command::new("sleep").arg("1").spawn().unwrap();
        let _result = child.wait().unwrap();

        println!("reached the end!");
    }
}

fn filesystem_operations() {
    use std::fs;
    use std::fs::{File, OpenOptions};
    use std::io;
    use std::io::prelude::*;
    use std::os::unix;
    use std::path::Path;

    // a simple implementation of 'cat path'
    fn cat(path: &Path) -> io::Result<String> {
        let mut f = try!(File::open(path));
        let mut s = String::new();
        match f.read_to_string(&mut s) {
            Ok(_) => Ok(s),
            Err(e) => Err(e),
        }
    }

    // a simple implementation of 'echo s > path'
    fn echo(s: &str, path: &Path) -> io::Result<()> {
        let mut f = try!(File::create(path));

        f.write_all(s.as_bytes())
    }

    // a simple implementation of 'touch path' (ignores existing files)
    fn touch(path: &Path) -> io::Result<()> {
        match OpenOptions::new().create(true).write(true).open(path) {
            Ok(_) => Ok(()),
            Err(e) => Err(e),
        }
    }

    println!("mkdir a");
    // create a directory, returns io::Result<()>
    match fs::create_dir("a") {
        Err(e) => println!("! {:?}", e.kind()),
        Ok(_) => {},
    }

    println!("'echo hello > a/b.txt'");
    // the previous match can be simplified with unwrap_or_else
    echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|e| {
        println!("! {:?}", e.kind());
    });

    println!("mkdir -p a/c/d '");
    // recursively create a directory
    fs::create_dir_all("a/c/d").unwrap_or_else(|e| {
        println!("! {:?}", e.kind());
    });

    println!("touch a/c/e.txt");
    touch(&Path::new("a/c/e.txt")).unwrap_or_else(|e| {
        println!("! {:?}", e.kind());
    });

    println!("ln -s ../b.txt a/c/b.txt");
    // create a symlink
    if cfg!(target_family = "unix") {
        unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|e| {
            println!("! {:?}", e.kind());
        });
    }

    println!("cat a/c/b.txt");
    match cat(&Path::new("a/c/b.txt")) {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(s) => println!("> {}", s),
    }

    println!("ls a");
    // read the contents of a directory, returns io::Result<Vec<Path>>
    match fs::read_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(paths) => for path in paths {
            println!("> {:?}", path.unwrap().path());
        },
    }

    println!("rm a/c/e.txt");
    fs::remove_file("a/c/e.txt").unwrap_or_else(|e| {
        println!("! {:?}", e.kind());
    });

    println!("rmdir a/c/d");
    // remove an empty directory
    fs::remove_dir("a/c/d").unwrap_or_else(|e| {
        println!("! {:?}", e.kind());
    });
}

fn program_arguments() {
    /* basics */
    {
        use std::env;

        let args: Vec<String> = env::args().collect();

        // the first argument is the path
        println!("My path is {}", args[0]);

        // the rest of the arguments are passed command line parameters
        println!("I got {:?} arguments: {:?}", args.len()-1, &args[1..]);
    }

    /* argument parsing */
    {
        use std::env;

        fn increase(number: i32) {
            println!("{}", number+1);
        }

        fn decrease(number: i32) {
            println!("{}", number-1);
        }

        fn help() {
            println!("usage:
match_args <string>
     Check whether given string is the answer.
match_args {{increase|decrease}} <integer>
     Increase or decrease given integer by one");
        }

        let args: Vec<String> = env::args().collect();

        match args.len() {
            // no arguments
            1 => println!("my name is match_args, pass some arguments!"),
            // one argument
            2 => {
                match args[1].parse() {
                    Ok(42) => println!("This is the answer!"),
                    _ => println!("This is not the answer!"),
                }
            },
            // one command and one argument passed
            3 => {
                let cmd = &args[1];
                let num = &args[2];
                // parse the number
                let number: i32 = match num.parse() {
                    Ok(n) => n,
                    Err(_) => {
                        println!("error: second argument is not an integer!");
                        help();
                        return;
                    },
                };
                // parse the command
                match &cmd[..] {
                    "increase" => increase(number),
                    "decrease" => decrease(number),
                    _ => {
                        println!("error: invalid command");
                        help();
                    },
                }
            },
            // all other cases
            _ => {
                // show a help message
                help();
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Add `adjust_bits_per_sample`<commit_after><|endoftext|>


<commit_before>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::runtime::Runtime as GRuntime;

/// The `UpdateHook` type
///
/// Represents a hook which is executed whenever a entry in the store is updated (written to disk).
///
/// # Time of execution
///
/// This hook is executed _after_ the store operation succeeded, so _after_ the file is written to
/// disk.
pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?})",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    /// Set the configuration of the hook. See
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`.
    ///
    /// This function traces the error (using `trace_error()`) that
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`
    /// returns, if any.
    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use libimagerror::into::IntoError;
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use vcs::git::config::abort_on_repo_init_err;
        use vcs::git::config::is_enabled;
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action = StoreAction::Update;
        let cfg    = try!(self.runtime.config_value_or_err(&action));

        if !is_enabled(cfg) {
            return Ok(());
        }

        if !self.runtime.has_repository() {
            debug!("[GIT UPDATE HOOK]: Runtime has no repository...");
            if try!(self.runtime.config_value_or_err(&action).map(|c| abort_on_repo_init_err(c))) {
                // Abort on repo init failure
                debug!("[GIT UPDATE HOOK]: Config says we should abort if we have no repository");
                debug!("[GIT UPDATE HOOK]: Returing Err(_)");
                return Err(GHEK::RepositoryInitError.into_error())
                    .map_err_into(GHEK::RepositoryError)
                    .map_into_hook_error()
            } else {
                debug!("[GIT UPDATE HOOK]: Config says it is okay to not have a repository");
                debug!("[GIT UPDATE HOOK]: Returing Ok(())");
                return Ok(())
            }
        }

        let _         = try!(self.runtime.ensure_cfg_branch_is_checked_out(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched signature object")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched file status")
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        debug!("File status: STATUS_WT_NEW      = {}", file_status.contains(STATUS_WT_NEW));
        debug!("File status: STATUS_WT_MODIFIED = {}", file_status.contains(STATUS_WT_MODIFIED));

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_dbg(|_| format!("[GIT UPDATE HOOK]: Added id ({:?}) to index", id))
                .map_into_hook_error()
        );

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_dbg_str("[GIT UPDATE HOOK]: Wrote tree")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_dbg_str("[GIT UPDATE HOOK]: Fetched commit parents of HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_dbg_str("[GIT UPDATE HOOK]: Found Tree for parents")
                .map_into_hook_error()
        );

        let message = try!(commit_message(&repo, cfg, StoreAction::Update, &id)
                .map_dbg_err_str("Failed to get commit message")
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched commit message"));

        try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_str("Committed")
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
        );

        index.write()
            .map_err_into(GHEK::RepositoryIndexWritingError)
            .map_dbg_err_str("Failed to write tree")
            .map_into_hook_error()
            .map(|_| ())
    }

}

<commit_msg>Return if the file is not new and not modified<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::runtime::Runtime as GRuntime;

/// The `UpdateHook` type
///
/// Represents a hook which is executed whenever a entry in the store is updated (written to disk).
///
/// # Time of execution
///
/// This hook is executed _after_ the store operation succeeded, so _after_ the file is written to
/// disk.
pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?})",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    /// Set the configuration of the hook. See
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`.
    ///
    /// This function traces the error (using `trace_error()`) that
    /// `libimagstorestdhook::vcs::git::runtime::Runtime::set_config()`
    /// returns, if any.
    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use libimagerror::into::IntoError;
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use vcs::git::config::abort_on_repo_init_err;
        use vcs::git::config::is_enabled;
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action = StoreAction::Update;
        let cfg    = try!(self.runtime.config_value_or_err(&action));

        if !is_enabled(cfg) {
            return Ok(());
        }

        if !self.runtime.has_repository() {
            debug!("[GIT UPDATE HOOK]: Runtime has no repository...");
            if try!(self.runtime.config_value_or_err(&action).map(|c| abort_on_repo_init_err(c))) {
                // Abort on repo init failure
                debug!("[GIT UPDATE HOOK]: Config says we should abort if we have no repository");
                debug!("[GIT UPDATE HOOK]: Returing Err(_)");
                return Err(GHEK::RepositoryInitError.into_error())
                    .map_err_into(GHEK::RepositoryError)
                    .map_into_hook_error()
            } else {
                debug!("[GIT UPDATE HOOK]: Config says it is okay to not have a repository");
                debug!("[GIT UPDATE HOOK]: Returing Ok(())");
                return Ok(())
            }
        }

        let _         = try!(self.runtime.ensure_cfg_branch_is_checked_out(&action));
        let repo      = try!(self.runtime.repository(&action));

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched file status")
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        debug!("File status: STATUS_WT_NEW      = {}", file_status.contains(STATUS_WT_NEW));
        debug!("File status: STATUS_WT_MODIFIED = {}", file_status.contains(STATUS_WT_MODIFIED));

        if !file_status.contains(STATUS_WT_NEW) && !file_status.contains(STATUS_WT_MODIFIED) {
            // File seems to be unmodified and not new. This means that the file is already
            // committed and we can return here.
            return Ok(())
        }

        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched signature object")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched HEAD")
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_dbg(|_| format!("[GIT UPDATE HOOK]: Added id ({:?}) to index", id))
                .map_into_hook_error()
        );

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_dbg_str("[GIT UPDATE HOOK]: Wrote tree")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_dbg_str("[GIT UPDATE HOOK]: Fetched commit parents of HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_dbg_str("[GIT UPDATE HOOK]: Found Tree for parents")
                .map_into_hook_error()
        );

        let message = try!(commit_message(&repo, cfg, StoreAction::Update, &id)
                .map_dbg_err_str("Failed to get commit message")
                .map_dbg_str("[GIT UPDATE HOOK]: Fetched commit message"));

        try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_str("Committed")
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
        );

        index.write()
            .map_err_into(GHEK::RepositoryIndexWritingError)
            .map_dbg_err_str("Failed to write tree")
            .map_into_hook_error()
            .map(|_| ())
    }

}

<|endoftext|>


<commit_before>use nix::errno::*;
use nix::fcntl::{open, openat, readlink, readlinkat, renameat, OFlag};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use nix::Error;
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;
use tempfile::{self, NamedTempFile};

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty()).unwrap();
    let fd = openat(
        dirfd,
        tmp.path().file_name().unwrap(),
        OFlag::O_RDONLY,
        Mode::empty(),
    )
    .unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(
        renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
        Error::Sys(Errno::ENOENT)
    );
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(
        readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
        expected_dir
    );
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(
            tmp.as_raw_fd(),
            Some(&mut offset),
            wr,
            None,
            2,
            SpliceFFlags::empty(),
        )
        .unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    #[test]
    #[cfg(not(any(target_arch = "aarch64",
                  target_arch = "arm",
                  target_arch = "armv7",
                  target_arch = "i686",
                  target_arch = "mips",
                  target_arch = "mips64",
                  target_arch = "mips64el",
                  target_arch = "powerpc64")))]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).expect("fstat failed").st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "WRITE".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "READ".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: usize) -> Option<(String, String)> {
        let file = File::open("/proc/locks").expect("open /proc/locks failed");
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: usize = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()));
            }
        }
        None
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    any(target_os = "wasi", target_env = "wasi"),
    target_env = "uclibc",
    target_env = "freebsd"
))]
mod test_posix_fadvise {

    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;
    use std::os::unix::io::{AsRawFd, RawFd};
    use tempfile::NamedTempFile;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno =
            posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    any(target_os = "wasi", target_env = "wasi"),
    target_os = "freebsd"
))]
mod test_posix_fallocate {

    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;
    use std::{
        io::Read,
        os::unix::io::{AsRawFd, RawFd},
    };
    use tempfile::NamedTempFile;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL) | Sys(Errno::ENODEV) | Sys(Errno::ESPIPE) | Sys(Errno::EBADF) => (),
            errno => panic!("unexpected errno {}", errno,),
        }
    }
}
<commit_msg>One more arch exclusion<commit_after>use nix::errno::*;
use nix::fcntl::{open, openat, readlink, readlinkat, renameat, OFlag};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use nix::Error;
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;
use tempfile::{self, NamedTempFile};

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty()).unwrap();
    let fd = openat(
        dirfd,
        tmp.path().file_name().unwrap(),
        OFlag::O_RDONLY,
        Mode::empty(),
    )
    .unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(
        renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
        Error::Sys(Errno::ENOENT)
    );
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(
        readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
        expected_dir
    );
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(
            tmp.as_raw_fd(),
            Some(&mut offset),
            wr,
            None,
            2,
            SpliceFFlags::empty(),
        )
        .unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    #[test]
    #[cfg(not(any(target_arch = "aarch64",
                  target_arch = "arm",
                  target_arch = "armv7",
                  target_arch = "i686",
                  target_arch = "mips",
                  target_arch = "mips64",
                  target_arch = "mips64el",
                  target_arch = "powerpc64",
                  target_arch = "powerpc64le")))]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).expect("fstat failed").st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "WRITE".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "READ".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: usize) -> Option<(String, String)> {
        let file = File::open("/proc/locks").expect("open /proc/locks failed");
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: usize = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()));
            }
        }
        None
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    any(target_os = "wasi", target_env = "wasi"),
    target_env = "uclibc",
    target_env = "freebsd"
))]
mod test_posix_fadvise {

    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;
    use std::os::unix::io::{AsRawFd, RawFd};
    use tempfile::NamedTempFile;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno =
            posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    any(target_os = "wasi", target_env = "wasi"),
    target_os = "freebsd"
))]
mod test_posix_fallocate {

    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;
    use std::{
        io::Read,
        os::unix::io::{AsRawFd, RawFd},
    };
    use tempfile::NamedTempFile;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL) | Sys(Errno::ENODEV) | Sys(Errno::ESPIPE) | Sys(Errno::EBADF) => (),
            errno => panic!("unexpected errno {}", errno,),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

use boxed::Box;
use convert::Into;
use error;
use fmt;
use marker::{Send, Sync};
use option::Option::{self, Some, None};
use result;
use sys;

/// A type for results generated by I/O related functions where the `Err` type
/// is hard-wired to `io::Error`.
///
/// This typedef is generally used to avoid writing out `io::Error` directly and
/// is otherwise a direct mapping to `std::result::Result`.
#[stable(feature = "rust1", since = "1.0.0")]
pub type Result<T> = result::Result<T, Error>;

/// The error type for I/O operations of the `Read`, `Write`, `Seek`, and
/// associated traits.
///
/// Errors mostly originate from the underlying OS, but custom instances of
/// `Error` can be created with crafted error messages and a particular value of
/// `ErrorKind`.
#[derive(Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Error {
    repr: Repr,
}

#[derive(Debug)]
enum Repr {
    Os(i32),
    Custom(Box<Custom>),
}

#[derive(Debug)]
struct Custom {
    kind: ErrorKind,
    error: Box<error::Error+Send+Sync>,
}

/// A list specifying general categories of I/O error.
///
/// This list is intended to grow over time and it is not recommended to
/// exhaustively match against it.
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
pub enum ErrorKind {
    /// An entity was not found, often a file.
    #[stable(feature = "rust1", since = "1.0.0")]
    NotFound,
    /// The operation lacked the necessary privileges to complete.
    #[stable(feature = "rust1", since = "1.0.0")]
    PermissionDenied,
    /// The connection was refused by the remote server.
    #[stable(feature = "rust1", since = "1.0.0")]
    ConnectionRefused,
    /// The connection was reset by the remote server.
    #[stable(feature = "rust1", since = "1.0.0")]
    ConnectionReset,
    /// The connection was aborted (terminated) by the remote server.
    #[stable(feature = "rust1", since = "1.0.0")]
    ConnectionAborted,
    /// The network operation failed because it was not connected yet.
    #[stable(feature = "rust1", since = "1.0.0")]
    NotConnected,
    /// A socket address could not be bound because the address is already in
    /// use elsewhere.
    #[stable(feature = "rust1", since = "1.0.0")]
    AddrInUse,
    /// A nonexistent interface was requested or the requested address was not
    /// local.
    #[stable(feature = "rust1", since = "1.0.0")]
    AddrNotAvailable,
    /// The operation failed because a pipe was closed.
    #[stable(feature = "rust1", since = "1.0.0")]
    BrokenPipe,
    /// An entity already exists, often a file.
    #[stable(feature = "rust1", since = "1.0.0")]
    AlreadyExists,
    /// The operation needs to block to complete, but the blocking operation was
    /// requested to not occur.
    #[stable(feature = "rust1", since = "1.0.0")]
    WouldBlock,
    /// A parameter was incorrect.
    #[stable(feature = "rust1", since = "1.0.0")]
    InvalidInput,
    /// Data not valid for the operation were encountered.
    ///
    /// Unlike `InvalidInput`, this typically means that the operation
    /// parameters were valid, however the error was caused by malformed
    /// input data.
    #[stable(feature = "io_invalid_data", since = "1.2.0")]
    InvalidData,
    /// The I/O operation's timeout expired, causing it to be canceled.
    #[stable(feature = "rust1", since = "1.0.0")]
    TimedOut,
    /// An error returned when an operation could not be completed because a
    /// call to `write` returned `Ok(0)`.
    ///
    /// This typically means that an operation could only succeed if it wrote a
    /// particular number of bytes but only a smaller number of bytes could be
    /// written.
    #[stable(feature = "rust1", since = "1.0.0")]
    WriteZero,
    /// This operation was interrupted.
    ///
    /// Interrupted operations can typically be retried.
    #[stable(feature = "rust1", since = "1.0.0")]
    Interrupted,
    /// Any I/O error not part of this list.
    #[stable(feature = "rust1", since = "1.0.0")]
    Other,

    /// Any I/O error not part of this list.
    #[unstable(feature = "io_error_internals",
               reason = "better expressed through extensible enums that this \
                         enum cannot be exhaustively matched against")]
    #[doc(hidden)]
    __Nonexhaustive,
}

impl Error {
    /// Creates a new I/O error from a known kind of error as well as an
    /// arbitrary error payload.
    ///
    /// This function is used to generically create I/O errors which do not
    /// originate from the OS itself. The `error` argument is an arbitrary
    /// payload which will be contained in this `Error`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::{Error, ErrorKind};
    ///
    /// // errors can be created from strings
    /// let custom_error = Error::new(ErrorKind::Other, "oh no!");
    ///
    /// // errors can also be created from other errors
    /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn new<E>(kind: ErrorKind, error: E) -> Error
        where E: Into<Box<error::Error+Send+Sync>>
    {
        Error {
            repr: Repr::Custom(Box::new(Custom {
                kind: kind,
                error: error.into(),
            }))
        }
    }

    /// Returns an error representing the last OS error which occurred.
    ///
    /// This function reads the value of `errno` for the target platform (e.g.
    /// `GetLastError` on Windows) and will return a corresponding instance of
    /// `Error` for the error code.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn last_os_error() -> Error {
        Error::from_raw_os_error(sys::os::errno() as i32)
    }

    /// Creates a new instance of an `Error` from a particular OS error code.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn from_raw_os_error(code: i32) -> Error {
        Error { repr: Repr::Os(code) }
    }

    /// Returns the OS error that this error represents (if any).
    ///
    /// If this `Error` was constructed via `last_os_error` or
    /// `from_raw_os_error`, then this function will return `Some`, otherwise
    /// it will return `None`.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn raw_os_error(&self) -> Option<i32> {
        match self.repr {
            Repr::Os(i) => Some(i),
            Repr::Custom(..) => None,
        }
    }

    /// Returns a reference to the inner error wrapped by this error (if any).
    ///
    /// If this `Error` was constructed via `new` then this function will
    /// return `Some`, otherwise it will return `None`.
    #[unstable(feature = "io_error_inner",
               reason = "recently added and requires UFCS to downcast")]
    pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(ref c) => Some(&*c.error),
        }
    }

    /// Returns a mutable reference to the inner error wrapped by this error
    /// (if any).
    ///
    /// If this `Error` was constructed via `new` then this function will
    /// return `Some`, otherwise it will return `None`.
    #[unstable(feature = "io_error_inner",
               reason = "recently added and requires UFCS to downcast")]
    pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(ref mut c) => Some(&mut *c.error),
        }
    }

    /// Consumes the `Error`, returning its inner error (if any).
    ///
    /// If this `Error` was constructed via `new` then this function will
    /// return `Some`, otherwise it will return `None`.
    #[unstable(feature = "io_error_inner",
               reason = "recently added and requires UFCS to downcast")]
    pub fn into_inner(self) -> Option<Box<error::Error+Send+Sync>> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(c) => Some(c.error)
        }
    }

    /// Returns the corresponding `ErrorKind` for this error.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn kind(&self) -> ErrorKind {
        match self.repr {
            Repr::Os(code) => sys::decode_error_kind(code),
            Repr::Custom(ref c) => c.kind,
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.repr {
            Repr::Os(code) => {
                let detail = sys::os::error_string(code);
                write!(fmt, "{} (os error {})", detail, code)
            }
            Repr::Custom(ref c) => c.error.fmt(fmt),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl error::Error for Error {
    fn description(&self) -> &str {
        match self.repr {
            Repr::Os(..) => "os error",
            Repr::Custom(ref c) => c.error.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(ref c) => c.error.cause(),
        }
    }
}

fn _assert_error_is_sync_send() {
    fn _is_sync_send<T: Sync+Send>() {}
    _is_sync_send::<Error>();
}

#[cfg(test)]
mod test {
    use prelude::v1::*;
    use super::{Error, ErrorKind};
    use error;
    use error::Error as error_Error;
    use fmt;

    #[test]
    fn test_downcasting() {
        #[derive(Debug)]
        struct TestError;

        impl fmt::Display for TestError {
            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                Ok(())
            }
        }

        impl error::Error for TestError {
            fn description(&self) -> &str {
                "asdf"
            }
        }

        // we have to call all of these UFCS style right now since method
        // resolution won't implicitly drop the Send+Sync bounds
        let mut err = Error::new(ErrorKind::Other, TestError);
        assert!(error::Error::is::<TestError>(err.get_ref().unwrap()));
        assert_eq!("asdf", err.get_ref().unwrap().description());
        assert!(error::Error::is::<TestError>(err.get_mut().unwrap()));
        let extracted = err.into_inner().unwrap();
        error::Error::downcast::<TestError>(extracted).unwrap();
    }
}
<commit_msg>Auto merge of #26416 - retep998:error-debug, r=sfackler<commit_after>// Copyright 2015 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.

use boxed::Box;
use convert::Into;
use error;
use fmt;
use marker::{Send, Sync};
use option::Option::{self, Some, None};
use result;
use sys;

/// A type for results generated by I/O related functions where the `Err` type
/// is hard-wired to `io::Error`.
///
/// This typedef is generally used to avoid writing out `io::Error` directly and
/// is otherwise a direct mapping to `std::result::Result`.
#[stable(feature = "rust1", since = "1.0.0")]
pub type Result<T> = result::Result<T, Error>;

/// The error type for I/O operations of the `Read`, `Write`, `Seek`, and
/// associated traits.
///
/// Errors mostly originate from the underlying OS, but custom instances of
/// `Error` can be created with crafted error messages and a particular value of
/// `ErrorKind`.
#[derive(Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Error {
    repr: Repr,
}

enum Repr {
    Os(i32),
    Custom(Box<Custom>),
}

#[derive(Debug)]
struct Custom {
    kind: ErrorKind,
    error: Box<error::Error+Send+Sync>,
}

/// A list specifying general categories of I/O error.
///
/// This list is intended to grow over time and it is not recommended to
/// exhaustively match against it.
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
pub enum ErrorKind {
    /// An entity was not found, often a file.
    #[stable(feature = "rust1", since = "1.0.0")]
    NotFound,
    /// The operation lacked the necessary privileges to complete.
    #[stable(feature = "rust1", since = "1.0.0")]
    PermissionDenied,
    /// The connection was refused by the remote server.
    #[stable(feature = "rust1", since = "1.0.0")]
    ConnectionRefused,
    /// The connection was reset by the remote server.
    #[stable(feature = "rust1", since = "1.0.0")]
    ConnectionReset,
    /// The connection was aborted (terminated) by the remote server.
    #[stable(feature = "rust1", since = "1.0.0")]
    ConnectionAborted,
    /// The network operation failed because it was not connected yet.
    #[stable(feature = "rust1", since = "1.0.0")]
    NotConnected,
    /// A socket address could not be bound because the address is already in
    /// use elsewhere.
    #[stable(feature = "rust1", since = "1.0.0")]
    AddrInUse,
    /// A nonexistent interface was requested or the requested address was not
    /// local.
    #[stable(feature = "rust1", since = "1.0.0")]
    AddrNotAvailable,
    /// The operation failed because a pipe was closed.
    #[stable(feature = "rust1", since = "1.0.0")]
    BrokenPipe,
    /// An entity already exists, often a file.
    #[stable(feature = "rust1", since = "1.0.0")]
    AlreadyExists,
    /// The operation needs to block to complete, but the blocking operation was
    /// requested to not occur.
    #[stable(feature = "rust1", since = "1.0.0")]
    WouldBlock,
    /// A parameter was incorrect.
    #[stable(feature = "rust1", since = "1.0.0")]
    InvalidInput,
    /// Data not valid for the operation were encountered.
    ///
    /// Unlike `InvalidInput`, this typically means that the operation
    /// parameters were valid, however the error was caused by malformed
    /// input data.
    #[stable(feature = "io_invalid_data", since = "1.2.0")]
    InvalidData,
    /// The I/O operation's timeout expired, causing it to be canceled.
    #[stable(feature = "rust1", since = "1.0.0")]
    TimedOut,
    /// An error returned when an operation could not be completed because a
    /// call to `write` returned `Ok(0)`.
    ///
    /// This typically means that an operation could only succeed if it wrote a
    /// particular number of bytes but only a smaller number of bytes could be
    /// written.
    #[stable(feature = "rust1", since = "1.0.0")]
    WriteZero,
    /// This operation was interrupted.
    ///
    /// Interrupted operations can typically be retried.
    #[stable(feature = "rust1", since = "1.0.0")]
    Interrupted,
    /// Any I/O error not part of this list.
    #[stable(feature = "rust1", since = "1.0.0")]
    Other,

    /// Any I/O error not part of this list.
    #[unstable(feature = "io_error_internals",
               reason = "better expressed through extensible enums that this \
                         enum cannot be exhaustively matched against")]
    #[doc(hidden)]
    __Nonexhaustive,
}

impl Error {
    /// Creates a new I/O error from a known kind of error as well as an
    /// arbitrary error payload.
    ///
    /// This function is used to generically create I/O errors which do not
    /// originate from the OS itself. The `error` argument is an arbitrary
    /// payload which will be contained in this `Error`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::{Error, ErrorKind};
    ///
    /// // errors can be created from strings
    /// let custom_error = Error::new(ErrorKind::Other, "oh no!");
    ///
    /// // errors can also be created from other errors
    /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn new<E>(kind: ErrorKind, error: E) -> Error
        where E: Into<Box<error::Error+Send+Sync>>
    {
        Error {
            repr: Repr::Custom(Box::new(Custom {
                kind: kind,
                error: error.into(),
            }))
        }
    }

    /// Returns an error representing the last OS error which occurred.
    ///
    /// This function reads the value of `errno` for the target platform (e.g.
    /// `GetLastError` on Windows) and will return a corresponding instance of
    /// `Error` for the error code.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn last_os_error() -> Error {
        Error::from_raw_os_error(sys::os::errno() as i32)
    }

    /// Creates a new instance of an `Error` from a particular OS error code.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn from_raw_os_error(code: i32) -> Error {
        Error { repr: Repr::Os(code) }
    }

    /// Returns the OS error that this error represents (if any).
    ///
    /// If this `Error` was constructed via `last_os_error` or
    /// `from_raw_os_error`, then this function will return `Some`, otherwise
    /// it will return `None`.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn raw_os_error(&self) -> Option<i32> {
        match self.repr {
            Repr::Os(i) => Some(i),
            Repr::Custom(..) => None,
        }
    }

    /// Returns a reference to the inner error wrapped by this error (if any).
    ///
    /// If this `Error` was constructed via `new` then this function will
    /// return `Some`, otherwise it will return `None`.
    #[unstable(feature = "io_error_inner",
               reason = "recently added and requires UFCS to downcast")]
    pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(ref c) => Some(&*c.error),
        }
    }

    /// Returns a mutable reference to the inner error wrapped by this error
    /// (if any).
    ///
    /// If this `Error` was constructed via `new` then this function will
    /// return `Some`, otherwise it will return `None`.
    #[unstable(feature = "io_error_inner",
               reason = "recently added and requires UFCS to downcast")]
    pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(ref mut c) => Some(&mut *c.error),
        }
    }

    /// Consumes the `Error`, returning its inner error (if any).
    ///
    /// If this `Error` was constructed via `new` then this function will
    /// return `Some`, otherwise it will return `None`.
    #[unstable(feature = "io_error_inner",
               reason = "recently added and requires UFCS to downcast")]
    pub fn into_inner(self) -> Option<Box<error::Error+Send+Sync>> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(c) => Some(c.error)
        }
    }

    /// Returns the corresponding `ErrorKind` for this error.
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn kind(&self) -> ErrorKind {
        match self.repr {
            Repr::Os(code) => sys::decode_error_kind(code),
            Repr::Custom(ref c) => c.kind,
        }
    }
}

impl fmt::Debug for Repr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Repr::Os(ref code) =>
                fmt.debug_struct("Os").field("code", code)
                   .field("message", &sys::os::error_string(*code)).finish(),
            &Repr::Custom(ref c) => fmt.debug_tuple("Custom").field(c).finish(),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self.repr {
            Repr::Os(code) => {
                let detail = sys::os::error_string(code);
                write!(fmt, "{} (os error {})", detail, code)
            }
            Repr::Custom(ref c) => c.error.fmt(fmt),
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl error::Error for Error {
    fn description(&self) -> &str {
        match self.repr {
            Repr::Os(..) => "os error",
            Repr::Custom(ref c) => c.error.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self.repr {
            Repr::Os(..) => None,
            Repr::Custom(ref c) => c.error.cause(),
        }
    }
}

fn _assert_error_is_sync_send() {
    fn _is_sync_send<T: Sync+Send>() {}
    _is_sync_send::<Error>();
}

#[cfg(test)]
mod test {
    use prelude::v1::*;
    use super::{Error, ErrorKind};
    use error;
    use error::Error as error_Error;
    use fmt;
    use sys::os::error_string;

    #[test]
    fn test_debug_error() {
        let code = 6;
        let msg = error_string(code);
        let err = Error { repr: super::Repr::Os(code) };
        let expected = format!("Error {{ repr: Os {{ code: {:?}, message: {:?} }} }}", code, msg);
        assert_eq!(format!("{:?}", err), expected);
    }

    #[test]
    fn test_downcasting() {
        #[derive(Debug)]
        struct TestError;

        impl fmt::Display for TestError {
            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                Ok(())
            }
        }

        impl error::Error for TestError {
            fn description(&self) -> &str {
                "asdf"
            }
        }

        // we have to call all of these UFCS style right now since method
        // resolution won't implicitly drop the Send+Sync bounds
        let mut err = Error::new(ErrorKind::Other, TestError);
        assert!(error::Error::is::<TestError>(err.get_ref().unwrap()));
        assert_eq!("asdf", err.get_ref().unwrap().description());
        assert!(error::Error::is::<TestError>(err.get_mut().unwrap()));
        let extracted = err.into_inner().unwrap();
        error::Error::downcast::<TestError>(extracted).unwrap();
    }
}
<|endoftext|>