<commit_before>//! The module defines MPD status data structures

use convert::FromIter;

use error::{Error, ParseError};
use rustc_serialize::{Encodable, Encoder};
use song::{Id, QueuePlace};
use std::fmt;
use std::str::FromStr;
use time::Duration;

/// MPD status
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Status {
    /// volume (0-100, or -1 if volume is unavailable (e.g. for HTTPD output type)
    pub volume: i8,
    /// repeat mode
    pub repeat: bool,
    /// random mode
    pub random: bool,
    /// single mode
    pub single: bool,
    /// consume mode
    pub consume: bool,
    /// queue version number
    pub queue_version: u32,
    /// queue length
    pub queue_len: u32,
    /// playback state
    pub state: State,
    /// currently playing song place in the queue
    pub song: Option<QueuePlace>,
    /// next song to play place in the queue
    pub nextsong: Option<QueuePlace>,
    /// time current song played, and total song duration (in seconds resolution)
    pub time: Option<(Duration, Duration)>,
    /// elapsed play time current song played (in milliseconds resolution)
    pub elapsed: Option<Duration>,
    /// current song duration
    pub duration: Option<Duration>,
    /// current song bitrate, kbps
    pub bitrate: Option<u32>,
    /// crossfade timeout, seconds
    pub crossfade: Option<Duration>,
    /// mixramp threshold, dB
    pub mixrampdb: f32,
    /// mixramp duration, seconds
    pub mixrampdelay: Option<Duration>,
    /// current audio playback format
    pub audio: Option<AudioFormat>,
    /// current DB updating job number (if DB updating is in progress)
    pub updating_db: Option<u32>,
    /// last player error (if happened, can be reset with `clearerror()` method)
    pub error: Option<String>,
    /// replay gain mode
    pub replaygain: Option<ReplayGain>,
}

impl Encodable for Status {
    fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
        e.emit_struct("Status", 21, |e| {
            e.emit_struct_field("volume", 0, |e| self.volume.encode(e))?;
            e.emit_struct_field("repeat", 1, |e| self.repeat.encode(e))?;
            e.emit_struct_field("random", 2, |e| self.random.encode(e))?;
            e.emit_struct_field("single", 3, |e| self.single.encode(e))?;
            e.emit_struct_field("consume", 4, |e| self.consume.encode(e))?;
            e.emit_struct_field("queue_version", 5, |e| self.queue_version.encode(e))?;
            e.emit_struct_field("queue_len", 6, |e| self.queue_len.encode(e))?;
            e.emit_struct_field("state", 7, |e| self.state.encode(e))?;
            e.emit_struct_field("song", 8, |e| self.song.encode(e))?;
            e.emit_struct_field("nextsong", 9, |e| self.nextsong.encode(e))?;
            e.emit_struct_field("time", 10, |e| {
                    e.emit_option(|e| {
                        match self.time {
                            Some(p) => {
                                e.emit_option_some(|e| {
                                    e.emit_tuple(2, |e| {
                                        e.emit_tuple_arg(0, |e| p.0.num_seconds().encode(e))?;
                                        e.emit_tuple_arg(1, |e| p.1.num_seconds().encode(e))?;
                                        Ok(())
                                    })
                                })
                            }
                            None => e.emit_option_none(),
                        };
                        Ok(())
                    })
                })?;
            e.emit_struct_field("elapsed", 11, |e| {
                    e.emit_option(|e| match self.elapsed {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })

                })?;
            e.emit_struct_field("duration", 12, |e| {
                    e.emit_option(|e| match self.duration {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })
                })?;
            e.emit_struct_field("bitrate", 13, |e| self.bitrate.encode(e))?;
            e.emit_struct_field("crossfade", 14, |e| {
                    e.emit_option(|e| match self.crossfade {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })
                })?;
            e.emit_struct_field("mixrampdb", 15, |e| self.mixrampdb.encode(e))?;
            e.emit_struct_field("mixrampdelay", 16, |e| {
                    e.emit_option(|e| match self.mixrampdelay {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })
                })?;
            e.emit_struct_field("audio", 17, |e| self.audio.encode(e))?;
            e.emit_struct_field("updating_db", 18, |e| self.updating_db.encode(e))?;
            e.emit_struct_field("error", 19, |e| self.error.encode(e))?;
            e.emit_struct_field("replaygain", 20, |e| self.replaygain.encode(e))?;
            Ok(())
        })

    }
}

impl FromIter for Status {
    fn from_iter<I: Iterator<Item = Result<(String, String), Error>>>(iter: I) -> Result<Status, Error> {
        let mut result = Status::default();

        for res in iter {
            let line = try!(res);
            match &*line.0 {
                "volume" => result.volume = try!(line.1.parse()),

                "repeat" => result.repeat = &*line.1 == "1",
                "random" => result.random = &*line.1 == "1",
                "single" => result.single = &*line.1 == "1",
                "consume" => result.consume = &*line.1 == "1",

                "playlist" => result.queue_version = try!(line.1.parse()),
                "playlistlength" => result.queue_len = try!(line.1.parse()),
                "state" => result.state = try!(line.1.parse()),
                "songid" => {
                    match result.song {
                        None => {
                            result.song = Some(QueuePlace {
                                id: Id(try!(line.1.parse())),
                                pos: 0,
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                    }
                }
                "song" => {
                    match result.song {
                        None => {
                            result.song = Some(QueuePlace {
                                pos: try!(line.1.parse()),
                                id: Id(0),
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.pos = try!(line.1.parse()),
                    }
                }
                "nextsongid" => {
                    match result.nextsong {
                        None => {
                            result.nextsong = Some(QueuePlace {
                                id: Id(try!(line.1.parse())),
                                pos: 0,
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                    }
                }
                "nextsong" => {
                    match result.nextsong {
                        None => {
                            result.nextsong = Some(QueuePlace {
                                pos: try!(line.1.parse()),
                                id: Id(0),
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.pos = try!(line.1.parse()),
                    }
                }
                "time" => {
                    result.time = try!({
                        let mut splits = line.1.splitn(2, ':').map(|v| v.parse().map_err(ParseError::BadInteger).map(Duration::seconds));
                        match (splits.next(), splits.next()) {
                            (Some(Ok(a)), Some(Ok(b))) => Ok(Some((a, b))),
                            (Some(Err(e)), _) |
                            (_, Some(Err(e))) => Err(e),
                            _ => Ok(None),
                        }
                    })
                }
                // TODO" => float errors don't work on stable
                "elapsed" => {
                    result.elapsed = line.1
                        .parse::<f32>()
                        .ok()
                        .map(|v| Duration::milliseconds((v * 1000.0) as i64))
                }
                "duration" => result.duration = Some(Duration::seconds(try!(line.1.parse()))),
                "bitrate" => result.bitrate = Some(try!(line.1.parse())),
                "xfade" => result.crossfade = Some(Duration::seconds(try!(line.1.parse()))),
                // "mixrampdb" => 0.0, //get_field!(map, "mixrampdb"),
                // "mixrampdelay" => None, //get_field!(map, opt "mixrampdelay").map(|v: f64| Duration::milliseconds((v * 1000.0) as i64)),
                "audio" => result.audio = Some(try!(line.1.parse())),
                "updating_db" => result.updating_db = Some(try!(line.1.parse())),
                "error" => result.error = Some(line.1.to_owned()),
                "replay_gain_mode" => result.replaygain = Some(try!(line.1.parse())),
                _ => (),
            }
        }

        Ok(result)
    }
}

/// Audio playback format
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable)]
pub struct AudioFormat {
    /// sample rate, kbps
    pub rate: u32,
    /// sample resolution in bits, can be 0 for floating point resolution
    pub bits: u8,
    /// number of channels
    pub chans: u8,
}

impl FromStr for AudioFormat {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<AudioFormat, ParseError> {
        let mut it = s.split(':');
        Ok(AudioFormat {
            rate: try!(it.next()
                .ok_or(ParseError::NoRate)
                .and_then(|v| v.parse().map_err(ParseError::BadRate))),
            bits: try!(it.next()
                .ok_or(ParseError::NoBits)
                .and_then(|v| if &*v == "f" {
                    Ok(0)
                } else {
                    v.parse().map_err(ParseError::BadBits)
                })),
            chans: try!(it.next()
                .ok_or(ParseError::NoChans)
                .and_then(|v| v.parse().map_err(ParseError::BadChans))),
        })
    }
}

/// Playback state
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum State {
    /// player stopped
    Stop,
    /// player is playing
    Play,
    /// player paused
    Pause,
}

impl Default for State {
    fn default() -> State {
        State::Stop
    }
}

impl FromStr for State {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<State, ParseError> {
        match s {
            "stop" => Ok(State::Stop),
            "play" => Ok(State::Play),
            "pause" => Ok(State::Pause),
            _ => Err(ParseError::BadState(s.to_owned())),
        }
    }
}

/// Replay gain mode
#[derive(Debug, Clone, Copy, PartialEq, RustcEncodable, RustcDecodable)]
pub enum ReplayGain {
    /// off
    Off,
    /// track
    Track,
    /// album
    Album,
    /// auto
    Auto,
}

impl FromStr for ReplayGain {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<ReplayGain, ParseError> {
        use self::ReplayGain::*;
        match s {
            "off" => Ok(Off),
            "track" => Ok(Track),
            "album" => Ok(Album),
            "auto" => Ok(Auto),
            _ => Err(ParseError::BadValue(s.to_owned())),
        }
    }
}

impl fmt::Display for ReplayGain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ReplayGain::*;
        f.write_str(match *self {
            Off => "off",
            Track => "track",
            Album => "album",
            Auto => "auto",
        })
    }
}
<commit_msg>Fix for #20.<commit_after>//! The module defines MPD status data structures

use convert::FromIter;

use error::{Error, ParseError};
use rustc_serialize::{Encodable, Encoder};
use song::{Id, QueuePlace};
use std::fmt;
use std::str::FromStr;
use time::Duration;

/// MPD status
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Status {
    /// volume (0-100, or -1 if volume is unavailable (e.g. for HTTPD output type)
    pub volume: i8,
    /// repeat mode
    pub repeat: bool,
    /// random mode
    pub random: bool,
    /// single mode
    pub single: bool,
    /// consume mode
    pub consume: bool,
    /// queue version number
    pub queue_version: u32,
    /// queue length
    pub queue_len: u32,
    /// playback state
    pub state: State,
    /// currently playing song place in the queue
    pub song: Option<QueuePlace>,
    /// next song to play place in the queue
    pub nextsong: Option<QueuePlace>,
    /// time current song played, and total song duration (in seconds resolution)
    pub time: Option<(Duration, Duration)>,
    /// elapsed play time current song played (in milliseconds resolution)
    pub elapsed: Option<Duration>,
    /// current song duration
    pub duration: Option<Duration>,
    /// current song bitrate, kbps
    pub bitrate: Option<u32>,
    /// crossfade timeout, seconds
    pub crossfade: Option<Duration>,
    /// mixramp threshold, dB
    pub mixrampdb: f32,
    /// mixramp duration, seconds
    pub mixrampdelay: Option<Duration>,
    /// current audio playback format
    pub audio: Option<AudioFormat>,
    /// current DB updating job number (if DB updating is in progress)
    pub updating_db: Option<u32>,
    /// last player error (if happened, can be reset with `clearerror()` method)
    pub error: Option<String>,
    /// replay gain mode
    pub replaygain: Option<ReplayGain>,
}

impl Encodable for Status {
    fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
        e.emit_struct("Status", 21, |e| {
            e.emit_struct_field("volume", 0, |e| self.volume.encode(e))?;
            e.emit_struct_field("repeat", 1, |e| self.repeat.encode(e))?;
            e.emit_struct_field("random", 2, |e| self.random.encode(e))?;
            e.emit_struct_field("single", 3, |e| self.single.encode(e))?;
            e.emit_struct_field("consume", 4, |e| self.consume.encode(e))?;
            e.emit_struct_field("queue_version", 5, |e| self.queue_version.encode(e))?;
            e.emit_struct_field("queue_len", 6, |e| self.queue_len.encode(e))?;
            e.emit_struct_field("state", 7, |e| self.state.encode(e))?;
            e.emit_struct_field("song", 8, |e| self.song.encode(e))?;
            e.emit_struct_field("nextsong", 9, |e| self.nextsong.encode(e))?;
            e.emit_struct_field("time", 10, |e| {
                    e.emit_option(|e| match self.time {
                        Some(p) => {
                            e.emit_option_some(|e| {
                                e.emit_tuple(2, |e| {
                                    e.emit_tuple_arg(0, |e| p.0.num_seconds().encode(e))?;
                                    e.emit_tuple_arg(1, |e| p.1.num_seconds().encode(e))?;
                                    Ok(())
                                })
                            })
                        }
                        None => e.emit_option_none(),
                    })
                })?;
            e.emit_struct_field("elapsed", 11, |e| {
                    e.emit_option(|e| match self.elapsed {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })

                })?;
            e.emit_struct_field("duration", 12, |e| {
                    e.emit_option(|e| match self.duration {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })
                })?;
            e.emit_struct_field("bitrate", 13, |e| self.bitrate.encode(e))?;
            e.emit_struct_field("crossfade", 14, |e| {
                    e.emit_option(|e| match self.crossfade {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })
                })?;
            e.emit_struct_field("mixrampdb", 15, |e| self.mixrampdb.encode(e))?;
            e.emit_struct_field("mixrampdelay", 16, |e| {
                    e.emit_option(|e| match self.mixrampdelay {
                        Some(d) => e.emit_option_some(|e| d.num_seconds().encode(e)),
                        None => e.emit_option_none(),
                    })
                })?;
            e.emit_struct_field("audio", 17, |e| self.audio.encode(e))?;
            e.emit_struct_field("updating_db", 18, |e| self.updating_db.encode(e))?;
            e.emit_struct_field("error", 19, |e| self.error.encode(e))?;
            e.emit_struct_field("replaygain", 20, |e| self.replaygain.encode(e))?;
            Ok(())
        })

    }
}

impl FromIter for Status {
    fn from_iter<I: Iterator<Item = Result<(String, String), Error>>>(iter: I) -> Result<Status, Error> {
        let mut result = Status::default();

        for res in iter {
            let line = try!(res);
            match &*line.0 {
                "volume" => result.volume = try!(line.1.parse()),

                "repeat" => result.repeat = &*line.1 == "1",
                "random" => result.random = &*line.1 == "1",
                "single" => result.single = &*line.1 == "1",
                "consume" => result.consume = &*line.1 == "1",

                "playlist" => result.queue_version = try!(line.1.parse()),
                "playlistlength" => result.queue_len = try!(line.1.parse()),
                "state" => result.state = try!(line.1.parse()),
                "songid" => {
                    match result.song {
                        None => {
                            result.song = Some(QueuePlace {
                                id: Id(try!(line.1.parse())),
                                pos: 0,
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                    }
                }
                "song" => {
                    match result.song {
                        None => {
                            result.song = Some(QueuePlace {
                                pos: try!(line.1.parse()),
                                id: Id(0),
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.pos = try!(line.1.parse()),
                    }
                }
                "nextsongid" => {
                    match result.nextsong {
                        None => {
                            result.nextsong = Some(QueuePlace {
                                id: Id(try!(line.1.parse())),
                                pos: 0,
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                    }
                }
                "nextsong" => {
                    match result.nextsong {
                        None => {
                            result.nextsong = Some(QueuePlace {
                                pos: try!(line.1.parse()),
                                id: Id(0),
                                prio: 0,
                            })
                        }
                        Some(ref mut place) => place.pos = try!(line.1.parse()),
                    }
                }
                "time" => {
                    result.time = try!({
                        let mut splits = line.1.splitn(2, ':').map(|v| v.parse().map_err(ParseError::BadInteger).map(Duration::seconds));
                        match (splits.next(), splits.next()) {
                            (Some(Ok(a)), Some(Ok(b))) => Ok(Some((a, b))),
                            (Some(Err(e)), _) |
                            (_, Some(Err(e))) => Err(e),
                            _ => Ok(None),
                        }
                    })
                }
                // TODO" => float errors don't work on stable
                "elapsed" => {
                    result.elapsed = line.1
                        .parse::<f32>()
                        .ok()
                        .map(|v| Duration::milliseconds((v * 1000.0) as i64))
                }
                "duration" => result.duration = Some(Duration::seconds(try!(line.1.parse()))),
                "bitrate" => result.bitrate = Some(try!(line.1.parse())),
                "xfade" => result.crossfade = Some(Duration::seconds(try!(line.1.parse()))),
                // "mixrampdb" => 0.0, //get_field!(map, "mixrampdb"),
                // "mixrampdelay" => None, //get_field!(map, opt "mixrampdelay").map(|v: f64| Duration::milliseconds((v * 1000.0) as i64)),
                "audio" => result.audio = Some(try!(line.1.parse())),
                "updating_db" => result.updating_db = Some(try!(line.1.parse())),
                "error" => result.error = Some(line.1.to_owned()),
                "replay_gain_mode" => result.replaygain = Some(try!(line.1.parse())),
                _ => (),
            }
        }

        Ok(result)
    }
}

/// Audio playback format
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable)]
pub struct AudioFormat {
    /// sample rate, kbps
    pub rate: u32,
    /// sample resolution in bits, can be 0 for floating point resolution
    pub bits: u8,
    /// number of channels
    pub chans: u8,
}

impl FromStr for AudioFormat {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<AudioFormat, ParseError> {
        let mut it = s.split(':');
        Ok(AudioFormat {
            rate: try!(it.next()
                .ok_or(ParseError::NoRate)
                .and_then(|v| v.parse().map_err(ParseError::BadRate))),
            bits: try!(it.next()
                .ok_or(ParseError::NoBits)
                .and_then(|v| if &*v == "f" {
                    Ok(0)
                } else {
                    v.parse().map_err(ParseError::BadBits)
                })),
            chans: try!(it.next()
                .ok_or(ParseError::NoChans)
                .and_then(|v| v.parse().map_err(ParseError::BadChans))),
        })
    }
}

/// Playback state
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum State {
    /// player stopped
    Stop,
    /// player is playing
    Play,
    /// player paused
    Pause,
}

impl Default for State {
    fn default() -> State {
        State::Stop
    }
}

impl FromStr for State {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<State, ParseError> {
        match s {
            "stop" => Ok(State::Stop),
            "play" => Ok(State::Play),
            "pause" => Ok(State::Pause),
            _ => Err(ParseError::BadState(s.to_owned())),
        }
    }
}

/// Replay gain mode
#[derive(Debug, Clone, Copy, PartialEq, RustcEncodable, RustcDecodable)]
pub enum ReplayGain {
    /// off
    Off,
    /// track
    Track,
    /// album
    Album,
    /// auto
    Auto,
}

impl FromStr for ReplayGain {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<ReplayGain, ParseError> {
        use self::ReplayGain::*;
        match s {
            "off" => Ok(Off),
            "track" => Ok(Track),
            "album" => Ok(Album),
            "auto" => Ok(Auto),
            _ => Err(ParseError::BadValue(s.to_owned())),
        }
    }
}

impl fmt::Display for ReplayGain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ReplayGain::*;
        f.write_str(match *self {
            Off => "off",
            Track => "track",
            Album => "album",
            Auto => "auto",
        })
    }
}
<|endoftext|>


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

use processus::*;
use process::*;
use std::fs::{File, read_link, PathExt};
use std::io::Read;
use std::str::FromStr;
use std::path::{Path, PathBuf};
use std::collections::VecMap;
use std::fs;
use libc::types::os::arch::posix01::stat;
use libc::c_char;
use libc::funcs::posix01::stat_::lstat;
use libc::consts::os::posix88::{S_IFLNK, S_IFMT};
use std::path::Component::Normal;

pub struct System {
    processus_list: VecMap<Processus>,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    processes: Vec<Process>,
}

impl System {
    pub fn new() -> System {
        let mut s = System {
            processus_list: VecMap::new(),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            processes: Vec::new(),
        };

        s.refresh();
        s
    }

    pub fn refresh(&mut self) {
        let mut proc_list : VecMap<Processus> = VecMap::new();

        match fs::read_dir(&Path::new("/proc")) {
            Ok(d) => {
                for entry in d {
                    if !entry.is_ok() {
                        continue;
                    }
                    let entry = entry.unwrap();
                    let entry = entry.path();

                    if entry.is_dir() {
                        match _get_processus_data(entry.as_path(), &self.processus_list) {
                            Some(p) => {
                                proc_list.insert(p.pid as usize, p);
                            }
                            None => {}
                        };
                    } else {
                        match entry.to_str().unwrap() {
                            "/proc/meminfo" => {
                                let data = get_all_data(entry.to_str().unwrap());
                                let lines : Vec<&str> = data.split('\n').collect();
                                for line in lines.iter() {
                                    match *line {
                                        l if l.starts_with("MemTotal:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.mem_total = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        l if l.starts_with("MemFree:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.mem_free = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        l if l.starts_with("SwapTotal:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.swap_total = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        l if l.starts_with("SwapFree:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.swap_free = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        _ => continue,
                                    }
                                }
                            },
                            "/proc/stat" => {
                                let data = get_all_data(entry.to_str().unwrap());
                                let lines : Vec<&str> = data.split('\n').collect();
                                let mut i = 0;
                                let first = self.processes.len() == 0;
                                for line in lines.iter() {
                                    if !line.starts_with("cpu") {
                                        break;
                                    }

                                    let (parts, _) : (Vec<&str>, Vec<&str>) = line.split(' ').partition(|s| s.len() > 0);
                                    if first {
                                        self.processes.push(new_process(parts[0], u64::from_str(parts[1]).unwrap(),
                                            u64::from_str(parts[2]).unwrap(),
                                            u64::from_str(parts[3]).unwrap(),
                                            u64::from_str(parts[4]).unwrap()));
                                    } else {
                                        set_process(self.processes.get_mut(i).unwrap(),
                                            u64::from_str(parts[1]).unwrap(),
                                            u64::from_str(parts[2]).unwrap(),
                                            u64::from_str(parts[3]).unwrap(),
                                            u64::from_str(parts[4]).unwrap());
                                        i += 1;
                                    }
                                }
                            }
                           _ => {},
                        }
                    }
                }
                for (pid, proc_) in proc_list.iter_mut() {
                    match self.processus_list.get(&pid) {
                        Some(p) => {
                            let (new, old) = get_raw_times(&self.processes[0]);
                            let (pnew, pold) = get_raw_processus_times(&p);
                            compute_cpu_usage(proc_, pnew, pold, new, old);
                        }
                        None => {}
                    }
                }
                self.processus_list = proc_list;
            }
            Err(_) => {}
        }
    }

    pub fn get_processus_list<'a>(&'a self) -> &'a VecMap<Processus> {
        &self.processus_list
    }

    pub fn get_processus(&self, pid: i64) -> Option<&Processus> {
        self.processus_list.get(&(pid as usize))
    }

    // The first process in the array is the "main" process
    pub fn get_process_list<'a>(&'a self) -> &'a [Process] {
        self.processes.as_slice()
    }

    pub fn get_total_memory(&self) -> u64 {
        self.mem_total
    }

    pub fn get_free_memory(&self) -> u64 {
        self.mem_free
    }

    pub fn get_total_swap(&self) -> u64 {
        self.swap_total
    }

    pub fn get_free_swap(&self) -> u64 {
        self.swap_free
    }
}

fn get_all_data(file_path: &str) -> String {
    let mut file = File::open(file_path).unwrap();
    let mut data = String::new();

    file.read_to_string(&mut data);
    data
}

fn _get_processus_data(path: &Path, proc_list: &VecMap<Processus>) -> Option<Processus> {
    if !path.exists() || !path.is_dir() {
        return None;
    }
    let paths : Vec<&str> = path.as_os_str().to_str().unwrap().split("/").collect();
    let last = paths[paths.len() - 1];
    match i64::from_str(last) {
        Ok(nb) => {
            let mut p = Processus::new(nb);
            let mut tmp = PathBuf::from(path);

            tmp.push("cmdline");
            p.cmd = String::from_str(copy_from_file(&tmp)[0].as_ref());
            {
                let tmp_line : Vec<&str> = p.cmd.split(" ").collect();
                let tmp_name : Vec<&str> = tmp_line[0].split("/").collect();

                p.name = String::from_str(tmp_name[tmp_name.len() - 1]);
            }
            tmp = PathBuf::from(path);
            tmp.push("environ");
            p.environ = copy_from_file(&tmp);
            tmp = PathBuf::from(path);
            tmp.push("exe");

            let s = read_link(tmp.to_str().unwrap());

            if s.is_ok() {
                p.exe = String::from_str(s.unwrap().to_str().unwrap());
            }
            tmp = PathBuf::from(path);
            tmp.push("cwd");
            p.cwd = String::from_str(realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap());
            tmp = PathBuf::from(path);
            tmp.push("root");
            p.root = String::from_str(realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap());
            tmp = PathBuf::from(path);
            tmp.push("status");
            let data = get_all_data(tmp.to_str().unwrap());
            let lines : Vec<&str> = data.split('\n').collect();

            for line in lines.iter() {
                match *line {
                    l if l.starts_with("VmRSS") => {
                        let parts : Vec<&str> = line.split(' ').collect();

                        p.memory = u64::from_str(parts[parts.len() - 2]).unwrap();
                        break;
                    }
                    _ => continue,
                }
            }
            tmp = PathBuf::from(path);
            tmp.push("stat");
            let data = get_all_data(tmp.to_str().unwrap());
            // ne marche pas car il faut aussi les anciennes valeurs !!!!
            let (parts, _) : (Vec<&str>, Vec<&str>) = data.split(' ').partition(|s| s.len() > 0);
            set_time(&mut p, u64::from_str(parts[13]).unwrap(), u64::from_str(parts[14]).unwrap());
            Some(p)
        }
        _ => None
    }
}

fn copy_from_file(entry: &Path) -> Vec<String> {
    match File::open(entry.to_str().unwrap()) {
        Ok(mut f) => {
            let mut d = String::new();

            f.read_to_string(&mut d);
            let v : Vec<&str> = d.split('\0').collect();
            let mut ret = Vec::new();

            for tmp in v.iter() {
                ret.push(String::from_str(tmp));
            }
            ret
        },
        Err(_) => Vec::new()
    }
}

fn old_realpath(ori: &Path) -> PathBuf {
    const MAX_LINKS_FOLLOWED: usize = 256;

    // Right now lstat on windows doesn't work quite well
    if cfg!(windows) {
        return PathBuf::from(ori);
    }
    let original = ::std::env::current_dir().unwrap().join(ori);
    let mut result = PathBuf::from(original.parent().unwrap());
    let mut followed = 0;
    for part in original.components() {
        match part {
            Normal(s) => {
                result.push(s);
                loop {
                    if followed == MAX_LINKS_FOLLOWED {
                        return PathBuf::new();
                    }
                    let mut buf : stat = unsafe { ::std::mem::uninitialized() };
                    let res = unsafe { lstat(result.to_str().unwrap().as_ptr() as *const c_char, &mut buf as *mut stat) };

                    if res < 0 || (buf.st_mode  & S_IFMT) != S_IFLNK {
                        break;
                    } else {
                        followed += 1;
                        let path = fs::read_link(&result).unwrap();
                        result.pop();
                        result.push(path);
                    }
                }
            }
            _ => {}
        }
    }
    result
}

fn realpath(original: &Path) -> PathBuf {
    let old = Path::new(original.to_str().unwrap());

    old_realpath(&old)
}
<commit_msg>Needed updates<commit_after>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

use processus::*;
use process::*;
use std::fs::{File, read_link, PathExt};
use std::io::Read;
use std::str::FromStr;
use std::path::{Path, PathBuf};
use std::collections::VecMap;
use std::fs;
use libc::types::os::arch::posix01::stat;
use libc::c_char;
use libc::funcs::posix01::stat_::lstat;
use libc::consts::os::posix88::{S_IFLNK, S_IFMT};
use std::path::Component::Normal;

pub struct System {
    processus_list: VecMap<Processus>,
    mem_total: u64,
    mem_used: u64,
    swap_total: u64,
    swap_free: u64,
    processes: Vec<Process>,
}

impl System {
    pub fn new() -> System {
        let mut s = System {
            processus_list: VecMap::new(),
            mem_total: 0,
            mem_used: 0,
            swap_total: 0,
            swap_free: 0,
            processes: Vec::new(),
        };

        s.refresh();
        s
    }

    pub fn refresh(&mut self) {
        let mut proc_list : VecMap<Processus> = VecMap::new();

        match fs::read_dir(&Path::new("/proc")) {
            Ok(d) => {
                for entry in d {
                    if !entry.is_ok() {
                        continue;
                    }
                    let entry = entry.unwrap();
                    let entry = entry.path();

                    if entry.is_dir() {
                        match _get_processus_data(entry.as_path(), &self.processus_list) {
                            Some(p) => {
                                proc_list.insert(p.pid as usize, p);
                            }
                            None => {}
                        };
                    } else {
                        match entry.to_str().unwrap() {
                            "/proc/meminfo" => {
                                let data = get_all_data(entry.to_str().unwrap());
                                let lines : Vec<&str> = data.split('\n').collect();
                                for line in lines.iter() {
                                    match *line {
                                        l if l.starts_with("MemTotal:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.mem_total = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        l if l.starts_with("MemFree:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.mem_used = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        l if l.starts_with("SwapTotal:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.swap_total = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        l if l.starts_with("SwapFree:") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            self.swap_free = u64::from_str(parts[parts.len() - 2]).unwrap();
                                        },
                                        _ => continue,
                                    }
                                }
                            },
                            "/proc/stat" => {
                                let data = get_all_data(entry.to_str().unwrap());
                                let lines : Vec<&str> = data.split('\n').collect();
                                let mut i = 0;
                                let first = self.processes.len() == 0;
                                for line in lines.iter() {
                                    if !line.starts_with("cpu") {
                                        break;
                                    }

                                    let (parts, _) : (Vec<&str>, Vec<&str>) = line.split(' ').partition(|s| s.len() > 0);
                                    if first {
                                        self.processes.push(new_process(parts[0], u64::from_str(parts[1]).unwrap(),
                                            u64::from_str(parts[2]).unwrap(),
                                            u64::from_str(parts[3]).unwrap(),
                                            u64::from_str(parts[4]).unwrap()));
                                    } else {
                                        set_process(self.processes.get_mut(i).unwrap(),
                                            u64::from_str(parts[1]).unwrap(),
                                            u64::from_str(parts[2]).unwrap(),
                                            u64::from_str(parts[3]).unwrap(),
                                            u64::from_str(parts[4]).unwrap());
                                        i += 1;
                                    }
                                }
                            }
                           _ => {},
                        }
                    }
                }
                for (pid, proc_) in proc_list.iter_mut() {
                    match self.processus_list.get(&pid) {
                        Some(p) => {
                            let (new, old) = get_raw_times(&self.processes[0]);
                            let (pnew, pold) = get_raw_processus_times(&p);
                            compute_cpu_usage(proc_, pnew, pold, new, old);
                        }
                        None => {}
                    }
                }
                self.processus_list = proc_list;
            }
            Err(_) => {}
        }
    }

    pub fn get_processus_list<'a>(&'a self) -> &'a VecMap<Processus> {
        &self.processus_list
    }

    pub fn get_processus(&self, pid: i64) -> Option<&Processus> {
        self.processus_list.get(&(pid as usize))
    }

    /// The first process in the array is the "main" process
    pub fn get_process_list<'a>(&'a self) -> &'a [Process] {
        self.processes.as_slice()
    }

    pub fn get_total_memory(&self) -> u64 {
        self.mem_total
    }

    pub fn get_used_memory(&self) -> u64 {
        self.mem_used
    }

    pub fn get_total_swap(&self) -> u64 {
        self.swap_total
    }

    // need to be checked
    pub fn get_used_swap(&self) -> u64 {
        self.swap_free
    }
}

fn get_all_data(file_path: &str) -> String {
    let mut file = File::open(file_path).unwrap();
    let mut data = String::new();

    file.read_to_string(&mut data);
    data
}

fn _get_processus_data(path: &Path, proc_list: &VecMap<Processus>) -> Option<Processus> {
    if !path.exists() || !path.is_dir() {
        return None;
    }
    let paths : Vec<&str> = path.as_os_str().to_str().unwrap().split("/").collect();
    let last = paths[paths.len() - 1];
    match i64::from_str(last) {
        Ok(nb) => {
            let mut p = Processus::new(nb);
            let mut tmp = PathBuf::from(path);

            tmp.push("cmdline");
            p.cmd = String::from_str(copy_from_file(&tmp)[0].as_ref());
            {
                let tmp_line : Vec<&str> = p.cmd.split(" ").collect();
                let tmp_name : Vec<&str> = tmp_line[0].split("/").collect();

                p.name = String::from_str(tmp_name[tmp_name.len() - 1]);
            }
            tmp = PathBuf::from(path);
            tmp.push("environ");
            p.environ = copy_from_file(&tmp);
            tmp = PathBuf::from(path);
            tmp.push("exe");

            let s = read_link(tmp.to_str().unwrap());

            if s.is_ok() {
                p.exe = String::from_str(s.unwrap().to_str().unwrap());
            }
            tmp = PathBuf::from(path);
            tmp.push("cwd");
            p.cwd = String::from_str(realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap());
            tmp = PathBuf::from(path);
            tmp.push("root");
            p.root = String::from_str(realpath(Path::new(tmp.to_str().unwrap())).to_str().unwrap());
            tmp = PathBuf::from(path);
            tmp.push("status");
            let data = get_all_data(tmp.to_str().unwrap());
            let lines : Vec<&str> = data.split('\n').collect();

            for line in lines.iter() {
                match *line {
                    l if l.starts_with("VmRSS") => {
                        let parts : Vec<&str> = line.split(' ').collect();

                        p.memory = u64::from_str(parts[parts.len() - 2]).unwrap();
                        break;
                    }
                    _ => continue,
                }
            }
            tmp = PathBuf::from(path);
            tmp.push("stat");
            let data = get_all_data(tmp.to_str().unwrap());
            // ne marche pas car il faut aussi les anciennes valeurs !!!!
            let (parts, _) : (Vec<&str>, Vec<&str>) = data.split(' ').partition(|s| s.len() > 0);
            set_time(&mut p, u64::from_str(parts[13]).unwrap(), u64::from_str(parts[14]).unwrap());
            Some(p)
        }
        _ => None
    }
}

fn copy_from_file(entry: &Path) -> Vec<String> {
    match File::open(entry.to_str().unwrap()) {
        Ok(mut f) => {
            let mut d = String::new();

            f.read_to_string(&mut d);
            let v : Vec<&str> = d.split('\0').collect();
            let mut ret = Vec::new();

            for tmp in v.iter() {
                ret.push(String::from_str(tmp));
            }
            ret
        },
        Err(_) => Vec::new()
    }
}

fn old_realpath(ori: &Path) -> PathBuf {
    const MAX_LINKS_FOLLOWED: usize = 256;

    // Right now lstat on windows doesn't work quite well
    if cfg!(windows) {
        return PathBuf::from(ori);
    }
    let original = ::std::env::current_dir().unwrap().join(ori);
    let mut result = PathBuf::from(original.parent().unwrap());
    let mut followed = 0;
    for part in original.components() {
        match part {
            Normal(s) => {
                result.push(s);
                loop {
                    if followed == MAX_LINKS_FOLLOWED {
                        return PathBuf::new();
                    }
                    let mut buf : stat = unsafe { ::std::mem::uninitialized() };
                    let res = unsafe { lstat(result.to_str().unwrap().as_ptr() as *const c_char, &mut buf as *mut stat) };

                    if res < 0 || (buf.st_mode  & S_IFMT) != S_IFLNK {
                        break;
                    } else {
                        followed += 1;
                        let path = fs::read_link(&result).unwrap();
                        result.pop();
                        result.push(path);
                    }
                }
            }
            _ => {}
        }
    }
    result
}

fn realpath(original: &Path) -> PathBuf {
    let old = Path::new(original.to_str().unwrap());

    old_realpath(&old)
}
<|endoftext|>


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

use tipb::expression::Expr;
use super::super::codec::datum::Datum;
use super::{Evaluator, EvalContext, Result, Error};

const ERROR_UNIMPLEMENTED: &'static str = "unimplemented";

impl Evaluator {
    pub fn abs_int(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn abs_real(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn ceil_int(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn ceil_real(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn floor_int(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn floor_real(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }
}
<commit_msg>coprocessor/xeval: add abs int function impl (#2033)<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use tipb::expression::Expr;
use super::super::codec::datum::Datum;
use super::{Evaluator, EvalContext, Result, Error};

const ERROR_UNIMPLEMENTED: &'static str = "unimplemented";
pub const TYPE_INT: &'static str = "int";

pub fn invalid_type_error(datum: &Datum, expected_type: &str) -> Result<Datum> {
    Err(Error::Eval(format!("invalid expr type: {:?}, expect: {}", datum, expected_type)))
}

impl Evaluator {
    pub fn abs_int(&mut self, ctx: &EvalContext, expr: &Expr) -> Result<Datum> {
        let child = try!(self.get_one_child(expr));
        let d = try!(self.eval(ctx, child));
        match d {
            Datum::I64(i) => {
                if i >= 0 {
                    Ok(Datum::I64(i))
                } else {
                    Ok(Datum::I64(-i))
                }
            }
            Datum::U64(_) => Ok(d),
            _ => invalid_type_error(&d, TYPE_INT),
        }
    }

    pub fn abs_real(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn ceil_int(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn ceil_real(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn floor_int(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }

    pub fn floor_real(&mut self, _ctx: &EvalContext, _expr: &Expr) -> Result<Datum> {
        // TODO add impl
        Err(Error::Eval(ERROR_UNIMPLEMENTED.to_owned()))
    }
}

#[cfg(test)]
mod test {
    use tipb::expression::{ExprType, ScalarFuncSig};
    use coprocessor::codec::datum::Datum;
    use super::super::Evaluator;
    use super::super::evaluator::test::build_expr_with_sig;

    macro_rules! test_eval {
        ($tag:ident, $cases:expr) => {
            #[test]
            fn $tag() {
                let mut test_cases = $cases;
                let mut evaluator = Evaluator::default();
                for (i, (expr, expected)) in test_cases.drain(..).enumerate() {
                    let res = evaluator.eval(&Default::default(), &expr);
                    assert!(res.is_ok(),
                            "#{} expect eval expr {:?} ok but got {:?}",
                            i,
                            expr,
                            res);
                    let res = res.unwrap();
                    assert_eq!(res,
                               expected,
                               "#{} expect {:?} but got {:?}",
                               i,
                               expected,
                               res);
                }
            }
        };
    }

    test_eval!(test_abs_int,
               vec![(build_expr_with_sig(vec![Datum::I64(-1)],
                                         ExprType::ScalarFunc,
                                         ScalarFuncSig::AbsInt),
                     Datum::I64(1)),
                    (build_expr_with_sig(vec![Datum::I64(1)],
                                         ExprType::ScalarFunc,
                                         ScalarFuncSig::AbsInt),
                     Datum::I64(1)),
                    (build_expr_with_sig(vec![Datum::U64(1)],
                                         ExprType::ScalarFunc,
                                         ScalarFuncSig::AbsInt),
                     Datum::U64(1))]);
}
<|endoftext|>


<commit_before>use error::Error;
use uuid::Uuid;
use std::collections::HashMap;

pub trait Entity: Sized {
    /// Searches MusicBrainz for entities based on the search query.
    ///
    /// Returns a `Vec` containing the entities matching the search query.
    /// If no entities were found, returns an empty `Vec`.
    ///
    /// **NOTE**: `&self` is any `MusicBrainz` entity struct.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use musicbrainz::*;
    /// # use std::collections::HashMap;
    /// let musicbrainz = MusicBrainz::new();
    /// let mut query = HashMap::new();
    ///
    /// query.insert("query", "deadmau5");
    ///
    /// let search_results = musicbrainz.artist().search(&musicbrainz, &mut query).unwrap();
    ///
    /// assert_eq!(search_results[0].id.hyphenated().to_string(), "4a00ec9d-c635-463a-8cd4-eb61725f0c60");
    /// ```
    fn search(&self, client: &super::MusicBrainz, params: &mut HashMap<&str, &str>) -> Result<Vec<Self>, Error>;

    /// Lookup an entity by using its MusicBrainz Identifier.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use musicbrainz::*;
    /// # use std::collections::HashMap;
    /// let musicbrainz = MusicBrainz::new();
    /// let mut query = HashMap::new();
    /// let mut compare = musicbrainz.artist();
    ///
    /// compare.id = Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60").expect("failed to parse artist ID as Uuid");
    /// compare.name = String::from("deadmau5");
    ///
    /// query.insert("inc", "tags+release-groups");
    ///
    /// let artist_id = Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60").expect("failed to parse artist ID as Uuid");
    /// let result = musicbrainz.artist().lookup(&musicbrainz, &artist_id, &mut query);
    /// assert_eq!(result.unwrap(), compare)
    fn lookup(&self, client: &super::MusicBrainz, entity_id: &Uuid, params: &mut HashMap<&str, &str>) -> Result<Self, Error>;
}
<commit_msg>Update doc in traits.rs<commit_after>use error::Error;
use uuid::Uuid;
use std::collections::HashMap;

pub trait Entity: Sized {
    /// Searches MusicBrainz for entities based on the search query.
    ///
    /// Returns a `Vec` containing the entities matching the search query.
    /// If no entities were found, returns an empty `Vec`.
    ///
    /// **NOTE**: `&self` is any `MusicBrainz` entity struct.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use musicbrainz::*;
    /// # use std::collections::HashMap;
    /// let musicbrainz = MusicBrainz::new();
    /// let mut query = HashMap::new();
    ///
    /// query.insert("query", "deadmau5");
    ///
    /// let search_results = musicbrainz.artist().search(&musicbrainz, &mut query).unwrap();
    ///
    /// assert_eq!(search_results[0].id.hyphenated().to_string(), "4a00ec9d-c635-463a-8cd4-eb61725f0c60");
    /// ```
    fn search(&self, client: &super::MusicBrainz, params: &mut HashMap<&str, &str>) -> Result<Vec<Self>, Error>;

    /// Performs a lookup of an entity by using its MusicBrainz Identifier.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use musicbrainz::*;
    /// # use std::collections::HashMap;
    /// let musicbrainz = MusicBrainz::new();
    /// let mut query = HashMap::new();
    /// let mut compare = musicbrainz.artist();
    ///
    /// compare.id = Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60").expect("failed to parse artist ID as Uuid");
    /// compare.name = String::from("deadmau5");
    ///
    /// query.insert("inc", "tags+release-groups");
    ///
    /// let artist_id = Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60").expect("failed to parse artist ID as Uuid");
    /// let result = musicbrainz.artist().lookup(&musicbrainz, &artist_id, &mut query);
    /// assert_eq!(result.unwrap(), compare)
    fn lookup(&self, client: &super::MusicBrainz, entity_id: &Uuid, params: &mut HashMap<&str, &str>) -> Result<Self, Error>;
}
<|endoftext|>


<commit_before>use std::{mem, ptr};
use std::c_str::{CString, ToCStr};
use libc::{c_char, c_void, c_int, size_t};
use fcntl::{fcntl, Fd, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC, F_SETFD, F_SETFL};
use errno::{SysResult, SysError, from_ffi};

#[cfg(target_os = "linux")]
pub use self::linux::*;

mod ffi {
    use libc::{c_char, c_int, size_t};
    pub use libc::{close, read, write, pipe};

    extern {
        // duplicate a file descriptor
        // doc: http://man7.org/linux/man-pages/man2/dup.2.html
        pub fn dup(oldfd: c_int) -> c_int;
        pub fn dup2(oldfd: c_int, newfd: c_int) -> c_int;
        pub fn dup3(oldfd: c_int, newfd: c_int, flags: c_int) -> c_int;

        // change working directory
        // doc: http://man7.org/linux/man-pages/man2/chdir.2.html
        pub fn chdir(path: *const c_char) -> c_int;

        // execute program
        // doc: http://man7.org/linux/man-pages/man2/execve.2.html
        pub fn execve(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // run the current process in the background
        // doc: http://man7.org/linux/man-pages/man3/daemon.3.html
        pub fn daemon(nochdir: c_int, noclose: c_int) -> c_int;

        // sets the hostname to the value given
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn gethostname(name: *mut c_char, len: size_t) -> c_int;

        // gets the hostname
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn sethostname(name: *const c_char, len: size_t) -> c_int;
    }
}

#[inline]
pub fn dup(oldfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup(oldfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn dup2(oldfd: Fd, newfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup2(oldfd, newfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn dup3(oldfd: Fd, newfd: Fd, flags: OFlag) -> SysResult<Fd> {
    let res = unsafe { ffi::dup3(oldfd, newfd, flags.bits()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn chdir<S: ToCStr>(path: S) -> SysResult<()> {
    let path = path.to_c_str();
    let res = unsafe { ffi::chdir(path.as_ptr()) };

    if res != 0 {
        return Err(SysError::last());
    }

    return Ok(())
}

#[inline]
pub fn execve(filename: CString, args: &[CString], env: &[CString]) -> SysResult<()> {
    let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
    args_p.push(ptr::null());

    let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect();
    env_p.push(ptr::null());

    let res = unsafe {
        ffi::execve(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
    };

    if res != 0 {
        return Err(SysError::last());
    }

    // Should never reach here
    Ok(())
}

pub fn daemon(nochdir: bool, noclose: bool) -> SysResult<()> {
    let res = unsafe { ffi::daemon(nochdir as c_int, noclose as c_int) };
    from_ffi(res)
}

pub fn sethostname(name: &[u8]) -> SysResult<()> {
    let ptr = name.as_ptr() as *const c_char;
    let len = name.len() as u64;

    let res = unsafe { ffi::sethostname(ptr, len) };
    from_ffi(res)
}

pub fn gethostname(name: &mut [u8]) -> SysResult<()> {
    let ptr = name.as_mut_ptr() as *mut c_char;
    let len = name.len() as u64;

    let res = unsafe { ffi::gethostname(ptr, len) };
    from_ffi(res)
}

pub fn close(fd: Fd) -> SysResult<()> {
    let res = unsafe { ffi::close(fd) };
    from_ffi(res)
}

pub fn read(fd: Fd, buf: &mut [u8]) -> SysResult<uint> {
    let res = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

pub fn write(fd: Fd, buf: &[u8]) -> SysResult<uint> {
    let res = unsafe { ffi::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

pub fn pipe() -> SysResult<(Fd, Fd)> {
    unsafe {
        let mut res;
        let mut fds: [c_int, ..2] = mem::uninitialized();

        res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(SysError::last());
        }

        Ok((fds[0], fds[1]))
    }
}

#[cfg(target_os = "linux")]
pub fn pipe2(flags: OFlag) -> SysResult<(Fd, Fd)> {
    type F = unsafe extern "C" fn(fds: *mut c_int, flags: c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static pipe2: *const ();
    }

    let feat_atomic = !pipe2.is_null();

    unsafe {
        let mut res;
        let mut fds: [c_int, ..2] = mem::uninitialized();

        if feat_atomic {
            res = mem::transmute::<*const (), F>(pipe2)(
                fds.as_mut_ptr(), flags.bits());
        } else {
            res = ffi::pipe(fds.as_mut_ptr());
        }

        if res < 0 {
            return Err(SysError::last());
        }

        if !feat_atomic {
            try!(pipe2_setflags(fds[0], fds[1], flags));
        }

        Ok((fds[0], fds[1]))
    }
}

#[cfg(target_os = "macos")]
#[cfg(target_os = "ios")]
pub fn pipe2(flags: OFlag) -> SysResult<(Fd, Fd)> {
    unsafe {
        let mut res;
        let mut fds: [c_int, ..2] = mem::uninitialized();

        res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(SysError::last());
        }

        try!(pipe2_setflags(fds[0], fds[1], flags));

        Ok((fds[0], fds[1]))
    }
}

fn pipe2_setflags(fd1: Fd, fd2: Fd, flags: OFlag) -> SysResult<()> {
    let mut res = Ok(());

    if flags.contains(O_CLOEXEC) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFD(FD_CLOEXEC)))
            .and_then(|_| fcntl(fd2, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(O_NONBLOCK) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFL(O_NONBLOCK)))
            .and_then(|_| fcntl(fd2, F_SETFL(O_NONBLOCK)));
    }

    match res {
        Ok(_) => Ok(()),
        Err(e) => {
            let _ = close(fd1);
            let _ = close(fd2);
            return Err(e);
        }
    }
}

#[cfg(target_os = "linux")]
mod linux {
    use std::path::Path;
    use syscall::{syscall, SysPivotRoot};
    use errno::{SysResult, SysError};

    pub fn pivot_root(new_root: &Path, put_old: &Path) -> SysResult<()> {
        let new_root = new_root.to_c_str();
        let put_old = put_old.to_c_str();

        let res = unsafe {
            syscall(SysPivotRoot, new_root.as_ptr(), put_old.as_ptr())
        };

        if res != 0 {
            return Err(SysError::last());
        }

        Ok(())
    }
}
<commit_msg>Temporarily remove dup3 support<commit_after>use std::{mem, ptr};
use std::c_str::{CString, ToCStr};
use libc::{c_char, c_void, c_int, size_t};
use fcntl::{fcntl, Fd, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC, F_SETFD, F_SETFL};
use errno::{SysResult, SysError, from_ffi};

#[cfg(target_os = "linux")]
pub use self::linux::*;

mod ffi {
    use libc::{c_char, c_int, size_t};
    pub use libc::{close, read, write, pipe};

    extern {
        // duplicate a file descriptor
        // doc: http://man7.org/linux/man-pages/man2/dup.2.html
        pub fn dup(oldfd: c_int) -> c_int;
        pub fn dup2(oldfd: c_int, newfd: c_int) -> c_int;

        // TODO: dup3 is only available in newer linux kernels and possibly a few *bsds
        // pub fn dup3(oldfd: c_int, newfd: c_int, flags: c_int) -> c_int;

        // change working directory
        // doc: http://man7.org/linux/man-pages/man2/chdir.2.html
        pub fn chdir(path: *const c_char) -> c_int;

        // execute program
        // doc: http://man7.org/linux/man-pages/man2/execve.2.html
        pub fn execve(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // run the current process in the background
        // doc: http://man7.org/linux/man-pages/man3/daemon.3.html
        pub fn daemon(nochdir: c_int, noclose: c_int) -> c_int;

        // sets the hostname to the value given
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn gethostname(name: *mut c_char, len: size_t) -> c_int;

        // gets the hostname
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn sethostname(name: *const c_char, len: size_t) -> c_int;
    }
}

#[inline]
pub fn dup(oldfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup(oldfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn dup2(oldfd: Fd, newfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup2(oldfd, newfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

/*
Same TODO as above
#[inline]
pub fn dup3(oldfd: Fd, newfd: Fd, flags: OFlag) -> SysResult<Fd> {
    let res = unsafe { ffi::dup3(oldfd, newfd, flags.bits()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}
*/

#[inline]
pub fn chdir<S: ToCStr>(path: S) -> SysResult<()> {
    let path = path.to_c_str();
    let res = unsafe { ffi::chdir(path.as_ptr()) };

    if res != 0 {
        return Err(SysError::last());
    }

    return Ok(())
}

#[inline]
pub fn execve(filename: CString, args: &[CString], env: &[CString]) -> SysResult<()> {
    let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
    args_p.push(ptr::null());

    let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect();
    env_p.push(ptr::null());

    let res = unsafe {
        ffi::execve(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
    };

    if res != 0 {
        return Err(SysError::last());
    }

    // Should never reach here
    Ok(())
}

pub fn daemon(nochdir: bool, noclose: bool) -> SysResult<()> {
    let res = unsafe { ffi::daemon(nochdir as c_int, noclose as c_int) };
    from_ffi(res)
}

pub fn sethostname(name: &[u8]) -> SysResult<()> {
    let ptr = name.as_ptr() as *const c_char;
    let len = name.len() as u64;

    let res = unsafe { ffi::sethostname(ptr, len) };
    from_ffi(res)
}

pub fn gethostname(name: &mut [u8]) -> SysResult<()> {
    let ptr = name.as_mut_ptr() as *mut c_char;
    let len = name.len() as u64;

    let res = unsafe { ffi::gethostname(ptr, len) };
    from_ffi(res)
}

pub fn close(fd: Fd) -> SysResult<()> {
    let res = unsafe { ffi::close(fd) };
    from_ffi(res)
}

pub fn read(fd: Fd, buf: &mut [u8]) -> SysResult<uint> {
    let res = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

pub fn write(fd: Fd, buf: &[u8]) -> SysResult<uint> {
    let res = unsafe { ffi::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

pub fn pipe() -> SysResult<(Fd, Fd)> {
    unsafe {
        let mut res;
        let mut fds: [c_int, ..2] = mem::uninitialized();

        res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(SysError::last());
        }

        Ok((fds[0], fds[1]))
    }
}

#[cfg(target_os = "linux")]
pub fn pipe2(flags: OFlag) -> SysResult<(Fd, Fd)> {
    type F = unsafe extern "C" fn(fds: *mut c_int, flags: c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static pipe2: *const ();
    }

    let feat_atomic = !pipe2.is_null();

    unsafe {
        let mut res;
        let mut fds: [c_int, ..2] = mem::uninitialized();

        if feat_atomic {
            res = mem::transmute::<*const (), F>(pipe2)(
                fds.as_mut_ptr(), flags.bits());
        } else {
            res = ffi::pipe(fds.as_mut_ptr());
        }

        if res < 0 {
            return Err(SysError::last());
        }

        if !feat_atomic {
            try!(pipe2_setflags(fds[0], fds[1], flags));
        }

        Ok((fds[0], fds[1]))
    }
}

#[cfg(target_os = "macos")]
#[cfg(target_os = "ios")]
pub fn pipe2(flags: OFlag) -> SysResult<(Fd, Fd)> {
    unsafe {
        let mut res;
        let mut fds: [c_int, ..2] = mem::uninitialized();

        res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(SysError::last());
        }

        try!(pipe2_setflags(fds[0], fds[1], flags));

        Ok((fds[0], fds[1]))
    }
}

fn pipe2_setflags(fd1: Fd, fd2: Fd, flags: OFlag) -> SysResult<()> {
    let mut res = Ok(());

    if flags.contains(O_CLOEXEC) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFD(FD_CLOEXEC)))
            .and_then(|_| fcntl(fd2, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(O_NONBLOCK) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFL(O_NONBLOCK)))
            .and_then(|_| fcntl(fd2, F_SETFL(O_NONBLOCK)));
    }

    match res {
        Ok(_) => Ok(()),
        Err(e) => {
            let _ = close(fd1);
            let _ = close(fd2);
            return Err(e);
        }
    }
}

#[cfg(target_os = "linux")]
mod linux {
    use std::path::Path;
    use syscall::{syscall, SysPivotRoot};
    use errno::{SysResult, SysError};

    pub fn pivot_root(new_root: &Path, put_old: &Path) -> SysResult<()> {
        let new_root = new_root.to_c_str();
        let put_old = put_old.to_c_str();

        let res = unsafe {
            syscall(SysPivotRoot, new_root.as_ptr(), put_old.as_ptr())
        };

        if res != 0 {
            return Err(SysError::last());
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use clap::{App, Arg, ArgMatches};
use environment::Environment;
use slashing_protection::{
    interchange::Interchange, InterchangeError, InterchangeImportOutcome, SlashingDatabase,
    SLASHING_PROTECTION_FILENAME,
};
use std::fs::File;
use std::path::PathBuf;
use std::str::FromStr;
use types::{BeaconState, Epoch, EthSpec, PublicKeyBytes, Slot};

pub const CMD: &str = "slashing-protection";
pub const IMPORT_CMD: &str = "import";
pub const EXPORT_CMD: &str = "export";

pub const IMPORT_FILE_ARG: &str = "IMPORT-FILE";
pub const EXPORT_FILE_ARG: &str = "EXPORT-FILE";

pub const MINIFY_FLAG: &str = "minify";
pub const PUBKEYS_FLAG: &str = "pubkeys";

pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
    App::new(CMD)
        .about("Import or export slashing protection data to or from another client")
        .subcommand(
            App::new(IMPORT_CMD)
                .about("Import an interchange file")
                .arg(
                    Arg::with_name(IMPORT_FILE_ARG)
                        .takes_value(true)
                        .value_name("FILE")
                        .help("The slashing protection interchange file to import (.json)"),
                )
                .arg(
                    Arg::with_name(MINIFY_FLAG)
                        .long(MINIFY_FLAG)
                        .takes_value(true)
                        .possible_values(&["false", "true"])
                        .help(
                            "Deprecated: Lighthouse no longer requires minification on import \
                             because it always minifies",
                        ),
                ),
        )
        .subcommand(
            App::new(EXPORT_CMD)
                .about("Export an interchange file")
                .arg(
                    Arg::with_name(EXPORT_FILE_ARG)
                        .takes_value(true)
                        .value_name("FILE")
                        .help("The filename to export the interchange file to"),
                )
                .arg(
                    Arg::with_name(PUBKEYS_FLAG)
                        .long(PUBKEYS_FLAG)
                        .takes_value(true)
                        .value_name("PUBKEYS")
                        .help(
                            "List of public keys to export history for. Keys should be 0x-prefixed, \
                             comma-separated. All known keys will be exported if omitted",
                        ),
                )
                .arg(
                    Arg::with_name(MINIFY_FLAG)
                        .long(MINIFY_FLAG)
                        .takes_value(true)
                        .default_value("false")
                        .possible_values(&["false", "true"])
                        .help(
                            "Minify the output file. This will make it smaller and faster to \
                             import, but not faster to generate.",
                        ),
                ),
        )
}

pub fn cli_run<T: EthSpec>(
    matches: &ArgMatches<'_>,
    env: Environment<T>,
    validator_base_dir: PathBuf,
) -> Result<(), String> {
    let slashing_protection_db_path = validator_base_dir.join(SLASHING_PROTECTION_FILENAME);

    let eth2_network_config = env
        .eth2_network_config
        .ok_or("Unable to get testnet configuration from the environment")?;

    let genesis_validators_root = eth2_network_config
        .beacon_state::<T>()
        .map(|state: BeaconState<T>| state.genesis_validators_root())
        .map_err(|e| {
            format!(
                "Unable to get genesis state, has genesis occurred? Detail: {:?}",
                e
            )
        })?;

    match matches.subcommand() {
        (IMPORT_CMD, Some(matches)) => {
            let import_filename: PathBuf = clap_utils::parse_required(matches, IMPORT_FILE_ARG)?;
            let minify: Option<bool> = clap_utils::parse_optional(matches, MINIFY_FLAG)?;
            let import_file = File::open(&import_filename).map_err(|e| {
                format!(
                    "Unable to open import file at {}: {:?}",
                    import_filename.display(),
                    e
                )
            })?;

            eprint!("Loading JSON file into memory & deserializing");
            let mut interchange = Interchange::from_json_reader(&import_file)
                .map_err(|e| format!("Error parsing file for import: {:?}", e))?;
            eprintln!(" [done].");

            if let Some(minify) = minify {
                eprintln!(
                    "WARNING: --minify flag is deprecated and will be removed in a future release"
                );
                if minify {
                    eprint!("Minifying input file for faster loading");
                    interchange = interchange
                        .minify()
                        .map_err(|e| format!("Minification failed: {:?}", e))?;
                    eprintln!(" [done].");
                }
            }

            let slashing_protection_database =
                SlashingDatabase::open_or_create(&slashing_protection_db_path).map_err(|e| {
                    format!(
                        "Unable to open database at {}: {:?}",
                        slashing_protection_db_path.display(),
                        e
                    )
                })?;

            let display_slot = |slot: Option<Slot>| {
                slot.map_or("none".to_string(), |slot| format!("slot {}", slot.as_u64()))
            };
            let display_epoch = |epoch: Option<Epoch>| {
                epoch.map_or("?".to_string(), |epoch| format!("epoch {}", epoch.as_u64()))
            };
            let display_attestation = |source, target| match (source, target) {
                (None, None) => "none".to_string(),
                (source, target) => {
                    format!("{} => {}", display_epoch(source), display_epoch(target))
                }
            };

            match slashing_protection_database
                .import_interchange_info(interchange, genesis_validators_root)
            {
                Ok(outcomes) => {
                    eprintln!("All records imported successfully:");
                    for outcome in &outcomes {
                        match outcome {
                            InterchangeImportOutcome::Success { pubkey, summary } => {
                                eprintln!("- {:?}", pubkey);
                                eprintln!(
                                    "    - latest block: {}",
                                    display_slot(summary.max_block_slot)
                                );
                                eprintln!(
                                    "    - latest attestation: {}",
                                    display_attestation(
                                        summary.max_attestation_source,
                                        summary.max_attestation_target
                                    )
                                );
                            }
                            InterchangeImportOutcome::Failure { pubkey, error } => {
                                panic!(
                                    "import should be atomic, but key {:?} was imported despite error: {:?}",
                                    pubkey, error
                                );
                            }
                        }
                    }
                }
                Err(InterchangeError::AtomicBatchAborted(outcomes)) => {
                    eprintln!("ERROR: import aborted due to one or more errors");
                    for outcome in &outcomes {
                        if let InterchangeImportOutcome::Failure { pubkey, error } = outcome {
                            eprintln!("- {:?}", pubkey);
                            eprintln!("    - error: {:?}", error);
                        }
                    }
                    return Err("ERROR: import aborted due to errors, see above.\n\
                                No data has been imported and the slashing protection \
                                database is in the same state it was in before the import.\n\
                                Due to the failed import it is NOT SAFE to start validating\n\
                                with any newly imported validator keys, as your database lacks\n\
                                slashing protection data for them."
                        .to_string());
                }
                Err(e) => {
                    return Err(format!(
                        "Fatal error during import: {:?}\n\
                         IT IS NOT SAFE TO START VALIDATING",
                        e
                    ));
                }
            }

            eprintln!("Import completed successfully.");
            eprintln!(
                "Please double-check that the latest blocks and attestations above \
                 match your expectations."
            );

            Ok(())
        }
        (EXPORT_CMD, Some(matches)) => {
            let export_filename: PathBuf = clap_utils::parse_required(matches, EXPORT_FILE_ARG)?;
            let minify: bool = clap_utils::parse_required(matches, MINIFY_FLAG)?;

            let selected_pubkeys = if let Some(pubkeys) =
                clap_utils::parse_optional::<String>(matches, PUBKEYS_FLAG)?
            {
                let pubkeys = pubkeys
                    .split(',')
                    .map(PublicKeyBytes::from_str)
                    .collect::<Result<Vec<_>, _>>()
                    .map_err(|e| format!("Invalid --{} value: {:?}", PUBKEYS_FLAG, e))?;
                Some(pubkeys)
            } else {
                None
            };

            if !slashing_protection_db_path.exists() {
                return Err(format!(
                    "No slashing protection database exists at: {}",
                    slashing_protection_db_path.display()
                ));
            }

            let slashing_protection_database = SlashingDatabase::open(&slashing_protection_db_path)
                .map_err(|e| {
                    format!(
                        "Unable to open database at {}: {:?}",
                        slashing_protection_db_path.display(),
                        e
                    )
                })?;

            let mut interchange = slashing_protection_database
                .export_interchange_info(genesis_validators_root, selected_pubkeys.as_deref())
                .map_err(|e| format!("Error during export: {:?}", e))?;

            if minify {
                eprintln!("Minifying output file");
                interchange = interchange
                    .minify()
                    .map_err(|e| format!("Unable to minify output: {:?}", e))?;
            }

            let output_file = File::create(export_filename)
                .map_err(|e| format!("Error creating output file: {:?}", e))?;

            interchange
                .write_to(&output_file)
                .map_err(|e| format!("Error writing output file: {:?}", e))?;

            eprintln!("Export completed successfully");

            Ok(())
        }
        ("", _) => Err("No subcommand provided, see --help for options".to_string()),
        (command, _) => Err(format!("No such subcommand `{}`", command)),
    }
}
<commit_msg>Improve slashing import log (#3122)<commit_after>use clap::{App, Arg, ArgMatches};
use environment::Environment;
use slashing_protection::{
    interchange::Interchange, InterchangeError, InterchangeImportOutcome, SlashingDatabase,
    SLASHING_PROTECTION_FILENAME,
};
use std::fs::File;
use std::path::PathBuf;
use std::str::FromStr;
use types::{BeaconState, Epoch, EthSpec, PublicKeyBytes, Slot};

pub const CMD: &str = "slashing-protection";
pub const IMPORT_CMD: &str = "import";
pub const EXPORT_CMD: &str = "export";

pub const IMPORT_FILE_ARG: &str = "IMPORT-FILE";
pub const EXPORT_FILE_ARG: &str = "EXPORT-FILE";

pub const MINIFY_FLAG: &str = "minify";
pub const PUBKEYS_FLAG: &str = "pubkeys";

pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
    App::new(CMD)
        .about("Import or export slashing protection data to or from another client")
        .subcommand(
            App::new(IMPORT_CMD)
                .about("Import an interchange file")
                .arg(
                    Arg::with_name(IMPORT_FILE_ARG)
                        .takes_value(true)
                        .value_name("FILE")
                        .help("The slashing protection interchange file to import (.json)"),
                )
                .arg(
                    Arg::with_name(MINIFY_FLAG)
                        .long(MINIFY_FLAG)
                        .takes_value(true)
                        .possible_values(&["false", "true"])
                        .help(
                            "Deprecated: Lighthouse no longer requires minification on import \
                             because it always minifies",
                        ),
                ),
        )
        .subcommand(
            App::new(EXPORT_CMD)
                .about("Export an interchange file")
                .arg(
                    Arg::with_name(EXPORT_FILE_ARG)
                        .takes_value(true)
                        .value_name("FILE")
                        .help("The filename to export the interchange file to"),
                )
                .arg(
                    Arg::with_name(PUBKEYS_FLAG)
                        .long(PUBKEYS_FLAG)
                        .takes_value(true)
                        .value_name("PUBKEYS")
                        .help(
                            "List of public keys to export history for. Keys should be 0x-prefixed, \
                             comma-separated. All known keys will be exported if omitted",
                        ),
                )
                .arg(
                    Arg::with_name(MINIFY_FLAG)
                        .long(MINIFY_FLAG)
                        .takes_value(true)
                        .default_value("false")
                        .possible_values(&["false", "true"])
                        .help(
                            "Minify the output file. This will make it smaller and faster to \
                             import, but not faster to generate.",
                        ),
                ),
        )
}

pub fn cli_run<T: EthSpec>(
    matches: &ArgMatches<'_>,
    env: Environment<T>,
    validator_base_dir: PathBuf,
) -> Result<(), String> {
    let slashing_protection_db_path = validator_base_dir.join(SLASHING_PROTECTION_FILENAME);

    let eth2_network_config = env
        .eth2_network_config
        .ok_or("Unable to get testnet configuration from the environment")?;

    let genesis_validators_root = eth2_network_config
        .beacon_state::<T>()
        .map(|state: BeaconState<T>| state.genesis_validators_root())
        .map_err(|e| {
            format!(
                "Unable to get genesis state, has genesis occurred? Detail: {:?}",
                e
            )
        })?;

    match matches.subcommand() {
        (IMPORT_CMD, Some(matches)) => {
            let import_filename: PathBuf = clap_utils::parse_required(matches, IMPORT_FILE_ARG)?;
            let minify: Option<bool> = clap_utils::parse_optional(matches, MINIFY_FLAG)?;
            let import_file = File::open(&import_filename).map_err(|e| {
                format!(
                    "Unable to open import file at {}: {:?}",
                    import_filename.display(),
                    e
                )
            })?;

            eprint!("Loading JSON file into memory & deserializing");
            let mut interchange = Interchange::from_json_reader(&import_file)
                .map_err(|e| format!("Error parsing file for import: {:?}", e))?;
            eprintln!(" [done].");

            if let Some(minify) = minify {
                eprintln!(
                    "WARNING: --minify flag is deprecated and will be removed in a future release"
                );
                if minify {
                    eprint!("Minifying input file for faster loading");
                    interchange = interchange
                        .minify()
                        .map_err(|e| format!("Minification failed: {:?}", e))?;
                    eprintln!(" [done].");
                }
            }

            let slashing_protection_database =
                SlashingDatabase::open_or_create(&slashing_protection_db_path).map_err(|e| {
                    format!(
                        "Unable to open database at {}: {:?}",
                        slashing_protection_db_path.display(),
                        e
                    )
                })?;

            let display_slot = |slot: Option<Slot>| {
                slot.map_or("none".to_string(), |slot| format!("slot {}", slot.as_u64()))
            };
            let display_epoch = |epoch: Option<Epoch>| {
                epoch.map_or("?".to_string(), |epoch| format!("epoch {}", epoch.as_u64()))
            };
            let display_attestation = |source, target| match (source, target) {
                (None, None) => "none".to_string(),
                (source, target) => {
                    format!("{} => {}", display_epoch(source), display_epoch(target))
                }
            };

            match slashing_protection_database
                .import_interchange_info(interchange, genesis_validators_root)
            {
                Ok(outcomes) => {
                    eprintln!("All records imported successfully:");
                    for outcome in &outcomes {
                        match outcome {
                            InterchangeImportOutcome::Success { pubkey, summary } => {
                                eprintln!("- {:?}", pubkey);
                                eprintln!(
                                    "    - latest proposed block: {}",
                                    display_slot(summary.max_block_slot)
                                );
                                eprintln!(
                                    "    - latest attestation: {}",
                                    display_attestation(
                                        summary.max_attestation_source,
                                        summary.max_attestation_target
                                    )
                                );
                            }
                            InterchangeImportOutcome::Failure { pubkey, error } => {
                                panic!(
                                    "import should be atomic, but key {:?} was imported despite error: {:?}",
                                    pubkey, error
                                );
                            }
                        }
                    }
                }
                Err(InterchangeError::AtomicBatchAborted(outcomes)) => {
                    eprintln!("ERROR: import aborted due to one or more errors");
                    for outcome in &outcomes {
                        if let InterchangeImportOutcome::Failure { pubkey, error } = outcome {
                            eprintln!("- {:?}", pubkey);
                            eprintln!("    - error: {:?}", error);
                        }
                    }
                    return Err("ERROR: import aborted due to errors, see above.\n\
                                No data has been imported and the slashing protection \
                                database is in the same state it was in before the import.\n\
                                Due to the failed import it is NOT SAFE to start validating\n\
                                with any newly imported validator keys, as your database lacks\n\
                                slashing protection data for them."
                        .to_string());
                }
                Err(e) => {
                    return Err(format!(
                        "Fatal error during import: {:?}\n\
                         IT IS NOT SAFE TO START VALIDATING",
                        e
                    ));
                }
            }

            eprintln!("Import completed successfully.");
            eprintln!(
                "Please double-check that the latest blocks and attestations above \
                 match your expectations."
            );

            Ok(())
        }
        (EXPORT_CMD, Some(matches)) => {
            let export_filename: PathBuf = clap_utils::parse_required(matches, EXPORT_FILE_ARG)?;
            let minify: bool = clap_utils::parse_required(matches, MINIFY_FLAG)?;

            let selected_pubkeys = if let Some(pubkeys) =
                clap_utils::parse_optional::<String>(matches, PUBKEYS_FLAG)?
            {
                let pubkeys = pubkeys
                    .split(',')
                    .map(PublicKeyBytes::from_str)
                    .collect::<Result<Vec<_>, _>>()
                    .map_err(|e| format!("Invalid --{} value: {:?}", PUBKEYS_FLAG, e))?;
                Some(pubkeys)
            } else {
                None
            };

            if !slashing_protection_db_path.exists() {
                return Err(format!(
                    "No slashing protection database exists at: {}",
                    slashing_protection_db_path.display()
                ));
            }

            let slashing_protection_database = SlashingDatabase::open(&slashing_protection_db_path)
                .map_err(|e| {
                    format!(
                        "Unable to open database at {}: {:?}",
                        slashing_protection_db_path.display(),
                        e
                    )
                })?;

            let mut interchange = slashing_protection_database
                .export_interchange_info(genesis_validators_root, selected_pubkeys.as_deref())
                .map_err(|e| format!("Error during export: {:?}", e))?;

            if minify {
                eprintln!("Minifying output file");
                interchange = interchange
                    .minify()
                    .map_err(|e| format!("Unable to minify output: {:?}", e))?;
            }

            let output_file = File::create(export_filename)
                .map_err(|e| format!("Error creating output file: {:?}", e))?;

            interchange
                .write_to(&output_file)
                .map_err(|e| format!("Error writing output file: {:?}", e))?;

            eprintln!("Export completed successfully");

            Ok(())
        }
        ("", _) => Err("No subcommand provided, see --help for options".to_string()),
        (command, _) => Err(format!("No such subcommand `{}`", command)),
    }
}
<|endoftext|>