<commit_before>/*!
Defines how css rules, both selectors and style specifications, are
stored.  CSS selector-matching rules, as presented by
http://www.w3.org/TR/CSS2/selector.html are represented by nested types.
*/

use SharedColor = color::Color;
use cmp::Eq;
use std::net::url::Url;
use netsurfcss::stylesheet::CssStylesheet;

// CSS Units

enum CSSValue<T> {
    Specified(T),
    Initial,
    Inherit
}

impl<T: Eq Copy> CSSValue<T> : Eq {
    pure fn eq(other: &CSSValue<T>) -> bool {
        match (self, *other) {
            (Initial, Initial) => true,
            (Inherit, Inherit) => true,
            (Specified(a), Specified(b)) => a == b,
            _ => false
        }
    }
    pure fn ne(other: &CSSValue<T>) -> bool {
        return !self.eq(other);
    }
}

enum Auto = ();

pub enum Length {
    Em(float), // normalized to 'em'
    Px(float) // normalized to 'px'
}

impl Length {
    pure fn rel() -> float {
        match self {
            Em(x) => x,
            _ => fail ~"attempted to access relative unit of an absolute length"
        }
    }
    pure fn abs() -> float {
        match self {
            Em(x) => x,
            _ => fail ~"attempted to access relative unit of an absolute length"
        }
    }
}

pub enum BoxSizing { // used by width, height, top, left, etc
    BoxLength(Length),
    BoxPercent(float),
    BoxAuto
}

enum AbsoluteSize {
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge
}

enum RelativeSize {
    Larger,
    Smaller
}

// CSS property values

enum CSSBackgroundAttachment {
    BgAttachScroll,
    BgAttachFixed
}

enum CSSBackgroundColor {
    BgColor(SharedColor),
    BgColorTransparent
}

enum CSSBackgroundRepeat {
    BgRepeat,
    BgRepeatX,
    BgRepeatY,
    BgNoRepeat
}

enum CSSBackgroundImage {
    BgImage(Url),
    BgImageNone,
}

enum CSSBorderColor {
    BdrColor(SharedColor),
    BdrColorTransparent
}

enum CSSBorderStyle {
    BdrStyleNone,
    BdrStyleHidden,
    BdrStyleDotted,
    BdrStyleDashed,
    BdrStyleSolid,
    BdrStyleDouble,
    BdrStyleGroove,
    BdrStyleRidge,
    BdrStyleInset,
    BdrStyleOutset,
}

enum CSSBorderWidth {
    BdrWidthThin,
    BdrWidthMedium,
    BdrWidthThick,
    BdrWidthLength(Length)
}

impl CSSBorderWidth: Eq {
    pure fn eq(other: &CSSBorderWidth) -> bool {
        match (self, *other) {
            (BdrWidthThin, BdrWidthThin) => true,
            (BdrWidthMedium, BdrWidthMedium) => true,
            (BdrWidthThick, BdrWidthThick) => true,
            (BdrWidthLength(l1), BdrWidthLength(l2)) => l1 == l2,
            (_, _) => false
        }
    }
    pure fn ne(other: &CSSBorderWidth) -> bool { !self.eq(other) }
}

enum CSSColor {
    TextColor(SharedColor)
}

enum CSSDirection {
    DirectionLtr,
    DirectionRtl
}

enum CSSDisplay {
    DisplayInline,
    DisplayBlock,
    DisplayListItem,
    DisplayInlineBlock,
    DisplayTable,
    DisplayInlineTable,
    DisplayTableRowGroup,
    DisplayTableHeaderGroup,
    DisplayTableFooterGroup,
    DisplayTableRow,
    DisplayTableColumnGroup,
    DisplayTableColumn,
    DisplayTableCell,
    DisplayTableCaption,
    DisplayNone
}

enum CSSFloat {
    FloatLeft,
    FloatRight,
    FloatNone
}

enum CSSFontSize {
    AbsoluteSize(AbsoluteSize),
    RelativeSize(RelativeSize),
    LengthSize(Length),
    PercentSize(float)
}

enum CSSPosition {
    PosStatic,
    PosRelative,
    PosAbsolute,
    PosFixed
}

// Stylesheet parts

enum StyleDeclaration {
    BackgroundColor(CSSValue<CSSBackgroundColor>),
    Display(CSSValue<CSSDisplay>),
    FontSize(CSSValue<CSSFontSize>),
    Height(CSSValue<BoxSizing>),
    Color(CSSValue<CSSColor>),
    Width(CSSValue<BoxSizing>),
    BorderColor(CSSValue<CSSBorderColor>),
    BorderWidth(CSSValue<Length>),
    Position(CSSValue<CSSPosition>),
    Top(CSSValue<Length>),
    Right(CSSValue<Length>),
    Bottom(CSSValue<Length>),
    Left(CSSValue<Length>),
}

pub enum Attr {
    Exists(~str),
    Exact(~str, ~str),
    Includes(~str, ~str),
    StartsWith(~str, ~str)
}

pub enum Selector {
    Element(~str, ~[Attr]),
    Child(~Selector, ~Selector),
    Descendant(~Selector, ~Selector),
    Sibling(~Selector, ~Selector)
}

pub type Rule = (~[~Selector], ~[StyleDeclaration]);

pub type Stylesheet = CssStylesheet;

impl Length: cmp::Eq {
    pure fn eq(other: &Length) -> bool {
        match (self, *other) {
          (Em(a), Em(b)) => a == b,
          (Px(a), Px(b)) => a == b,
          (_, _) => false
        }
    }
    pure fn ne(other: &Length) -> bool {
        return !self.eq(other);
    }
}

impl BoxSizing: cmp::Eq {
    pure fn eq(other: &BoxSizing) -> bool {
        match (self, *other) {
          (BoxLength(a), BoxLength(b)) => a == b,
          (BoxPercent(a), BoxPercent(b)) => a == b,
          (BoxAuto, BoxAuto) => true,
          (_, _) => false
        }
    }
    pure fn ne(other: &BoxSizing) -> bool {
        return !self.eq(other);
    }
}

impl AbsoluteSize: cmp::Eq {
    pure fn eq(other: &AbsoluteSize) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &AbsoluteSize) -> bool {
        return !self.eq(other);
    }
}

impl RelativeSize: cmp::Eq {
    pure fn eq(other: &RelativeSize) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &RelativeSize) -> bool {
        return !self.eq(other);
    }
}

impl CSSBackgroundColor: cmp::Eq {
    pure fn eq(other: &CSSBackgroundColor) -> bool {
        match (self, *other) {
            (BgColor(a), BgColor(b)) => a == b,
            (BgColorTransparent, BgColorTransparent) => true,
            (_, _) => false
        }
    }
    pure fn ne(other: &CSSBackgroundColor) -> bool {
        return !self.eq(other);
    }
}

impl CSSColor: cmp::Eq {
    pure fn eq(other: &CSSColor) -> bool {
        match (self, *other) {
            (TextColor(a), TextColor(b)) => a == b
        }
    }
    pure fn ne(other: &CSSColor) -> bool {
        return !self.eq(other);
    }
}

impl CSSDisplay: cmp::Eq {
    pure fn eq(other: &CSSDisplay) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &CSSDisplay) -> bool {
        return !self.eq(other);
    }
}

impl CSSFontSize: cmp::Eq {
    pure fn eq(other: &CSSFontSize) -> bool {
        match (self, *other) {
            (AbsoluteSize(a), AbsoluteSize(b)) => a == b,
            (RelativeSize(a), RelativeSize(b)) => a == b,
            (LengthSize(a),   LengthSize(b))   => a == b,
            (PercentSize(a),  PercentSize(b))  => a == b,
            (_, _) => false
        }
    }
    pure fn ne(other: &CSSFontSize) -> bool {
        return !self.eq(other);
    }
}
/*
impl StyleDeclaration: cmp::Eq {
    pure fn eq(&&other: StyleDeclaration) -> bool {
        match (self, other) {
          (BackgroundColor(a), BackgroundColor(b)) => a == b,
          (Display(a), Display(b)) => a == b,
          (FontSize(a), FontSize(b)) => a == b,
          (Height(a), Height(b)) => a == b,
          (Color(a), Color(b)) => a == b,
          (Width(a), Width(b)) => a == b,

          (BackgroundColor(*), _)
          | (Display(*), _)
          | (FontSize(*), _)
          | (Height(*), _)
          | (Color(*), _)
          | (Width(*), _) => false
        }
    }
}*/

impl Attr: cmp::Eq {
    pure fn eq(other: &Attr) -> bool {
        match (copy self, copy *other) {
          (Exists(a), Exists(b)) => a == b,

          (Exact(a, aa), Exact(b, bb))
          | (Includes(a, aa), Includes(b, bb))
          | (StartsWith(a, aa), StartsWith(b, bb)) => a == b && aa == bb,

          (Exists(*), _)
          | (Exact(*), _)
          | (Includes(*), _)
          | (StartsWith(*), _) => false
        }
    }
    pure fn ne(other: &Attr) -> bool {
        return !self.eq(other);
    }
}

impl Selector: cmp::Eq {
    pure fn eq(other: &Selector) -> bool {
        // FIXME: Lots of copying here
        match (copy self, copy *other) {
          (Element(s_a, attrs_a), Element(s_b, attrs_b)) => s_a == s_b && attrs_a == attrs_b,

          (Child(s1a, s2a), Child(s1b, s2b))
          | (Descendant(s1a, s2a), Descendant(s1b, s2b))
          | (Sibling(s1a, s2a), Sibling(s1b, s2b)) => {
            s1a == s1b && s2a == s2b
          }

          (Element(*), _) => false,
          (Child(*), _) => false,
          (Descendant(*), _) => false,
          (Sibling(*), _) => false
        }
    }
    pure fn ne(other: &Selector) -> bool {
        return !self.eq(other);
    }
}
<commit_msg>Remove Initial variant from CSSValue<commit_after>/*!
Defines how css rules, both selectors and style specifications, are
stored.  CSS selector-matching rules, as presented by
http://www.w3.org/TR/CSS2/selector.html are represented by nested types.
*/

use SharedColor = color::Color;
use cmp::Eq;
use std::net::url::Url;
use netsurfcss::stylesheet::CssStylesheet;

// CSS Units

enum CSSValue<T> {
    Inherit,
    Specified(T),
}

impl<T: Eq Copy> CSSValue<T> : Eq {
    pure fn eq(other: &CSSValue<T>) -> bool {
        match (self, *other) {
            (Inherit, Inherit) => true,
            (Specified(a), Specified(b)) => a == b,
            _ => false
        }
    }
    pure fn ne(other: &CSSValue<T>) -> bool {
        return !self.eq(other);
    }
}

enum Auto = ();

pub enum Length {
    Em(float), // normalized to 'em'
    Px(float) // normalized to 'px'
}

impl Length {
    pure fn rel() -> float {
        match self {
            Em(x) => x,
            _ => fail ~"attempted to access relative unit of an absolute length"
        }
    }
    pure fn abs() -> float {
        match self {
            Em(x) => x,
            _ => fail ~"attempted to access relative unit of an absolute length"
        }
    }
}

pub enum BoxSizing { // used by width, height, top, left, etc
    BoxLength(Length),
    BoxPercent(float),
    BoxAuto
}

enum AbsoluteSize {
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge
}

enum RelativeSize {
    Larger,
    Smaller
}

// CSS property values

enum CSSBackgroundAttachment {
    BgAttachScroll,
    BgAttachFixed
}

enum CSSBackgroundColor {
    BgColor(SharedColor),
    BgColorTransparent
}

enum CSSBackgroundRepeat {
    BgRepeat,
    BgRepeatX,
    BgRepeatY,
    BgNoRepeat
}

enum CSSBackgroundImage {
    BgImage(Url),
    BgImageNone,
}

enum CSSBorderColor {
    BdrColor(SharedColor),
    BdrColorTransparent
}

enum CSSBorderStyle {
    BdrStyleNone,
    BdrStyleHidden,
    BdrStyleDotted,
    BdrStyleDashed,
    BdrStyleSolid,
    BdrStyleDouble,
    BdrStyleGroove,
    BdrStyleRidge,
    BdrStyleInset,
    BdrStyleOutset,
}

enum CSSBorderWidth {
    BdrWidthThin,
    BdrWidthMedium,
    BdrWidthThick,
    BdrWidthLength(Length)
}

impl CSSBorderWidth: Eq {
    pure fn eq(other: &CSSBorderWidth) -> bool {
        match (self, *other) {
            (BdrWidthThin, BdrWidthThin) => true,
            (BdrWidthMedium, BdrWidthMedium) => true,
            (BdrWidthThick, BdrWidthThick) => true,
            (BdrWidthLength(l1), BdrWidthLength(l2)) => l1 == l2,
            (_, _) => false
        }
    }
    pure fn ne(other: &CSSBorderWidth) -> bool { !self.eq(other) }
}

enum CSSColor {
    TextColor(SharedColor)
}

enum CSSDirection {
    DirectionLtr,
    DirectionRtl
}

enum CSSDisplay {
    DisplayInline,
    DisplayBlock,
    DisplayListItem,
    DisplayInlineBlock,
    DisplayTable,
    DisplayInlineTable,
    DisplayTableRowGroup,
    DisplayTableHeaderGroup,
    DisplayTableFooterGroup,
    DisplayTableRow,
    DisplayTableColumnGroup,
    DisplayTableColumn,
    DisplayTableCell,
    DisplayTableCaption,
    DisplayNone
}

enum CSSFloat {
    FloatLeft,
    FloatRight,
    FloatNone
}

enum CSSFontSize {
    AbsoluteSize(AbsoluteSize),
    RelativeSize(RelativeSize),
    LengthSize(Length),
    PercentSize(float)
}

enum CSSPosition {
    PosStatic,
    PosRelative,
    PosAbsolute,
    PosFixed
}

// Stylesheet parts

enum StyleDeclaration {
    BackgroundColor(CSSValue<CSSBackgroundColor>),
    Display(CSSValue<CSSDisplay>),
    FontSize(CSSValue<CSSFontSize>),
    Height(CSSValue<BoxSizing>),
    Color(CSSValue<CSSColor>),
    Width(CSSValue<BoxSizing>),
    BorderColor(CSSValue<CSSBorderColor>),
    BorderWidth(CSSValue<Length>),
    Position(CSSValue<CSSPosition>),
    Top(CSSValue<Length>),
    Right(CSSValue<Length>),
    Bottom(CSSValue<Length>),
    Left(CSSValue<Length>),
}

pub enum Attr {
    Exists(~str),
    Exact(~str, ~str),
    Includes(~str, ~str),
    StartsWith(~str, ~str)
}

pub enum Selector {
    Element(~str, ~[Attr]),
    Child(~Selector, ~Selector),
    Descendant(~Selector, ~Selector),
    Sibling(~Selector, ~Selector)
}

pub type Rule = (~[~Selector], ~[StyleDeclaration]);

pub type Stylesheet = CssStylesheet;

impl Length: cmp::Eq {
    pure fn eq(other: &Length) -> bool {
        match (self, *other) {
          (Em(a), Em(b)) => a == b,
          (Px(a), Px(b)) => a == b,
          (_, _) => false
        }
    }
    pure fn ne(other: &Length) -> bool {
        return !self.eq(other);
    }
}

impl BoxSizing: cmp::Eq {
    pure fn eq(other: &BoxSizing) -> bool {
        match (self, *other) {
          (BoxLength(a), BoxLength(b)) => a == b,
          (BoxPercent(a), BoxPercent(b)) => a == b,
          (BoxAuto, BoxAuto) => true,
          (_, _) => false
        }
    }
    pure fn ne(other: &BoxSizing) -> bool {
        return !self.eq(other);
    }
}

impl AbsoluteSize: cmp::Eq {
    pure fn eq(other: &AbsoluteSize) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &AbsoluteSize) -> bool {
        return !self.eq(other);
    }
}

impl RelativeSize: cmp::Eq {
    pure fn eq(other: &RelativeSize) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &RelativeSize) -> bool {
        return !self.eq(other);
    }
}

impl CSSBackgroundColor: cmp::Eq {
    pure fn eq(other: &CSSBackgroundColor) -> bool {
        match (self, *other) {
            (BgColor(a), BgColor(b)) => a == b,
            (BgColorTransparent, BgColorTransparent) => true,
            (_, _) => false
        }
    }
    pure fn ne(other: &CSSBackgroundColor) -> bool {
        return !self.eq(other);
    }
}

impl CSSColor: cmp::Eq {
    pure fn eq(other: &CSSColor) -> bool {
        match (self, *other) {
            (TextColor(a), TextColor(b)) => a == b
        }
    }
    pure fn ne(other: &CSSColor) -> bool {
        return !self.eq(other);
    }
}

impl CSSDisplay: cmp::Eq {
    pure fn eq(other: &CSSDisplay) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &CSSDisplay) -> bool {
        return !self.eq(other);
    }
}

impl CSSFontSize: cmp::Eq {
    pure fn eq(other: &CSSFontSize) -> bool {
        match (self, *other) {
            (AbsoluteSize(a), AbsoluteSize(b)) => a == b,
            (RelativeSize(a), RelativeSize(b)) => a == b,
            (LengthSize(a),   LengthSize(b))   => a == b,
            (PercentSize(a),  PercentSize(b))  => a == b,
            (_, _) => false
        }
    }
    pure fn ne(other: &CSSFontSize) -> bool {
        return !self.eq(other);
    }
}
/*
impl StyleDeclaration: cmp::Eq {
    pure fn eq(&&other: StyleDeclaration) -> bool {
        match (self, other) {
          (BackgroundColor(a), BackgroundColor(b)) => a == b,
          (Display(a), Display(b)) => a == b,
          (FontSize(a), FontSize(b)) => a == b,
          (Height(a), Height(b)) => a == b,
          (Color(a), Color(b)) => a == b,
          (Width(a), Width(b)) => a == b,

          (BackgroundColor(*), _)
          | (Display(*), _)
          | (FontSize(*), _)
          | (Height(*), _)
          | (Color(*), _)
          | (Width(*), _) => false
        }
    }
}*/

impl Attr: cmp::Eq {
    pure fn eq(other: &Attr) -> bool {
        match (copy self, copy *other) {
          (Exists(a), Exists(b)) => a == b,

          (Exact(a, aa), Exact(b, bb))
          | (Includes(a, aa), Includes(b, bb))
          | (StartsWith(a, aa), StartsWith(b, bb)) => a == b && aa == bb,

          (Exists(*), _)
          | (Exact(*), _)
          | (Includes(*), _)
          | (StartsWith(*), _) => false
        }
    }
    pure fn ne(other: &Attr) -> bool {
        return !self.eq(other);
    }
}

impl Selector: cmp::Eq {
    pure fn eq(other: &Selector) -> bool {
        // FIXME: Lots of copying here
        match (copy self, copy *other) {
          (Element(s_a, attrs_a), Element(s_b, attrs_b)) => s_a == s_b && attrs_a == attrs_b,

          (Child(s1a, s2a), Child(s1b, s2b))
          | (Descendant(s1a, s2a), Descendant(s1b, s2b))
          | (Sibling(s1a, s2a), Sibling(s1b, s2b)) => {
            s1a == s1b && s2a == s2b
          }

          (Element(*), _) => false,
          (Child(*), _) => false,
          (Descendant(*), _) => false,
          (Sibling(*), _) => false
        }
    }
    pure fn ne(other: &Selector) -> bool {
        return !self.eq(other);
    }
}
<|endoftext|>


<commit_before><commit_msg>Fix loop_fn doc example<commit_after><|endoftext|>


<commit_before><commit_msg>Add pad and scale options to the font code, which will be useful for adding additional fonts<commit_after><|endoftext|>


<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("ExtractorError") {
            Err(DownloadError::ExtractorError)
        } 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())
                                    .stderr(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)
    }
}<commit_msg>added download_file wrapper to fix #11<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
    fn download_file_in(&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("ExtractorError") {
            Err(DownloadError::ExtractorError)
        } else {
            Err(DownloadError::InternalError(stderr))
        }

    }

    ///Wrapper for download_file_fn to retry on Extract Error, appearing randomly
    pub fn download_file(&self, file_path: &str, download_audio: bool) -> Result<bool,DownloadError> {
        for attempts in 0..2 {
            match self.download_file_in(file_path, download_audio) {
                Ok(v) => return Ok(v),
                Err(e) => {
                    match e {
                        DownloadError::ExtractorError =>  { println!("download try no {}",attempts)},
                        _ => return Err(e),
                    }
                },
            }
        }
        Err(DownloadError::ExtractorError)
    }

    ///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())
                                    .stderr(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>extern crate cemu_smm;

use cemu_smm::thumbnail2::*;
use std::fs::{read, read_dir, File};
use std::io::{self, prelude::*};
use std::path::PathBuf;
use std::process::Command;

fn decrypt_test_assets() -> io::Result<()> {
    for entry in read_dir("tests/assets/saves/smm2")? {
        let entry = entry?;
        let file_name = entry.file_name();
        let file_name = file_name.to_str().unwrap();
        if file_name.starts_with("course_") && file_name.ends_with(".btl") {
            let path = entry.path();
            let out_path: Vec<&str> = path.to_str().unwrap().split('.').collect();
            let out_path = out_path[0].to_owned() + ".decrypted";
            let mut command = Command::new("./decryptor_linux");
            command.arg(entry.path()).arg(out_path);
            command.output().unwrap();
        }
    }
    Ok(())
}

#[test]
fn thumbnail_decrypt() {
    decrypt_test_assets().unwrap();
}

#[test]
fn thumbnail_get_jpeg() {
    for (_, thumbnail, expected) in get_test_assets().into_iter() {
        let mut thumbnail = Thumbnail2::new(thumbnail);

        assert_eq!(thumbnail.move_jpeg(), expected);
    }
}

#[test]
fn thumbnail_optimize_jpeg() {
    for (path, thumbnail, decrypted) in get_test_assets().into_iter() {
        let mut thumbnail = Thumbnail2::new(thumbnail);

        thumbnail.optimize_jpeg().unwrap();

        let out_path: Vec<&str> = path.to_str().unwrap().split('.').collect();
        let out_path = out_path[0].to_owned() + ".opt";
        let mut file = File::create(out_path).unwrap();
        file.write_all(thumbnail.get_jpeg()).unwrap();

        assert!(thumbnail.get_jpeg().len() <= decrypted.len());
    }
}

fn get_test_assets() -> Vec<(PathBuf, Vec<u8>, Vec<u8>)> {
    let mut thumbnails = vec![];
    for entry in read_dir("tests/assets/saves/smm2").unwrap() {
        let entry = entry.unwrap();
        let file_name = entry.file_name();
        let file_name = file_name.to_str().unwrap();
        if file_name.starts_with("course_thumb_") && file_name.ends_with(".btl") {
            let path = entry.path();
            let out_path: Vec<&str> = path.to_str().unwrap().split('.').collect();
            let out_path = out_path[0].to_owned() + ".decrypted";
            thumbnails.push((path.clone(), read(path).unwrap(), read(out_path).unwrap()));
        }
    }
    thumbnails
}
<commit_msg>potentially fix random failing test<commit_after>extern crate cemu_smm;

use cemu_smm::thumbnail2::*;
use std::fs::{read, read_dir, File};
use std::io::{self, prelude::*};
use std::path::PathBuf;
use std::process::Command;

fn decrypt_test_assets() -> io::Result<()> {
    for entry in read_dir("tests/assets/saves/smm2")? {
        let entry = entry?;
        let file_name = entry.file_name();
        let file_name = file_name.to_str().unwrap();
        if file_name.starts_with("course_") && file_name.ends_with(".btl") {
            let path = entry.path();
            let out_path: Vec<&str> = path.to_str().unwrap().split('.').collect();
            let out_path = out_path[0].to_owned() + ".decrypted";
            let mut command = Command::new("./decryptor_linux");
            command.arg(entry.path()).arg(out_path);
            command.output().unwrap();
        }
    }
    Ok(())
}

#[test]
fn thumbnail_decrypt() {
    decrypt_test_assets().unwrap();
}

#[test]
fn thumbnail_get_jpeg() {
    for (_, thumbnail, expected) in get_test_assets().into_iter() {
        let mut thumbnail = Thumbnail2::new(thumbnail);

        assert_eq!(thumbnail.get_jpeg(), &expected[..]);
    }
}

#[test]
fn thumbnail_optimize_jpeg() {
    for (path, thumbnail, decrypted) in get_test_assets().into_iter() {
        let mut thumbnail = Thumbnail2::new(thumbnail);

        thumbnail.optimize_jpeg().unwrap();

        let out_path: Vec<&str> = path.to_str().unwrap().split('.').collect();
        let out_path = out_path[0].to_owned() + ".opt";
        let mut file = File::create(out_path).unwrap();
        file.write_all(thumbnail.get_jpeg()).unwrap();

        assert!(thumbnail.get_jpeg().len() <= decrypted.len());
    }
}

fn get_test_assets() -> Vec<(PathBuf, Vec<u8>, Vec<u8>)> {
    let mut thumbnails = vec![];
    for entry in read_dir("tests/assets/saves/smm2").unwrap() {
        let entry = entry.unwrap();
        let file_name = entry.file_name();
        let file_name = file_name.to_str().unwrap();
        if file_name.starts_with("course_thumb_") && file_name.ends_with(".btl") {
            let path = entry.path();
            let out_path: Vec<&str> = path.to_str().unwrap().split('.').collect();
            let out_path = out_path[0].to_owned() + ".decrypted";
            thumbnails.push((path.clone(), read(path).unwrap(), read(out_path).unwrap()));
        }
    }
    thumbnails
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::fmt;
use std::error::Error;

use self::err::ConfigError;
/* includes the generated code from PEG
	- ${PROJECT_DIR}/targetbuild/ruwm-*out/
*/
// TODO
// This code need some major cleanup

use config_parser::*;

#[allow(dead_code)]
pub mod config_grammar {
    include!(concat!(env!("OUT_DIR"), "/config_grammar.rs"));
}

#[derive(Debug)]
pub struct Parser {
    vars: Variables,
    bind_sym: Vec<Config>,
    exec: Vec<Config>,
    floating_mod: Vec<Config>,
}

/*
 * Set up the Parser Struct
 * move ownership of the parsed file from
 * the rust-peg grammar (rust-peg) to my own structures
 * Set Keybindings
 * Handle Reloads
 *
 */
impl Parser {
    #[allow(dead_code)]
    pub fn new(config_file: &str) -> Result<Parser, ConfigError> {
        let mut f = File::open(config_file)?;

        let mut config = String::new();
        f.read_to_string(&mut config)?;
        let config = config_grammar::content(config.as_ref())?;

        // create data structs for each configuration type
        let mut vars = Variables::new();
        let mut bind_sym = Vec::new();
        let mut exec = Vec::new();
        let mut floating_mod = Vec::new();

        for x in config.iter() {
            match *x {
                Config::Set(ref v, ref s) => vars.set(v.to_owned(), s.to_owned())?,
                Config::Exec(ref a) => exec.push(Config::Exec(a.to_owned())),
                Config::BindSym(ref s, ref a) => {
                    bind_sym.push(Config::BindSym(s.to_owned(), a.to_owned()))
                }
                Config::FloatingMod(ref s) => floating_mod.push(Config::FloatingMod(s.to_owned())),
                _ => {}
            };
        }

        Ok(Parser {
            vars,
            bind_sym,
            exec,
            floating_mod,
        })
    }
}

impl fmt::Display for Parser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "\n
    {:?}, \n
	{:?}, \n
    {:?}, \n
    {:?}  \n",
            self.vars,
            self.bind_sym,
            self.exec,
            self.floating_mod
        )
    }
}

#[cfg(test)]
#[test]
fn test_grammar() {
    // banner for --nocapture
    // TODO
    // the fact that the first line must be at the very beginning
    // is not a very big deal, since we can just strip all whitespace until
    // we reach an ascii character
    // but if there is a easy way to fix this in the grammar, then that is the
    // better option
    match config_grammar::content(
        "# A comment
		bindsym LeftGui+1 workspace 1

bindsym $super+$rand exec 'termite --config $HOME/.config/termite/config'
   bindsym RightGui+2 workspace 2 # this is another comment

set $super Ctrl+Mod4
  # This is a comment
set $ws0 '0:emp'
bindsym RightGui+3 workspace $ws0
bindsym Hello+3 workspace 5

bindsym $super+$left focus left
bindsym $super+Ctrl focus down
bindsym $super+$up focus up
bindsym $super+$right focus right
bindsym $super+1 workspace $ws0

exec --no-startup-id 'nm-applet'

set $left h

",
    ) {
        Ok(r) => println!("Parsed as: {:?}", r),
        Err(e) => println!("Parse error: {}", e),
    };

}

#[cfg(test)]
#[test]
fn test_parser() {
    // banner for --nocapture
    let parser = match Parser::new("src/config_parser/config.test") {
        Ok(s) => s,
        Err(e) => panic!("{}", e),
    };

    match parser.variables.get(&"$longchain".to_string()) {
        Ok(s) => {
            println!("Long Chain: {:?}", s);
            assert_eq!(s, vec!["l"]);
        }
        Err(e) => panic!("{}", e),
    };

    match parser.variables.get(&"$long_ordered_chain".to_string()) {
        Ok(s) => {
            println!("Long Ordered Chain: {:?}", s);
            assert_eq!(s, vec!["a", "b", "l", "Mod3", "f", "h", "z"]);
        }
        Err(e) => panic!("{}", e),
    };
    println!("Parser: {}", parser.to_string());
}
<commit_msg>renamed variables to vars<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::fmt;
use std::error::Error;

use self::err::ConfigError;
/* includes the generated code from PEG
	- ${PROJECT_DIR}/targetbuild/ruwm-*out/
*/
// TODO
// This code need some major cleanup

use config_parser::*;

#[allow(dead_code)]
pub mod config_grammar {
    include!(concat!(env!("OUT_DIR"), "/config_grammar.rs"));
}

#[derive(Debug)]
pub struct Parser {
    vars: Variables,
    bind_sym: Vec<Config>,
    exec: Vec<Config>,
    floating_mod: Vec<Config>,
}

/*
 * Set up the Parser Struct
 * move ownership of the parsed file from
 * the rust-peg grammar (rust-peg) to my own structures
 * Set Keybindings
 * Handle Reloads
 *
 */
impl Parser {
    #[allow(dead_code)]
    pub fn new(config_file: &str) -> Result<Parser, ConfigError> {
        let mut f = File::open(config_file)?;

        let mut config = String::new();
        f.read_to_string(&mut config)?;
        let config = config_grammar::content(config.as_ref())?;

        // create data structs for each configuration type
        let mut vars = Variables::new();
        let mut bind_sym = Vec::new();
        let mut exec = Vec::new();
        let mut floating_mod = Vec::new();

        for x in config.iter() {
            match *x {
                Config::Set(ref v, ref s) => vars.set(v.to_owned(), s.to_owned())?,
                Config::Exec(ref a) => exec.push(Config::Exec(a.to_owned())),
                Config::BindSym(ref s, ref a) => {
                    bind_sym.push(Config::BindSym(s.to_owned(), a.to_owned()))
                }
                Config::FloatingMod(ref s) => floating_mod.push(Config::FloatingMod(s.to_owned())),
                _ => {}
            };
        }

        Ok(Parser {
            vars,
            bind_sym,
            exec,
            floating_mod,
        })
    }
}

impl fmt::Display for Parser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "\n
    {:?}, \n
	{:?}, \n
    {:?}, \n
    {:?}  \n",
            self.vars,
            self.bind_sym,
            self.exec,
            self.floating_mod
        )
    }
}

#[cfg(test)]
#[test]
fn test_grammar() {
    // banner for --nocapture
    // TODO
    // the fact that the first line must be at the very beginning
    // is not a very big deal, since we can just strip all whitespace until
    // we reach an ascii character
    // but if there is a easy way to fix this in the grammar, then that is the
    // better option
    match config_grammar::content(
        "# A comment
		bindsym LeftGui+1 workspace 1

bindsym $super+$rand exec 'termite --config $HOME/.config/termite/config'
   bindsym RightGui+2 workspace 2 # this is another comment

set $super Ctrl+Mod4
  # This is a comment
set $ws0 '0:emp'
bindsym RightGui+3 workspace $ws0
bindsym Hello+3 workspace 5

bindsym $super+$left focus left
bindsym $super+Ctrl focus down
bindsym $super+$up focus up
bindsym $super+$right focus right
bindsym $super+1 workspace $ws0

exec --no-startup-id 'nm-applet'

set $left h

",
    ) {
        Ok(r) => println!("Parsed as: {:?}", r),
        Err(e) => println!("Parse error: {}", e),
    };

}

#[cfg(test)]
#[test]
fn test_parser() {
    // banner for --nocapture
    let parser = match Parser::new("src/config_parser/config.test") {
        Ok(s) => s,
        Err(e) => panic!("{}", e),
    };

    match parser.vars.get(&"$longchain".to_string()) {
        Ok(s) => {
            println!("Long Chain: {:?}", s);
            assert_eq!(s, vec!["l"]);
        }
        Err(e) => panic!("{}", e),
    };

    match parser.vars.get(&"$long_ordered_chain".to_string()) {
        Ok(s) => {
            println!("Long Ordered Chain: {:?}", s);
            assert_eq!(s, vec!["a", "b", "l", "Mod3", "f", "h", "z"]);
        }
        Err(e) => panic!("{}", e),
    };
    println!("Parser: {}", parser.to_string());
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Helpers for serializing and deserializing Cerberus protocol messages.

use std::any::type_name;
use std::io::Read;
use std::path::PathBuf;

use serde::de::Deserialize;

use manticore::io::write::StdWrite;
use manticore::mem::BumpArena;
use manticore::protocol;
use manticore::protocol::wire::FromWire;
use manticore::protocol::wire::ToWire;
use manticore::protocol::Command;
use manticore::protocol::CommandType;

macro_rules! proto_match {
    (($cmd:expr, $is_req:expr) in |$mty:ident: type| $expr:expr) => {
        proto_match!(($cmd, $is_req, $mty, $expr) in {
            protocol::DeviceId,
            protocol::DeviceInfo,
            protocol::DeviceUptime,
            protocol::DeviceCapabilities,
            protocol::FirmwareVersion,
            protocol::GetDigests,
            protocol::GetCert,
            protocol::GetHostState,
            protocol::Challenge,
            protocol::KeyExchange,
            protocol::ResetCounter,
            protocol::RequestCounter,
        })
    };
    (($cmd:expr, $is_req:expr, $mty:ident, $expr:expr) in {$($t:ty,)*}) => {
        match ($cmd, $is_req) {
            $(
                (<<$t as Command<'static>>::Req as protocol::Request<'static>>::TYPE, true) => {
                    type $mty = <$t as Command<'static>>::Req;
                    $expr
                }
                (<<$t as Command<'static>>::Resp as protocol::Response<'static>>::TYPE, false) => {
                    type $mty = <$t as Command<'static>>::Resp;
                    $expr
                }
            )*
            _ => todo!(),
        }
    };
}

/// Construct a Cerberus message from a JSON representation.
#[derive(structopt::StructOpt)]
pub enum Message {
    /// Converts a Cerberus message from wire form to JSON form.
    #[structopt(name = "wire2json", alias = "w2j")]
    Wire2Json {
        /// The command type for the message.
        #[structopt(short = "c", long)]
        command: CommandType,

        /// Parse a request for this command.
        #[structopt(
            long,
            conflicts_with = "respose",
            required_unless = "response"
        )]
        request: bool,

        /// Parse a response for this command.
        #[structopt(
            long,
            conflicts_with = "request",
            required_unless = "request"
        )]
        response: bool,

        /// Whether to pretty-print JSON output.
        #[structopt(long)]
        pretty: bool,

        /// Input file; defaults to stdin.
        #[structopt(short = "i", long, parse(from_os_str))]
        input: Option<PathBuf>,

        /// Output file; defaults to stdout.
        #[structopt(short = "o", long, parse(from_os_str))]
        output: Option<PathBuf>,
    },

    /// Converts a Cerberus message from JSON form to wire form.
    #[structopt(name = "json2wire", alias = "j2w")]
    Json2Wire {
        /// The command type for the message.
        #[structopt(short = "c", long)]
        command: CommandType,

        /// Parse a request for this command.
        #[structopt(
            long,
            conflicts_with = "respose",
            required_unless = "response"
        )]
        request: bool,

        /// Parse a response for this command.
        #[structopt(
            long,
            conflicts_with = "request",
            required_unless = "request"
        )]
        response: bool,

        /// Whether to pretty-print JSON output.
        #[structopt(long)]
        pretty: bool,

        /// Input file; defaults to stdin.
        #[structopt(short = "i", long, parse(from_os_str))]
        input: Option<PathBuf>,

        /// Output file; defaults to stdout.
        #[structopt(short = "o", long, parse(from_os_str))]
        output: Option<PathBuf>,
    },
}

impl Message {
    pub fn run(self) {
        match self {
            Self::Json2Wire {
                command,
                request,
                input,
                output,
                ..
            } => {
                let (r, w) = crate::util::stdio(input, output);
                let mut w = StdWrite(w);

                proto_match!((command, request) in |Msg: type| {
                    let mut de = serde_json::Deserializer::from_reader(r);
                    let msg = check!(
                        Msg::deserialize(&mut de),
                        "failed to deserialize {} from JSON",
                        type_name::<Msg>(),
                    );
                    check!(
                        msg.to_wire(&mut w),
                        "failed to serialize {}",
                        type_name::<Msg>(),
                    );
                })
            }
            Self::Wire2Json {
                command,
                request,
                pretty,
                input,
                output,
                ..
            } => {
                let (mut r, w) = crate::util::stdio(input, output);

                let mut read_buf = Vec::new();
                check!(
                    r.read_to_end(&mut read_buf),
                    "failed to read input to end",
                );
                let mut r = read_buf.as_slice();

                let arena = BumpArena::new(vec![0u8; 1024]);
                proto_match!((command, request) in |Msg: type| {
                    let msg = check!(
                        Msg::from_wire(&mut r, &arena),
                        "failed to deserialize {}",
                        type_name::<Msg>(),
                    );
                    let r = match pretty {
                        true => serde_json::to_writer_pretty(w, &msg),
                        false => serde_json::to_writer(w, &msg),
                    };
                    check!(r, "failed to serialize {} as JSON", type_name::<Msg>());
                })
            }
        }
    }
}
<commit_msg>[tool] Update the tool to use the new trait<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Helpers for serializing and deserializing Cerberus protocol messages.

use std::any::type_name;
use std::io::Read;
use std::path::PathBuf;

use serde::de::Deserialize;

use manticore::io::write::StdWrite;
use manticore::mem::BumpArena;
use manticore::protocol;
use manticore::protocol::borrowed::AsStatic;
use manticore::protocol::borrowed::Borrowed;
use manticore::protocol::wire::FromWire;
use manticore::protocol::wire::ToWire;
use manticore::protocol::Command;
use manticore::protocol::CommandType;

macro_rules! proto_match {
    (($cmd:expr, $is_req:expr) in |$mty:ident: type| $expr:expr) => {
        proto_match!(($cmd, $is_req, $mty, $expr) in {
            protocol::DeviceId,
            protocol::DeviceInfo,
            protocol::DeviceUptime,
            protocol::DeviceCapabilities,
            protocol::FirmwareVersion,
            protocol::GetDigests,
            protocol::GetCert,
            protocol::GetHostState,
            protocol::Challenge,
            protocol::KeyExchange,
            protocol::ResetCounter,
            protocol::RequestCounter,
        })
    };
    (($cmd:expr, $is_req:expr, $mty:ident, $expr:expr) in {$($t:ty,)*}) => {
        match ($cmd, $is_req) {
            $(
                (<<$t as Command<'static>>::Req as protocol::Request<'static>>::TYPE, true) => {
                    type $mty = <$t as Command<'static>>::Req;
                    $expr
                }
                (<<$t as Command<'static>>::Resp as protocol::Response<'static>>::TYPE, false) => {
                    type $mty = <$t as Command<'static>>::Resp;
                    $expr
                }
            )*
            _ => todo!(),
        }
    };
}

/// Construct a Cerberus message from a JSON representation.
#[derive(structopt::StructOpt)]
pub enum Message {
    /// Converts a Cerberus message from wire form to JSON form.
    #[structopt(name = "wire2json", alias = "w2j")]
    Wire2Json {
        /// The command type for the message.
        #[structopt(short = "c", long)]
        command: CommandType,

        /// Parse a request for this command.
        #[structopt(
            long,
            conflicts_with = "respose",
            required_unless = "response"
        )]
        request: bool,

        /// Parse a response for this command.
        #[structopt(
            long,
            conflicts_with = "request",
            required_unless = "request"
        )]
        response: bool,

        /// Whether to pretty-print JSON output.
        #[structopt(long)]
        pretty: bool,

        /// Input file; defaults to stdin.
        #[structopt(short = "i", long, parse(from_os_str))]
        input: Option<PathBuf>,

        /// Output file; defaults to stdout.
        #[structopt(short = "o", long, parse(from_os_str))]
        output: Option<PathBuf>,
    },

    /// Converts a Cerberus message from JSON form to wire form.
    #[structopt(name = "json2wire", alias = "j2w")]
    Json2Wire {
        /// The command type for the message.
        #[structopt(short = "c", long)]
        command: CommandType,

        /// Parse a request for this command.
        #[structopt(
            long,
            conflicts_with = "respose",
            required_unless = "response"
        )]
        request: bool,

        /// Parse a response for this command.
        #[structopt(
            long,
            conflicts_with = "request",
            required_unless = "request"
        )]
        response: bool,

        /// Whether to pretty-print JSON output.
        #[structopt(long)]
        pretty: bool,

        /// Input file; defaults to stdin.
        #[structopt(short = "i", long, parse(from_os_str))]
        input: Option<PathBuf>,

        /// Output file; defaults to stdout.
        #[structopt(short = "o", long, parse(from_os_str))]
        output: Option<PathBuf>,
    },
}

impl Message {
    pub fn run(self) {
        match self {
            Self::Json2Wire {
                command,
                request,
                input,
                output,
                ..
            } => {
                let (r, w) = crate::util::stdio(input, output);
                let mut w = StdWrite(w);

                proto_match!((command, request) in |Msg: type| {
                    let mut de = serde_json::Deserializer::from_reader(r);
                    let msg = check!(
                        AsStatic::<'static, Msg>::deserialize(&mut de),
                        "failed to deserialize {} from JSON",
                        type_name::<Msg>(),
                    );
                    check!(
                        Msg::borrow(&msg).to_wire(&mut w),
                        "failed to serialize {}",
                        type_name::<Msg>(),
                    );
                })
            }
            Self::Wire2Json {
                command,
                request,
                pretty,
                input,
                output,
                ..
            } => {
                let (mut r, w) = crate::util::stdio(input, output);

                let mut read_buf = Vec::new();
                check!(
                    r.read_to_end(&mut read_buf),
                    "failed to read input to end",
                );
                let mut r = read_buf.as_slice();

                let arena = BumpArena::new(vec![0u8; 1024]);
                proto_match!((command, request) in |Msg: type| {
                    let msg = check!(
                        Msg::from_wire(&mut r, &arena),
                        "failed to deserialize {}",
                        type_name::<Msg>(),
                    );
                    let r = match pretty {
                        true => serde_json::to_writer_pretty(w, &msg),
                        false => serde_json::to_writer(w, &msg),
                    };
                    check!(r, "failed to serialize {} as JSON", type_name::<Msg>());
                })
            }
        }
    }
}
<|endoftext|>


<commit_before>//!
//! This module is an attempt to provide a friendly, rust-esque interface to
//! Apple's Audio Unit API.
//!
//! Learn more about the Audio Unit API here:
//! https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003278-CH1-SW2
//!

use bindings::audio_unit as au;
use error::{Error, AudioUnitError};
use libc;
use self::stream_format::StreamFormat;
use std::mem;
use std::ptr;

pub mod audio_format;
pub mod stream_format;

/// Represents the input and output scope.
#[derive(Copy, Clone, Debug)]
pub enum Scope {
    Output = 0,
    Input  = 1,
}

/// Represents the input and output elements .
#[derive(Copy, Clone, Debug)]
pub enum Element {
    Output = 0,
    Input  = 1,
}

/// Represents the different types of Audio Units.
#[derive(Copy, Clone, Debug)]
pub enum Type {
    Output          = 1635086197,
    MusicDevice     = 1635085685,
    MusicEffect     = 1635085670,
    FormatConverter = 1635083875,
    Effect          = 1635083896,
    Mixer           = 1635085688,
    Panner          = 1635086446,
    Generator       = 1635084142,
    OfflineEffect   = 1635086188,
    MidiProcessor   = 1635085673,
}

/// Represents the different audio unit sub types.
#[derive(Copy, Clone, Debug)]
pub enum SubType {
    GenericOutput        = 1734700658,
    HalOutput            = 1634230636,
    DefaultOutput        = 1684366880,
    SystemOutput         = 1937339168,
    VoiceProcessingIO    = 1987078511,
    DLSSynth             = 1684828960,
    Sampler              = 1935764848,
    MIDISynth            = 1836284270,
    AUConverter          = 1668247158,
    Varispeed            = 1986097769,
    DeferredRenderer     = 1684366962,
    Splitter             = 1936747636,
    Merger               = 1835364967,
    NewTimePitch         = 1853191280,
    AUiPodTimeOther      = 1768977519,
    RoundTripAAC         = 1918984547,
    PeakLimiter          = 1819112562,
    DynamicsProcessor    = 1684237680,
    LowPassFilter        = 1819304307,
    HighPassFilter       = 1752195443,
    BandPassFilter       = 1651532147,
    HighShelfFilter      = 1752393830,
    LowShelfFilter       = 1819502694,
    ParametricEQ         = 1886217585,
    Distortion           = 1684632436,
    Delay                = 1684368505,
    SampleDelay          = 1935961209,
    GraphicEQ            = 1735550321,
    MultiBandCompressor  = 1835232624,
    MatrixReverb         = 1836213622,
    Pitch                = 1953329268,
    AUFilter             = 1718185076,
    NetSend              = 1853058660,
    RogerBeep            = 1919903602,
    NBandEQ              = 1851942257,
    MultiChannelMixer    = 1835232632,
    MatrixMixer          = 1836608888,
    SpatialMixer         = 862217581,
    StereoMixer          = 1936554098,
    Mixer3D              = 862219640,
    SphericalHeadPanner  = 1936746610,
    VectorPanner         = 1986158963,
    SoundFieldPanner     = 1634558569,
    HRTFPanner           = 1752331366,
    NetReceive           = 1852990326,
    ScheduledSoundPlayer = 1936945260,
    AudioFilePlayer      = 1634103404,
}

pub type NumFrames = usize;

/// A type representing a render callback (aka "Input Procedure")
/// If set on an AudioUnit, this will be called every time the AudioUnit requests audio.
/// The first arg is [frames[channels]]; the second is the number of frames to render.
pub type RenderCallback = FnMut(&mut[&mut[f32]], NumFrames) -> Result<(), String>;

/// A rust representation of the au::AudioUnit, including a pointer to the current rendering callback.
pub struct AudioUnit {
    instance: au::AudioUnit,
    callback: Option<*mut libc::c_void>
}

macro_rules! try_os_status {
    ($expr:expr) => (try!(Error::from_os_status($expr)))
}

impl AudioUnit {

    /// Construct a new AudioUnit.
    pub fn new(au_type: Type, sub_type: SubType) -> Result<AudioUnit, Error> {

        // A description of the audio unit we desire.
        let desc = au::AudioComponentDescription {
            componentType         : au_type as libc::c_uint,
            componentSubType      : sub_type as libc::c_uint,
            componentManufacturer : au::kAudioUnitManufacturer_Apple,
            componentFlags        : 0,
            componentFlagsMask    : 0,
        };

        unsafe {
            // Find the default audio unit for the description.
            let component = match au::AudioComponentFindNext(ptr::null_mut(), &desc as *const _) {
                component if component.is_null() => return Err(Error::NoMatchingDefaultAudioUnitFound),
                component                        => component,
            };

            // Get an instance of the default audio unit using the component.
            let mut instance: au::AudioUnit = mem::uninitialized();

            try_os_status!(au::AudioComponentInstanceNew(component, &mut instance as *mut au::AudioUnit));
            // Initialise the audio unit!
            try_os_status!(au::AudioUnitInitialize(instance));
            Ok(AudioUnit {
                instance: instance,
                callback: None
            })
        }
    }

    fn free_render_callback(&self) {
        if let Some(callback) = self.callback {
            // Here, we transfer ownership of the callback back to the current scope so that it
            // is dropped and cleaned up. Without this line, we would leak the Boxed callback.
            let _: Box<Box<RenderCallback>> = unsafe { mem::transmute(callback) };
        }
    }

    /// Pass a render callback (aka "Input Procedure") to the audio unit.
    pub fn render_callback(&mut self, callback: Option<Box<RenderCallback>>) -> Result<(), Error>
    {
        // TODO: Double-check naming etc in here; could probably be better.
        unsafe {
            // Setup render callback. Notice that we relinquish ownership of the Callback
            // here so that it can be used as the C render callback via a void pointer.
            // We do however store the *mut so that we can transmute back to a
            // Box<RenderCallback> within our AudioUnit's Drop implementation (otherwise it
            // would leak).
            let callback_ptr: *mut libc::c_void = match callback {
                Some(x) => mem::transmute(Box::new(x)),
                _ => ptr::null_mut()
            };
            let render_callback = au::AURenderCallbackStruct {
                inputProc: Some(input_proc),
                inputProcRefCon: callback_ptr
            };

            try_os_status!(au::AudioUnitSetProperty(
                self.instance,
                au::kAudioUnitProperty_SetRenderCallback,
                Scope::Input as libc::c_uint,
                Element::Output as libc::c_uint,
                &render_callback as *const _ as *const libc::c_void,
                mem::size_of::<au::AURenderCallbackStruct>() as u32));

            self.free_render_callback();
            self.callback = if !callback_ptr.is_null() { Some(callback_ptr) } else { None };
            Ok(())
        }
    }

    /// Start the audio unit.
    // TOOD: Test and see what happens when this is called and the audio unit is already started.
    pub fn start(&self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStart(self.instance)); }
        Ok(())
    }

    /// Stop the audio unit.
    // TOOD: Test and see what happens when this is called and the audio unit is already stopped.
    pub fn stop(&self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStop(self.instance)); }
        Ok(())
    }

    /// Return the current Stream Format for the AudioUnit.
    pub fn stream_format(&self) -> Result<StreamFormat, Error> {
        unsafe {
            let mut asbd: au::AudioStreamBasicDescription = mem::uninitialized();
            let mut size = ::std::mem::size_of::<au::AudioStreamBasicDescription>() as u32;
            try_os_status!(au::AudioUnitGetProperty(
                self.instance,
                au::kAudioUnitProperty_StreamFormat,
                Scope::Output as libc::c_uint,
                Element::Output as libc::c_uint,
                &mut asbd as *mut _ as *mut libc::c_void,
                &mut size as *mut au::UInt32));
            Ok(StreamFormat::from_asbd(asbd))
        }
    }

}

impl Drop for AudioUnit {
    fn drop(&mut self) {
        unsafe {
            use error;
            use std::error::Error;
            if let Err(err) = self.stop() {
                panic!("{:?}", err.description());
            }
            if let Err(err) = error::Error::from_os_status(au::AudioUnitUninitialize(self.instance)) {
                panic!("{:?}", err.description());
            }
            self.free_render_callback();
        }
    }
}

/// Callback procedure that will be called each time our audio_unit requests audio.
extern "C" fn input_proc(in_ref_con: *mut libc::c_void,
                         _io_action_flags: *mut au::AudioUnitRenderActionFlags,
                         _in_time_stamp: *const au::AudioTimeStamp,
                         _in_bus_number: au::UInt32,
                         in_number_frames: au::UInt32,
                         io_data: *mut au::AudioBufferList) -> au::OSStatus {
    let callback: *mut Box<RenderCallback> = in_ref_con as *mut _;
    unsafe {
        let num_channels = (*io_data).mNumberBuffers as usize;

        // FIXME: We shouldn't need a Vec for this, it should probably be something like
        // `&[&mut [f32]]` instead.
        let mut channels: Vec<&mut [f32]> =
            (0..num_channels)
                .map(|i| {
                    let slice_ptr = (*io_data).mBuffers[i].mData as *mut libc::c_float;
                    ::std::slice::from_raw_parts_mut(slice_ptr, in_number_frames as usize)
                })
                .collect();

        match (*callback)(&mut channels[..], in_number_frames as usize) {
            Ok(()) => 0 as au::OSStatus,
            Err(description) => {
                use std::io::Write;
                writeln!(::std::io::stderr(), "{:?}", description).unwrap();
                AudioUnitError::NoConnection as au::OSStatus
            },
        }
    }
}
<commit_msg>Small cleanups, renaming, and comment fixes<commit_after>//!
//! This module is an attempt to provide a friendly, rust-esque interface to
//! Apple's Audio Unit API.
//!
//! Learn more about the Audio Unit API here:
//! https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003278-CH1-SW2
//!

use bindings::audio_unit as au;
use error::{Error, AudioUnitError};
use libc;
use self::stream_format::StreamFormat;
use std::mem;
use std::ptr;

pub mod audio_format;
pub mod stream_format;

/// Represents the input and output scope.
#[derive(Copy, Clone, Debug)]
pub enum Scope {
    Output = 0,
    Input  = 1,
}

/// Represents the input and output elements .
#[derive(Copy, Clone, Debug)]
pub enum Element {
    Output = 0,
    Input  = 1,
}

/// Represents the different types of Audio Units.
#[derive(Copy, Clone, Debug)]
pub enum Type {
    Output          = 1635086197,
    MusicDevice     = 1635085685,
    MusicEffect     = 1635085670,
    FormatConverter = 1635083875,
    Effect          = 1635083896,
    Mixer           = 1635085688,
    Panner          = 1635086446,
    Generator       = 1635084142,
    OfflineEffect   = 1635086188,
    MidiProcessor   = 1635085673,
}

/// Represents the different audio unit sub types.
#[derive(Copy, Clone, Debug)]
pub enum SubType {
    GenericOutput        = 1734700658,
    HalOutput            = 1634230636,
    DefaultOutput        = 1684366880,
    SystemOutput         = 1937339168,
    VoiceProcessingIO    = 1987078511,
    DLSSynth             = 1684828960,
    Sampler              = 1935764848,
    MIDISynth            = 1836284270,
    AUConverter          = 1668247158,
    Varispeed            = 1986097769,
    DeferredRenderer     = 1684366962,
    Splitter             = 1936747636,
    Merger               = 1835364967,
    NewTimePitch         = 1853191280,
    AUiPodTimeOther      = 1768977519,
    RoundTripAAC         = 1918984547,
    PeakLimiter          = 1819112562,
    DynamicsProcessor    = 1684237680,
    LowPassFilter        = 1819304307,
    HighPassFilter       = 1752195443,
    BandPassFilter       = 1651532147,
    HighShelfFilter      = 1752393830,
    LowShelfFilter       = 1819502694,
    ParametricEQ         = 1886217585,
    Distortion           = 1684632436,
    Delay                = 1684368505,
    SampleDelay          = 1935961209,
    GraphicEQ            = 1735550321,
    MultiBandCompressor  = 1835232624,
    MatrixReverb         = 1836213622,
    Pitch                = 1953329268,
    AUFilter             = 1718185076,
    NetSend              = 1853058660,
    RogerBeep            = 1919903602,
    NBandEQ              = 1851942257,
    MultiChannelMixer    = 1835232632,
    MatrixMixer          = 1836608888,
    SpatialMixer         = 862217581,
    StereoMixer          = 1936554098,
    Mixer3D              = 862219640,
    SphericalHeadPanner  = 1936746610,
    VectorPanner         = 1986158963,
    SoundFieldPanner     = 1634558569,
    HRTFPanner           = 1752331366,
    NetReceive           = 1852990326,
    ScheduledSoundPlayer = 1936945260,
    AudioFilePlayer      = 1634103404,
}

pub type NumFrames = usize;

/// A type representing a render callback (aka "Input Procedure")
/// If set on an AudioUnit, this will be called every time the AudioUnit requests audio.
/// The first arg is [frames[channels]]; the second is the number of frames to render.
pub type RenderCallback = FnMut(&mut[&mut[f32]], NumFrames) -> Result<(), String>;

/// A rust representation of the au::AudioUnit, including a pointer to the current rendering callback.
pub struct AudioUnit {
    instance: au::AudioUnit,
    callback: Option<*mut libc::c_void>
}

macro_rules! try_os_status {
    ($expr:expr) => (try!(Error::from_os_status($expr)))
}

impl AudioUnit {

    /// Construct a new AudioUnit.
    pub fn new(au_type: Type, sub_type: SubType) -> Result<AudioUnit, Error> {

        // A description of the audio unit we desire.
        let desc = au::AudioComponentDescription {
            componentType         : au_type as libc::c_uint,
            componentSubType      : sub_type as libc::c_uint,
            componentManufacturer : au::kAudioUnitManufacturer_Apple,
            componentFlags        : 0,
            componentFlagsMask    : 0,
        };

        unsafe {
            // Find the default audio unit for the description.
            let component = match au::AudioComponentFindNext(ptr::null_mut(), &desc as *const _) {
                component if component.is_null() => return Err(Error::NoMatchingDefaultAudioUnitFound),
                component                        => component,
            };

            // Get an instance of the default audio unit using the component.
            let mut instance: au::AudioUnit = mem::uninitialized();

            try_os_status!(au::AudioComponentInstanceNew(component, &mut instance as *mut au::AudioUnit));
            // Initialise the audio unit!
            try_os_status!(au::AudioUnitInitialize(instance));
            Ok(AudioUnit {
                instance: instance,
                callback: None
            })
        }
    }

    fn free_render_callback(&self) {
        if let Some(callback) = self.callback {
            // Here, we transfer ownership of the callback back to the current scope so that it
            // is dropped and cleaned up. Without this line, we would leak the Boxed callback.
            let _: Box<Box<RenderCallback>> = unsafe { mem::transmute(callback) };
        }
    }

    /// Pass a render callback (aka "Input Procedure") to the audio unit.
    pub fn render_callback(&mut self, f: Option<Box<RenderCallback>>) -> Result<(), Error>
    {
        unsafe {
            // Setup render callback. Notice that we relinquish ownership of the Callback
            // here so that it can be used as the C render callback via a void pointer.
            // We do however store the *mut so that we can transmute back to a
            // Box<Box<RenderCallback>> within our AudioUnit's Drop implementation
            // (otherwise it would leak). The double-boxing is due to incompleteness with
            // Rust's FnMut implemetation and is necessary to be able to transmute to the
            // correct pointer size.
            let callback_ptr: *mut libc::c_void = match f {
                Some(x) => mem::transmute(Box::new(x)),
                _ => ptr::null_mut()
            };
            let render_callback = au::AURenderCallbackStruct {
                inputProc: Some(input_proc),
                inputProcRefCon: callback_ptr
            };

            try_os_status!(au::AudioUnitSetProperty(
                self.instance,
                au::kAudioUnitProperty_SetRenderCallback,
                Scope::Input as libc::c_uint,
                Element::Output as libc::c_uint,
                &render_callback as *const _ as *const libc::c_void,
                mem::size_of::<au::AURenderCallbackStruct>() as u32));

            self.free_render_callback();
            self.callback = if !callback_ptr.is_null() { Some(callback_ptr) } else { None };
            Ok(())
        }
    }

    /// Start the audio unit.
    pub fn start(&self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStart(self.instance)); }
        Ok(())
    }

    /// Stop the audio unit.
    pub fn stop(&self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStop(self.instance)); }
        Ok(())
    }

    /// Return the current Stream Format for the AudioUnit.
    pub fn stream_format(&self) -> Result<StreamFormat, Error> {
        unsafe {
            let mut asbd: au::AudioStreamBasicDescription = mem::uninitialized();
            let mut size = ::std::mem::size_of::<au::AudioStreamBasicDescription>() as u32;
            try_os_status!(au::AudioUnitGetProperty(
                self.instance,
                au::kAudioUnitProperty_StreamFormat,
                Scope::Output as libc::c_uint,
                Element::Output as libc::c_uint,
                &mut asbd as *mut _ as *mut libc::c_void,
                &mut size as *mut au::UInt32));
            Ok(StreamFormat::from_asbd(asbd))
        }
    }

}

impl Drop for AudioUnit {
    fn drop(&mut self) {
        unsafe {
            use error;
            use std::error::Error;
            if let Err(err) = self.stop() {
                panic!("{:?}", err.description());
            }
            if let Err(err) = error::Error::from_os_status(au::AudioUnitUninitialize(self.instance)) {
                panic!("{:?}", err.description());
            }
            self.free_render_callback();
        }
    }
}

/// Callback procedure that will be called each time our audio_unit requests audio.
extern "C" fn input_proc(in_ref_con: *mut libc::c_void,
                         _io_action_flags: *mut au::AudioUnitRenderActionFlags,
                         _in_time_stamp: *const au::AudioTimeStamp,
                         _in_bus_number: au::UInt32,
                         in_number_frames: au::UInt32,
                         io_data: *mut au::AudioBufferList) -> au::OSStatus {
    let callback: *mut Box<RenderCallback> = in_ref_con as *mut _;
    unsafe {
        let num_channels = (*io_data).mNumberBuffers as usize;

        // FIXME: We shouldn't need a Vec for this, it should probably be something like
        // `&[&mut [f32]]` instead.
        let mut channels: Vec<&mut [f32]> =
            (0..num_channels)
                .map(|i| {
                    let slice_ptr = (*io_data).mBuffers[i].mData as *mut libc::c_float;
                    ::std::slice::from_raw_parts_mut(slice_ptr, in_number_frames as usize)
                })
                .collect();

        match (*callback)(&mut channels[..], in_number_frames as usize) {
            Ok(()) => 0 as au::OSStatus,
            Err(description) => {
                use std::io::Write;
                writeln!(::std::io::stderr(), "{:?}", description).unwrap();
                AudioUnitError::NoConnection as au::OSStatus
            },
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::iter;
use std::path::Path;

/*
#[doc(hidden)]
#[macro_export]
macro_rules! count {
    () => { proc_macro_call_0!() };
    (!) => { proc_macro_call_1!() };
    (!!) => { proc_macro_call_2!() };
    ...
}
*/

fn main() {
    // Tell Cargo not to rerun on src/lib.rs changes.
    println!("cargo:rerun-if-changed=build.rs");

    let mut content = String::new();
    content += "
        #[doc(hidden)]
        #[macro_export]
        macro_rules! count {
    ";
    for i in 0..=64 {
        let bangs = iter::repeat("!").take(i).collect::<String>();
        content += &format!("({}) => {{ proc_macro_call_{}!() }};", bangs, i);
    }
    content += "
            ($(!)+) => {
                compile_error!(\"this macro does not support >64 nested macro invocations\")
            };
        }
    ";

    let content = content.as_bytes();
    let out_dir = env::var("OUT_DIR").unwrap();
    let ref dest_path = Path::new(&out_dir).join("count.rs");

    // Avoid bumping filetime if content is up to date. Possibly related to
    // https://github.com/dtolnay/proc-macro-hack/issues/56 ...?
    if fs::read(dest_path)
        .map(|existing| existing != content)
        .unwrap_or(true)
    {
        fs::write(dest_path, content).unwrap();
    }
}
<commit_msg>Generate formatted count.rs for easier examination<commit_after>use std::env;
use std::fs;
use std::iter;
use std::path::Path;

/*
#[doc(hidden)]
#[macro_export]
macro_rules! count {
    () => { proc_macro_call_0!() };
    (!) => { proc_macro_call_1!() };
    (!!) => { proc_macro_call_2!() };
    ...
}
*/

fn main() {
    // Tell Cargo not to rerun on src/lib.rs changes.
    println!("cargo:rerun-if-changed=build.rs");

    let mut content = String::new();
    content += "#[doc(hidden)]\n";
    content += "#[macro_export]\n";
    content += "macro_rules! count {\n";
    for i in 0..=64 {
        let bangs = iter::repeat("!").take(i).collect::<String>();
        content += &format!("    ({}) => {{ proc_macro_call_{}!() }};\n", bangs, i);
    }
    content += "    ($(!)+) => {\n";
    content += "        compile_error! { \"this macro does not support >64 nested macro invocations\" }\n";
    content += "    };\n";
    content += "}\n";

    let content = content.as_bytes();
    let out_dir = env::var("OUT_DIR").unwrap();
    let ref dest_path = Path::new(&out_dir).join("count.rs");

    // Avoid bumping filetime if content is up to date. Possibly related to
    // https://github.com/dtolnay/proc-macro-hack/issues/56 ...?
    if fs::read(dest_path)
        .map(|existing| existing != content)
        .unwrap_or(true)
    {
        fs::write(dest_path, content).unwrap();
    }
}
<|endoftext|>