<commit_before>extern crate slack;

use std::env;
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::channel;
use std::thread;

use rustc_serialize::json::Json;
use rustc_serialize::json;
use rustc_serialize::json::DecoderError::MissingFieldError;
use rustc_serialize::Decodable;
use rustc_serialize::Decoder;

use slack::Message;

use message::AdapterMsg;
use message::IncomingMessage;
use adapter::ChatAdapter;

/// SlackAdapter sends and receives messages from the Slack chat service. Until actualy
/// configuration is added, the slack token should be placed in the environment variable
/// `SLACK_BOT_TOKEN`
pub struct SlackAdapter {
    token: String
}

impl SlackAdapter {
    pub fn new() -> SlackAdapter {
        SlackAdapter {
            token: match env::var("SLACK_BOT_TOKEN") {
                Ok(t) => t,
                Err(_) => panic!("Failed to get SLACK_BOT_TOKEN from env")
            }
        }
    }
}

struct MyHandler {
  count: i64,
  tx_bot: Sender<IncomingMessage>,
  tx_adapter: Sender<AdapterMsg>
}

/// Data for a SlackMsg::Message
#[allow(dead_code)]
struct MessageData {
    text: String,
    channel: String,
    user: String,
    ts: String,
    team: String
}

macro_rules! str_accessor {
    ($s:ident) => {
        pub fn $s(&self) -> &str {
            self.$s.as_ref()
        }
    }
}

#[allow(dead_code)]
impl MessageData {
    str_accessor!(text);
    str_accessor!(channel);
    str_accessor!(user);
    str_accessor!(ts);
    str_accessor!(team);
}

/// Incoming slack messages on the websocket api
enum SlackMsg {
    /// A message was sent to a channel
    Message(MessageData),

    /// Some other type of message arrived.
    /// [The list](https://api.slack.com/rtm) is quite extensive, and only the
    /// messages the adapter is concerned with are enumerated here.
    Other
}

impl Decodable for SlackMsg {
    fn decode<D: Decoder>(d: &mut D) -> Result<SlackMsg, D::Error> {
        d.read_struct("root", 0, |root| {
            let msg_type = try!(root.read_struct_field("type", 0, |f| f.read_str() ));

            match msg_type.as_ref() {
                "message" => {
                    Ok(SlackMsg::Message(MessageData {
                        text: try!(root.read_struct_field("text", 0, |f| f.read_str())),
                        channel: try!(root.read_struct_field("channel", 0, |f| f.read_str())),
                        user: try!(root.read_struct_field("user", 0, |f| f.read_str())),
                        ts: try!(root.read_struct_field("ts", 0, |f| f.read_str())),
                        team: try!(root.read_struct_field("team", 0, |f| f.read_str())),
                    }))
                },
                _ => {
                    Ok(SlackMsg::Other)
                }
            }
        })
    }
}

fn string_to_slack_msg(raw: &str) -> Result<SlackMsg, json::DecoderError> {
    // Some messages arriving from the slack client don't have a type. So far I've only
    // witnessed confirmation messages arriving in this fashion. Since they go through the same
    // pipeline as content messages, the decoder should be able to handle them.
    match json::decode::<SlackMsg>(raw) {
        Ok(value) => Ok(value),
        Err(e) => {
            match e {
                MissingFieldError(field) => {
                    match field.as_ref() {
                        "type" => Ok(SlackMsg::Other),
                        _ => Err(MissingFieldError(field))
                    }
                },
                _ => Err(e)
            }
        }
    }
}

#[allow(unused_variables)]
impl slack::MessageHandler for MyHandler {
    fn on_receive(&mut self, cli: &mut slack::RtmClient, raw: &str) {
        println!("Received[{}]: {}", self.count, raw.to_string());
        self.count = self.count + 1;

        match string_to_slack_msg(raw) {
            Ok(slack_msg) => {
                match slack_msg {
                    SlackMsg::Message(msg) => {
                        let incoming = IncomingMessage::new("SlackAdapter".to_owned(), None,
                            Some(msg.channel().to_owned()), Some(msg.user().to_owned()),
                            msg.text().to_owned(), self.tx_adapter.clone());

                        self.tx_bot.send(incoming).ok().expect("Bot unable to process messages");
                    },
                    _ => ()
                }
            },
            Err(e) => {
                println!("error decoding slack message: {:?}", e);
                println!("please consider reporting this to jwilm/chatbot as it is probably a bug");
            }
        }
    }

    fn on_ping(&mut self, cli: &mut slack::RtmClient) {
        println!("<on_ping>");
    }

    fn on_close(&mut self, cli: &mut slack::RtmClient) {
        println!("<on_close>");
    }

    fn on_connect(&mut self, cli: &mut slack::RtmClient) {
        println!("<on_connect>");
        // let _ = cli.send_message("#general", "bla");
    }
}

impl ChatAdapter for SlackAdapter {
    /// SlackAdapter name
    fn get_name(&self) -> &str {
        "SlackAdapter"
    }

    fn process_events(&self) -> Receiver<IncomingMessage> {
        println!("SlackAdapter: process_events");
        let (tx_bot, rx_bot) = channel();
        let (tx_adapter, rx_adapter) = channel();

        let uid = AtomicIsize::new(0);

        let mut cli = slack::RtmClient::new();
        let (client, slack_rx) = cli.login(self.token.as_ref()).unwrap();
        let slack_tx = cli.get_outs().unwrap();

        thread::Builder::new().name("Chatbot Slack Receiver".to_owned()).spawn(move || {
            let mut handler = MyHandler{count: 0, tx_bot: tx_bot, tx_adapter: tx_adapter};
            cli.run::<MyHandler>(&mut handler, client, slack_rx).unwrap();
        }).ok().expect("failed to create thread for slack receiver");

        thread::Builder::new().name("Chatbot Slack Sender".to_owned()).spawn(move || {
            loop {
                match rx_adapter.recv() {
                    Ok(msg) => {
                        match msg {
                            AdapterMsg::Outgoing(msg) => {
                                let n = uid.fetch_add(1, Ordering::SeqCst);
                                let chan = msg.get_incoming().channel().unwrap();
                                let payload = msg.as_ref();
                                let mut obj = json::Object::new();
                                obj.insert("id".to_string(), Json::I64(n as i64));
                                obj.insert("channel".to_string(), Json::String(chan.to_owned()));
                                obj.insert("type".to_string(), Json::String("message".to_owned()));
                                obj.insert("text".to_string(), Json::String(payload.to_owned()));
                                let json = Json::Object(obj);
                                let slack_msg = Message::Text(json.to_string());
                                slack_tx.send(slack_msg).unwrap();
                            }
                            _ => println!("TODO")
                        }
                    },
                    Err(e) => {
                        println!("error receiving outgoing messages to the slack adapter: {}", e);
                        break
                    }
                }
            }
        }).ok().expect("failed to create thread for slack sender");

        rx_bot
    }
}

#[cfg(test)]
mod tests {
    use adapter::slack::SlackMsg;
    use adapter::slack::string_to_slack_msg;

    #[test]
    fn decode_message() {
        let raw = "{\"type\":\"message\", \
                    \"channel\":\"D04UYUAMW\", \
                    \"user\":\"U02ALMR84\", \
                    \"text\":\"ping\", \
                    \"ts\":\"1432563914.000007\", \
                    \"team\":\"T02ALMR82\"}";

        let slack_msg = string_to_slack_msg(raw).unwrap();

        match slack_msg {
            SlackMsg::Message(data) => {
                assert_eq!(data.text(), "ping");
                assert_eq!(data.channel(), "D04UYUAMW");
                assert_eq!(data.user(), "U02ALMR84");
                assert_eq!(data.ts(), "1432563914.000007");
                assert_eq!(data.team(), "T02ALMR82");
            },
            _ => panic!("Expected SlackMsg::Message")
        }
    }
    #[test]
    fn decode_confirmation_message() {
        let raw = r#"{"ok":true,"reply_to":0,"ts":"1432566639.000014","text":"pong"}"#;

        match string_to_slack_msg(raw).unwrap() {
            SlackMsg::Other => (),
            _ => panic!("expected SlackMsg::Other")
        }
    }

    #[test]
    fn decode_unexpected_type() {
        let raw = r#"{"type":"not_a_slack_msg_type"}"#;

        match string_to_slack_msg(raw).unwrap() {
            SlackMsg::Other => (),
            _ => panic!("expected SlackMsg::Other")
        }
    }
}
<commit_msg>Add doc comment to string_to_slack_msg<commit_after>extern crate slack;

use std::env;
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::channel;
use std::thread;

use rustc_serialize::json::Json;
use rustc_serialize::json;
use rustc_serialize::json::DecoderError::MissingFieldError;
use rustc_serialize::Decodable;
use rustc_serialize::Decoder;

use slack::Message;

use message::AdapterMsg;
use message::IncomingMessage;
use adapter::ChatAdapter;

/// SlackAdapter sends and receives messages from the Slack chat service. Until actualy
/// configuration is added, the slack token should be placed in the environment variable
/// `SLACK_BOT_TOKEN`
pub struct SlackAdapter {
    token: String
}

impl SlackAdapter {
    pub fn new() -> SlackAdapter {
        SlackAdapter {
            token: match env::var("SLACK_BOT_TOKEN") {
                Ok(t) => t,
                Err(_) => panic!("Failed to get SLACK_BOT_TOKEN from env")
            }
        }
    }
}

struct MyHandler {
  count: i64,
  tx_bot: Sender<IncomingMessage>,
  tx_adapter: Sender<AdapterMsg>
}

/// Data for a SlackMsg::Message
#[allow(dead_code)]
struct MessageData {
    text: String,
    channel: String,
    user: String,
    ts: String,
    team: String
}

macro_rules! str_accessor {
    ($s:ident) => {
        pub fn $s(&self) -> &str {
            self.$s.as_ref()
        }
    }
}

#[allow(dead_code)]
impl MessageData {
    str_accessor!(text);
    str_accessor!(channel);
    str_accessor!(user);
    str_accessor!(ts);
    str_accessor!(team);
}

/// Incoming slack messages on the websocket api
enum SlackMsg {
    /// A message was sent to a channel
    Message(MessageData),

    /// Some other type of message arrived.
    /// [The list](https://api.slack.com/rtm) is quite extensive, and only the
    /// messages the adapter is concerned with are enumerated here.
    Other
}

impl Decodable for SlackMsg {
    fn decode<D: Decoder>(d: &mut D) -> Result<SlackMsg, D::Error> {
        d.read_struct("root", 0, |root| {
            let msg_type = try!(root.read_struct_field("type", 0, |f| f.read_str() ));

            match msg_type.as_ref() {
                "message" => {
                    Ok(SlackMsg::Message(MessageData {
                        text: try!(root.read_struct_field("text", 0, |f| f.read_str())),
                        channel: try!(root.read_struct_field("channel", 0, |f| f.read_str())),
                        user: try!(root.read_struct_field("user", 0, |f| f.read_str())),
                        ts: try!(root.read_struct_field("ts", 0, |f| f.read_str())),
                        team: try!(root.read_struct_field("team", 0, |f| f.read_str())),
                    }))
                },
                _ => {
                    Ok(SlackMsg::Other)
                }
            }
        })
    }
}

/// Convert a JSON string to a SlackMsg
///
/// This methods provides additional error handling around json::decode for certain errors
/// that cannot be handled in the Decodable implementation. Specifically, MissingFieldError
/// where the field is "type" are actually valid messages despite missing the "type" field.
fn string_to_slack_msg(raw: &str) -> Result<SlackMsg, json::DecoderError> {
    // Some messages arriving from the slack client don't have a type. So far I've only
    // witnessed confirmation messages arriving in this fashion. Since they go through the same
    // pipeline as content messages, the decoder should be able to handle them.
    match json::decode::<SlackMsg>(raw) {
        Ok(value) => Ok(value),
        Err(e) => {
            match e {
                MissingFieldError(field) => {
                    match field.as_ref() {
                        "type" => Ok(SlackMsg::Other),
                        _ => Err(MissingFieldError(field))
                    }
                },
                _ => Err(e)
            }
        }
    }
}

#[allow(unused_variables)]
impl slack::MessageHandler for MyHandler {
    fn on_receive(&mut self, cli: &mut slack::RtmClient, raw: &str) {
        println!("Received[{}]: {}", self.count, raw.to_string());
        self.count = self.count + 1;

        match string_to_slack_msg(raw) {
            Ok(slack_msg) => {
                match slack_msg {
                    SlackMsg::Message(msg) => {
                        let incoming = IncomingMessage::new("SlackAdapter".to_owned(), None,
                            Some(msg.channel().to_owned()), Some(msg.user().to_owned()),
                            msg.text().to_owned(), self.tx_adapter.clone());

                        self.tx_bot.send(incoming).ok().expect("Bot unable to process messages");
                    },
                    _ => ()
                }
            },
            Err(e) => {
                println!("error decoding slack message: {:?}", e);
                println!("please consider reporting this to jwilm/chatbot as it is probably a bug");
            }
        }
    }

    fn on_ping(&mut self, cli: &mut slack::RtmClient) {
        println!("<on_ping>");
    }

    fn on_close(&mut self, cli: &mut slack::RtmClient) {
        println!("<on_close>");
    }

    fn on_connect(&mut self, cli: &mut slack::RtmClient) {
        println!("<on_connect>");
        // let _ = cli.send_message("#general", "bla");
    }
}

impl ChatAdapter for SlackAdapter {
    /// SlackAdapter name
    fn get_name(&self) -> &str {
        "SlackAdapter"
    }

    fn process_events(&self) -> Receiver<IncomingMessage> {
        println!("SlackAdapter: process_events");
        let (tx_bot, rx_bot) = channel();
        let (tx_adapter, rx_adapter) = channel();

        let uid = AtomicIsize::new(0);

        let mut cli = slack::RtmClient::new();
        let (client, slack_rx) = cli.login(self.token.as_ref()).unwrap();
        let slack_tx = cli.get_outs().unwrap();

        thread::Builder::new().name("Chatbot Slack Receiver".to_owned()).spawn(move || {
            let mut handler = MyHandler{count: 0, tx_bot: tx_bot, tx_adapter: tx_adapter};
            cli.run::<MyHandler>(&mut handler, client, slack_rx).unwrap();
        }).ok().expect("failed to create thread for slack receiver");

        thread::Builder::new().name("Chatbot Slack Sender".to_owned()).spawn(move || {
            loop {
                match rx_adapter.recv() {
                    Ok(msg) => {
                        match msg {
                            AdapterMsg::Outgoing(msg) => {
                                let n = uid.fetch_add(1, Ordering::SeqCst);
                                let chan = msg.get_incoming().channel().unwrap();
                                let payload = msg.as_ref();
                                let mut obj = json::Object::new();
                                obj.insert("id".to_string(), Json::I64(n as i64));
                                obj.insert("channel".to_string(), Json::String(chan.to_owned()));
                                obj.insert("type".to_string(), Json::String("message".to_owned()));
                                obj.insert("text".to_string(), Json::String(payload.to_owned()));
                                let json = Json::Object(obj);
                                let slack_msg = Message::Text(json.to_string());
                                slack_tx.send(slack_msg).unwrap();
                            }
                            _ => println!("TODO")
                        }
                    },
                    Err(e) => {
                        println!("error receiving outgoing messages to the slack adapter: {}", e);
                        break
                    }
                }
            }
        }).ok().expect("failed to create thread for slack sender");

        rx_bot
    }
}

#[cfg(test)]
mod tests {
    use adapter::slack::SlackMsg;
    use adapter::slack::string_to_slack_msg;

    #[test]
    fn decode_message() {
        let raw = "{\"type\":\"message\", \
                    \"channel\":\"D04UYUAMW\", \
                    \"user\":\"U02ALMR84\", \
                    \"text\":\"ping\", \
                    \"ts\":\"1432563914.000007\", \
                    \"team\":\"T02ALMR82\"}";

        let slack_msg = string_to_slack_msg(raw).unwrap();

        match slack_msg {
            SlackMsg::Message(data) => {
                assert_eq!(data.text(), "ping");
                assert_eq!(data.channel(), "D04UYUAMW");
                assert_eq!(data.user(), "U02ALMR84");
                assert_eq!(data.ts(), "1432563914.000007");
                assert_eq!(data.team(), "T02ALMR82");
            },
            _ => panic!("Expected SlackMsg::Message")
        }
    }
    #[test]
    fn decode_confirmation_message() {
        let raw = r#"{"ok":true,"reply_to":0,"ts":"1432566639.000014","text":"pong"}"#;

        match string_to_slack_msg(raw).unwrap() {
            SlackMsg::Other => (),
            _ => panic!("expected SlackMsg::Other")
        }
    }

    #[test]
    fn decode_unexpected_type() {
        let raw = r#"{"type":"not_a_slack_msg_type"}"#;

        match string_to_slack_msg(raw).unwrap() {
            SlackMsg::Other => (),
            _ => panic!("expected SlackMsg::Other")
        }
    }
}
<|endoftext|>


<commit_before>// This module implements the snapshot files used by zakopane.
// ATOW a snapshot file is pretty much the output of the ``sha256sum''
// command with three extra lines atop.

use std::collections::HashMap;
use std::io::{Error, ErrorKind};
use std::result::Result;
use std::str::Lines;
use std::string::String;

// Defines the number of lines preceding the actual checksum content.
const HEADER_LINES: usize = 3;

// Defines the number of hex characters in a sha256sum.
const CHECKSUM_CHARS: usize = 64;

// Defines a zakopane snapshot, which maps paths to checksums.
pub struct Snapshot {
    contents: HashMap<String, String>,
}

// Borrows the string representation of a line in a zakopane snapshot
// and returns sliced str's in a tuple of (path, checksum).
fn parse_snapshot_line(line: &str) -> Result<(&str, &str), Error> {
    let bad_line: Error = Error::new(
        ErrorKind::InvalidData,
        format!("malformed snapshot line: ``{}''", line),
    );
    // A snapshot line should consist of the checksum, a space, and a
    // non-empty pathname.
    if line.len() < CHECKSUM_CHARS + 2
        || !line.is_char_boundary(CHECKSUM_CHARS)
        || !line.is_char_boundary(CHECKSUM_CHARS + 1)
    {
        return Err(bad_line);
    }

    let (checksum, path_with_leading_space) = line.split_at(CHECKSUM_CHARS);
    if !path_with_leading_space.starts_with(" ") {
        return Err(bad_line);
    }
    Ok((checksum, &path_with_leading_space[1..]))
}

impl Snapshot {
    // Borrows the string representation of a zakopane snapshot and
    // returns the corresponding Snapshot struct.
    pub fn new(snapshot: &str) -> Result<Snapshot, Error> {
        let mut lines: Lines = snapshot.lines();

        // A zakopane snapshot starts with three extra lines intended
        // for human readers. zakocmp doesn't care about this header.
        let mut header_drain: usize = HEADER_LINES;
        while header_drain > 0 {
            match lines.next() {
                Some(_) => (),
                None => {
                    return Err(Error::new(
                        ErrorKind::InvalidData,
                        "truncated zakopane snapshot",
                    ))
                }
            };
            header_drain -= 1;
        }

        // Ingests the rest of the snapshot representation.
        let mut contents: HashMap<String, String> = HashMap::new();
        for line in lines {
            let (path, checksum) = parse_snapshot_line(line)?;
            match contents.insert(path.to_string(), checksum.to_string()) {
                None => (),
                Some(old_checksum) => {
                    return Err(Error::new(
                        ErrorKind::AlreadyExists,
                        format!(
                            "path collision: {} (was already {}, is now {})",
                            path, old_checksum, checksum
                        ),
                    ))
                }
            };
        }
        Ok(Snapshot { contents: contents })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Defines a valid zakopane snapshot header.
    const SNAPSHOT_HEADER_FOR_TESTING: &'static str = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
# this line is typically empty but must be present
"#;

    // Accepts a borrowed string representation of some zakopane
    // checksums, prepends the standard zakopane snapshot header to the
    // same, and returns the owned result.
    fn snapshot_string_for_testing(checksums: &str) -> String {
        let mut snapshot: String = SNAPSHOT_HEADER_FOR_TESTING.to_string();
        snapshot.push_str(checksums);
        snapshot
    }

    #[test]
    fn snapshot_must_have_proper_header() {
        let snapshot = Snapshot::new(SNAPSHOT_HEADER_FOR_TESTING).unwrap();
        assert!(snapshot.contents.len() == 0);

        let snapshot_without_proper_header = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
"#;
        assert!(!Snapshot::new(snapshot_without_proper_header).is_ok());
    }

    #[test]
    fn snapshot_checksum_is_hex() {
        let checksum_ok: &str =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5 ./hello/there.txt";
        let snapshot = Snapshot::new(&snapshot_string_for_testing(checksum_ok)).unwrap();
        assert!(snapshot.contents.len() == 1);

        // Oh no! This checksum dropped a character off the end.
        let checksum_short: &str =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a ./hello/there.txt";
        assert!(!Snapshot::new(&snapshot_string_for_testing(checksum_short)).is_ok());
    }
}
<commit_msg>zakocmp: improve Snapshot test coverage<commit_after>// This module implements the snapshot files used by zakopane.
// ATOW a snapshot file is pretty much the output of the ``sha256sum''
// command with three extra lines atop.

use std::collections::HashMap;
use std::io::{Error, ErrorKind};
use std::result::Result;
use std::str::Lines;
use std::string::String;

// Defines the number of lines preceding the actual checksum content.
const HEADER_LINES: usize = 3;

// Defines the number of hex characters in a sha256sum.
const CHECKSUM_CHARS: usize = 64;

// Defines a zakopane snapshot, which maps paths to checksums.
pub struct Snapshot {
    contents: HashMap<String, String>,
}

// Borrows the string representation of a line in a zakopane snapshot
// and returns sliced str's in a tuple of (path, checksum).
fn parse_snapshot_line(line: &str) -> Result<(&str, &str), Error> {
    let bad_line: Error = Error::new(
        ErrorKind::InvalidData,
        format!("malformed snapshot line: ``{}''", line),
    );
    // A snapshot line should consist of the checksum, a space, and a
    // non-empty pathname.
    if line.len() < CHECKSUM_CHARS + 2
        || !line.is_char_boundary(CHECKSUM_CHARS)
        || !line.is_char_boundary(CHECKSUM_CHARS + 1)
    {
        return Err(bad_line);
    }

    let (checksum, path_with_leading_space) = line.split_at(CHECKSUM_CHARS);
    if !path_with_leading_space.starts_with(" ") {
        return Err(bad_line);
    }
    Ok((checksum, &path_with_leading_space[1..]))
}

impl Snapshot {
    // Borrows the string representation of a zakopane snapshot and
    // returns the corresponding Snapshot struct.
    pub fn new(snapshot: &str) -> Result<Snapshot, Error> {
        let mut lines: Lines = snapshot.lines();

        // A zakopane snapshot starts with three extra lines intended
        // for human readers. zakocmp doesn't care about this header.
        let mut header_drain: usize = HEADER_LINES;
        while header_drain > 0 {
            match lines.next() {
                Some(_) => (),
                None => {
                    return Err(Error::new(
                        ErrorKind::InvalidData,
                        "truncated zakopane snapshot",
                    ))
                }
            };
            header_drain -= 1;
        }

        // Ingests the rest of the snapshot representation.
        let mut contents: HashMap<String, String> = HashMap::new();
        for line in lines {
            let (path, checksum) = parse_snapshot_line(line)?;
            match contents.insert(path.to_string(), checksum.to_string()) {
                None => (),
                Some(old_checksum) => {
                    return Err(Error::new(
                        ErrorKind::AlreadyExists,
                        format!(
                            "path collision: {} (was already {}, is now {})",
                            path, old_checksum, checksum
                        ),
                    ))
                }
            };
        }
        Ok(Snapshot { contents: contents })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Defines a valid zakopane snapshot header.
    const SNAPSHOT_HEADER_FOR_TESTING: &'static str = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
# this line is typically empty but must be present
"#;

    // Accepts a borrowed string representation of some zakopane
    // checksums, prepends the standard zakopane snapshot header to the
    // same, and returns the owned result.
    fn snapshot_string_for_testing(checksums: &str) -> String {
        let mut snapshot: String = SNAPSHOT_HEADER_FOR_TESTING.to_string();
        snapshot.push_str(checksums);
        snapshot
    }

    #[test]
    fn snapshot_must_have_proper_header() {
        let snapshot = Snapshot::new(SNAPSHOT_HEADER_FOR_TESTING).unwrap();
        assert!(snapshot.contents.len() == 0);

        let snapshot_without_proper_header = r#"simple-zakopane.sh: 2019-07-27-090032
simple-zakopane.sh: /home/kalvin
"#;
        assert!(!Snapshot::new(snapshot_without_proper_header).is_ok());
    }

    #[test]
    fn snapshot_checksum_is_hex() {
        let checksum_ok =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5 ./hello/there.txt";
        let snapshot = Snapshot::new(&snapshot_string_for_testing(checksum_ok)).unwrap();
        assert!(snapshot.contents.len() == 1);

        // Oh no! This checksum dropped a character off the end.
        let checksum_short =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a ./hello/there.txt";
        assert!(!Snapshot::new(&snapshot_string_for_testing(checksum_short)).is_ok());

        // Oh no! This checksum line does not refer to a path.
        let checksum_without_path =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5 ";
        assert!(!Snapshot::new(&snapshot_string_for_testing(checksum_without_path)).is_ok());

        // Checksum lines may not be empty or too short.
        assert!(!Snapshot::new(&snapshot_string_for_testing("\n")).is_ok());
        assert!(!Snapshot::new(&snapshot_string_for_testing("Hello there!")).is_ok());
    }

    #[test]
    fn snapshot_paths_may_not_repeat() {
        let checksums =
            "4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5 ./hello/there.txt
        4e8401b759a877c0d215ba95bb75bd7d08318cbdc395b3fae9763337ee3614a5 ./hello/there.txt";
        assert!(!Snapshot::new(&snapshot_string_for_testing(checksums)).is_ok());
    }
}
<|endoftext|>


<commit_before>use commands::{Command, CommandExecutor};
use std::error;
use std::sync::Arc;
use errors::anoncreds::AnoncredsError;

pub struct AnoncredsAPI {
    command_executor: Arc<CommandExecutor>,
}

impl AnoncredsAPI {
    pub fn new(command_executor: Arc<CommandExecutor>) -> AnoncredsAPI {
        AnoncredsAPI { command_executor: command_executor }
    }

    /// Creates master secret key for prover.
    ///
    /// #Params
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Master secret key as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_master_secret(cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    /// Creates public/private keys pair for issuer.
    ///
    /// #Params
    /// schema: claim definition schema as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Tuple of
    /// (Public primary key, Secret Key, Public non-revocation key, Secret non-revocation key)
    /// as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_key_pair(schema: &[&u8],
                       cb: Box<Fn(
                           Result<((String, String, String, String)), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    pub fn create_context_attribute(i_a: String, user_id: String,
                                    cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {

    }

    /// Issues accumulator.
    ///
    /// #Params
    /// schema: claim definition schema as a byte array.
    /// accumulator_id: accumulator id as a byte array.
    /// max_claims: maximum number of claims within accumulator as a byte array.
    /// public_key_non_revocation: issuer's non-revocation public key as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Tuple of (Accumulator, Tails, Accumulator public key, Accumulator secret key) as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn issue_accumulator(schema: &[&u8], accumulator_id: &[&u8], max_claims: &[&u8],
                             public_key_non_revocation: &[&u8],
                             cb: Box<Fn(
                                 Result<((String, String, String, String)),
                                     AnoncredsError>
                             ) + Send>) {
        unimplemented!();
    }

    pub fn issue_claim(attributes: String, accumulator: String,i_a: String, i: String,
                       claim_request: String, context_attribute: String, public_key: String,
                       secret_key: String, public_key_revocation: String,
                       secret_key_revocation: String, tails: String,
                       secret_key_accumulator: String,
                       cb: Box<Fn(Result<(String, String), AnoncredsError>) + Send>) {

    }

    /// Creates claim request.
    ///
    /// #Params
    /// master_secret: prover's master secret as a byte array.
    /// public_key: issuer's public_key as a byte array.
    /// public_key_non_revocation: issuer's non-revocation public key as a byte array.
    /// request_non_revocation: whether to request non-revocation claim as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Claim request as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_claim_request(master_secret: &[&u8], public_key: &[&u8],
                                public_key_non_revocation: &[&u8], request_non_revocation: &[&u8],
                                cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    /// Creates proof.
    ///
    /// #Params
    /// proof_input: description of a proof to be presented
    ///     (revealed attributes, predicates, timestamps for non-revocation) as a byte array.
    /// nonce: verifier's nonce as a byte array.
    /// claims: necessary claims for proof as a byte array.
    /// master_secret: prover's master secret key as a byte array.
    /// public_key: issuer's public key as a byte array.
    /// public_key_non_revocation: issuer's non-revocation public key as a byte array.
    /// accumulator: accumulator as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Typle of (
    ///         Proof (both primary and non-revocation),
    ///         Revealed attributes (initial non-encoded values)
    ///     ) as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_proof(proof_input: &[&u8], nonce: &[&u8], claims: &[&u8],
                        public_key_non_revocation: &[&u8], accumulator: &[&u8], public_key: &[&u8],
                        master_secret: &[&u8],
                        cb: Box<Fn(Result<(String, String), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    pub fn verify_proof(proof_input: String, proof: String, revealed_attributes: String,
                        nonce: String, public_key_revocation: String,
                        public_key_accumulator: String, accumulator: String,
                        public_key: String, attributes: String,
                        cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {

    }

    pub fn create_nonce() {

    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn anoncreds_api_can_be_created() {
        let anoncreds_api = AnoncredsAPI::new(Arc::new(CommandExecutor::new()));
        assert! (true, "No crashes on AnoncredsAPI::new");
    }

    #[test]
    fn anoncredsn_api_can_be_dropped() {
        fn drop_test() {
            let anoncreds_api = AnoncredsAPI::new(Arc::new(CommandExecutor::new()));
        }

        drop_test();
        assert! (true, "No crashes on AnoncredsAPI::drop");
    }
}<commit_msg>Added documentation<commit_after>use commands::{Command, CommandExecutor};
use std::error;
use std::sync::Arc;
use errors::anoncreds::AnoncredsError;

pub struct AnoncredsAPI {
    command_executor: Arc<CommandExecutor>,
}

impl AnoncredsAPI {
    pub fn new(command_executor: Arc<CommandExecutor>) -> AnoncredsAPI {
        AnoncredsAPI { command_executor: command_executor }
    }

    /// Creates master secret key for prover.
    ///
    /// #Params
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Master secret key as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_master_secret(cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    /// Creates public/private keys pair for issuer.
    ///
    /// #Params
    /// schema: claim definition schema as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Tuple of
    /// (Public primary key, Secret Key, Public non-revocation key, Secret non-revocation key)
    /// as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_key_pair(schema: &[&u8],
                       cb: Box<Fn(
                           Result<((String, String, String, String)), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    pub fn create_context_attribute(i_a: String, user_id: String,
                                    cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {

    }

    /// Issues accumulator.
    ///
    /// #Params
    /// schema: claim definition schema as a byte array.
    /// accumulator_id: accumulator id as a byte array.
    /// max_claims: maximum number of claims within accumulator as a byte array.
    /// public_key_non_revocation: issuer's non-revocation public key as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Tuple of (Accumulator, Tails, Accumulator public key, Accumulator secret key) as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn issue_accumulator(schema: &[&u8], accumulator_id: &[&u8], max_claims: &[&u8],
                             public_key_non_revocation: &[&u8],
                             cb: Box<Fn(
                                 Result<((String, String, String, String)),
                                     AnoncredsError>
                             ) + Send>) {
        unimplemented!();
    }

    pub fn issue_claim(attributes: String, accumulator: String,i_a: String, i: String,
                       claim_request: String, context_attribute: String, public_key: String,
                       secret_key: String, public_key_revocation: String,
                       secret_key_revocation: String, tails: String,
                       secret_key_accumulator: String,
                       cb: Box<Fn(Result<(String, String), AnoncredsError>) + Send>) {

    }

    /// Creates claim request.
    ///
    /// #Params
    /// master_secret: prover's master secret as a byte array.
    /// public_key: issuer's public_key as a byte array.
    /// public_key_non_revocation: issuer's non-revocation public key as a byte array.
    /// request_non_revocation: whether to request non-revocation claim as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Claim request as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_claim_request(master_secret: &[&u8], public_key: &[&u8],
                                public_key_non_revocation: &[&u8], request_non_revocation: &[&u8],
                                cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    /// Creates proof.
    ///
    /// #Params
    /// proof_input: description of a proof to be presented
    ///     (revealed attributes, predicates, timestamps for non-revocation) as a byte array.
    /// nonce: verifier's nonce as a byte array.
    /// claims: necessary claims for proof as a byte array.
    /// master_secret: prover's master secret key as a byte array.
    /// public_key: issuer's public key as a byte array.
    /// public_key_non_revocation: issuer's non-revocation public key as a byte array.
    /// accumulator: accumulator as a byte array.
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Typle of (
    ///         Proof (both primary and non-revocation),
    ///         Revealed attributes (initial non-encoded values)
    ///     ) as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_proof(proof_input: &[&u8], nonce: &[&u8], claims: &[&u8],
                        public_key_non_revocation: &[&u8], accumulator: &[&u8], public_key: &[&u8],
                        master_secret: &[&u8],
                        cb: Box<Fn(Result<(String, String), AnoncredsError>) + Send>) {
        unimplemented!();
    }

    pub fn verify_proof(proof_input: String, proof: String, revealed_attributes: String,
                        nonce: String, public_key_revocation: String,
                        public_key_accumulator: String, accumulator: String,
                        public_key: String, attributes: String,
                        cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {

    }

    /// Creates verifier's nonce.
    ///
    /// #Params
    /// cb: Callback that takes command result as a parameter.
    ///
    /// #Returns
    /// Nonce as a String.
    ///
    /// #Errors
    /// No method specific errors.
    /// See `AnoncredsError` docs for common errors description.
    pub fn create_nonce(cb: Box<Fn(Result<(String), AnoncredsError>) + Send>) {
        unimplemented!();
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn anoncreds_api_can_be_created() {
        let anoncreds_api = AnoncredsAPI::new(Arc::new(CommandExecutor::new()));
        assert! (true, "No crashes on AnoncredsAPI::new");
    }

    #[test]
    fn anoncredsn_api_can_be_dropped() {
        fn drop_test() {
            let anoncreds_api = AnoncredsAPI::new(Arc::new(CommandExecutor::new()));
        }

        drop_test();
        assert! (true, "No crashes on AnoncredsAPI::drop");
    }
}<|endoftext|>


<commit_before>// Traits in Rust are a way of guaranteeing particular functionality for a type.
// They let the compiler know that a type is capable of necessary functions to
// help it ensure safety. For more information, check the book:
// https://doc.rust-lang.org/nightly/book/traits.html
#[test]
fn implementing_traits() {
    struct Person {
        first_name: &'static str,
        last_name: &'static str,
    }

    // Any type that implements the HasName trait is guaranteed to have a
    // function called full_name() that returns a String
    trait HasName {
        fn full_name(&self) -> String;
    }

    impl Person {
        fn full_name(&self) -> String {
            format!("{} {}", self.first_name, self.last_name)
        }
    }

    let person = Person {
        first_name: "Chris",
        last_name: "Cerami",
    };

    // The assert_full_name function needs to know that its argument can call
    // full_name(). In order to guarantee this, it is cast to receive any
    // argument type that has implemented the HasName trait.
    fn assert_full_name<T: HasName>(person: T) {
        assert_eq!(person.full_name(), "Chris Cerami");
    }

    assert_full_name(person);
}

// In order to implement a trait, a type must implement ALL of its functions.
// This is required in order to guarantee that any type that shares that trait
// will be able to respond to those functions.
#[test]
fn implementing_traits2() {
    struct Character {
        name: &'static str,
        level: u16,
    }

    trait HasLevel {
        fn level_up(&mut self) -> u16;

        fn print_level(&self);
    }

    impl HasLevel for Character {
        fn level_up(&mut self) -> u16 {
            self.level += 1;
            self.level
        }
    }

    let mut durz = Character {
        name: "Durz",
        level: 2,
    };

    fn test_level_up<T: HasLevel>(character: &mut T) {
        assert_eq!(character.level_up(), 3);
    }

    test_level_up(&mut durz);
}

// Now let's try creating a trait and implementing it for an existing type.
#[test]
fn creating_traits() {
    let num_one: u16 = 3;
    let num_two: u16 = 4;

    fn asserts<T: IsEvenOrOdd>(x: T, y: T) {
        assert!(!x.is_even());
        assert!(y.is_even());
    }

    asserts(num_one, num_two);
}

// We can also add trait constraints, or "bounds", to structs that we create.
// Using this pattern, we can use generic types and still ensure type safety.
#[test]
fn trait_constraints_on_structs() {
    struct Language<T> {
        stable_version: T,
        latest_version: T,
    }

    impl<__> Language<T> {
        fn is_stable(&self) -> bool {
            self.latest_version >= self.stable_version
        }
    }

    let rust = Language {
        stable_version: "1.3.0",
        latest_version: "1.5.0",
    };

    assert!(rust.is_stable());
}

// There is an alternate syntax for placing trait bounds on a function, the
// where clause. Let's revisit a previous example, this time using 'where'.
#[test]
fn where_clause() {
    let num_one: u16 = 3;
    let num_two: u16 = 4;

    trait IsEvenOrOdd {
        fn is_even(&self) -> bool;
    }

    impl IsEvenOrOdd for u16 {
        fn is_even(&self) -> bool {
            self % 2 == 0
        }
    }

    fn asserts<T>(x: T, y: T) {
        assert!(!x.is_even());
        assert!(y.is_even());
    }

    asserts(num_one, num_two);
}

// While you can always allow the implementor of a trait declare its functions,
// you can also supply default functionality. Let's revisit IsEvenOrOdd.
#[test]
fn default_functions() {
    let num_one: u16 = 3;
    let num_two: u16 = 4;

    trait IsEvenOrOdd {
        fn is_even(&self) -> bool;
        fn is_odd(&self) -> bool {
            __
        }
    }

    impl IsEvenOrOdd for u16 {
        fn is_even(&self) -> bool {
            self % 2 == 0
        }
    }

    fn asserts<T: IsEvenOrOdd>(x: T, y: T) {
        assert!(x.is_odd());
        assert!(y.is_even());
    }

    asserts(num_one, num_two);
}

// You can also create traits that inherit from other traits.
// In order to implement a child trait, you must first implement its parent.
// In this example, Bawks doesn't implement PartialOrd, so it fails to
// meet the requirements for the Ordered trait.
#[test]
fn inheritance() {
    use std::cmp::Ordering;

    #[derive(PartialEq)]
    struct Bawks<T> {
        thingy: T
    }

    impl<T: PartialOrd> PartialOrd for Bawks<T> {
        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
            __
        }
    }

    trait Ordered: PartialOrd {
        fn is_before(&self, other: &Self) -> bool;
    }

    impl<T: PartialOrd> Ordered for Bawks<T> {
        fn is_before(&self, other: &Self) -> bool {
            self < other
        }
    }

    let a = Bawks { thingy: 5.0 };
    let b = Bawks { thingy: 7.0 };

    assert!(a.is_before(&b));
}
<commit_msg>Update traits.rs<commit_after>// Traits in Rust are a way of guaranteeing particular functionality for a type.
// They let the compiler know that a type is capable of necessary functions to
// help it ensure safety. For more information, check the book:
// https://doc.rust-lang.org/nightly/book/traits.html
#[test]
fn implementing_traits() {
    struct Person {
        first_name: &'static str,
        last_name: &'static str,
    }

    // Any type that implements the HasName trait is guaranteed to have a
    // function called full_name() that returns a String
    trait HasName {
        fn full_name(&self) -> String;
    }

    impl Person {
        fn full_name(&self) -> String {
            format!("{} {}", self.first_name, self.last_name)
        }
    }

    let person = Person {
        first_name: "Chris",
        last_name: "Cerami",
    };

    // The assert_full_name function needs to know that its argument can call
    // full_name(). In order to guarantee this, it is cast to receive any
    // argument type that has implemented the HasName trait.
    fn assert_full_name<T: HasName>(person: T) {
        assert_eq!(person.full_name(), "Chris Cerami");
    }

    assert_full_name(person);
}

// In order to implement a trait, a type must implement ALL of its functions.
// This is required in order to guarantee that any type that shares that trait
// will be able to respond to those functions.
#[test]
fn implementing_traits2() {
    struct Character {
        name: &'static str,
        level: u16,
    }

    trait HasLevel {
        fn level_up(&mut self) -> u16;

        fn print_level(&self);
    }

    impl HasLevel for Character {
        fn level_up(&mut self) -> u16 {
            self.level += 1;
            self.level
        }
    }

    let mut durz = Character {
        name: "Durz",
        level: 2,
    };

    fn test_level_up<T: HasLevel>(character: &mut T) {
        assert_eq!(character.level_up(), 3);
    }

    test_level_up(&mut durz);
}

// Now let's try creating a trait and implementing it for an existing type.
#[test]
fn creating_traits() {
    let num_one: u16 = 3;
    let num_two: u16 = 4;

    fn asserts<T: IsEvenOrOdd>(x: T, y: T) {
        assert!(!x.is_even());
        assert!(y.is_even());
    }

    asserts(num_one, num_two);
}

// We can also add trait constraints, or "bounds", to structs that we create.
// Using this pattern, we can use generic types and still ensure type safety.
#[test]
fn trait_constraints_on_structs() {
    struct Language<T> {
        stable_version: T,
        latest_version: T,
    }

    impl<__> Language<T> {
        fn is_stable(&self) -> bool {
            self.latest_version >= self.stable_version
        }
    }

    let rust = Language {
        stable_version: "1.3.0",
        latest_version: "1.5.0",
    };

    assert!(rust.is_stable());
}

// There is an alternate syntax for placing trait bounds on a function, the
// where clause. Let's revisit a previous example, this time using 'where'.
#[test]
fn where_clause() {
    let num_one: u16 = 3;
    let num_two: u16 = 4;

    trait IsEvenOrOdd {
        fn is_even(&self) -> bool;
    }

    impl IsEvenOrOdd for u16 {
        fn is_even(&self) -> bool {
            self % 2 == 0
        }
    }

    fn asserts<T>(x: T, y: T) {
        assert!(!x.is_even());
        assert!(y.is_even());
    }

    asserts(num_one, num_two);
}

// While you can always allow the implementor of a trait to declare its functions,
// you can also supply default functionality. Let's revisit IsEvenOrOdd.
#[test]
fn default_functions() {
    let num_one: u16 = 3;
    let num_two: u16 = 4;

    trait IsEvenOrOdd {
        fn is_even(&self) -> bool;
        fn is_odd(&self) -> bool {
            __
        }
    }

    impl IsEvenOrOdd for u16 {
        fn is_even(&self) -> bool {
            self % 2 == 0
        }
    }

    fn asserts<T: IsEvenOrOdd>(x: T, y: T) {
        assert!(x.is_odd());
        assert!(y.is_even());
    }

    asserts(num_one, num_two);
}

// You can also create traits that inherit from other traits.
// In order to implement a child trait, you must first implement its parent.
// In this example, Bawks doesn't implement PartialOrd, so it fails to
// meet the requirements for the Ordered trait.
#[test]
fn inheritance() {
    use std::cmp::Ordering;

    #[derive(PartialEq)]
    struct Bawks<T> {
        thingy: T
    }

    impl<T: PartialOrd> PartialOrd for Bawks<T> {
        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
            __
        }
    }

    trait Ordered: PartialOrd {
        fn is_before(&self, other: &Self) -> bool;
    }

    impl<T: PartialOrd> Ordered for Bawks<T> {
        fn is_before(&self, other: &Self) -> bool {
            self < other
        }
    }

    let a = Bawks { thingy: 5.0 };
    let b = Bawks { thingy: 7.0 };

    assert!(a.is_before(&b));
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use crate::sys::ffi;

use libc::{self, c_char, CTL_NET, NET_RT_IFLIST2, PF_ROUTE, RTM_IFINFO2};

use std::collections::HashMap;
use std::ptr::null_mut;

use crate::{NetworkExt, NetworksExt, NetworksIter};

macro_rules! old_and_new {
    ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{
        $ty_.$old = $ty_.$name;
        $ty_.$name = $new_val;
    }};
}

/// Network interfaces.
///
/// ```no_run
/// use sysinfo::{NetworksExt, System, SystemExt};
///
/// let s = System::new_all();
/// let networks = s.get_networks();
/// ```
pub struct Networks {
    interfaces: HashMap<String, NetworkData>,
}

impl Networks {
    pub(crate) fn new() -> Self {
        Networks {
            interfaces: HashMap::new(),
        }
    }

    #[allow(clippy::cast_ptr_alignment)]
    fn update_networks(&mut self) {
        let mib = &mut [CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0];
        let mut len = 0;
        if unsafe { libc::sysctl(mib.as_mut_ptr(), 6, null_mut(), &mut len, null_mut(), 0) } < 0 {
            // TODO: might be nice to put an error in here...
            return;
        }
        let mut buf = Vec::with_capacity(len);
        unsafe {
            buf.set_len(len);
            if libc::sysctl(
                mib.as_mut_ptr(),
                6,
                buf.as_mut_ptr(),
                &mut len,
                null_mut(),
                0,
            ) < 0
            {
                // TODO: might be nice to put an error in here...
                return;
            }
        }
        let buf = buf.as_ptr() as *const c_char;
        let lim = unsafe { buf.add(len) };
        let mut next = buf;
        while next < lim {
            unsafe {
                let ifm = next as *const libc::if_msghdr;
                next = next.offset((*ifm).ifm_msglen as isize);
                if (*ifm).ifm_type == RTM_IFINFO2 as u8 {
                    // The interface (line description) name stored at ifname will be returned in
                    // the default coded character set identifier (CCSID) currently in effect for
                    // the job. If this is not a single byte CCSID, then storage greater than
                    // IFNAMSIZ (16) bytes may be needed. 22 bytes is large enough for all CCSIDs.
                    let mut name = vec![0u8; libc::IFNAMSIZ + 6];

                    let if2m: *const ffi::if_msghdr2 = ifm as *const ffi::if_msghdr2;
                    let pname =
                        libc::if_indextoname((*if2m).ifm_index as _, name.as_mut_ptr() as _);
                    if pname.is_null() {
                        continue;
                    }
                    name.set_len(libc::strlen(pname));
                    let name = String::from_utf8_unchecked(name);
                    let interface = self.interfaces.entry(name).or_insert_with(|| NetworkData {
                        current_in: (*if2m).ifm_data.ifi_ibytes,
                        old_in: 0,
                        current_out: (*if2m).ifm_data.ifi_obytes,
                        old_out: 0,
                        packets_in: (*if2m).ifm_data.ifi_ipackets,
                        old_packets_in: 0,
                        packets_out: (*if2m).ifm_data.ifi_opackets,
                        old_packets_out: 0,
                        errors_in: (*if2m).ifm_data.ifi_ierrors,
                        old_errors_in: 0,
                        errors_out: (*if2m).ifm_data.ifi_oerrors,
                        old_errors_out: 0,
                        updated: true,
                    });
                    old_and_new!(interface, current_out, old_out, (*if2m).ifm_data.ifi_obytes);
                    old_and_new!(interface, current_in, old_in, (*if2m).ifm_data.ifi_ibytes);
                    old_and_new!(
                        interface,
                        packets_in,
                        old_packets_in,
                        (*if2m).ifm_data.ifi_ipackets
                    );
                    old_and_new!(
                        interface,
                        packets_out,
                        old_packets_out,
                        (*if2m).ifm_data.ifi_opackets
                    );
                    old_and_new!(
                        interface,
                        errors_in,
                        old_errors_in,
                        (*if2m).ifm_data.ifi_ierrors
                    );
                    old_and_new!(
                        interface,
                        errors_out,
                        old_errors_out,
                        (*if2m).ifm_data.ifi_oerrors
                    );
                    interface.updated = true;
                }
            }
        }
    }
}

impl NetworksExt for Networks {
    #[allow(clippy::needless_lifetimes)]
    fn iter<'a>(&'a self) -> NetworksIter<'a> {
        NetworksIter::new(self.interfaces.iter())
    }

    fn refresh_networks_list(&mut self) {
        for (_, data) in self.interfaces.iter_mut() {
            data.updated = false;
        }
        self.update_networks();
        self.interfaces.retain(|_, data| data.updated);
    }

    fn refresh(&mut self) {
        self.update_networks();
    }
}

/// Contains network information.
#[derive(PartialEq, Eq)]
pub struct NetworkData {
    current_in: u64,
    old_in: u64,
    current_out: u64,
    old_out: u64,
    packets_in: u64,
    old_packets_in: u64,
    packets_out: u64,
    old_packets_out: u64,
    errors_in: u64,
    old_errors_in: u64,
    errors_out: u64,
    old_errors_out: u64,
    updated: bool,
}

impl NetworkExt for NetworkData {
    fn get_received(&self) -> u64 {
        self.current_in.saturating_sub(self.old_in)
    }

    fn get_total_received(&self) -> u64 {
        self.current_in
    }

    fn get_transmitted(&self) -> u64 {
        self.current_out.saturating_sub(self.old_out)
    }

    fn get_total_transmitted(&self) -> u64 {
        self.current_out
    }

    fn get_packets_received(&self) -> u64 {
        self.packets_in.saturating_sub(self.old_packets_in)
    }

    fn get_total_packets_received(&self) -> u64 {
        self.packets_in
    }

    fn get_packets_transmitted(&self) -> u64 {
        self.packets_out.saturating_sub(self.old_packets_out)
    }

    fn get_total_packets_transmitted(&self) -> u64 {
        self.packets_out
    }

    fn get_errors_on_received(&self) -> u64 {
        self.errors_in.saturating_sub(self.old_errors_in)
    }

    fn get_total_errors_on_received(&self) -> u64 {
        self.errors_in
    }

    fn get_errors_on_transmitted(&self) -> u64 {
        self.errors_out.saturating_sub(self.old_errors_out)
    }

    fn get_total_errors_on_transmitted(&self) -> u64 {
        self.errors_out
    }
}
<commit_msg>Improve refresh_networks_list code on Apple targets<commit_after>//
// Sysinfo
//
// Copyright (c) 2017 Guillaume Gomez
//

use crate::sys::ffi;

use libc::{self, c_char, CTL_NET, NET_RT_IFLIST2, PF_ROUTE, RTM_IFINFO2};

use std::collections::{hash_map, HashMap};
use std::ptr::null_mut;

use crate::{NetworkExt, NetworksExt, NetworksIter};

macro_rules! old_and_new {
    ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{
        $ty_.$old = $ty_.$name;
        $ty_.$name = $new_val;
    }};
}

/// Network interfaces.
///
/// ```no_run
/// use sysinfo::{NetworksExt, System, SystemExt};
///
/// let s = System::new_all();
/// let networks = s.get_networks();
/// ```
pub struct Networks {
    interfaces: HashMap<String, NetworkData>,
}

impl Networks {
    pub(crate) fn new() -> Self {
        Networks {
            interfaces: HashMap::new(),
        }
    }

    #[allow(clippy::cast_ptr_alignment)]
    fn update_networks(&mut self) {
        let mib = &mut [CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0];
        let mut len = 0;
        if unsafe { libc::sysctl(mib.as_mut_ptr(), 6, null_mut(), &mut len, null_mut(), 0) } < 0 {
            // TODO: might be nice to put an error in here...
            return;
        }
        let mut buf = Vec::with_capacity(len);
        unsafe {
            buf.set_len(len);
            if libc::sysctl(
                mib.as_mut_ptr(),
                6,
                buf.as_mut_ptr(),
                &mut len,
                null_mut(),
                0,
            ) < 0
            {
                // TODO: might be nice to put an error in here...
                return;
            }
        }
        let buf = buf.as_ptr() as *const c_char;
        let lim = unsafe { buf.add(len) };
        let mut next = buf;
        while next < lim {
            unsafe {
                let ifm = next as *const libc::if_msghdr;
                next = next.offset((*ifm).ifm_msglen as isize);
                if (*ifm).ifm_type == RTM_IFINFO2 as u8 {
                    // The interface (line description) name stored at ifname will be returned in
                    // the default coded character set identifier (CCSID) currently in effect for
                    // the job. If this is not a single byte CCSID, then storage greater than
                    // IFNAMSIZ (16) bytes may be needed. 22 bytes is large enough for all CCSIDs.
                    let mut name = vec![0u8; libc::IFNAMSIZ + 6];

                    let if2m: *const ffi::if_msghdr2 = ifm as *const ffi::if_msghdr2;
                    let pname =
                        libc::if_indextoname((*if2m).ifm_index as _, name.as_mut_ptr() as _);
                    if pname.is_null() {
                        continue;
                    }
                    name.set_len(libc::strlen(pname));
                    let name = String::from_utf8_unchecked(name);
                    match self.interfaces.entry(name) {
                        hash_map::Entry::Occupied(mut e) => {
                            let mut interface = e.get_mut();
                            old_and_new!(
                                interface,
                                current_out,
                                old_out,
                                (*if2m).ifm_data.ifi_obytes
                            );
                            old_and_new!(
                                interface,
                                current_in,
                                old_in,
                                (*if2m).ifm_data.ifi_ibytes
                            );
                            old_and_new!(
                                interface,
                                packets_in,
                                old_packets_in,
                                (*if2m).ifm_data.ifi_ipackets
                            );
                            old_and_new!(
                                interface,
                                packets_out,
                                old_packets_out,
                                (*if2m).ifm_data.ifi_opackets
                            );
                            old_and_new!(
                                interface,
                                errors_in,
                                old_errors_in,
                                (*if2m).ifm_data.ifi_ierrors
                            );
                            old_and_new!(
                                interface,
                                errors_out,
                                old_errors_out,
                                (*if2m).ifm_data.ifi_oerrors
                            );
                            interface.updated = true;
                        }
                        hash_map::Entry::Vacant(e) => {
                            let current_in = (*if2m).ifm_data.ifi_ibytes;
                            let current_out = (*if2m).ifm_data.ifi_obytes;
                            let packets_in = (*if2m).ifm_data.ifi_ipackets;
                            let packets_out = (*if2m).ifm_data.ifi_opackets;
                            let errors_in = (*if2m).ifm_data.ifi_ierrors;
                            let errors_out = (*if2m).ifm_data.ifi_oerrors;

                            e.insert(NetworkData {
                                current_in,
                                old_in: current_in,
                                current_out,
                                old_out: current_out,
                                packets_in,
                                old_packets_in: packets_in,
                                packets_out,
                                old_packets_out: packets_out,
                                errors_in,
                                old_errors_in: errors_in,
                                errors_out,
                                old_errors_out: errors_out,
                                updated: true,
                            });
                        }
                    }
                }
            }
        }
    }
}

impl NetworksExt for Networks {
    #[allow(clippy::needless_lifetimes)]
    fn iter<'a>(&'a self) -> NetworksIter<'a> {
        NetworksIter::new(self.interfaces.iter())
    }

    fn refresh_networks_list(&mut self) {
        for (_, data) in self.interfaces.iter_mut() {
            data.updated = false;
        }
        self.update_networks();
        self.interfaces.retain(|_, data| data.updated);
    }

    fn refresh(&mut self) {
        self.update_networks();
    }
}

/// Contains network information.
#[derive(PartialEq, Eq)]
pub struct NetworkData {
    current_in: u64,
    old_in: u64,
    current_out: u64,
    old_out: u64,
    packets_in: u64,
    old_packets_in: u64,
    packets_out: u64,
    old_packets_out: u64,
    errors_in: u64,
    old_errors_in: u64,
    errors_out: u64,
    old_errors_out: u64,
    updated: bool,
}

impl NetworkExt for NetworkData {
    fn get_received(&self) -> u64 {
        self.current_in.saturating_sub(self.old_in)
    }

    fn get_total_received(&self) -> u64 {
        self.current_in
    }

    fn get_transmitted(&self) -> u64 {
        self.current_out.saturating_sub(self.old_out)
    }

    fn get_total_transmitted(&self) -> u64 {
        self.current_out
    }

    fn get_packets_received(&self) -> u64 {
        self.packets_in.saturating_sub(self.old_packets_in)
    }

    fn get_total_packets_received(&self) -> u64 {
        self.packets_in
    }

    fn get_packets_transmitted(&self) -> u64 {
        self.packets_out.saturating_sub(self.old_packets_out)
    }

    fn get_total_packets_transmitted(&self) -> u64 {
        self.packets_out
    }

    fn get_errors_on_received(&self) -> u64 {
        self.errors_in.saturating_sub(self.old_errors_in)
    }

    fn get_total_errors_on_received(&self) -> u64 {
        self.errors_in
    }

    fn get_errors_on_transmitted(&self) -> u64 {
        self.errors_out.saturating_sub(self.old_errors_out)
    }

    fn get_total_errors_on_transmitted(&self) -> u64 {
        self.errors_out
    }
}
<|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.

// Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/)

#![cfg(not(test))]
#![cfg(feature="cargo-fmt")]

extern crate getopts;
extern crate rustc_serialize;

use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::str;

use getopts::Options;
use rustc_serialize::json::Json;

fn main() {
    let mut opts = getopts::Options::new();
    opts.optflag("h", "help", "show this message");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return;
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
    } else {
        format_crate(&opts);
    }
}

fn print_usage(opts: &Options, reason: &str) {
    let msg = format!("{}\nusage: cargo fmt [options]", reason);
    println!("{}\nThis utility formats all bin and lib files of the current crate using rustfmt.",
             opts.usage(&msg));
}

fn format_crate(opts: &Options) {
    let targets = match get_targets() {
        Ok(t) => t,
        Err(e) => {
            print_usage(opts, &e.to_string());
            return;
        }
    };

    // Currently only bin and lib files get formatted
    let files: Vec<_> = targets.into_iter()
                               .filter(|t| t.kind.is_lib() | t.kind.is_bin())
                               .map(|t| t.path)
                               .collect();

    format_files(&files).unwrap_or_else(|e| print_usage(opts, &e.to_string()));
}

#[derive(Debug)]
enum TargetKind {
    Lib, // dylib, staticlib, lib
    Bin, // bin
    Other, // test, plugin,...
}

impl TargetKind {
    fn is_lib(&self) -> bool {
        match self {
            &TargetKind::Lib => true,
            _ => false,
        }
    }

    fn is_bin(&self) -> bool {
        match self {
            &TargetKind::Bin => true,
            _ => false,
        }
    }
}

#[derive(Debug)]
struct Target {
    path: PathBuf,
    kind: TargetKind,
}

// Returns a vector of all compile targets of a crate
fn get_targets() -> Result<Vec<Target>, std::io::Error> {
    let mut targets: Vec<Target> = vec![];
    let output = try!(Command::new("cargo").arg("read-manifest").output());
    if output.status.success() {
        // None of the unwraps should fail if output of `cargo read-manifest` is correct
        let data = &String::from_utf8(output.stdout).unwrap();
        let json = Json::from_str(data).unwrap();
        let jtargets = json.find("targets").unwrap().as_array().unwrap();
        for jtarget in jtargets {
            targets.push(target_from_json(jtarget));
        }

        Ok(targets)
    } else {
        // This happens when cargo-fmt is not used inside a crate
        Err(std::io::Error::new(std::io::ErrorKind::NotFound,
                                str::from_utf8(&output.stderr).unwrap()))
    }
}

fn target_from_json(jtarget: &Json) -> Target {
    let jtarget = jtarget.as_object().unwrap();
    let path = PathBuf::from(jtarget.get("src_path").unwrap().as_string().unwrap());
    let kinds = jtarget.get("kind").unwrap().as_array().unwrap();
    let kind = match kinds[0].as_string().unwrap() {
        "bin" => TargetKind::Bin,
        "lib" | "dylib" | "staticlib" => TargetKind::Lib,
        _ => TargetKind::Other,
    };

    Target {
        path: path,
        kind: kind,
    }
}

fn format_files(files: &Vec<PathBuf>) -> Result<(), std::io::Error> {
    let mut command = try!(Command::new("rustfmt")
                               .arg("--write-mode=overwrite")
                               .args(files)
                               .spawn());
    try!(command.wait());

    Ok(())
}
<commit_msg>Allow to pass arguments to rustfmt<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.

// Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/)

#![cfg(not(test))]
#![cfg(feature="cargo-fmt")]

extern crate getopts;
extern crate rustc_serialize;

use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::str;

use getopts::Options;
use rustc_serialize::json::Json;

fn main() {
    let mut opts = getopts::Options::new();
    opts.optflag("h", "help", "show this message");

    let matches = match opts.parse(env::args().skip(1).take_while(|a| a != "--")) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return;
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
    } else {
        format_crate(&opts);
    }
}

fn print_usage(opts: &Options, reason: &str) {
    let msg = format!("{}\nusage: cargo fmt [options]", reason);
    println!("{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \
              Arguments after `--` are passes to rustfmt.",
             opts.usage(&msg));
}

fn format_crate(opts: &Options) {
    let targets = match get_targets() {
        Ok(t) => t,
        Err(e) => {
            print_usage(opts, &e.to_string());
            return;
        }
    };

    // Currently only bin and lib files get formatted
    let files: Vec<_> = targets.into_iter()
                               .filter(|t| t.kind.is_lib() | t.kind.is_bin())
                               .map(|t| t.path)
                               .collect();

    format_files(&files, &get_fmt_args()).unwrap_or_else(|e| print_usage(opts, &e.to_string()));
}

fn get_fmt_args() -> Vec<String> {
    let mut args = vec!["--write-mode=overwrite".to_string()];
    // All arguments after -- are passed to rustfmt
    args.extend(env::args().skip_while(|a| a != "--").skip(1));

    args
}

#[derive(Debug)]
enum TargetKind {
    Lib, // dylib, staticlib, lib
    Bin, // bin
    Other, // test, plugin,...
}

impl TargetKind {
    fn is_lib(&self) -> bool {
        match self {
            &TargetKind::Lib => true,
            _ => false,
        }
    }

    fn is_bin(&self) -> bool {
        match self {
            &TargetKind::Bin => true,
            _ => false,
        }
    }
}

#[derive(Debug)]
struct Target {
    path: PathBuf,
    kind: TargetKind,
}

// Returns a vector of all compile targets of a crate
fn get_targets() -> Result<Vec<Target>, std::io::Error> {
    let mut targets: Vec<Target> = vec![];
    let output = try!(Command::new("cargo").arg("read-manifest").output());
    if output.status.success() {
        // None of the unwraps should fail if output of `cargo read-manifest` is correct
        let data = &String::from_utf8(output.stdout).unwrap();
        let json = Json::from_str(data).unwrap();
        let jtargets = json.find("targets").unwrap().as_array().unwrap();
        for jtarget in jtargets {
            targets.push(target_from_json(jtarget));
        }

        Ok(targets)
    } else {
        // This happens when cargo-fmt is not used inside a crate
        Err(std::io::Error::new(std::io::ErrorKind::NotFound,
                                str::from_utf8(&output.stderr).unwrap()))
    }
}

fn target_from_json(jtarget: &Json) -> Target {
    let jtarget = jtarget.as_object().unwrap();
    let path = PathBuf::from(jtarget.get("src_path").unwrap().as_string().unwrap());
    let kinds = jtarget.get("kind").unwrap().as_array().unwrap();
    let kind = match kinds[0].as_string().unwrap() {
        "bin" => TargetKind::Bin,
        "lib" | "dylib" | "staticlib" => TargetKind::Lib,
        _ => TargetKind::Other,
    };

    Target {
        path: path,
        kind: kind,
    }
}

fn format_files(files: &Vec<PathBuf>, fmt_args: &Vec<String>) -> Result<(), std::io::Error> {
    let mut command = try!(Command::new("rustfmt")
                               .args(files)
                               .args(fmt_args)
                               .spawn());
    try!(command.wait());

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use helpers::*;

use console;
use crowbook::{Result, Book, BookOptions};
use crowbook_intl_runtime::set_lang;
use crowbook::Stats;
use tempdir::TempDir;
use clap::ArgMatches;
use std::process::exit;
use std::io;
use std::io::Read;
use std::env;
use std::fs::File;
use simplelog::{Config, TermLogger, LogLevel, LogLevelFilter, SimpleLogger, WriteLogger};

/// Render a book to specific format
fn render_format(book: &mut Book, emoji: bool, matches: &ArgMatches, format: &str) {
    let mut key = String::from("output.");
    key.push_str(format);

    let mut stdout = false;
    let mut file = None;

    if let Some(f) = matches.value_of("output") {
        if f == "-" {
            stdout = true;
        } else {
            file = Some(String::from(f));
        }
    }

    let res = book.options.get_path(&key);

    let result = match(file, res, stdout) {
        (Some(file), _, _) |
        (None, Ok(file), false) => book.render_format_to_file(format, file),

        (None, Err(_), _) |
        (None, _, true)
        => book.render_format_to(format, &mut io::stdout()),
    };

    match result {
        Err(err) => print_error(&format!("{}", err), emoji),
        Ok(_) => {}
    }
}

pub fn try_main() -> Result<()> {
    let lang = get_lang()
        .or_else(|| {
            match env::var("LANG") {
                Ok(val) => {
                    Some(val)
                },
                Err(_) => None,
            }
        });
    if let Some(val) = lang {
        if val.starts_with("fr") {
            set_lang("fr");
        } else {
            set_lang("en");
        }
    }

    let mut fancy_ui = true;
    let mut emoji = console::Term::stderr().want_emoji();

    let (matches, help, version) = create_matches();

    if matches.is_present("force-emoji") {
        emoji = true;
    }

    display_header(emoji);

    if matches.is_present("list-options") {
        println!("{}", BookOptions::description(false));
        exit(0);
    }

    if matches.is_present("no-fancy") {
        fancy_ui = false;
        emoji = false;
    }

    if matches.is_present("list-options-md") {
        println!("{}", BookOptions::description(true));
        exit(0);
    }

    if matches.is_present("print-template") {
        let template = matches.value_of("print-template").unwrap();
        let mut book = Book::new();
        set_book_options(&mut book, &matches);
        let result = book.get_template(template.as_ref());
        match result {
            Ok(s) => {
                println!("{}", s);
                exit(0);
            }
            Err(_) => print_error_and_exit(&lformat!("{} is not a valid template name.",
                                                     template),
                                           emoji),
        }
    }

    if matches.is_present("help") {
        println!("{}", help);
        exit(0);
    }

    if matches.is_present("version") {
        println!("{}", version);
        exit(0);
    }

    if matches.is_present("create") {
        create_book(&matches);
    }

    if !matches.is_present("BOOK") {
        print_error_and_exit(&lformat!("You must pass the file of a book configuration \
                               file.\n\n{}\n\nFor more information try --help.",
                                       matches.usage()),
                             emoji);
    }

    // ok to unwrap since clap checks it's there
    let s = matches.value_of("BOOK").unwrap();

    // Initalize logger
    let mut log_config = Config::default();
    log_config.target = None;
    log_config.location = None;
    log_config.time = None;
    let verbosity = if matches.is_present("verbose") {
        log_config.time = Some(LogLevel::Error);
        log_config.target = Some(LogLevel::Error);
        fancy_ui = false;
        LogLevelFilter::Debug
    } else if matches.is_present("quiet") {
        fancy_ui = false;
        LogLevelFilter::Error
    } else if fancy_ui {
        LogLevelFilter::Warn
    } else {
        LogLevelFilter::Info
    };

    let error_dir = TempDir::new("crowbook").unwrap();
    let error_path = "error.log";
    if fancy_ui {
        let errors = File::create(error_dir.path().join(error_path)).unwrap();
        let _ = WriteLogger::init(verbosity, log_config, errors);
    } else {
        if TermLogger::init(verbosity, log_config).is_err() {
            // If it failed, not much we can do, we just won't display log
            let _ = SimpleLogger::init(verbosity, log_config);
        }
    }

    {
        let mut book = Book::new();
        if fancy_ui {
            book.add_progress_bar(emoji);
        }
        book.set_options(&get_book_options(&matches));

        {
            let res = if matches.is_present("single") {
                if s != "-" {
                    book.load_markdown_file(s)
                } else {
                    book.read_markdown_config(io::stdin())
                }
            } else if s != "-" {
                book.load_file(s)
            } else {
                book.read_config(io::stdin())
            }.map(|_| ());

            match res {
                Ok(..) => {},
                Err(err) => {
                    book.set_error(&format!("{}", err));
                    return Err(err);
                }
            }
        }

        set_book_options(&mut book, &matches);

        if matches.is_present("stats") {
            let stats = Stats::new(&book);
            println!("{}", stats);
            exit(0);
        }

        if let Some(format) = matches.value_of("to") {
            render_format(&mut book, emoji,& matches, format);
        } else {
            book.render_all();
        }
    }
    if fancy_ui {
        let mut errors = String::new();
        let mut file = File::open(error_dir.path().join(error_path)).unwrap();
        file.read_to_string(&mut errors).unwrap();
        if !errors.is_empty() {
            print_warning(&lformat!("Crowbook exited successfully, but the following errors occurred:"),
                          emoji);
            let mut lines: Vec<_> = errors.lines().collect();
            lines.sort();
            lines.dedup();
            for line in &lines {
                if line.starts_with("[ERROR]") {
                    let line = &line[8..];
                    print_error(line, emoji);
                } else if line.starts_with("[WARN]") {
                    let line = &line[7..];
                    print_warning(line, emoji);
                }
            }
        }
    }

    Ok(())
}

pub fn real_main() {
    if let Err(err) = try_main() {
        print_error_and_exit(&format!("{}", err), false);
    }
}
<commit_msg>Avoid displaying progress bar when using -S<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use helpers::*;

use console;
use crowbook::{Result, Book, BookOptions};
use crowbook_intl_runtime::set_lang;
use crowbook::Stats;
use tempdir::TempDir;
use clap::ArgMatches;
use std::process::exit;
use std::io;
use std::io::Read;
use std::env;
use std::fs::File;
use simplelog::{Config, TermLogger, LogLevel, LogLevelFilter, SimpleLogger, WriteLogger};

/// Render a book to specific format
fn render_format(book: &mut Book, emoji: bool, matches: &ArgMatches, format: &str) {
    let mut key = String::from("output.");
    key.push_str(format);

    let mut stdout = false;
    let mut file = None;

    if let Some(f) = matches.value_of("output") {
        if f == "-" {
            stdout = true;
        } else {
            file = Some(String::from(f));
        }
    }

    let res = book.options.get_path(&key);

    let result = match(file, res, stdout) {
        (Some(file), _, _) |
        (None, Ok(file), false) => book.render_format_to_file(format, file),

        (None, Err(_), _) |
        (None, _, true)
        => book.render_format_to(format, &mut io::stdout()),
    };

    match result {
        Err(err) => print_error(&format!("{}", err), emoji),
        Ok(_) => {}
    }
}

pub fn try_main() -> Result<()> {
    let lang = get_lang()
        .or_else(|| {
            match env::var("LANG") {
                Ok(val) => {
                    Some(val)
                },
                Err(_) => None,
            }
        });
    if let Some(val) = lang {
        if val.starts_with("fr") {
            set_lang("fr");
        } else {
            set_lang("en");
        }
    }

    let mut fancy_ui = true;
    let mut emoji = console::Term::stderr().want_emoji();

    let (matches, help, version) = create_matches();

    if matches.is_present("force-emoji") {
        emoji = true;
    }

    display_header(emoji);

    if matches.is_present("list-options") {
        println!("{}", BookOptions::description(false));
        exit(0);
    }

    if matches.is_present("no-fancy") || matches.is_present("stats") {
        fancy_ui = false;
        emoji = false;
    }

    if matches.is_present("list-options-md") {
        println!("{}", BookOptions::description(true));
        exit(0);
    }

    if matches.is_present("print-template") {
        let template = matches.value_of("print-template").unwrap();
        let mut book = Book::new();
        set_book_options(&mut book, &matches);
        let result = book.get_template(template.as_ref());
        match result {
            Ok(s) => {
                println!("{}", s);
                exit(0);
            }
            Err(_) => print_error_and_exit(&lformat!("{} is not a valid template name.",
                                                     template),
                                           emoji),
        }
    }

    if matches.is_present("help") {
        println!("{}", help);
        exit(0);
    }

    if matches.is_present("version") {
        println!("{}", version);
        exit(0);
    }

    if matches.is_present("create") {
        create_book(&matches);
    }

    if !matches.is_present("BOOK") {
        print_error_and_exit(&lformat!("You must pass the file of a book configuration \
                               file.\n\n{}\n\nFor more information try --help.",
                                       matches.usage()),
                             emoji);
    }

    // ok to unwrap since clap checks it's there
    let s = matches.value_of("BOOK").unwrap();

    // Initalize logger
    let mut log_config = Config::default();
    log_config.target = None;
    log_config.location = None;
    log_config.time = None;
    let verbosity = if matches.is_present("verbose") {
        log_config.time = Some(LogLevel::Error);
        log_config.target = Some(LogLevel::Error);
        fancy_ui = false;
        LogLevelFilter::Debug
    } else if matches.is_present("quiet") {
        fancy_ui = false;
        LogLevelFilter::Error
    } else if fancy_ui {
        LogLevelFilter::Warn
    } else {
        LogLevelFilter::Info
    };

    let error_dir = TempDir::new("crowbook").unwrap();
    let error_path = "error.log";
    if fancy_ui {
        let errors = File::create(error_dir.path().join(error_path)).unwrap();
        let _ = WriteLogger::init(verbosity, log_config, errors);
    } else {
        if TermLogger::init(verbosity, log_config).is_err() {
            // If it failed, not much we can do, we just won't display log
            let _ = SimpleLogger::init(verbosity, log_config);
        }
    }

    {
        let mut book = Book::new();
        if fancy_ui {
            book.add_progress_bar(emoji);
        }
        book.set_options(&get_book_options(&matches));

        {
            let res = if matches.is_present("single") {
                if s != "-" {
                    book.load_markdown_file(s)
                } else {
                    book.read_markdown_config(io::stdin())
                }
            } else if s != "-" {
                book.load_file(s)
            } else {
                book.read_config(io::stdin())
            }.map(|_| ());

            match res {
                Ok(..) => {},
                Err(err) => {
                    book.set_error(&format!("{}", err));
                    return Err(err);
                }
            }
        }

        set_book_options(&mut book, &matches);

        if matches.is_present("stats") {
            let stats = Stats::new(&book);
            println!("{}", stats);
            exit(0);
        }

        if let Some(format) = matches.value_of("to") {
            render_format(&mut book, emoji,& matches, format);
        } else {
            book.render_all();
        }
    }
    if fancy_ui {
        let mut errors = String::new();
        let mut file = File::open(error_dir.path().join(error_path)).unwrap();
        file.read_to_string(&mut errors).unwrap();
        if !errors.is_empty() {
            print_warning(&lformat!("Crowbook exited successfully, but the following errors occurred:"),
                          emoji);
            let mut lines: Vec<_> = errors.lines().collect();
            lines.sort();
            lines.dedup();
            for line in &lines {
                if line.starts_with("[ERROR]") {
                    let line = &line[8..];
                    print_error(line, emoji);
                } else if line.starts_with("[WARN]") {
                    let line = &line[7..];
                    print_warning(line, emoji);
                }
            }
        }
    }

    Ok(())
}

pub fn real_main() {
    if let Err(err) = try_main() {
        print_error_and_exit(&format!("{}", err), false);
    }
}
<|endoftext|>