<commit_before>extern crate regex;
use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

use std::process::{Command, Stdio, Child};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
use lib::config::ConfigGen;
use std::convert::Into;

use lib::DownloadError;

use CONFIG;
use lib;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

#[derive(Clone)]
pub struct DownloadDB {
    pub url: String,
    pub quality: i16,
    pub playlist: bool,
    pub compress: bool,
    pub qid: i64,
    pub from: i32,
    pub to: i32,
    pub folder: String, // download folder, changes for playlists
    pub pool: MyPool,
}

impl DownloadDB {
    pub fn update_video(&mut self,url: String, qid: i64) -> &mut DownloadDB{
        self.qid = qid;
        self.url = url;
        self
    }
    pub fn update_folder(&mut self, folder: String){
        self.folder = folder;
    }
}

pub struct Downloader<'a> {
    pub ddb: &'a DownloadDB,
    defaults: &'a ConfigGen,
    // pool: MyPool,
}

impl<'a> Downloader<'a>{
    pub fn new(ddb: &'a DownloadDB, defaults: &'a ConfigGen) -> Downloader<'a>{
        Downloader {ddb: ddb, defaults: defaults}
    }

    ///Regex matching: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
    ///
    ///Downloads a video, updates the DB
    ///TODO: get the sql statements out of the class
    ///TODO: wrap errors
    ///Doesn't care about DMCAs, will emit errors on them
    ///download_audio: ignore quality & download audio_raw codec for split containers
    pub fn download_file(&self, file_path: &str, download_audio: bool) -> Result<bool,DownloadError> {
        println!("{:?}", self.ddb.url);

        let curr_quality = if download_audio {
            &CONFIG.codecs.audio_raw
        }else{
            &self.ddb.quality
        };

        println!("quality: {}",curr_quality);
        let mut child = try!(self.run_download_process(file_path,curr_quality));
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"\d+\.\d%");

        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        if !self.ddb.playlist {
                            match re.find(&text) {
                                Some(s) => { //println!("Match at {}", s.0);
                                            println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
                                            try!(self.update_progress(&mut statement, &text[s.0..s.1].to_string()));
                                        },
                                None => {},
                            }
                        }
                },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process, fixes #10

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        if stderr.is_empty() {
            Ok(true)
        } else if stderr.contains("requested format not available") {
            Err(DownloadError::QualityNotAvailable)
        /*} else if stderr.contains("") {
            Err(DownloadError::NotAvailable) */
        } else {
            Err(DownloadError::InternalError(stderr))
        }

    }

    ///Trys to get the original name of a file, while checking for availability
    ///As an ExtractError can appear randomly, bug 11, we're retrying again 2 times if it should occour
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        for attempts in 0..2 {
            let mut child = try!(self.run_filename_process());
            let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
            let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

            let mut stdout: String = String::new();
            try!(stdout_buffer.read_to_string(&mut stdout));
            let mut stderr: String = String::new();
            try!(stderr_buffer.read_to_string(&mut stderr));

            try!(child.wait());
            if stderr.is_empty() {
                println!("get_file_name: {:?}", stdout);
                return Ok(stdout.trim().to_string());
            }else{
                if stderr.contains("not available in your country") || stderr.contains("contains content from") || stderr.contains("This video is available in") {
                    return Err(DownloadError::DMCAError);
                } else if stderr.contains("Please sign in to view this video") {
                    return Err(DownloadError::NotAvailable);
                } else if stderr.contains("ExtractorError") { // #11
                    println!("ExtractorError on attempt {}", attempts +1);
                } else {
                    return Err(DownloadError::DownloadError(stderr));
                }
            }
        }
        Err(DownloadError::ExtractorError)
    }

    ///Get playlist file ids
    pub fn get_playlist_ids(&self) -> Result<Vec<String>,DownloadError> {
        let mut child = try!(self.run_playlist_extract());
        println!("retrieving playlist ids");
        let stdout = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let re = regex!(r#""url": "([a-zA-Z0-9_-]+)""#);

        let mut id_list: Vec<String> = Vec::new();
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => { //println!("Match at {}", s.0);
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        id_list.push(cap.at(1).unwrap().to_string());
                                    },
                            None => {},
                        }
                    },
            }
        }

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            return Err(DownloadError::InternalError(stderr));
        }

        Ok(id_list)
    }

    ///Retrive playlist name, will kill the process due to yt-dl starting detail retrieval
    pub fn get_playlist_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_playlist_get_name());
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let re = regex!(r"\[download\] Downloading playlist: (.*)");

        let name: String;
        for line in stdout.lines(){

            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => {
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        name = cap.at(1).unwrap().to_string();
                                        try!(child.kill());
                                        println!("killed it");
                                        return Ok(name);
                                    },
                            None => {},
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        Err(DownloadError::DownloadError("no playlist name".to_string()))
    }

    fn run_download_process(&self, file_path: &str, quality: &i16) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--newline")
                                    .arg("-r")
                                    .arg(format!("{}M",self.defaults.download_mbps))
                                    .arg("-f")
                                    .arg(quality.to_string())
                                    .arg("-o")
                                    .arg(file_path)
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_filename_process(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--get-filename")
                                    .arg("-o")
                                    .arg("%(title)s")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .stderr(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_extract(&self) -> Result<Child,DownloadError> {
        let mut cmd = Command::new("youtube-dl");
        cmd.arg("-s").arg("--dump-json").arg("--flat-playlist");
        if self.ddb.from > 0 {
            cmd.arg("--playlist-start");
            cmd.arg(self.ddb.from.to_string());
        }
        if self.ddb.to > 0 {
            cmd.arg("--playlist-end");
            cmd.arg(self.ddb.to.to_string());
        }
        match cmd.arg(&self.ddb.url)
                    .stdin(Stdio::null())
                    .stdout(Stdio::piped())
                    .stderr(Stdio::piped())
                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_get_name(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("-s")
                                    .arg("--no-warnings")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: &String) -> Result<(),DownloadError>{
        try!(stmt.execute(&[progress,&self.ddb.qid]).map(|_| Ok(())))
        //-> only return errors, ignore the return value of stmt.execute
    }

    ///This function does a 3rd party binding in case it's needed
    ///due to the country restrictions
    ///Because hyper doesn't support timeout settings atm, we're calling an external
    ///lib
    ///The returned value contains the original video name, the lib downloads & saves
    ///the file at the given folder under the given name
    pub fn lib_request_video(&self, current_steps: i32,max_steps: i32) -> Result<String,DownloadError> {
        let mut child = try!(self.lib_request_video_cmd());
        println!("Requesting video via lib..");
        let stdout = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"step (\d)");

        let mut last_line = String::new();
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => { //println!("Match at {}", s.0);
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        if !self.ddb.playlist {
                                            lib::update_steps(&self.ddb.pool ,&self.ddb.qid, current_steps + &cap.at(1).unwrap().parse::<i32>().unwrap(), max_steps, false);
                                        }
                                    },
                            None => {last_line = text.clone()},
                        }
                    },
            }
        }

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            return Err(DownloadError::InternalError(stderr));
        }
        //this ONLY works because `filename ` is ascii..
        let mut out = last_line[last_line.find("filename ").unwrap()+9..].trim().to_string();
        out = lib::url_encode(&out);//stdout.trim();

        Ok(out)
    }

    ///Generate the lib-cmd `request [..]?v=asdf -folder /downloads -a -name testfile`
    fn lib_request_video_cmd(&self) -> Result<Child,DownloadError> {
        let java_path = Path::new(&self.defaults.jar_cmd);
        println!("{:?}", format!("{}/java -jar {}/offliberty.jar",self.defaults.jar_cmd,self.defaults.jar_folder));
        match Command::new("./java")
                                        .current_dir(java_path)
                                        .arg("-jar")
                                        .arg(format!("{}/offliberty.jar",&self.defaults.jar_folder))
                                        .arg("request")
                                        .arg(&self.ddb.url)
                                        .arg("-folder")
                                        .arg(&self.ddb.folder)
                                        .arg(self.gen_request_str())
                                        .arg("-name")
                                        .arg(self.ddb.qid.to_string()) //eq. format! https://botbot.me/mozilla/rust/msg/37524131/
                                        .stdin(Stdio::null())
                                        .stdout(Stdio::piped())
                                        .stderr(Stdio::piped())
                                        .spawn() {
                Err(why) => {println!("{:?}",why); Err(DownloadError::InternalError(Error::description(&why).into()))},
                Ok(process) => Ok(process),
            }
    }

    ///Generate -a or -v, based on if an audio or video quality is requested
    fn gen_request_str(&self) -> &'a str{
        if self.is_audio() && !self.is_aac() {
            "-a"
        } else {
            "-v"
        }
    }

    ///Check if the quality is 141, standing for audio or not
    pub fn is_audio(&self) -> bool {
        if self.ddb.quality == CONFIG.codecs.audio_raw {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_source_hq {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_mp3 {
            true
        } else {
            false
        }
    }

    ///Check if is AAC file
    fn is_aac(&self) -> bool {
        CONFIG.extensions.aac.contains(&self.ddb.quality)
    }
}<commit_msg>added more debug steps for #21 removed some unnecessary code<commit_after>extern crate regex;
use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

use std::process::{Command, Stdio, Child};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
use lib::config::ConfigGen;
use std::convert::Into;

use lib::DownloadError;

use CONFIG;
use lib;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

#[derive(Clone)]
pub struct DownloadDB {
    pub url: String,
    pub quality: i16,
    pub playlist: bool,
    pub compress: bool,
    pub qid: i64,
    pub from: i32,
    pub to: i32,
    pub folder: String, // download folder, changes for playlists
    pub pool: MyPool,
}

impl DownloadDB {
    pub fn update_video(&mut self,url: String, qid: i64) -> &mut DownloadDB{
        self.qid = qid;
        self.url = url;
        self
    }
    pub fn update_folder(&mut self, folder: String){
        self.folder = folder;
    }
}

pub struct Downloader<'a> {
    pub ddb: &'a DownloadDB,
    defaults: &'a ConfigGen,
    // pool: MyPool,
}

impl<'a> Downloader<'a>{
    pub fn new(ddb: &'a DownloadDB, defaults: &'a ConfigGen) -> Downloader<'a>{
        Downloader {ddb: ddb, defaults: defaults}
    }

    ///Regex matching: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
    ///
    ///Downloads a video, updates the DB
    ///TODO: get the sql statements out of the class
    ///TODO: wrap errors
    ///Doesn't care about DMCAs, will emit errors on them
    ///download_audio: ignore quality & download audio_raw codec for split containers
    pub fn download_file(&self, file_path: &str, download_audio: bool) -> Result<bool,DownloadError> {
        println!("{:?}", self.ddb.url);

        let curr_quality = if download_audio {
            &CONFIG.codecs.audio_raw
        }else{
            &self.ddb.quality
        };

        println!("quality: {}",curr_quality);
        let mut child = try!(self.run_download_process(file_path,curr_quality));
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"\d+\.\d%");

        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        if !self.ddb.playlist {
                            match re.find(&text) {
                                Some(s) => { //println!("Match at {}", s.0);
                                            println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
                                            try!(self.update_progress(&mut statement, &text[s.0..s.1].to_string()));
                                        },
                                None => {},
                            }
                        }
                },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process, fixes #10

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        if stderr.is_empty() {
            Ok(true)
        } else if stderr.contains("requested format not available") {
            Err(DownloadError::QualityNotAvailable)
        /*} else if stderr.contains("") {
            Err(DownloadError::NotAvailable) */
        } else {
            Err(DownloadError::InternalError(stderr))
        }

    }

    ///Trys to get the original name of a file, while checking for availability
    ///As an ExtractError can appear randomly, bug 11, we're retrying again 2 times if it should occour
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        for attempts in 0..2 {
            let mut child = try!(self.run_filename_process());
            let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
            let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

            let mut stdout: String = String::new();
            try!(stdout_buffer.read_to_string(&mut stdout));
            let mut stderr: String = String::new();
            try!(stderr_buffer.read_to_string(&mut stderr));

            try!(child.wait());
            if stderr.is_empty() {
                println!("get_file_name: {:?}", stdout);
                return Ok(stdout.trim().to_string());
            }else{
                if stderr.contains("not available in your country") || stderr.contains("contains content from") || stderr.contains("This video is available in") {
                    return Err(DownloadError::DMCAError);
                } else if stderr.contains("Please sign in to view this video") {
                    return Err(DownloadError::NotAvailable);
                } else if stderr.contains("ExtractorError") { // #11
                    println!("ExtractorError on attempt {}", attempts +1);
                } else {
                    return Err(DownloadError::DownloadError(stderr));
                }
            }
        }
        Err(DownloadError::ExtractorError)
    }

    ///Get playlist file ids
    pub fn get_playlist_ids(&self) -> Result<Vec<String>,DownloadError> {
        let mut child = try!(self.run_playlist_extract());
        println!("retrieving playlist ids");
        let stdout = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let re = regex!(r#""url": "([a-zA-Z0-9_-]+)""#);

        let mut id_list: Vec<String> = Vec::new();
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => { //println!("Match at {}", s.0);
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        id_list.push(cap.at(1).unwrap().to_string());
                                    },
                            None => {},
                        }
                    },
            }
        }

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            return Err(DownloadError::InternalError(stderr));
        }

        Ok(id_list)
    }

    ///Retrive playlist name, will kill the process due to yt-dl starting detail retrieval
    pub fn get_playlist_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_playlist_get_name());
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let re = regex!(r"\[download\] Downloading playlist: (.*)");

        let name: String;
        for line in stdout.lines(){

            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => {
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        name = cap.at(1).unwrap().to_string();
                                        try!(child.kill());
                                        println!("killed it");
                                        return Ok(name);
                                    },
                            None => {},
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        Err(DownloadError::DownloadError("no playlist name".to_string()))
    }

    fn run_download_process(&self, file_path: &str, quality: &i16) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--newline")
                                    .arg("-r")
                                    .arg(format!("{}M",self.defaults.download_mbps))
                                    .arg("-f")
                                    .arg(quality.to_string())
                                    .arg("-o")
                                    .arg(file_path)
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_filename_process(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--get-filename")
                                    .arg("-o")
                                    .arg("%(title)s")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .stderr(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_extract(&self) -> Result<Child,DownloadError> {
        let mut cmd = Command::new("youtube-dl");
        cmd.arg("-s").arg("--dump-json").arg("--flat-playlist");
        if self.ddb.from > 0 {
            cmd.arg("--playlist-start");
            cmd.arg(self.ddb.from.to_string());
        }
        if self.ddb.to > 0 {
            cmd.arg("--playlist-end");
            cmd.arg(self.ddb.to.to_string());
        }
        match cmd.arg(&self.ddb.url)
                    .stdin(Stdio::null())
                    .stdout(Stdio::piped())
                    .stderr(Stdio::piped())
                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_get_name(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("-s")
                                    .arg("--no-warnings")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: &String) -> Result<(),DownloadError>{
        try!(stmt.execute(&[progress,&self.ddb.qid]).map(|_| Ok(())))
        //-> only return errors, ignore the return value of stmt.execute
    }

    ///This function does a 3rd party binding in case it's needed
    ///due to the country restrictions
    ///Because hyper doesn't support timeout settings atm, we're calling an external
    ///lib
    ///The returned value contains the original video name, the lib downloads & saves
    ///the file at the given folder under the given name
    pub fn lib_request_video(&self, current_steps: i32,max_steps: i32) -> Result<String,DownloadError> {
        let mut child = try!(self.lib_request_video_cmd());
        println!("Requesting video via lib..");
        let stdout = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let re = regex!(r"step (\d)");

        let mut last_line = String::new();
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => { print!("Match ");
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        if !self.ddb.playlist {
                                            lib::update_steps(&self.ddb.pool ,&self.ddb.qid, current_steps + &cap.at(1).unwrap().parse::<i32>().unwrap(), max_steps, false);
                                        }
                                    },
                            None => {last_line = text.clone()},
                        }
                    },
            }
        }

        println!("reading stderr");

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            return Err(DownloadError::InternalError(stderr));
        }
        //this ONLY works because `filename ` is ascii..
        let mut out = last_line[last_line.find("filename ").unwrap()+9..].trim().to_string();
        out = lib::url_encode(&out);//stdout.trim();

        Ok(out)
    }

    ///Generate the lib-cmd `request [..]?v=asdf -folder /downloads -a -name testfile`
    fn lib_request_video_cmd(&self) -> Result<Child,DownloadError> {
        let java_path = Path::new(&self.defaults.jar_cmd);
        println!("{:?}", format!("{}/java -jar {}/offliberty.jar",self.defaults.jar_cmd,self.defaults.jar_folder));
        match Command::new("./java")
                                        .current_dir(java_path)
                                        .arg("-jar")
                                        .arg(format!("{}/offliberty.jar",&self.defaults.jar_folder))
                                        .arg("request")
                                        .arg(&self.ddb.url)
                                        .arg("-folder")
                                        .arg(&self.ddb.folder)
                                        .arg(self.gen_request_str())
                                        .arg("-name")
                                        .arg(self.ddb.qid.to_string()) //eq. format! https://botbot.me/mozilla/rust/msg/37524131/
                                        .stdin(Stdio::null())
                                        .stdout(Stdio::piped())
                                        .stderr(Stdio::piped())
                                        .spawn() {
                Err(why) => {println!("{:?}",why); Err(DownloadError::InternalError(Error::description(&why).into()))},
                Ok(process) => Ok(process),
            }
    }

    ///Generate -a or -v, based on if an audio or video quality is requested
    fn gen_request_str(&self) -> &'a str{
        if self.is_audio() && !self.is_aac() {
            "-a"
        } else {
            "-v"
        }
    }

    ///Check if the quality is 141, standing for audio or not
    pub fn is_audio(&self) -> bool {
        if self.ddb.quality == CONFIG.codecs.audio_raw {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_source_hq {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_mp3 {
            true
        } else {
            false
        }
    }

    ///Check if is AAC file
    fn is_aac(&self) -> bool {
        CONFIG.extensions.aac.contains(&self.ddb.quality)
    }
}<|endoftext|>


<commit_before>use num_traits::Zero;
use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{AddAssign, MulAssign};

use approximation::*;
use push_down_automaton::*;
use tree_stack_automaton::*;

/// `ApproximationStrategy` that approximates a `TreeStackAutomaton` into a `PushDownAutomaton`
#[derive(Clone, Debug)]
pub struct TTSElement {
    _dummy: PhantomData,
}

impl TTSElement {
    pub fn new() -> Self {
        TTSElement {
            _dummy: PhantomData,
        }
    }
}

impl ApproximationStrategy<T, W> for TTSElement
    where A: Clone + Hash + Ord,
          T: Clone + Eq + Hash + Ord,
          W: AddAssign + Copy + MulAssign + One + Ord + Zero,
{
    type I1 = TreeStackInstruction;
    type I2 = PushDownInstruction;
    type A1 = TreeStackAutomaton;
    type A2 = PushDownAutomaton;

    fn approximate_storage(&self, mut ts: TreeStack)-> PushDown {
        let mut pd = Vec::new();
        pd.push(ts.current_symbol().clone());

        while let Ok(smaller) = ts.down() {
            pd.push(smaller.current_symbol().clone());
            ts = smaller;
        }

        pd.reverse();
        PushDown::from_vec(pd)
    }

    fn approximate_instruction(&self, instr: &TreeStackInstruction)
                               -> PushDownInstruction
    {
        match *instr {
            TreeStackInstruction::Up { ref current_val, ref new_val, ..}
            | TreeStackInstruction::Push { ref current_val, ref new_val, .. } => {
                PushDownInstruction::Replace {
                    current_val: vec![current_val.clone()],
                    new_val: vec![current_val.clone(), new_val.clone()],
                }
            },
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } => {
                PushDownInstruction::Replace {
                    current_val: vec![current_val.clone(), old_val.clone()],
                    new_val: vec![new_val.clone()],
                }
            },
        }
    }

}
<commit_msg>src/approximation/tts.rs: added tests<commit_after>use num_traits::Zero;
use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{AddAssign, MulAssign};

use approximation::*;
use push_down_automaton::*;
use tree_stack_automaton::*;

/// `ApproximationStrategy` that approximates a `TreeStackAutomaton` into a `PushDownAutomaton`
#[derive(Clone, Debug)]
pub struct TTSElement {
    _dummy: PhantomData,
}

impl TTSElement {
    pub fn new() -> Self {
        TTSElement {
            _dummy: PhantomData,
        }
    }
}

impl ApproximationStrategy<T, W> for TTSElement
    where A: Clone + Hash + Ord,
          T: Clone + Eq + Hash + Ord,
          W: AddAssign + Copy + MulAssign + One + Ord + Zero,
{
    type I1 = TreeStackInstruction;
    type I2 = PushDownInstruction;
    type A1 = TreeStackAutomaton;
    type A2 = PushDownAutomaton;

    fn approximate_storage(&self, mut ts: TreeStack)-> PushDown {
        let mut pd = Vec::new();
        pd.push(ts.current_symbol().clone());

        while let Ok(smaller) = ts.down() {
            pd.push(smaller.current_symbol().clone());
            ts = smaller;
        }

        pd.reverse();
        PushDown::from_vec(pd)
    }

    fn approximate_instruction(&self, instr: &TreeStackInstruction)
                               -> PushDownInstruction
    {
        match *instr {
            TreeStackInstruction::Up { ref current_val, ref new_val, ..}
            | TreeStackInstruction::Push { ref current_val, ref new_val, .. } => {
                PushDownInstruction::Replace {
                    current_val: vec![current_val.clone()],
                    new_val: vec![current_val.clone(), new_val.clone()],
                }
            },
            TreeStackInstruction::Down { ref current_val, ref old_val, ref new_val } => {
                PushDownInstruction::Replace {
                    current_val: vec![current_val.clone(), old_val.clone()],
                    new_val: vec![new_val.clone()],
                }
            },
        }
    }
}

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

    #[test]
    fn test_approximate_storage() {
        let mut ts = TreeStack::new('@');
        ts = ts.push(1, '1').unwrap();
        ts = ts.down().unwrap();
        ts = ts.push(2, '2').unwrap();
        ts = ts.push(2, '3').unwrap();
        ts = ts.down().unwrap();
        ts = ts.push(7, '4').unwrap();

        let tts = TTSElement::new();
        let control_pushdown = PushDown::from_vec(vec![
            '@', '2', '4',
        ]);

        assert_eq!(
            control_pushdown,
            <TTSElement<_> as ApproximationStrategy<char, u8>>::approximate_storage(&tts, ts)
        );
    }

    #[test]
    fn test_approximate_instruction() {
        let tts = TTSElement::new();
        let inputs = vec![
            (TreeStackInstruction::Up { n: 2, current_val: '@', old_val: '2', new_val: '3' },
                PushDownInstruction::Replace { current_val: vec!['@'] , new_val: vec!['@', '3'] }),
            (TreeStackInstruction::Down { current_val: '4', old_val: '2', new_val: '3' },
                PushDownInstruction::Replace { current_val: vec!['4', '2'], new_val: vec!['3'] }),
            (TreeStackInstruction::Push { n: 2, current_val: '2', new_val: '3' },
                PushDownInstruction::Replace { current_val: vec!['2'], new_val: vec!['2', '3'] }),
        ];

        for (ts_instruction, pd_control_instruction) in inputs {
            assert_eq!(
                pd_control_instruction,
                <TTSElement<_> as ApproximationStrategy<char, u8>>::approximate_instruction(&tts, &ts_instruction)
            );
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interfaces to the operating system provided random number
//! generators.

use rand::Rng;
use ops::Drop;

#[cfg(unix)]
use rand::reader::ReaderRng;
#[cfg(unix)]
use rt::io::File;

#[cfg(windows)]
use cast;
#[cfg(windows)]
use libc::{c_long, DWORD, BYTE};
#[cfg(windows)]
type HCRYPTPROV = c_long;
// the extern functions imported from the runtime on Windows are
// implemented so that they either succeed or abort(), so we can just
// assume they work when we call them.

/// A random number generator that retrieves randomness straight from
/// the operating system. Platform sources:
///
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
///   `/dev/urandom`.
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
///   service provider with the `PROV_RSA_FULL` type.
///
/// This does not block.
#[cfg(unix)]
pub struct OSRng {
    priv inner: ReaderRng<File>
}
/// A random number generator that retrieves randomness straight from
/// the operating system. Platform sources:
///
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
///   `/dev/urandom`.
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
///   service provider with the `PROV_RSA_FULL` type.
///
/// This does not block.
#[cfg(windows)]
pub struct OSRng {
    priv hcryptprov: HCRYPTPROV
}

impl OSRng {
    /// Create a new `OSRng`.
    #[cfg(unix)]
    pub fn new() -> OSRng {
        use path::Path;
        let reader = File::open(&Path::new("/dev/urandom"));
        let reader = reader.expect("Error opening /dev/urandom");
        let reader_rng = ReaderRng::new(reader);

        OSRng { inner: reader_rng }
    }

    /// Create a new `OSRng`.
    #[cfg(windows)]
    pub fn new() -> OSRng {
        externfn!(fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV))

        let mut hcp = 0;
        unsafe {rust_win32_rand_acquire(&mut hcp)};

        OSRng { hcryptprov: hcp }
    }
}

#[cfg(unix)]
impl Rng for OSRng {
    fn next_u32(&mut self) -> u32 {
        self.inner.next_u32()
    }
    fn next_u64(&mut self) -> u64 {
        self.inner.next_u64()
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        self.inner.fill_bytes(v)
    }
}

#[cfg(windows)]
impl Rng for OSRng {
    fn next_u32(&mut self) -> u32 {
        let mut v = [0u8, .. 4];
        self.fill_bytes(v);
        unsafe { cast::transmute(v) }
    }
    fn next_u64(&mut self) -> u64 {
        let mut v = [0u8, .. 8];
        self.fill_bytes(v);
        unsafe { cast::transmute(v) }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        externfn!(fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE))

        do v.as_mut_buf |ptr, len| {
            unsafe {rust_win32_rand_gen(self.hcryptprov, len as DWORD, ptr)}
        }
    }
}

impl Drop for OSRng {
    #[cfg(unix)]
    fn drop(&mut self) {
        // ensure that OSRng is not implicitly copyable on all
        // platforms, for consistency.
    }

    #[cfg(windows)]
    fn drop(&mut self) {
        externfn!(fn rust_win32_rand_release(hProv: HCRYPTPROV))

        unsafe {rust_win32_rand_release(self.hcryptprov)}
    }
}

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

    #[test]
    fn test_os_rng() {
        let mut r = OSRng::new();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8, .. 1000];
        r.fill_bytes(v);
    }

    #[test]
    fn test_os_rng_tasks() {
        use task;
        use comm;
        use comm::{GenericChan, GenericPort};
        use option::{None, Some};
        use iter::{Iterator, range};
        use vec::{ImmutableVector, OwnedVector};

        let mut chans = ~[];
        for _ in range(0, 20) {
            let (p, c) = comm::stream();
            chans.push(c);
            do task::spawn_with(p) |p| {
                // wait until all the tasks are ready to go.
                p.recv();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OSRng::new();
                task::deschedule();
                let mut v = [0u8, .. 1000];

                for _ in range(0, 100) {
                    r.next_u32();
                    task::deschedule();
                    r.next_u64();
                    task::deschedule();
                    r.fill_bytes(v);
                    task::deschedule();
                }
            }
        }

        // start all the tasks
        for c in chans.iter() {
            c.send(())
        }
    }
}
<commit_msg>Remove #[fixed_stack_segment] and #[rust_stack]<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interfaces to the operating system provided random number
//! generators.

use rand::Rng;
use ops::Drop;

#[cfg(unix)]
use rand::reader::ReaderRng;
#[cfg(unix)]
use rt::io::File;

#[cfg(windows)]
use cast;
#[cfg(windows)]
use libc::{c_long, DWORD, BYTE};
#[cfg(windows)]
type HCRYPTPROV = c_long;
// the extern functions imported from the runtime on Windows are
// implemented so that they either succeed or abort(), so we can just
// assume they work when we call them.

/// A random number generator that retrieves randomness straight from
/// the operating system. Platform sources:
///
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
///   `/dev/urandom`.
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
///   service provider with the `PROV_RSA_FULL` type.
///
/// This does not block.
#[cfg(unix)]
pub struct OSRng {
    priv inner: ReaderRng<File>
}
/// A random number generator that retrieves randomness straight from
/// the operating system. Platform sources:
///
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
///   `/dev/urandom`.
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
///   service provider with the `PROV_RSA_FULL` type.
///
/// This does not block.
#[cfg(windows)]
pub struct OSRng {
    priv hcryptprov: HCRYPTPROV
}

impl OSRng {
    /// Create a new `OSRng`.
    #[cfg(unix)]
    pub fn new() -> OSRng {
        use path::Path;
        let reader = File::open(&Path::new("/dev/urandom"));
        let reader = reader.expect("Error opening /dev/urandom");
        let reader_rng = ReaderRng::new(reader);

        OSRng { inner: reader_rng }
    }

    /// Create a new `OSRng`.
    #[cfg(windows)]
    pub fn new() -> OSRng {
        extern { fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV); }

        let mut hcp = 0;
        unsafe {rust_win32_rand_acquire(&mut hcp)};

        OSRng { hcryptprov: hcp }
    }
}

#[cfg(unix)]
impl Rng for OSRng {
    fn next_u32(&mut self) -> u32 {
        self.inner.next_u32()
    }
    fn next_u64(&mut self) -> u64 {
        self.inner.next_u64()
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        self.inner.fill_bytes(v)
    }
}

#[cfg(windows)]
impl Rng for OSRng {
    fn next_u32(&mut self) -> u32 {
        let mut v = [0u8, .. 4];
        self.fill_bytes(v);
        unsafe { cast::transmute(v) }
    }
    fn next_u64(&mut self) -> u64 {
        let mut v = [0u8, .. 8];
        self.fill_bytes(v);
        unsafe { cast::transmute(v) }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        extern {
            fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD,
                                   pbBuffer: *mut BYTE);
        }

        do v.as_mut_buf |ptr, len| {
            unsafe {rust_win32_rand_gen(self.hcryptprov, len as DWORD, ptr)}
        }
    }
}

impl Drop for OSRng {
    #[cfg(unix)]
    fn drop(&mut self) {
        // ensure that OSRng is not implicitly copyable on all
        // platforms, for consistency.
    }

    #[cfg(windows)]
    fn drop(&mut self) {
        extern { fn rust_win32_rand_release(hProv: HCRYPTPROV); }

        unsafe {rust_win32_rand_release(self.hcryptprov)}
    }
}

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

    #[test]
    fn test_os_rng() {
        let mut r = OSRng::new();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8, .. 1000];
        r.fill_bytes(v);
    }

    #[test]
    fn test_os_rng_tasks() {
        use task;
        use comm;
        use comm::{GenericChan, GenericPort};
        use option::{None, Some};
        use iter::{Iterator, range};
        use vec::{ImmutableVector, OwnedVector};

        let mut chans = ~[];
        for _ in range(0, 20) {
            let (p, c) = comm::stream();
            chans.push(c);
            do task::spawn_with(p) |p| {
                // wait until all the tasks are ready to go.
                p.recv();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OSRng::new();
                task::deschedule();
                let mut v = [0u8, .. 1000];

                for _ in range(0, 100) {
                    r.next_u32();
                    task::deschedule();
                    r.next_u64();
                    task::deschedule();
                    r.fill_bytes(v);
                    task::deschedule();
                }
            }
        }

        // start all the tasks
        for c in chans.iter() {
            c.send(())
        }
    }
}
<|endoftext|>


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

use super::Composite::{List, Tuple};
use super::Op::{Add, Bind, Cp, Div, Element, InitList, InitTuple, Mul, Sub, Sym, Val, FIELD};
use super::Primitive::{Float, Int, Str};
use super::Value::{C, P};
use super::VM;

#[test]
fn test_math_ops() {
    let mut cases = vec![
        // 1+1;
        (vec![Val(Int(1)), Val(Int(1)), Add], P(Int(2))),
        // 1-1;
        (vec![Val(Int(1)), Val(Int(1)), Sub], P(Int(0))),
        // 2*2;
        (vec![Val(Int(2)), Val(Int(2)), Mul], P(Int(4))),
        // 6/3;
        (vec![Val(Int(2)), Val(Int(6)), Div], P(Int(3))),
        // 1.0+1.0;
        (vec![Val(Float(1.0)), Val(Float(1.0)), Add], P(Float(2.0))),
        // 1.0-1.0;
        (vec![Val(Float(1.0)), Val(Float(1.0)), Sub], P(Float(0.0))),
        // 2.0*2.0;
        (vec![Val(Float(2.0)), Val(Float(2.0)), Mul], P(Float(4.0))),
        // 6.0/3.0;
        (vec![Val(Float(2.0)), Val(Float(6.0)), Div], P(Float(3.0))),
        // string concatenation
        (
            vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add],
            P(Str("foobar".to_owned())),
        ),
        // Composite operations
        (
            vec![
                Val(Int(1)),
                Val(Int(1)),
                Add, // 1 + 1
                Val(Int(1)),
                Add, // 2 + 1
                Val(Int(1)),
                Add, // 3 + 1
            ],
            P(Int(4)),
        ),
    ];
    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        assert_eq!(vm.pop().unwrap(), case.1);
    }
}

#[test]
fn test_bind_op() {
    let mut cases = vec![(
        vec![Sym("foo".to_owned()), Val(Int(1)), Bind],
        ("foo", P(Int(1))),
        vec![Sym("foo".to_owned()), Val(Int(1)), Val(Int(1)), Add, Bind],
        ("foo", P(Int(2))),
    )];

    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        let (name, result) = case.1;
        let v = vm.get_binding(name).unwrap();
        assert_eq!(&result, v);
    }
}

#[test]
fn test_list_ops() {
    let mut cases = vec![
        (vec![InitList], C(List(Vec::new()))),
        (
            vec![InitList, Val(Int(1)), Element],
            C(List(vec![P(Int(1))])),
        ),
        (
            vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element],
            C(List(vec![P(Int(2)), P(Int(1))])),
        ),
    ];
    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        assert_eq!(vm.pop().unwrap(), case.1);
    }
}

#[test]
fn test_tuple_ops() {
    let mut cases = vec![
        (vec![InitTuple], C(Tuple(Vec::new()))),
        (
            vec![InitTuple, Val(Str("foo".to_owned())), Val(Int(1)), FIELD],
            C(Tuple(vec![("foo".to_owned(), P(Int(1)))])),
        ),
        (
            vec![
                InitTuple,
                Sym("bar".to_owned()),
                Val(Str("quux".to_owned())),
                FIELD,
                Val(Str("foo".to_owned())),
                Val(Int(1)),
                FIELD,
            ],
            C(Tuple(vec![
                ("bar".to_owned(), P(Str("quux".to_owned()))),
                ("foo".to_owned(), P(Int(1))),
            ])),
        ),
        (
            vec![
                InitTuple,
                Sym("bar".to_owned()),
                Val(Str("quux".to_owned())),
                FIELD,
                Val(Str("foo".to_owned())),
                Val(Int(1)),
                FIELD,
                Cp,
                Val(Str("foo".to_owned())),
                Val(Int(2)),
                FIELD,
            ],
            C(Tuple(vec![
                ("bar".to_owned(), P(Str("quux".to_owned()))),
                ("foo".to_owned(), P(Int(2))),
            ])),
        ),
    ];
    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        assert_eq!(vm.pop().unwrap(), case.1);
    }
}
<commit_msg>TESTS: Expresions as field values in lists and tuples.<commit_after>// Copyright 2019 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::Composite::{List, Tuple};
use super::Op::{Add, Bind, Cp, Div, Element, InitList, InitTuple, Mul, Sub, Sym, Val, FIELD};
use super::Primitive::{Float, Int, Str};
use super::Value::{C, P};
use super::VM;

#[test]
fn test_math_ops() {
    let mut cases = vec![
        // 1+1;
        (vec![Val(Int(1)), Val(Int(1)), Add], P(Int(2))),
        // 1-1;
        (vec![Val(Int(1)), Val(Int(1)), Sub], P(Int(0))),
        // 2*2;
        (vec![Val(Int(2)), Val(Int(2)), Mul], P(Int(4))),
        // 6/3;
        (vec![Val(Int(2)), Val(Int(6)), Div], P(Int(3))),
        // 1.0+1.0;
        (vec![Val(Float(1.0)), Val(Float(1.0)), Add], P(Float(2.0))),
        // 1.0-1.0;
        (vec![Val(Float(1.0)), Val(Float(1.0)), Sub], P(Float(0.0))),
        // 2.0*2.0;
        (vec![Val(Float(2.0)), Val(Float(2.0)), Mul], P(Float(4.0))),
        // 6.0/3.0;
        (vec![Val(Float(2.0)), Val(Float(6.0)), Div], P(Float(3.0))),
        // string concatenation
        (
            vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add],
            P(Str("foobar".to_owned())),
        ),
        // Composite operations
        (
            vec![
                Val(Int(1)),
                Val(Int(1)),
                Add, // 1 + 1
                Val(Int(1)),
                Add, // 2 + 1
                Val(Int(1)),
                Add, // 3 + 1
            ],
            P(Int(4)),
        ),
    ];
    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        assert_eq!(vm.pop().unwrap(), case.1);
    }
}

#[test]
fn test_bind_op() {
    let mut cases = vec![(
        vec![Sym("foo".to_owned()), Val(Int(1)), Bind],
        ("foo", P(Int(1))),
        vec![Sym("foo".to_owned()), Val(Int(1)), Val(Int(1)), Add, Bind],
        ("foo", P(Int(2))),
    )];

    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        let (name, result) = case.1;
        let v = vm.get_binding(name).unwrap();
        assert_eq!(&result, v);
    }
}

#[test]
fn test_list_ops() {
    let mut cases = vec![
        (vec![InitList], C(List(Vec::new()))),
        (
            vec![InitList, Val(Int(1)), Element],
            C(List(vec![P(Int(1))])),
        ),
        (
            vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element],
            C(List(vec![P(Int(2)), P(Int(1))])),
        ),
        (
            vec![
                InitList,
                Val(Int(1)),
                Element,
                Val(Int(1)),
                Val(Int(1)),
                Add,
                Element,
            ],
            C(List(vec![P(Int(1)), P(Int(2))])),
        ),
    ];
    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        assert_eq!(vm.pop().unwrap(), case.1);
    }
}

#[test]
fn test_tuple_ops() {
    let mut cases = vec![
        (vec![InitTuple], C(Tuple(Vec::new()))),
        (
            vec![InitTuple, Val(Str("foo".to_owned())), Val(Int(1)), FIELD],
            C(Tuple(vec![("foo".to_owned(), P(Int(1)))])),
        ),
        (
            vec![
                InitTuple,
                Sym("bar".to_owned()),
                Val(Str("quux".to_owned())),
                FIELD,
                Val(Str("foo".to_owned())),
                Val(Int(1)),
                FIELD,
            ],
            C(Tuple(vec![
                ("bar".to_owned(), P(Str("quux".to_owned()))),
                ("foo".to_owned(), P(Int(1))),
            ])),
        ),
        (
            vec![
                InitTuple,
                Sym("bar".to_owned()),
                Val(Str("quux".to_owned())),
                FIELD,
                Val(Str("foo".to_owned())),
                Val(Int(1)),
                FIELD,
                Cp,
                Val(Str("foo".to_owned())),
                Val(Int(2)),
                FIELD,
            ],
            C(Tuple(vec![
                ("bar".to_owned(), P(Str("quux".to_owned()))),
                ("foo".to_owned(), P(Int(2))),
            ])),
        ),
        (
            vec![
                InitTuple,
                Sym("bar".to_owned()),
                Val(Str("ux".to_owned())),
                Val(Str("qu".to_owned())),
                Add,
                FIELD,
                Val(Str("foo".to_owned())),
                Val(Int(1)),
                FIELD,
                Cp,
                Val(Str("foo".to_owned())),
                Val(Int(2)),
                FIELD,
            ],
            C(Tuple(vec![
                ("bar".to_owned(), P(Str("quux".to_owned()))),
                ("foo".to_owned(), P(Int(2))),
            ])),
        ),
    ];
    for case in cases.drain(0..) {
        let mut vm = VM::new(case.0);
        vm.run().unwrap();
        assert_eq!(vm.pop().unwrap(), case.1);
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::convert::TryInto;
use std::mem;
use std::ptr::{read_volatile, write_volatile};
use std::sync::atomic::{AtomicUsize, Ordering};

use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::{self, Device, DeviceCapabilities};
use smoltcp::socket::SocketSet;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};

use x86::io::outl;

const SHAREDQUEUE_START: usize = 0x80000;
//const UHYVE_MAX_MSG_SIZE: usize = 1792;
const UHYVE_NET_MTU: usize = 1500;
const UHYVE_QUEUE_SIZE: usize = 8;
const UHYVE_PORT_NETWRITE: u16 = 0x640;

pub type Tid = u32;

extern "Rust" {
	fn uhyve_get_ip() -> [u8; 4];
	fn uhyve_get_gateway() -> [u8; 4];
	fn uhyve_get_mask() -> [u8; 4];
	fn uhyve_get_mac_address() -> [u8; 6];
	fn uhyve_is_polling() -> bool;
	fn uhyve_netwait(millis: Option<u64>);
	fn uhyve_set_polling(mode: bool);
}

extern "C" {
	fn sys_spawn(
		id: *mut Tid,
		func: extern "C" fn(usize),
		arg: usize,
		prio: u8,
		selector: isize,
	) -> i32;
}

#[no_mangle]
extern "C" fn uhyve_thread(_: usize) {
	debug!("Initialize uhyve network interface!");

	let myip = unsafe { uhyve_get_ip() };
	if myip[0] == 0xff && myip[1] == 0xff && myip[2] == 0xff && myip[3] == 0xff {
		panic!("Unable to determine IP address");
	}

	let mygw = unsafe { uhyve_get_gateway() };
	let mymask = unsafe { uhyve_get_mask() };
	let mac = unsafe { uhyve_get_mac_address() };

	// calculate the netmask length
	// => count the number of contiguous 1 bits,
	// starting at the most significant bit in the first octet
	let mut prefix_len = (!mymask[0]).trailing_zeros();
	if prefix_len == 8 {
		prefix_len += (!mymask[1]).trailing_zeros();
	}
	if prefix_len == 16 {
		prefix_len += (!mymask[2]).trailing_zeros();
	}
	if prefix_len == 24 {
		prefix_len += (!mymask[3]).trailing_zeros();
	}

	let device = UhyveNet {};
	let neighbor_cache = NeighborCache::new(BTreeMap::new());
	let ethernet_addr = EthernetAddress([mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]]);
	let ip_addrs = [IpCidr::new(
		IpAddress::v4(myip[0], myip[1], myip[2], myip[3]),
		prefix_len.try_into().unwrap(),
	)];
	let default_v4_gw = Ipv4Address::new(mygw[0], mygw[1], mygw[2], mygw[3]);
	let mut routes_storage = [None; 1];
	let mut routes = Routes::new(&mut routes_storage[..]);
	routes.add_default_ipv4_route(default_v4_gw).unwrap();

	info!("MAC address {}", ethernet_addr);
	info!("Configure network interface with address {}", ip_addrs[0]);
	info!("Configure gatway with address {}", default_v4_gw);

	let mut iface = EthernetInterfaceBuilder::new(device)
		.ethernet_addr(ethernet_addr)
		.neighbor_cache(neighbor_cache)
		.ip_addrs(ip_addrs)
		.routes(routes)
		.finalize();

	let mut sockets = SocketSet::new(vec![]);
	let mut counter: usize = 0;
	loop {
		let timestamp = Instant::now();

		iface
			.poll(&mut sockets, timestamp)
			.map(|_| {
				trace!("receive message {}", counter);
				counter += 1;
			})
			.unwrap_or_else(|e| debug!("Poll: {:?}", e));

		if unsafe { !uhyve_is_polling() } {
			let delay = match iface.poll_delay(&sockets, timestamp) {
				Some(duration) => {
					if duration.millis() > 0 {
						Some(duration.millis())
					} else {
						Some(1)
					}
				}
				None => None,
			};

			unsafe {
				uhyve_netwait(delay);
			}
		}
	}
}

pub fn network_init() {
	let mut tid: Tid = 0;
	let ret = unsafe { sys_spawn(&mut tid, uhyve_thread, 0, 3, 0) };
	if ret >= 0 {
		info!("Spawn network thread with id {}", tid);
	}
}

#[repr(C)]
pub struct QueueInner {
	pub len: u16,
	pub data: [u8; UHYVE_NET_MTU],
}

#[repr(C)]
pub struct SharedQueue {
	pub read: AtomicUsize,
	pub written: AtomicUsize,
	pub inner: [QueueInner; UHYVE_QUEUE_SIZE],
}

/// Data type to determine the mac address
#[derive(Debug, Default)]
#[repr(C)]
struct UhyveNet;

impl<'a> Device<'a> for UhyveNet {
	type RxToken = RxToken;
	type TxToken = TxToken;

	fn capabilities(&self) -> DeviceCapabilities {
		let mut cap = DeviceCapabilities::default();
		cap.max_transmission_unit = UHYVE_NET_MTU;
		cap
	}

	fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
		let rx_queue = unsafe { &mut *(SHAREDQUEUE_START as *mut u8 as *mut SharedQueue) };
		let written = rx_queue.written.load(Ordering::SeqCst);
		let read = rx_queue.read.load(Ordering::SeqCst);
		let distance = written - read;

		if distance > 0 {
			let idx = read % UHYVE_QUEUE_SIZE;
			let len = unsafe { read_volatile(&rx_queue.inner[idx].len) };
			let tx = TxToken::new();
			let mut rx = RxToken::new(len);

			rx.buffer[0..len as usize].copy_from_slice(&rx_queue.inner[idx].data[0..len as usize]);
			rx_queue.read.fetch_add(1, Ordering::SeqCst);

			Some((rx, tx))
		} else {
			trace!("Disable polling mode");

			unsafe {
				uhyve_set_polling(false);
			}

			None
		}
	}

	fn transmit(&'a mut self) -> Option<Self::TxToken> {
		trace!("create TxToken to transfer data");
		Some(TxToken::new())
	}
}

#[doc(hidden)]
struct RxToken {
	buffer: [u8; UHYVE_NET_MTU],
	len: u16,
}

impl RxToken {
	pub fn new(len: u16) -> RxToken {
		RxToken {
			buffer: [0; UHYVE_NET_MTU],
			len: len,
		}
	}
}

impl phy::RxToken for RxToken {
	fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let (first, _) = self.buffer.split_at_mut(self.len as usize);
		f(first)
	}
}

#[doc(hidden)]
struct TxToken;

impl TxToken {
	pub const fn new() -> Self {
		TxToken {}
	}
}

impl phy::TxToken for TxToken {
	fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let tx_queue = unsafe {
			&mut *((SHAREDQUEUE_START + mem::size_of::<SharedQueue>()) as *mut u8
				as *mut SharedQueue)
		};
		let written = tx_queue.written.load(Ordering::SeqCst);
		let read = tx_queue.read.load(Ordering::SeqCst);
		let distance = written - read;

		if distance < UHYVE_QUEUE_SIZE {
			let idx = written % UHYVE_QUEUE_SIZE;
			let result = f(&mut tx_queue.inner[idx].data[0..len]);

			if result.is_ok() == true {
				unsafe {
					write_volatile(&mut tx_queue.inner[idx].len, len.try_into().unwrap());
				}
				tx_queue.written.fetch_add(1, Ordering::SeqCst);

				if distance == 0 {
					unsafe {
						outl(UHYVE_PORT_NETWRITE, 0);
					}
				}
			}

			result
		} else {
			Err(smoltcp::Error::Dropped)
		}
	}
}
<commit_msg>remove obsolete constant<commit_after>use std::collections::BTreeMap;
use std::convert::TryInto;
use std::mem;
use std::ptr::{read_volatile, write_volatile};
use std::sync::atomic::{AtomicUsize, Ordering};

use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::{self, Device, DeviceCapabilities};
use smoltcp::socket::SocketSet;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};

use x86::io::outl;

const SHAREDQUEUE_START: usize = 0x80000;
const UHYVE_NET_MTU: usize = 1500;
const UHYVE_QUEUE_SIZE: usize = 8;
const UHYVE_PORT_NETWRITE: u16 = 0x640;

pub type Tid = u32;

extern "Rust" {
	fn uhyve_get_ip() -> [u8; 4];
	fn uhyve_get_gateway() -> [u8; 4];
	fn uhyve_get_mask() -> [u8; 4];
	fn uhyve_get_mac_address() -> [u8; 6];
	fn uhyve_is_polling() -> bool;
	fn uhyve_netwait(millis: Option<u64>);
	fn uhyve_set_polling(mode: bool);
}

extern "C" {
	fn sys_spawn(
		id: *mut Tid,
		func: extern "C" fn(usize),
		arg: usize,
		prio: u8,
		selector: isize,
	) -> i32;
}

#[no_mangle]
extern "C" fn uhyve_thread(_: usize) {
	debug!("Initialize uhyve network interface!");

	let myip = unsafe { uhyve_get_ip() };
	if myip[0] == 0xff && myip[1] == 0xff && myip[2] == 0xff && myip[3] == 0xff {
		panic!("Unable to determine IP address");
	}

	let mygw = unsafe { uhyve_get_gateway() };
	let mymask = unsafe { uhyve_get_mask() };
	let mac = unsafe { uhyve_get_mac_address() };

	// calculate the netmask length
	// => count the number of contiguous 1 bits,
	// starting at the most significant bit in the first octet
	let mut prefix_len = (!mymask[0]).trailing_zeros();
	if prefix_len == 8 {
		prefix_len += (!mymask[1]).trailing_zeros();
	}
	if prefix_len == 16 {
		prefix_len += (!mymask[2]).trailing_zeros();
	}
	if prefix_len == 24 {
		prefix_len += (!mymask[3]).trailing_zeros();
	}

	let device = UhyveNet {};
	let neighbor_cache = NeighborCache::new(BTreeMap::new());
	let ethernet_addr = EthernetAddress([mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]]);
	let ip_addrs = [IpCidr::new(
		IpAddress::v4(myip[0], myip[1], myip[2], myip[3]),
		prefix_len.try_into().unwrap(),
	)];
	let default_v4_gw = Ipv4Address::new(mygw[0], mygw[1], mygw[2], mygw[3]);
	let mut routes_storage = [None; 1];
	let mut routes = Routes::new(&mut routes_storage[..]);
	routes.add_default_ipv4_route(default_v4_gw).unwrap();

	info!("MAC address {}", ethernet_addr);
	info!("Configure network interface with address {}", ip_addrs[0]);
	info!("Configure gatway with address {}", default_v4_gw);

	let mut iface = EthernetInterfaceBuilder::new(device)
		.ethernet_addr(ethernet_addr)
		.neighbor_cache(neighbor_cache)
		.ip_addrs(ip_addrs)
		.routes(routes)
		.finalize();

	let mut sockets = SocketSet::new(vec![]);
	let mut counter: usize = 0;
	loop {
		let timestamp = Instant::now();

		iface
			.poll(&mut sockets, timestamp)
			.map(|_| {
				trace!("receive message {}", counter);
				counter += 1;
			})
			.unwrap_or_else(|e| debug!("Poll: {:?}", e));

		if unsafe { !uhyve_is_polling() } {
			let delay = match iface.poll_delay(&sockets, timestamp) {
				Some(duration) => {
					if duration.millis() > 0 {
						Some(duration.millis())
					} else {
						Some(1)
					}
				}
				None => None,
			};

			unsafe {
				uhyve_netwait(delay);
			}
		}
	}
}

pub fn network_init() {
	let mut tid: Tid = 0;
	let ret = unsafe { sys_spawn(&mut tid, uhyve_thread, 0, 3, 0) };
	if ret >= 0 {
		debug!("Spawn network thread with id {}", tid);
	}
}

#[repr(C)]
pub struct QueueInner {
	pub len: u16,
	pub data: [u8; UHYVE_NET_MTU],
}

#[repr(C)]
pub struct SharedQueue {
	pub read: AtomicUsize,
	pub written: AtomicUsize,
	pub inner: [QueueInner; UHYVE_QUEUE_SIZE],
}

/// Data type to determine the mac address
#[derive(Debug, Default)]
#[repr(C)]
struct UhyveNet;

impl<'a> Device<'a> for UhyveNet {
	type RxToken = RxToken;
	type TxToken = TxToken;

	fn capabilities(&self) -> DeviceCapabilities {
		let mut cap = DeviceCapabilities::default();
		cap.max_transmission_unit = UHYVE_NET_MTU;
		cap
	}

	fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
		let rx_queue = unsafe { &mut *(SHAREDQUEUE_START as *mut u8 as *mut SharedQueue) };
		let written = rx_queue.written.load(Ordering::SeqCst);
		let read = rx_queue.read.load(Ordering::SeqCst);
		let distance = written - read;

		if distance > 0 {
			let idx = read % UHYVE_QUEUE_SIZE;
			let len = unsafe { read_volatile(&rx_queue.inner[idx].len) };
			let tx = TxToken::new();
			let mut rx = RxToken::new(len);

			rx.buffer[0..len as usize].copy_from_slice(&rx_queue.inner[idx].data[0..len as usize]);
			rx_queue.read.fetch_add(1, Ordering::SeqCst);

			Some((rx, tx))
		} else {
			trace!("Disable polling mode");

			unsafe {
				uhyve_set_polling(false);
			}

			None
		}
	}

	fn transmit(&'a mut self) -> Option<Self::TxToken> {
		trace!("create TxToken to transfer data");
		Some(TxToken::new())
	}
}

#[doc(hidden)]
struct RxToken {
	buffer: [u8; UHYVE_NET_MTU],
	len: u16,
}

impl RxToken {
	pub fn new(len: u16) -> RxToken {
		RxToken {
			buffer: [0; UHYVE_NET_MTU],
			len: len,
		}
	}
}

impl phy::RxToken for RxToken {
	fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let (first, _) = self.buffer.split_at_mut(self.len as usize);
		f(first)
	}
}

#[doc(hidden)]
struct TxToken;

impl TxToken {
	pub const fn new() -> Self {
		TxToken {}
	}
}

impl phy::TxToken for TxToken {
	fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
	where
		F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
	{
		let tx_queue = unsafe {
			&mut *((SHAREDQUEUE_START + mem::size_of::<SharedQueue>()) as *mut u8
				as *mut SharedQueue)
		};
		let written = tx_queue.written.load(Ordering::SeqCst);
		let read = tx_queue.read.load(Ordering::SeqCst);
		let distance = written - read;

		if distance < UHYVE_QUEUE_SIZE {
			let idx = written % UHYVE_QUEUE_SIZE;
			let result = f(&mut tx_queue.inner[idx].data[0..len]);

			if result.is_ok() == true {
				unsafe {
					write_volatile(&mut tx_queue.inner[idx].len, len.try_into().unwrap());
				}
				tx_queue.written.fetch_add(1, Ordering::SeqCst);

				if distance == 0 {
					unsafe {
						outl(UHYVE_PORT_NETWRITE, 0);
					}
				}
			}

			result
		} else {
			Err(smoltcp::Error::Dropped)
		}
	}
}
<|endoftext|>


<commit_before>use std::{io, path, ptr, mem, ffi, slice, time, fs};
use std::path::PathBuf;
use std::io::Read;
use std::ops::Sub;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::collections::BTreeMap;
use std::time::Duration;
use libc::{c_void, c_int, c_ulong, c_ushort, c_uint, c_long, c_schar, c_uchar, size_t, uid_t};
use data::*;
use super::common::*;

// utility functions:
fn value_from_file(path: String) -> i32 {
    let mut val = String::new();
    fs::File::open(path).unwrap().read_to_string(&mut val);
    val.trim_right_matches("\n").parse().unwrap()
}

fn capacity(charge_full: i32, charge_now: i32) -> f32 {
    charge_now as f32 / charge_full as f32
}

fn time(charge_full: i32, charge_now: i32, current_now: i32) -> Duration {
    Duration::from_secs((charge_full - charge_now).abs() as u64 * 3600u64 / current_now as u64)
}

pub struct PlatformImpl;

/// An implementation of `Platform` for Linux.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        let mut loads: [f64; 3] = [0.0, 0.0, 0.0];
        if unsafe { getloadavg(&mut loads[0], 3) } != 3 {
            return Err(io::Error::new(io::ErrorKind::Other, "getloadavg() failed"));
        }
        Ok(LoadAverage {
            one: loads[0] as f32,
            five: loads[1] as f32,
            fifteen: loads[2] as f32,
        })
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut info: sysinfo = unsafe { mem::zeroed() };
        unsafe { sysinfo(&mut info) };
        let unit = info.mem_unit as usize;
        let pmem = PlatformMemory {
            total: ByteSize::b(info.totalram as usize * unit),
            free: ByteSize::b(info.freeram as usize * unit),
            shared: ByteSize::b(info.sharedram as usize * unit),
            buffer: ByteSize::b(info.bufferram as usize * unit),
        };
        Ok(Memory {
            total: pmem.total,
            free: pmem.free,
            platform_memory: pmem,
        })
    }

    fn battery_life(&self) -> io::Result<BatteryLife> {
        let dir = "/sys/class/power_supply";
        let entries = fs::read_dir(&dir).unwrap();
        let mut full = 0;
        let mut now = 0;
        let mut current = 0;
        for e in entries {
            let p = e.unwrap().path();
            let s = p.to_str().unwrap();
            let f = p.file_name().unwrap().to_str().unwrap();
            if f.len() > 3 {
                if f.split_at(3).0 == "BAT" {
                    full += value_from_file(s.to_string() + "/charge_full");
                    now += value_from_file(s.to_string() + "/charge_now");
                    current += value_from_file(s.to_string() + "/current_now");
                }
            }
        }
        if full != 0 {
            Ok(BatteryLife {
                remaining_capacity: capacity(full, now),
                remaining_time: time(full, now, current),
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
        }
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        let mut s = String::new();
        match fs::File::open("/sys/class/power_supply/AC/online").unwrap().read_to_string(&mut s)/*.expect("Failed to read file")*/ {
		    Ok(_) => { Ok(s.split_at(1).0 != "0".to_string()) }
		    Err(e) => { Err(io::Error::new(io::ErrorKind::Other, format!("Error: {} in function: on_ac_power()", e))) }
		}
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, path: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }
}

#[repr(C)]
#[derive(Debug)]
struct sysinfo {
    uptime: c_long,
    loads: [c_ulong; 3],
    totalram: c_ulong,
    freeram: c_ulong,
    sharedram: c_ulong,
    bufferram: c_ulong,
    totalswap: c_ulong,
    freeswap: c_ulong,
    procs: c_ushort,
    totalhigh: c_ulong,
    freehigh: c_ulong,
    mem_unit: c_uint,
    padding: [c_schar; 8],
}

#[link(name = "c")]
extern "C" {
    fn getloadavg(loadavg: *mut f64, nelem: c_int) -> c_int;
    fn sysinfo(info: *mut sysinfo);
}
<commit_msg>Improve error handling in io code<commit_after>use std::{io, path, ptr, mem, ffi, slice, time, fs};
use std::path::PathBuf;
use std::io::Read;
use std::ops::Sub;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::collections::BTreeMap;
use std::time::Duration;
use libc::{c_void, c_int, c_ulong, c_ushort, c_uint, c_long, c_schar, c_uchar, size_t, uid_t};
use data::*;
use super::common::*;

fn read_file(path: &str) -> io::Result<String> {
    let mut s = String::new();
    fs::File::open(path)
        .and_then(|mut f| f.read_to_string(&mut s))
        .map(|_| s)
}

fn value_from_file(path: &str) -> io::Result<i32> {
    try!(read_file(path))
        .trim_right_matches("\n")
        .parse()
        .and_then(|n| Ok(n))
        .or_else(|_| {
            Err(io::Error::new(io::ErrorKind::Other,
                               format!("File: \"{}\" doesn't contain an int value", &path)))
        })
}

fn capacity(charge_full: i32, charge_now: i32) -> f32 {
    charge_now as f32 / charge_full as f32
}

fn time(charge_full: i32, charge_now: i32, current_now: i32) -> Duration {
    Duration::from_secs((charge_full - charge_now).abs() as u64 * 3600u64 / current_now as u64)
}

pub struct PlatformImpl;

/// An implementation of `Platform` for Linux.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
    #[inline(always)]
    fn new() -> Self {
        PlatformImpl
    }

    fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn load_average(&self) -> io::Result<LoadAverage> {
        let mut loads: [f64; 3] = [0.0, 0.0, 0.0];
        if unsafe { getloadavg(&mut loads[0], 3) } != 3 {
            return Err(io::Error::new(io::ErrorKind::Other, "getloadavg() failed"));
        }
        Ok(LoadAverage {
            one: loads[0] as f32,
            five: loads[1] as f32,
            fifteen: loads[2] as f32,
        })
    }

    fn memory(&self) -> io::Result<Memory> {
        let mut info: sysinfo = unsafe { mem::zeroed() };
        unsafe { sysinfo(&mut info) };
        let unit = info.mem_unit as usize;
        let pmem = PlatformMemory {
            total: ByteSize::b(info.totalram as usize * unit),
            free: ByteSize::b(info.freeram as usize * unit),
            shared: ByteSize::b(info.sharedram as usize * unit),
            buffer: ByteSize::b(info.bufferram as usize * unit),
        };
        Ok(Memory {
            total: pmem.total,
            free: pmem.free,
            platform_memory: pmem,
        })
    }

    fn battery_life(&self) -> io::Result<BatteryLife> {
        let dir = "/sys/class/power_supply";
        let entries = try!(fs::read_dir(&dir));
        let mut full = 0;
        let mut now = 0;
        let mut current = 0;
        for e in entries {
            let p = e.unwrap().path();
            let s = p.to_str().unwrap();
            let f = p.file_name().unwrap().to_str().unwrap();
            if f.len() > 3 {
                if f.split_at(3).0 == "BAT" {
                    full += try!(value_from_file(&(s.to_string() + "/charge_full")));
                    now += try!(value_from_file(&(s.to_string() + "/charge_now")));
                    current += try!(value_from_file(&(s.to_string() + "/current_now")));
                }
            }
        }
        if full != 0 {
            Ok(BatteryLife {
                remaining_capacity: capacity(full, now),
                remaining_time: time(full, now, current),
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, "Missing battery information"))
        }
    }

    fn on_ac_power(&self) -> io::Result<bool> {
        value_from_file("/sys/class/power_supply/AC/online").map(|v| v == 0)
    }

    fn mounts(&self) -> io::Result<Vec<Filesystem>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn mount_at<P: AsRef<path::Path>>(&self, path: P) -> io::Result<Filesystem> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }

    fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
        Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
    }
}

#[repr(C)]
#[derive(Debug)]
struct sysinfo {
    uptime: c_long,
    loads: [c_ulong; 3],
    totalram: c_ulong,
    freeram: c_ulong,
    sharedram: c_ulong,
    bufferram: c_ulong,
    totalswap: c_ulong,
    freeswap: c_ulong,
    procs: c_ushort,
    totalhigh: c_ulong,
    freehigh: c_ulong,
    mem_unit: c_uint,
    padding: [c_schar; 8],
}

#[link(name = "c")]
extern "C" {
    fn getloadavg(loadavg: *mut f64, nelem: c_int) -> c_int;
    fn sysinfo(info: *mut sysinfo);
}
<|endoftext|>


<commit_before>pub const NRBITS: u32 = 8;
pub const TYPEBITS: u32 = 8;

#[cfg(target_arch = "mips")]
mod consts {
    pub const NONE: u8 = 1;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 4;
    pub const SIZEBITS: u8 = 13;
    pub const DIRBITS: u8 = 3;
}
#[cfg(target_arch = "powerpc")]
mod consts {
    pub const NONE: u8 = 1;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 4;
    pub const SIZEBITS: u8 = 13;
    pub const DIRBITS: u8 = 3;
}
#[cfg(not(any(target_arch = "powerpc", target_arch = "mips")))]
mod consts {
    pub const NONE: u8 = 0;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 1;
    pub const SIZEBITS: u8 = 14;
    pub const DIRBITS: u8 = 2;
}

pub use self::consts::*;

pub const NRSHIFT: u32 = 0;
pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
pub const SIZESHIFT: u32 = TYPESHIFT + SIZEBITS as u32;
pub const DIRSHIFT: u32 = SIZESHIFT + DIRBITS as u32;

pub const NRMASK: u32 = (1 << NRBITS) - 1;
pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1;
pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1;
pub const DIRMASK: u32 = (1 << DIRBITS) - 1;

/// Encode an ioctl command.
#[macro_export]
macro_rules! ioc {
    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
    ($dir as u32) << $crate::DIRBITS |
        ($ty as u32) << $crate::TYPEBITS |
        ($nr as u32) << $crate::NRSHIFT |
        ($sz as u32) << $crate::SIZESHIFT)
}

/// Encode an ioctl command that reads.
#[macro_export]
macro_rules! ior {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::READ, $ty, $nr, $sz))
}

/// Encode an ioctl command that writes.
#[macro_export]
macro_rules! iow {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::WRITE, $ty, $nr, $sz))
}

/// Encode an ioctl command that both reads and writes.
#[macro_export]
macro_rules! iorw {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::READ|$crate::WRITE, $ty, $nr, $sz))
}

/// Declare a wrapper function around an ioctl.
#[macro_export]
macro_rules! ioctl {
    (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)
        }
    );
    (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)
        }
    );
    (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::libc::c_int {
            $crate::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)
        }
    );
    (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty, len: usize) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)
        }
    );
    (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, len: usize) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)
        }
    );
    (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, len: usize) -> $crate::libc::c_int {
            $crate::ioctl(fd, iorw!($ioty, $nr, len) as $crate::libc::c_ulong, val)
        }
    );
}

#[inline(always)]
pub fn ioc_dir(nr: u32) -> u8 {
    ((nr >> DIRSHIFT) & DIRMASK) as u8
}

#[inline(always)]
pub fn ioc_type(nr: u32) -> u32 {
    (nr >> TYPESHIFT) & TYPEMASK
}

#[inline(always)]
pub fn ioc_nr(nr: u32) -> u32 {
    (nr >> NRSHIFT) & NRMASK
}

#[inline(always)]
pub fn ioc_size(nr: u32) -> u32 {
    ((nr >> SIZESHIFT) as u32) & SIZEMASK
}

pub const IN: u32 = (WRITE as u32) << DIRSHIFT;
pub const OUT: u32 = (READ as u32) << DIRSHIFT;
pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT;
pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT;
<commit_msg>[doc] Document the extraction functions<commit_after>pub const NRBITS: u32 = 8;
pub const TYPEBITS: u32 = 8;

#[cfg(target_arch = "mips")]
mod consts {
    pub const NONE: u8 = 1;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 4;
    pub const SIZEBITS: u8 = 13;
    pub const DIRBITS: u8 = 3;
}
#[cfg(target_arch = "powerpc")]
mod consts {
    pub const NONE: u8 = 1;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 4;
    pub const SIZEBITS: u8 = 13;
    pub const DIRBITS: u8 = 3;
}
#[cfg(not(any(target_arch = "powerpc", target_arch = "mips")))]
mod consts {
    pub const NONE: u8 = 0;
    pub const READ: u8 = 2;
    pub const WRITE: u8 = 1;
    pub const SIZEBITS: u8 = 14;
    pub const DIRBITS: u8 = 2;
}

pub use self::consts::*;

pub const NRSHIFT: u32 = 0;
pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
pub const SIZESHIFT: u32 = TYPESHIFT + SIZEBITS as u32;
pub const DIRSHIFT: u32 = SIZESHIFT + DIRBITS as u32;

pub const NRMASK: u32 = (1 << NRBITS) - 1;
pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1;
pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1;
pub const DIRMASK: u32 = (1 << DIRBITS) - 1;

/// Encode an ioctl command.
#[macro_export]
macro_rules! ioc {
    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
    ($dir as u32) << $crate::DIRBITS |
        ($ty as u32) << $crate::TYPEBITS |
        ($nr as u32) << $crate::NRSHIFT |
        ($sz as u32) << $crate::SIZESHIFT)
}

/// Encode an ioctl command that reads.
#[macro_export]
macro_rules! ior {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::READ, $ty, $nr, $sz))
}

/// Encode an ioctl command that writes.
#[macro_export]
macro_rules! iow {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::WRITE, $ty, $nr, $sz))
}

/// Encode an ioctl command that both reads and writes.
#[macro_export]
macro_rules! iorw {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::READ|$crate::WRITE, $ty, $nr, $sz))
}

/// Declare a wrapper function around an ioctl.
#[macro_export]
macro_rules! ioctl {
    (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)
        }
    );
    (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)
        }
    );
    (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty) -> $crate::libc::c_int {
            $crate::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::libc::c_ulong, val)
        }
    );
    (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *mut $ty, len: usize) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)
        }
    );
    (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, len: usize) -> $crate::libc::c_int {
            $crate::ioctl(fd, ior!($ioty, $nr, len) as $crate::libc::c_ulong, val)
        }
    );
    (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::libc::c_int, val: *const $ty, len: usize) -> $crate::libc::c_int {
            $crate::ioctl(fd, iorw!($ioty, $nr, len) as $crate::libc::c_ulong, val)
        }
    );
}

/// Extracts the "direction" (read/write/none) from an encoded ioctl command.
#[inline(always)]
pub fn ioc_dir(nr: u32) -> u8 {
    ((nr >> DIRSHIFT) & DIRMASK) as u8
}

/// Extracts the type from an encoded ioctl command.
#[inline(always)]
pub fn ioc_type(nr: u32) -> u32 {
    (nr >> TYPESHIFT) & TYPEMASK
}

/// Extracts the ioctl number from an encoded ioctl command.
#[inline(always)]
pub fn ioc_nr(nr: u32) -> u32 {
    (nr >> NRSHIFT) & NRMASK
}

/// Extracts the size from an encoded ioctl command.
#[inline(always)]
pub fn ioc_size(nr: u32) -> u32 {
    ((nr >> SIZESHIFT) as u32) & SIZEMASK
}

pub const IN: u32 = (WRITE as u32) << DIRSHIFT;
pub const OUT: u32 = (READ as u32) << DIRSHIFT;
pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT;
pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT;
<|endoftext|>


<commit_before>use syntax::ast::{FunctionRetTy, Method_};
use syntax::ext::quote::rt::ToSource;
use racer::ast::with_error_checking_parse;
use racer::{Match, MatchType};

pub fn snippet_for_match(m : &Match) -> String {
    match m.mtype {
        MatchType::Function => MethodInfo::from_source_str(&m.contextstr).snippet(),
        _ => m.matchstr.clone()
    }
}

struct MethodInfo {
    name : String,
    output : Option<String>,
    args : Vec<String>

}

impl MethodInfo {

    ///Parses method declaration as string and returns relevant data
    fn from_source_str(source : &str) -> MethodInfo {

        let trim: &[_] = &['\n', '\r', '{', ' '];
        let decorated = format!("{} {{}}()", source.trim_right_matches(trim));

        with_error_checking_parse(decorated, |p| {

            let ref method = p.parse_method_with_outer_attributes();

            if let Method_::MethDecl(ident, _, _, _, _,ref decl, _, _) = method.node {
                let mut args = Vec::new();
                let mut out = None;

                if let FunctionRetTy::Return(ref tp) = decl.output {
                    out = Some(tp.to_source());
                }

                for arg in decl.inputs.iter() {
                    args.push(arg.to_source());
                }

                return MethodInfo{ name:ident.to_source(), args:args, output:out};
            }

            panic!("Unable to parse method declaration.");
        })
    }

    ///Returns completion snippets usable by some editors
    fn snippet(&self) -> String {
        let mut args = String::new();
        let mut counter = 1;
        for arg in self.args.iter() {
            if arg.as_slice() == "self" {
                continue;
            }

            if counter > 1 {
                args.push_str(", ");
            }

            args.push_str(format!("${{{}:{}}}", counter, arg).as_slice());
            counter = counter + 1;
        }

        return format!("{}({})", self.name, args);
    }
}

#[test]
fn method_info_test() {
    let info = MethodInfo::from_source_str("pub fn new() -> Vec<T>");
    assert_eq!(info.name, "new".as_slice());
    assert_eq!(info.args.len(), 0);
    assert_eq!(info.output, Some("Vec<T>".to_string()));

    let info = MethodInfo::from_source_str("pub fn reserve(&mut self, additional: uint)");
    assert_eq!(info.name, "reserve".as_slice());
    assert_eq!(info.args.len(), 2);
    assert_eq!(info.args[0].as_slice(), "self");
}<commit_msg>shorter snippet<commit_after>use syntax::ast::{FunctionRetTy, Method_};
use syntax::ext::quote::rt::ToSource;
use racer::ast::with_error_checking_parse;
use racer::{Match, MatchType};

pub fn snippet_for_match(m : &Match) -> String {
    match m.mtype {
        MatchType::Function => MethodInfo::from_source_str(&m.contextstr).snippet(),
        _ => m.matchstr.clone()
    }
}

struct MethodInfo {
    name : String,
    output : Option<String>,
    args : Vec<String>
}

impl MethodInfo {

    ///Parses method declaration as string and returns relevant data
    fn from_source_str(source : &str) -> MethodInfo {

        let trim: &[_] = &['\n', '\r', '{', ' '];
        let decorated = format!("{} {{}}()", source.trim_right_matches(trim));

        with_error_checking_parse(decorated, |p| {

            let ref method = p.parse_method_with_outer_attributes();

            match method.node {
                Method_::MethDecl(ident, _, _, _, _,ref decl, _, _) =>
                    MethodInfo {
                        name: ident.to_source(),
                        args: decl.inputs.iter().map(|a| (*a).to_source()).collect(),
                        output: match decl.output {
                            FunctionRetTy::Return(ref tp) => Some(tp.to_source()),
                            _ => None
                        }
                    },
                _ => panic!("Unable to parse method declaration.")
            }
        })
    }

    ///Returns completion snippets usable by some editors
    fn snippet(&self) -> String {
        format!("{}({})", self.name, &self.args.iter()
                .filter(|&s| &s[] != "self").enumerate()
                .fold(String::new(), |cur, (i, ref s)|
                      cur + &format!(", ${{{}:{}}}", i+1, s)[])[2..])
    }
}

#[test]
fn method_info_test() {
    let info = MethodInfo::from_source_str("pub fn new() -> Vec<T>");
    assert_eq!(info.name, "new".as_slice());
    assert_eq!(info.args.len(), 0);
    assert_eq!(info.output, Some("Vec<T>".to_string()));

    let info = MethodInfo::from_source_str("pub fn reserve(&mut self, additional: uint)");
    assert_eq!(info.name, "reserve".as_slice());
    assert_eq!(info.args.len(), 2);
    assert_eq!(info.args[0].as_slice(), "self");
}
<|endoftext|>