<commit_before>extern crate specs;

use specs::Join;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::fs::File;

static MAP: &'static str = include_str!("../map");

#[derive(Copy, Clone, Debug, PartialEq)]
struct Tile {
    c: char,
}

impl specs::Component for Tile {
    type Storage = specs::VecStorage<Tile>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct PlayerController;

#[derive(Copy, Clone, Debug, PartialEq)]
struct NonPlayerController;

#[derive(Copy, Clone, Debug, PartialEq)]
struct RenderController;

#[derive(Copy, Clone, Debug, PartialEq)]
struct ImmovableController;

impl specs::Component for PlayerController {
    type Storage = specs::VecStorage<PlayerController >;
}

impl specs::Component for NonPlayerController {
    type Storage = specs::VecStorage<NonPlayerController >;
}

impl specs::Component for RenderController {
    type Storage = specs::VecStorage<RenderController >;
}

impl specs::Component for ImmovableController {
    type Storage = specs::VecStorage<ImmovableController>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct Position {
    x: i32,
    y: i32,
}

impl specs::Component for Position {
    type Storage = specs::VecStorage<Position>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct Velocity {
    dx: i32,
    dy: i32,
}

impl specs::Component for Velocity {
    type Storage = specs::VecStorage<Velocity>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct Size {
    w: i32,
    h: i32,
}

impl specs::Component for Size {
    type Storage = specs::VecStorage<Size>;
}

#[derive(Clone, Debug, PartialEq)]
struct WorldMap(Vec<char>);

#[derive(Clone, Debug)]
struct Command(String);

//#[cfg(not(feature="parallel"))]
//fn main() {
//}
//
//#[cfg(feature="parallel")]
fn main() {
    let map_width = 10;
    let height = 10;
    let mut lines: Vec<&str> = MAP.lines().collect();
    let map_settings = lines.remove(0);
    for word in map_settings.split_whitespace() {
    }
    let map: Vec<char> = lines.iter().flat_map(|line| line.chars()).collect();

    let mut planner = {
        let mut w = specs::World::new();
        w.register::<Size>();
        w.register::<Tile>();
        w.register::<Position>();
        w.register::<Velocity>();
        w.register::<PlayerController>();
        w.register::<NonPlayerController>();
        w.register::<RenderController>();
        w.register::<ImmovableController>();

        // Entities
        let _player = w.create_now()
            .with(Tile { c: 'X' })
            .with(Velocity { dx: 0, dy: 0 })
            .with(Position { x: 0, y: 0 })
            .with(PlayerController)
            .with(Size { w: 1, h: 1 })
            .build();

        let _view = w.create_now()
            .with(Position { x: 0, y: 0 })
            .with(Size { w: 10, h: 10 })
            .with(RenderController)
            .build();

        for (i, tile) in map.iter().enumerate() {
            let _map_tile = w.create_now()
                .with(ImmovableController)
                .with(Size { w: 1, h: 1 })
                .with(Position { x: i as i32 % map_width, y: i as i32 / map_width, })
                .with(Tile { c: tile.clone() })
                .build();
        }

        // resources can be installed, these are nothing fancy, but allow you
        // to pass data to systems and follow the same sync strategy as the
        // component storage does.
        w.add_resource(Command(String::new()));

        // Planner is used to run systems on the specified world with a specified number of threads
        (specs::Planner::<()>::new(w, 4))
    };
    loop {
        planner.run_custom(|arg| {
            let (mut positions, velocities) = arg.fetch(|w| {
                (w.write::<Position>(), w.read::<Velocity>())
            });

            for (mut p, v) in (&mut positions, &velocities).iter() {
                p.x += v.dx;
                p.y += v.dy;
                println!("{:?}", p);
            }
        });

        planner.wait();

        // render view
        planner.run_custom(|arg| {
            let (tiles, positions, players, map_objects) = arg.fetch(|w| {
                (w.read::<Tile>(), w.read::<Position>(), w.read::<PlayerController>(),
                w.read::<ImmovableController>())
            });
            let mut output = vec![vec![' ';10];10];

            for (tile, position, _) in (&tiles, &positions, &map_objects).iter() {
                //println!("{:?} {:?}", tile, position);
                let _ = output[position.y as usize].remove(position.x as usize);
                output[position.y as usize].insert(position.x as usize, tile.c);
            }
            for (tile, position, _) in (&tiles, &positions, &players).iter() {
                //println!("{:?} {:?}", tile, position);
                let _ = output[position.y as usize].remove(position.x as usize);
                output[position.y as usize].insert(position.x as usize, tile.c);
            }
            for out in output {
                let out: String = out.into_iter().collect();
                println!("{}", out);
            }
        });

        planner.wait();

        // set view
        planner.run_custom(|arg| {
            let (mut positions, players, views, map_objects, sizes, entities) = arg.fetch(|w| {
                (w.write::<Position>(), w.read::<PlayerController>(), w.read::<RenderController>(),
                w.read::<ImmovableController>(), w.read::<Size>(),
                w.entities())
            });

            let mut pos = (None, None);
            for (_, position) in (&players, &positions).iter() {
                pos.0 = Some(position.x);
                pos.1 = Some(position.y);
                break;
            }

            if let (Some(x), Some(y)) = pos {
                for (_, size, entity) in (&views, &sizes, &entities).iter() {
                    let view_pos = positions.get_mut(entity)
                        .expect("render controller expect position component");
                    let w = size.w / 2;
                    let h = size.h / 2;
                    if x > w { view_pos.x = x - (size.w / 2); }
                    if y > h { view_pos.y = y - (size.h / 2); }
                }
            }
        });

        // get input
        planner.run_custom(|arg| {
            let mut cmd = arg.fetch(|w| {
                (w.write_resource::<Command>())
            });
            let mut input = String::new();
            if let Ok(_) = io::stdin().read_line(&mut input) {
                cmd.0 = input;
            }
        });

        planner.wait();

        // process input
        planner.run_custom(|arg| {
            let (mut velocities, players, cmds, entities) = arg.fetch(|w| {
                (w.write::<Velocity>(), w.read::<PlayerController>(),
                 w.read_resource::<Command>(), w.entities())
            });
            let mut commands: Vec<&str> = cmds.0.split_whitespace()
                .rev()
                .collect();

            for (_, entity) in (&players, &entities).iter() {
                let velocity = velocities.get_mut(entity)
                    .expect("player controller expect velocity");
                let mut dx = 0;
                let mut dy = 0;
                while let Some(word) = commands.pop() {
                    match word {
                        "left" | "l" => {
                            if let Some(val) = commands.pop() {
                                dx -= val.parse::<i32>().unwrap_or(1);
                            } else {
                                dx -= 1;
                            }
                        }
                        "right" | "r" => {
                            if let Some(val) = commands.pop() {
                                dx += val.parse::<i32>().unwrap_or(1);
                            } else {
                                dx += 1;
                            }
                        }
                        "up" | "u" => {
                            if let Some(val) = commands.pop() {
                                dy -= val.parse::<i32>().unwrap_or(1);
                            } else {
                                dy -= 1;
                            }
                        }
                        "down" | "d" => {
                            if let Some(val) = commands.pop() {
                                dy += val.parse::<i32>().unwrap_or(1);
                            } else {
                                dy += 1;
                            }
                        }
                        _ => {}
                    }
                }
                velocity.dx += dx;
                velocity.dy += dy;
                println!("{:?}", velocity);
            }
        });

        planner.wait();
    }
}
<commit_msg>Reorder systems execution.<commit_after>extern crate specs;

use specs::Join;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::fs::File;

static MAP: &'static str = include_str!("../map");

#[derive(Copy, Clone, Debug, PartialEq)]
struct Tile {
    c: char,
}

impl specs::Component for Tile {
    type Storage = specs::VecStorage<Tile>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct PlayerController;

#[derive(Copy, Clone, Debug, PartialEq)]
struct NonPlayerController;

#[derive(Copy, Clone, Debug, PartialEq)]
struct RenderController;

#[derive(Copy, Clone, Debug, PartialEq)]
struct ImmovableController;

impl specs::Component for PlayerController {
    type Storage = specs::VecStorage<PlayerController>;
}

impl specs::Component for NonPlayerController {
    type Storage = specs::VecStorage<NonPlayerController>;
}

impl specs::Component for RenderController {
    type Storage = specs::VecStorage<RenderController>;
}

impl specs::Component for ImmovableController {
    type Storage = specs::VecStorage<ImmovableController>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct Position {
    x: i32,
    y: i32,
}

impl specs::Component for Position {
    type Storage = specs::VecStorage<Position>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct Velocity {
    dx: i32,
    dy: i32,
}

impl specs::Component for Velocity {
    type Storage = specs::VecStorage<Velocity>;
}

#[derive(Copy, Clone, Debug, PartialEq)]
struct Size {
    w: i32,
    h: i32,
}

impl specs::Component for Size {
    type Storage = specs::VecStorage<Size>;
}

#[derive(Clone, Debug, PartialEq)]
struct WorldMap(Vec<char>);

#[derive(Clone, Debug)]
struct Command(String);

//#[cfg(not(feature="parallel"))]
//fn main() {
//}
//
//#[cfg(feature="parallel")]
fn main() {
    let map_width = 10;
    let height = 10;
    let mut lines: Vec<&str> = MAP.lines().collect();
    let map_settings = lines.remove(0);
    for word in map_settings.split_whitespace() {
    }
    let map: Vec<char> = lines.iter().flat_map(|line| line.chars()).collect();

    let mut planner = {
        let mut w = specs::World::new();
        w.register::<Size>();
        w.register::<Tile>();
        w.register::<Position>();
        w.register::<Velocity>();
        w.register::<PlayerController>();
        w.register::<NonPlayerController>();
        w.register::<RenderController>();
        w.register::<ImmovableController>();

        // Entities
        let _player = w.create_now()
            .with(Tile { c: 'X' })
            .with(Velocity { dx: 0, dy: 0 })
            .with(Position { x: 0, y: 0 })
            .with(PlayerController)
            .with(Size { w: 1, h: 1 })
            .build();

        let _view = w.create_now()
            .with(Position { x: 0, y: 0 })
            .with(Size { w: 10, h: 10 })
            .with(RenderController)
            .build();

        for (i, tile) in map.iter().enumerate() {
            let _map_tile = w.create_now()
                .with(ImmovableController)
                .with(Size { w: 1, h: 1 })
                .with(Position { x: i as i32 % map_width, y: i as i32 / map_width, })
                .with(Tile { c: tile.clone() })
                .build();
        }

        // resources can be installed, these are nothing fancy, but allow you
        // to pass data to systems and follow the same sync strategy as the
        // component storage does.
        w.add_resource(Command(String::new()));

        // Planner is used to run systems on the specified world with a specified number of threads
        (specs::Planner::<()>::new(w, 4))
    };
    loop {
        // update positions
        planner.run_custom(|arg| {
            let (mut positions, velocities) = arg.fetch(|w| {
                (w.write::<Position>(), w.read::<Velocity>())
            });

            for (mut p, v) in (&mut positions, &velocities).iter() {
                p.x += v.dx;
                p.y += v.dy;
                println!("{:?}", p);
            }
        });

        planner.wait();

        // update view area
        planner.run_custom(|arg| {
            let (mut positions, players, views, sizes, entities) = arg.fetch(|w| {
                (w.write::<Position>(), w.read::<PlayerController>(), w.read::<RenderController>(),
                 w.read::<Size>(), w.entities())
            });

            let mut pos = (None, None);
            for (_, position) in (&players, &positions).iter() {
                pos.0 = Some(position.x);
                pos.1 = Some(position.y);
                break;
            }

            if let (Some(x), Some(y)) = pos {
                for (_, size, entity) in (&views, &sizes, &entities).iter() {
                    let view_pos = positions.get_mut(entity)
                        .expect("render controller expect position component");
                    let w = size.w / 2;
                    let h = size.h / 2;
                    if x > w { view_pos.x = x - (size.w / 2); }
                    if y > h { view_pos.y = y - (size.h / 2); }
                }
            }
        });

        planner.wait();

        // render view
        planner.run_custom(|arg| {
            let (tiles, positions, non_players, players, map_objects) = arg.fetch(|w| {
                (w.read::<Tile>(), w.read::<Position>(), w.read::<NonPlayerController>(),
                w.read::<PlayerController>(), w.read::<ImmovableController>())
            });
            let mut output = vec![vec![' ';10];10];

            // render in layers
            for (tile, position, _) in (&tiles, &positions, &map_objects).iter() {
                let _ = output[position.y as usize].remove(position.x as usize);
                output[position.y as usize].insert(position.x as usize, tile.c);
            }
            for (tile, position, _) in (&tiles, &positions, &non_players).iter() {
                let _ = output[position.y as usize].remove(position.x as usize);
                output[position.y as usize].insert(position.x as usize, tile.c);
            }
            for (tile, position, _) in (&tiles, &positions, &players).iter() {
                let _ = output[position.y as usize].remove(position.x as usize);
                output[position.y as usize].insert(position.x as usize, tile.c);
            }
            for out in output {
                let out: String = out.into_iter().collect();
                println!("{}", out);
            }
        });

        // get input
        planner.run_custom(|arg| {
            let mut cmd = arg.fetch(|w| {
                (w.write_resource::<Command>())
            });
            let mut input = String::new();
            if let Ok(_) = io::stdin().read_line(&mut input) {
                cmd.0 = input;
            }
        });

        planner.wait();

        // process input
        planner.run_custom(|arg| {
            let (mut velocities, players, cmds, entities) = arg.fetch(|w| {
                (w.write::<Velocity>(), w.read::<PlayerController>(),
                 w.read_resource::<Command>(), w.entities())
            });
            let mut commands: Vec<&str> = cmds.0.split_whitespace()
                .rev()
                .collect();

            for (_, entity) in (&players, &entities).iter() {
                let velocity = velocities.get_mut(entity)
                    .expect("player controller expect velocity");
                let mut dx = 0;
                let mut dy = 0;
                while let Some(word) = commands.pop() {
                    match word {
                        "left" | "l" => {
                            if let Some(val) = commands.pop() {
                                dx -= val.parse::<i32>().unwrap_or(1);
                            } else {
                                dx -= 1;
                            }
                        }
                        "right" | "r" => {
                            if let Some(val) = commands.pop() {
                                dx += val.parse::<i32>().unwrap_or(1);
                            } else {
                                dx += 1;
                            }
                        }
                        "up" | "u" => {
                            if let Some(val) = commands.pop() {
                                dy -= val.parse::<i32>().unwrap_or(1);
                            } else {
                                dy -= 1;
                            }
                        }
                        "down" | "d" => {
                            if let Some(val) = commands.pop() {
                                dy += val.parse::<i32>().unwrap_or(1);
                            } else {
                                dy += 1;
                            }
                        }
                        _ => {}
                    }
                }
                velocity.dx += dx;
                velocity.dy += dy;
                println!("{:?}", velocity);
            }
        });

        planner.wait();
    }
}
<|endoftext|>


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

mod playlist;
mod default_ui;

use argparse::{ArgumentParser, Store, StoreTrue, List};
use default_ui::*;
use regex::Regex;
use gst::ElementT;
use std::fs;
use std::io;

fn recurse_songs(songs: &mut Vec<String>, recurse: bool, is_first: bool) ->
                    Result<(), io::Error>{
    let mut new : Vec<String> = vec![];

    for song in songs.iter() {
        if try!(fs::metadata(&song)).is_dir() {
            if recurse || is_first {
                let mut contents: Vec<String> = vec![];
                for entry in try!(fs::read_dir(song)) {
                    let memes = String::from(entry.unwrap().path().to_str().unwrap());
                    contents.push(memes);
                }
                recurse_songs(&mut contents, recurse, false).unwrap();
                new.append(&mut contents);
            }
            else {
                println!("WARNING: Ignoring directory {}", song);
                println!("Use -r to make recursive");
                println!("Press Enter to continue");
                let mut temp = String::new();
                io::stdin().read_line(&mut temp);
            }
        }
        else {
            new.push(song.clone());
        }
    }
    songs.clear();
    songs.append(&mut new);
    Ok(())
}

fn main() {
    let mut global_err = String::from("");
    {
    let mut regex = String::new();
    let mut songs : Vec<String> = vec![]; // TODO: add with capacity!
    let mut recurse = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Rust Fucking Simple Music Player");
        ap.refer(&mut recurse)
            .add_option(&["-r", "--recursive"], StoreTrue, "Recurse through directories if passed them");
        ap.refer(&mut regex)
            .add_option(&["-s", "--search"], Store, "Search songs using regular expressions");
        ap.refer(&mut songs)
            .add_argument("arguments", List, "Songs to play");
        ap.parse_args_or_exit();
    }

    if songs.len() == 0 {
        println!("Usage:");
        println!("    rfsmp [OPTIONS] [SONGS ...]");
        return;
    }

    if regex != "" {
        let re = Regex::new(&regex).expect("regex invalid");
        songs.retain(|i| re.is_match(i));
    }

    let mut recur_one = false;
    if songs.len() == 1 {
        recur_one = true;
    }

    recurse_songs(&mut songs, recurse, recur_one);

    let mut playlist = playlist::Playlist::new(songs);

    gst::init();
    let mut playbin = gst::PlayBin::new("audio_player")
        .expect("Couldn't create playlist");
    let mut main_loop = gst::MainLoop::new();

    let mut bus;
    let bus_receiver;

    let song = match playlist.get_next_song() {
        Some(a) => gst::filename_to_uri(a).expect("URI Error"),
        None => panic!("can't get song"),
    };

    playbin.set_uri(&song);
    bus = playbin.bus().expect("Couldn't get pipeline bus");
    bus_receiver = bus.receiver();

    let mut ui = UI::new(&playlist);

    main_loop.spawn();
    playbin.play();

    let mut song_buffered = false;

    'outer: loop {
        loop {
            match bus_receiver.try_recv() {
                Ok(message) => {
                    match message.parse(){
                        gst::Message::ErrorParsed{ref error, ..} => {
                            global_err.push_str(
                                &format!("error msg from element `{}`: {}, quit",
                                        message.src_name(), error.message()));
                            break 'outer;
                        }
                        gst::Message::Eos(ref _msg) => {
                            break 'outer;
                        }
                        gst::Message::StreamStart(ref _msg) => {
                            song_buffered = false;
                            playlist.go_to_next();
                        }
                        _ => {
                            //println!("msg of type `{}` from element `{}`",
                            //         message.type_name(), message.src_name());
                        }
                    }
                }
                Err(err) => {
                    match err {
                        std::sync::mpsc::TryRecvError::Empty => break,
                        std::sync::mpsc::TryRecvError::Disconnected => break,
                    }
                }
            }
        }
        let stream_dir = playbin.duration_s();
        let stream_pos = playbin.position_s();

        if stream_dir.is_some() && stream_pos.is_some() && !song_buffered {
            if stream_dir.unwrap() - stream_pos.unwrap() < 3.0 {
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        //println!("All songs played");
                    }
                };
            }
        }

        let stream_dir = match stream_dir {
            Some(a) => a as i32,
            None => 0,
        };
        let stream_pos = match stream_pos {
            Some(a) => a as i32,
            None => 0,
        };

        match ui.manage_ui(&playlist, stream_pos, stream_dir) {
            UIResult::PlayPause => {
                if playbin.is_paused() {
                    playbin.play();
                }
                else {
                    playbin.pause();
                }
            }
            UIResult::Previous => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                playlist.go_to_prev();
                playlist.go_to_prev();
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();

            }
            UIResult::Next => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();
            }
            UIResult::Exit => {
                break;
            }
            UIResult::Error(a) => {
                global_err = a;
                break 'outer;
            }
            UIResult::NA => {}
        }
        std::thread::sleep(std::time::Duration::new(0, 20000000));
    }
    main_loop.quit();
    }
    if global_err != "" {
        println!("{}", global_err);
    }
}
<commit_msg>Fix some error handling issues.<commit_after>//  Copyright 2015 Nathanael Merrill
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
extern crate rustty;
extern crate gst;
extern crate argparse;
extern crate regex;

mod playlist;
mod default_ui;

use argparse::{ArgumentParser, Store, StoreTrue, List};
use default_ui::*;
use regex::Regex;
use gst::ElementT;
use std::fs;
use std::io;

fn recurse_songs(songs: &mut Vec<String>, recurse: bool, is_first: bool) ->
                    Result<(), io::Error>{
    let mut new : Vec<String> = vec![];

    for song in songs.iter() {
        if try!(fs::metadata(&song)).is_dir() {
            if recurse || is_first {
                let mut contents: Vec<String> = vec![];
                for entry in try!(fs::read_dir(song)) {
                    let memes = String::from(match try!(entry).path().to_str() {
                        Some(a) => a,
                        None => return Err(io::Error::new(
                                io::ErrorKind::InvalidInput, "Failed to parse song file names.")),
                    });
                    contents.push(memes);
                }
                try!(recurse_songs(&mut contents, recurse, false));
                new.append(&mut contents);
            }
            else {
                println!("WARNING: Ignoring directory {}", song);
                println!("Use -r to make recursive");
                println!("Press Enter to continue");
                let mut temp = String::new();
                match io::stdin().read_line(&mut temp) {
                    Ok(_) => {},
                    Err(err) => return Err(err),
                }
            }
        }
        else {
            new.push(song.clone());
        }
    }
    songs.clear();
    songs.append(&mut new);
    Ok(())
}

fn main() {
    let mut global_err = String::from("");
    {
    let mut regex = String::new();
    let mut songs : Vec<String> = vec![]; // TODO: add with capacity!
    let mut recurse = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Rust Fucking Simple Music Player");
        ap.refer(&mut recurse)
            .add_option(&["-r", "--recursive"], StoreTrue, "Recurse through directories if passed them");
        ap.refer(&mut regex)
            .add_option(&["-s", "--search"], Store, "Search songs using regular expressions");
        ap.refer(&mut songs)
            .add_argument("arguments", List, "Songs to play");
        ap.parse_args_or_exit();
    }

    let mut recur_one = false;
    if songs.len() == 1 {
        recur_one = true;
    }

    match recurse_songs(&mut songs, recurse, recur_one) {
        Ok(_) => {},
        Err(e) => {
            println!("{}", e.to_string());
            return;
        }
    }

    if songs.len() == 0 {
        println!("Usage:");
        println!("    rfsmp [OPTIONS] [SONGS ...]");
        return;
    }

    if regex != "" {
        let re = match Regex::new(&regex) {
            Ok(o) => o,
            Err(e) => {
                println!("Regex invalid: {}", e.to_string());
                return;
            }
        };
        songs.retain(|i| re.is_match(i));
        if songs.len() == 0 {
            println!("Failed to find songs matching regex patern.");
            return;
        }
    }

    let mut playlist = playlist::Playlist::new(songs);

    gst::init();
    let mut playbin = match gst::PlayBin::new("audio_player") {
        Some(a) => a,
        None => {
            println!("Couldn't create PlayBin.");
            return;
        }
    };
    let mut main_loop = gst::MainLoop::new();

    let mut bus;
    let bus_receiver;

    let song = match playlist.get_next_song() {
        Some(a) => gst::filename_to_uri(a).expect("URI Error"),
        None => {
            println!("Can't get song.");
            return;
        }
    };

    playbin.set_uri(&song);
    bus = playbin.bus().expect("Couldn't get pipeline bus");
    bus_receiver = bus.receiver();

    let mut ui = UI::new(&playlist);

    main_loop.spawn();
    playbin.play();

    let mut song_buffered = false;

    'outer: loop {
        loop {
            match bus_receiver.try_recv() {
                Ok(message) => {
                    match message.parse(){
                        gst::Message::ErrorParsed{ref error, ..} => {
                            global_err.push_str(
                                &format!("error msg from element `{}`: {}, quit",
                                        message.src_name(), error.message()));
                            break 'outer;
                        }
                        gst::Message::Eos(ref _msg) => {
                            break 'outer;
                        }
                        gst::Message::StreamStart(ref _msg) => {
                            song_buffered = false;
                            playlist.go_to_next();
                        }
                        _ => {
                            //println!("msg of type `{}` from element `{}`",
                            //         message.type_name(), message.src_name());
                        }
                    }
                }
                Err(err) => {
                    match err {
                        std::sync::mpsc::TryRecvError::Empty => break,
                        std::sync::mpsc::TryRecvError::Disconnected => break,
                    }
                }
            }
        }
        let stream_dir = playbin.duration_s();
        let stream_pos = playbin.position_s();

        if stream_dir.is_some() && stream_pos.is_some() && !song_buffered {
            if stream_dir.unwrap() - stream_pos.unwrap() < 3.0 {
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        //println!("All songs played");
                    }
                };
            }
        }

        let stream_dir = match stream_dir {
            Some(a) => a as i32,
            None => 0,
        };
        let stream_pos = match stream_pos {
            Some(a) => a as i32,
            None => 0,
        };

        match ui.manage_ui(&playlist, stream_pos, stream_dir) {
            UIResult::PlayPause => {
                if playbin.is_paused() {
                    playbin.play();
                }
                else {
                    playbin.pause();
                }
            }
            UIResult::Previous => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                playlist.go_to_prev();
                playlist.go_to_prev();
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();

            }
            UIResult::Next => {
                playbin.set_state(gst::ffi::GstState::GST_STATE_NULL);
                match playlist.get_next_song() {
                    Some(a) => {
                        let song = match gst::filename_to_uri(a) {
                            Ok(a) => a,
                            Err(e) => {
                                global_err.push_str("URI Error ");
                                global_err.push_str(&e.message());
                                break 'outer;
                            }
                        };
                        playbin.set_uri(&song);
                        song_buffered = true;
                    }
                    None => {
                        println!("All songs played");
                        break;
                    }
                };
                playbin.play();
            }
            UIResult::Exit => {
                break;
            }
            UIResult::Error(a) => {
                global_err = a;
                break;
            }
            UIResult::NA => {}
        }
        std::thread::sleep(std::time::Duration::new(0, 20000000));
    }
    main_loop.quit();
    }
    if global_err != "" {
        println!("{}", global_err);
    }
}
<|endoftext|>


<commit_before>mod git_history;
mod git_client;

fn main() {
    println!("semantic.rs 🚀");
}
<commit_msg>feat(main): Include logger in main module<commit_after>mod git_history;
mod git_client;
mod logger;

fn print_log(log_entries: Vec<git_history::LogEntry>) {
    for entry in log_entries {
        logger::stdout(entry.revision);
        logger::stdout(entry.title);
    }
}

fn main() {
    println!("semantic.rs 🚀");

    logger::stdout("Fetched git history");
    match git_client::log() {
        Ok(log) => print_log(log),
        Err(err) => {
            logger::stderr("fatal: Failed to retrieve git history");
            logger::stderr(err);
        }
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate toml;

#[cfg(test)]
#[macro_use]
extern crate hyper;

use std::env;
use std::io::prelude::*;
use std::fs::File;
use clap::App;

mod github;

static GITHUB_TOKEN: &'static str = "RP_GITHUBTOKEN";

fn main() {
    let yaml = load_yaml!("release-party.yml");
    let matches = App::from_yaml(yaml).get_matches();

    let org_url = make_org_url(&matches);
    let token = get_github_token();
    let reqwest_client = get_reqwest_client();

    print_party_links(get_pr_links(
        &get_repos_we_care_about(&token, &org_url, &reqwest_client),
        &token,
        &reqwest_client,
        is_dryrun(&matches),
    ));
}

fn is_dryrun(matches: &clap::ArgMatches) -> bool {
    matches.is_present("DRYRUN")
}

fn make_org_url(matches: &clap::ArgMatches) -> String {
    let org = matches
        .value_of("ORG")
        .expect("Please specify a github org");

    format!("https://api.github.com/orgs/{}/repos", org)
}

fn get_pr_links(repos: &Vec<github::GithubRepo>, token: &str, reqwest_client: &reqwest::Client, dryrun: bool) -> Vec<Option<String>> {
    let mut pr_links: Vec<Option<String>> = repos
        .into_iter()
        .map(|repo| match get_release_pr_for(&repo, &token, reqwest_client, dryrun) {
            Some(pr_url) => Some(pr_url),
            None => None,
        })
        .collect();
    // only keep the Some(PR_URL) items:
    pr_links.retain(|maybe_pr_link| maybe_pr_link.is_some());
    pr_links
}

fn get_reqwest_client() -> reqwest::Client {
    match reqwest::Client::new() {
        Ok(new_client) => new_client,
        Err(e) => panic!("Couldn't create new reqwest client: {}", e),
    }
}

fn get_repos_we_care_about(token: &str, github_org_url: &str, reqwest_client: &reqwest::Client) -> Vec<github::GithubRepo> {
    let mut repos = match github::get_repos_at(github_org_url, token, reqwest_client) {
        Ok(repos) => repos,
        Err(e) => panic!(format!("Couldn't get repos from github: {}", e)),
    };
    let repos_to_ignore = ignored_repos();

    // remove repos we don't care about:
    repos.retain(|repo| !repos_to_ignore.contains(&repo.name));

    repos
}

fn get_release_pr_for(repo: &github::GithubRepo, token: &str, client: &reqwest::Client, dryrun: bool) -> Option<String> {
    println!("looking for release PR for {}", repo.name);
    match github::existing_release_pr_location(repo, token, client) {
        Some(url) => Some(url),
        None => if !github::is_release_up_to_date_with_master(&repo.url, token, client) {
            if dryrun {
                Some(format!("Dry run: {} would get a release PR.", repo.url))
            } else {
                match github::create_release_pull_request(repo, token, client) {
                    Ok(pr_url) => Some(pr_url),
                    Err(_) => None,
                }
            }
        } else {
            None
        },
    }
}

fn print_party_links(pr_links: Vec<Option<String>>) {
    if !pr_links.is_empty() {
        println!("\nIt's a release party!  PRs to review and approve:");
        for link in pr_links {
            match link {
                Some(pr_link) => println!("{}", pr_link),
                None => println!("Party link is None: this shouldn't happen."),
            }
        }
    } else {
        println!("\nNo party today, all releases are done.");
    }
}

#[derive(Deserialize, Debug)]
struct IgnoredRepo {
    ignore: Option<Vec<String>>,
}

fn ignored_repos() -> Vec<String> {
    let mut f = match File::open("ignoredrepos.toml") {
        Ok(file) => file,
        Err(e) => {
            println!("Couldn't load ignoredrepos.toml, not ignoring any repos. Reason: {}", e);
            return Vec::new();
        }
    };
    let mut buffer = String::new();
    match f.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => {
            println!("Couldn't read from ignoredrepos.toml, not ignoring any repos. Reason: {}", e);
            return Vec::new();
        }
    }

    let ignored: IgnoredRepo = match toml::from_str(&buffer) {
        Ok(ignored_repos) => ignored_repos,
        Err(e) => {
            println!(
                "Couldn't parse toml from ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    };
    match ignored.ignore {
        Some(repos_to_ignore) => repos_to_ignore,
        None => Vec::new(),
    }
}

fn get_github_token() -> String {
    match env::var(GITHUB_TOKEN) {
        Ok(gh_token) => gh_token,
        Err(e) => panic!(format!("Couldn't find {}: {}", GITHUB_TOKEN, e)),
    }
}

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

    #[test]
    fn token_from_env_happy_path() {
        env::set_var(GITHUB_TOKEN, "a token");
        assert_eq!("a token", get_github_token());
        env::remove_var(GITHUB_TOKEN);
    }

    #[test]
    #[should_panic]
    fn token_from_env_sad_path() {
        env::remove_var(GITHUB_TOKEN);
        get_github_token();
    }

    #[test]
    fn get_ignored_repos_happy_path() {
        let ignored_repositories = vec!["calagator".to_owned(), "moe".to_owned()];
        assert_eq!(ignored_repositories, ignored_repos());
    }
}<commit_msg>Remove race condition for env var test.<commit_after>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate toml;

#[cfg(test)]
#[macro_use]
extern crate hyper;

use std::env;
use std::io::prelude::*;
use std::fs::File;
use clap::App;

mod github;

static GITHUB_TOKEN: &'static str = "RP_GITHUBTOKEN";

fn main() {
    let yaml = load_yaml!("release-party.yml");
    let matches = App::from_yaml(yaml).get_matches();

    let org_url = make_org_url(&matches);
    let token = get_github_token(GITHUB_TOKEN);
    let reqwest_client = get_reqwest_client();

    print_party_links(get_pr_links(
        &get_repos_we_care_about(&token, &org_url, &reqwest_client),
        &token,
        &reqwest_client,
        is_dryrun(&matches),
    ));
}

fn is_dryrun(matches: &clap::ArgMatches) -> bool {
    matches.is_present("DRYRUN")
}

fn make_org_url(matches: &clap::ArgMatches) -> String {
    let org = matches
        .value_of("ORG")
        .expect("Please specify a github org");

    format!("https://api.github.com/orgs/{}/repos", org)
}

fn get_pr_links(repos: &Vec<github::GithubRepo>, token: &str, reqwest_client: &reqwest::Client, dryrun: bool) -> Vec<Option<String>> {
    let mut pr_links: Vec<Option<String>> = repos
        .into_iter()
        .map(|repo| match get_release_pr_for(&repo, &token, reqwest_client, dryrun) {
            Some(pr_url) => Some(pr_url),
            None => None,
        })
        .collect();
    // only keep the Some(PR_URL) items:
    pr_links.retain(|maybe_pr_link| maybe_pr_link.is_some());
    pr_links
}

fn get_reqwest_client() -> reqwest::Client {
    match reqwest::Client::new() {
        Ok(new_client) => new_client,
        Err(e) => panic!("Couldn't create new reqwest client: {}", e),
    }
}

fn get_repos_we_care_about(token: &str, github_org_url: &str, reqwest_client: &reqwest::Client) -> Vec<github::GithubRepo> {
    let mut repos = match github::get_repos_at(github_org_url, token, reqwest_client) {
        Ok(repos) => repos,
        Err(e) => panic!(format!("Couldn't get repos from github: {}", e)),
    };
    let repos_to_ignore = ignored_repos();

    // remove repos we don't care about:
    repos.retain(|repo| !repos_to_ignore.contains(&repo.name));

    repos
}

fn get_release_pr_for(repo: &github::GithubRepo, token: &str, client: &reqwest::Client, dryrun: bool) -> Option<String> {
    println!("looking for release PR for {}", repo.name);
    match github::existing_release_pr_location(repo, token, client) {
        Some(url) => Some(url),
        None => if !github::is_release_up_to_date_with_master(&repo.url, token, client) {
            if dryrun {
                Some(format!("Dry run: {} would get a release PR.", repo.url))
            } else {
                match github::create_release_pull_request(repo, token, client) {
                    Ok(pr_url) => Some(pr_url),
                    Err(_) => None,
                }
            }
        } else {
            None
        },
    }
}

fn print_party_links(pr_links: Vec<Option<String>>) {
    if !pr_links.is_empty() {
        println!("\nIt's a release party!  PRs to review and approve:");
        for link in pr_links {
            match link {
                Some(pr_link) => println!("{}", pr_link),
                None => println!("Party link is None: this shouldn't happen."),
            }
        }
    } else {
        println!("\nNo party today, all releases are done.");
    }
}

#[derive(Deserialize, Debug)]
struct IgnoredRepo {
    ignore: Option<Vec<String>>,
}

fn ignored_repos() -> Vec<String> {
    let mut f = match File::open("ignoredrepos.toml") {
        Ok(file) => file,
        Err(e) => {
            println!("Couldn't load ignoredrepos.toml, not ignoring any repos. Reason: {}", e);
            return Vec::new();
        }
    };
    let mut buffer = String::new();
    match f.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => {
            println!("Couldn't read from ignoredrepos.toml, not ignoring any repos. Reason: {}", e);
            return Vec::new();
        }
    }

    let ignored: IgnoredRepo = match toml::from_str(&buffer) {
        Ok(ignored_repos) => ignored_repos,
        Err(e) => {
            println!(
                "Couldn't parse toml from ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    };
    match ignored.ignore {
        Some(repos_to_ignore) => repos_to_ignore,
        None => Vec::new(),
    }
}

fn get_github_token(env_var_location: &str) -> String {
    match env::var(env_var_location) {
        Ok(gh_token) => gh_token,
        Err(e) => panic!(format!("Couldn't find {}: {}", GITHUB_TOKEN, e)),
    }
}

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

    #[test]
    fn token_from_env_happy_path() {
        env::set_var("happytoken", "a token");
        assert_eq!("a token", get_github_token(&"happytoken".to_owned()));
        env::remove_var("happytoken");
    }

    #[test]
    #[should_panic]
    fn token_from_env_sad_path() {
        get_github_token("randomtokenlocation");
    }

    #[test]
    fn get_ignored_repos_happy_path() {
        let ignored_repositories = vec!["calagator".to_owned(), "moe".to_owned()];
        assert_eq!(ignored_repositories, ignored_repos());
    }
}<|endoftext|>


<commit_before>extern crate crypto;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
#[macro_use]
extern crate log;
extern crate rpassword;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_process;
extern crate tokio_signal;
extern crate url;

use crypto::digest::Digest;
use crypto::sha1::Sha1;
use env_logger::LogBuilder;
use futures::sync::mpsc::UnboundedReceiver;
use futures::{Async, Future, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::mem;
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Core, Handle};
use tokio_io::IoStream;
use url::Url;

use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache;
use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig};
use librespot::core::session::Session;
use librespot::core::version;

use librespot::connect::discovery::{discovery, DiscoveryStream};
use librespot::connect::spirc::{Spirc, SpircTask};
use librespot::playback::audio_backend::{self, Sink, BACKENDS};
use librespot::playback::config::{Bitrate, PlayerConfig};
use librespot::playback::mixer::{self, Mixer};
use librespot::playback::player::{Player, PlayerEvent};

mod player_event_handler;
use player_event_handler::run_program_on_events;

fn device_id(name: &str) -> String {
    let mut h = Sha1::new();
    h.input_str(name);
    h.result_str()
}

fn usage(program: &str, opts: &getopts::Options) -> String {
    let brief = format!("Usage: {} [options]", program);
    opts.usage(&brief)
}

fn setup_logging(verbose: bool) {
    let mut builder = LogBuilder::new();
    match env::var("RUST_LOG") {
        Ok(config) => {
            builder.parse(&config);
            builder.init().unwrap();

            if verbose {
                warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
            }
        }
        Err(_) => {
            if verbose {
                builder.parse("mdns=info,librespot=trace");
            } else {
                builder.parse("mdns=info,librespot=info");
            }
            builder.init().unwrap();
        }
    }
}

fn list_backends() {
    println!("Available Backends : ");
    for (&(name, _), idx) in BACKENDS.iter().zip(0..) {
        if idx == 0 {
            println!("- {} (default)", name);
        } else {
            println!("- {}", name);
        }
    }
}

#[derive(Clone)]
struct Setup {
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,

    mixer: fn() -> Box<Mixer>,

    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    credentials: Option<Credentials>,
    enable_discovery: bool,
    zeroconf_port: u16,
    player_event_program: Option<String>,
}

fn setup(args: &[String]) -> Setup {
    let mut opts = getopts::Options::new();
    opts.optopt(
        "c",
        "cache",
        "Path to a directory where files will be cached.",
        "CACHE",
    ).optflag("", "disable-audio-cache", "Disable caching of the audio data.")
        .reqopt("n", "name", "Device name", "NAME")
        .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
        .optopt(
            "b",
            "bitrate",
            "Bitrate (96, 160 or 320). Defaults to 160",
            "BITRATE",
        )
        .optopt(
            "",
            "onevent",
            "Run PROGRAM when playback is about to begin.",
            "PROGRAM",
        )
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .optopt("", "proxy", "HTTP proxy to use when connecting", "PROXY")
        .optopt("", "ap-port", "Connect to AP with specified port. If no AP with that port are present fallback AP will be used. Available ports are usually 80, 443 and 4070", "AP_PORT")
        .optflag("", "disable-discovery", "Disable discovery mode")
        .optopt(
            "",
            "backend",
            "Audio backend to use. Use '?' to list options",
            "BACKEND",
        )
        .optopt(
            "",
            "device",
            "Audio device to use. Use '?' to list options if using portaudio",
            "DEVICE",
        )
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt(
            "",
            "initial-volume",
            "Initial volume in %, once connected (must be from 0 to 100)",
            "VOLUME",
        )
        .optopt(
            "",
            "zeroconf-port",
            "The port the internal server advertised over zeroconf uses.",
            "ZEROCONF_PORT",
        )
        .optflag(
            "",
            "enable-volume-normalisation",
            "Play all tracks at the same volume",
        )
        .optopt(
            "",
            "normalisation-pregain",
            "Pregain (dB) applied by volume normalisation",
            "PREGAIN",
        )
        .optflag(
            "",
            "linear-volume",
            "increase volume linear instead of logarithmic.",
        );

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
            exit(1);
        }
    };

    let verbose = matches.opt_present("verbose");
    setup_logging(verbose);

    info!(
        "librespot {} ({}). Built on {}. Build ID: {}",
        version::short_sha(),
        version::commit_date(),
        version::short_now(),
        version::build_id()
    );

    let backend_name = matches.opt_str("backend");
    if backend_name == Some("?".into()) {
        list_backends();
        exit(0);
    }

    let backend = audio_backend::find(backend_name).expect("Invalid backend");

    let device = matches.opt_str("device");

    let mixer_name = matches.opt_str("mixer");
    let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer");

    let use_audio_cache = !matches.opt_present("disable-audio-cache");

    let cache = matches
        .opt_str("c")
        .map(|cache_location| Cache::new(PathBuf::from(cache_location), use_audio_cache));

    let initial_volume = matches
        .opt_str("initial-volume")
        .map(|volume| {
            let volume = volume.parse::<u16>().unwrap();
            if volume > 100 {
                panic!("Initial volume must be in the range 0-100");
            }
            (volume as i32 * 0xFFFF / 100) as u16
        })
        .or_else(|| cache.as_ref().and_then(Cache::volume))
        .unwrap_or(0x8000);

    let zeroconf_port = matches
        .opt_str("zeroconf-port")
        .map(|port| port.parse::<u16>().unwrap())
        .unwrap_or(0);

    let name = matches.opt_str("name").unwrap();

    let credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

        let password = |username: &String| -> String {
            write!(stderr(), "Password for {}: ", username).unwrap();
            stderr().flush().unwrap();
            rpassword::read_password().unwrap()
        };

        get_credentials(
            matches.opt_str("username"),
            matches.opt_str("password"),
            cached_credentials,
            password,
        )
    };

    let session_config = {
        let device_id = device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
            proxy: matches.opt_str("proxy").or(std::env::var("http_proxy").ok()).map(
                |s| {
                    match Url::parse(&s) {
                        Ok(url) => {
                            if url.host().is_none() || url.port().is_none() {
                                panic!("Invalid proxy url, only urls on the format \"http://host:port\" are allowed");
                            }

                            if url.scheme() != "http" {
                                panic!("Only unsecure http:// proxies are supported");
                            }
                            url
                        },
                    Err(err) => panic!("Invalid proxy url: {}, only urls on the format \"http://host:port\" are allowed", err)
                    }
                },
            ),
            ap_port: matches
                .opt_str("ap-port")
                .map(|port| port.parse::<u16>().expect("Invalid port")),
        }
    };

    let player_config = {
        let bitrate = matches
            .opt_str("b")
            .as_ref()
            .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
            .unwrap_or(Bitrate::default());

        PlayerConfig {
            bitrate: bitrate,
            normalisation: matches.opt_present("enable-volume-normalisation"),
            normalisation_pregain: matches
                .opt_str("normalisation-pregain")
                .map(|pregain| pregain.parse::<f32>().expect("Invalid pregain float value"))
                .unwrap_or(PlayerConfig::default().normalisation_pregain),
        }
    };

    let connect_config = {
        let device_type = matches
            .opt_str("device-type")
            .as_ref()
            .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
            .unwrap_or(DeviceType::default());

        ConnectConfig {
            name: name,
            device_type: device_type,
            volume: initial_volume,
            linear_volume: matches.opt_present("linear-volume"),
        }
    };

    let enable_discovery = !matches.opt_present("disable-discovery");

    Setup {
        backend: backend,
        cache: cache,
        session_config: session_config,
        player_config: player_config,
        connect_config: connect_config,
        credentials: credentials,
        device: device,
        enable_discovery: enable_discovery,
        zeroconf_port: zeroconf_port,
        mixer: mixer,
        player_event_program: matches.opt_str("onevent"),
    }
}

struct Main {
    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,
    mixer: fn() -> Box<Mixer>,
    handle: Handle,

    discovery: Option<DiscoveryStream>,
    signal: IoStream<()>,

    spirc: Option<Spirc>,
    spirc_task: Option<SpircTask>,
    connect: Box<Future<Item = Session, Error = io::Error>>,

    shutdown: bool,

    player_event_channel: Option<UnboundedReceiver<PlayerEvent>>,
    player_event_program: Option<String>,
}

impl Main {
    fn new(handle: Handle, setup: Setup) -> Main {
        let mut task = Main {
            handle: handle.clone(),
            cache: setup.cache,
            session_config: setup.session_config,
            player_config: setup.player_config,
            connect_config: setup.connect_config,
            backend: setup.backend,
            device: setup.device,
            mixer: setup.mixer,

            connect: Box::new(futures::future::empty()),
            discovery: None,
            spirc: None,
            spirc_task: None,
            shutdown: false,
            signal: Box::new(tokio_signal::ctrl_c(&handle).flatten_stream()),

            player_event_channel: None,
            player_event_program: setup.player_event_program,
        };

        if setup.enable_discovery {
            let config = task.connect_config.clone();
            let device_id = task.session_config.device_id.clone();

            task.discovery = Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap());
        }

        if let Some(credentials) = setup.credentials {
            task.credentials(credentials);
        }

        task
    }

    fn credentials(&mut self, credentials: Credentials) {
        let config = self.session_config.clone();
        let handle = self.handle.clone();

        let connection = Session::connect(config, credentials, self.cache.clone(), handle);

        self.connect = connection;
        self.spirc = None;
        let task = mem::replace(&mut self.spirc_task, None);
        if let Some(task) = task {
            self.handle.spawn(task);
        }
    }
}

impl Future for Main {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        loop {
            let mut progress = false;

            if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
                if let Some(ref spirc) = self.spirc {
                    spirc.shutdown();
                }
                self.credentials(creds);

                progress = true;
            }

            if let Async::Ready(session) = self.connect.poll().unwrap() {
                self.connect = Box::new(futures::future::empty());
                let device = self.device.clone();
                let mixer = (self.mixer)();
                let player_config = self.player_config.clone();
                let connect_config = self.connect_config.clone();

                let audio_filter = mixer.get_audio_filter();
                let backend = self.backend;
                let (player, event_channel) =
                    Player::new(player_config, session.clone(), audio_filter, move || {
                        (backend)(device)
                    });

                let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
                self.spirc = Some(spirc);
                self.spirc_task = Some(spirc_task);
                self.player_event_channel = Some(event_channel);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                trace!("Ctrl-C received");
                if !self.shutdown {
                    if let Some(ref spirc) = self.spirc {
                        spirc.shutdown();
                    }
                    self.shutdown = true;
                } else {
                    return Ok(Async::Ready(()));
                }

                progress = true;
            }

            if let Some(ref mut spirc_task) = self.spirc_task {
                if let Async::Ready(()) = spirc_task.poll().unwrap() {
                    if self.shutdown {
                        return Ok(Async::Ready(()));
                    } else {
                        panic!("Spirc shut down unexpectedly");
                    }
                }
            }

            if let Some(ref mut player_event_channel) = self.player_event_channel {
                if let Async::Ready(Some(event)) = player_event_channel.poll().unwrap() {
                    if let Some(ref program) = self.player_event_program {
                        let child = run_program_on_events(event, program)
                            .expect("program failed to start")
                            .map(|status| if !status.success() {
                                error!("child exited with status {:?}", status.code());
                            })
                            .map_err(|e| error!("failed to wait on child process: {}", e));

                        self.handle.spawn(child);

                    }
                }
            }

            if !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    if env::var("RUST_BACKTRACE").is_err() {
        env::set_var("RUST_BACKTRACE", "full")
    }
    let mut core = Core::new().unwrap();
    let handle = core.handle();

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

    core.run(Main::new(handle, setup(&args))).unwrap()
}
<commit_msg>main: exit librespot after the first ctrl+c if no currently active spirc session.<commit_after>extern crate crypto;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
#[macro_use]
extern crate log;
extern crate rpassword;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_process;
extern crate tokio_signal;
extern crate url;

use crypto::digest::Digest;
use crypto::sha1::Sha1;
use env_logger::LogBuilder;
use futures::sync::mpsc::UnboundedReceiver;
use futures::{Async, Future, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::mem;
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Core, Handle};
use tokio_io::IoStream;
use url::Url;

use librespot::core::authentication::{get_credentials, Credentials};
use librespot::core::cache::Cache;
use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig};
use librespot::core::session::Session;
use librespot::core::version;

use librespot::connect::discovery::{discovery, DiscoveryStream};
use librespot::connect::spirc::{Spirc, SpircTask};
use librespot::playback::audio_backend::{self, Sink, BACKENDS};
use librespot::playback::config::{Bitrate, PlayerConfig};
use librespot::playback::mixer::{self, Mixer};
use librespot::playback::player::{Player, PlayerEvent};

mod player_event_handler;
use player_event_handler::run_program_on_events;

fn device_id(name: &str) -> String {
    let mut h = Sha1::new();
    h.input_str(name);
    h.result_str()
}

fn usage(program: &str, opts: &getopts::Options) -> String {
    let brief = format!("Usage: {} [options]", program);
    opts.usage(&brief)
}

fn setup_logging(verbose: bool) {
    let mut builder = LogBuilder::new();
    match env::var("RUST_LOG") {
        Ok(config) => {
            builder.parse(&config);
            builder.init().unwrap();

            if verbose {
                warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
            }
        }
        Err(_) => {
            if verbose {
                builder.parse("mdns=info,librespot=trace");
            } else {
                builder.parse("mdns=info,librespot=info");
            }
            builder.init().unwrap();
        }
    }
}

fn list_backends() {
    println!("Available Backends : ");
    for (&(name, _), idx) in BACKENDS.iter().zip(0..) {
        if idx == 0 {
            println!("- {} (default)", name);
        } else {
            println!("- {}", name);
        }
    }
}

#[derive(Clone)]
struct Setup {
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,

    mixer: fn() -> Box<Mixer>,

    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    credentials: Option<Credentials>,
    enable_discovery: bool,
    zeroconf_port: u16,
    player_event_program: Option<String>,
}

fn setup(args: &[String]) -> Setup {
    let mut opts = getopts::Options::new();
    opts.optopt(
        "c",
        "cache",
        "Path to a directory where files will be cached.",
        "CACHE",
    ).optflag("", "disable-audio-cache", "Disable caching of the audio data.")
        .reqopt("n", "name", "Device name", "NAME")
        .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
        .optopt(
            "b",
            "bitrate",
            "Bitrate (96, 160 or 320). Defaults to 160",
            "BITRATE",
        )
        .optopt(
            "",
            "onevent",
            "Run PROGRAM when playback is about to begin.",
            "PROGRAM",
        )
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .optopt("", "proxy", "HTTP proxy to use when connecting", "PROXY")
        .optopt("", "ap-port", "Connect to AP with specified port. If no AP with that port are present fallback AP will be used. Available ports are usually 80, 443 and 4070", "AP_PORT")
        .optflag("", "disable-discovery", "Disable discovery mode")
        .optopt(
            "",
            "backend",
            "Audio backend to use. Use '?' to list options",
            "BACKEND",
        )
        .optopt(
            "",
            "device",
            "Audio device to use. Use '?' to list options if using portaudio",
            "DEVICE",
        )
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt(
            "",
            "initial-volume",
            "Initial volume in %, once connected (must be from 0 to 100)",
            "VOLUME",
        )
        .optopt(
            "",
            "zeroconf-port",
            "The port the internal server advertised over zeroconf uses.",
            "ZEROCONF_PORT",
        )
        .optflag(
            "",
            "enable-volume-normalisation",
            "Play all tracks at the same volume",
        )
        .optopt(
            "",
            "normalisation-pregain",
            "Pregain (dB) applied by volume normalisation",
            "PREGAIN",
        )
        .optflag(
            "",
            "linear-volume",
            "increase volume linear instead of logarithmic.",
        );

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
            exit(1);
        }
    };

    let verbose = matches.opt_present("verbose");
    setup_logging(verbose);

    info!(
        "librespot {} ({}). Built on {}. Build ID: {}",
        version::short_sha(),
        version::commit_date(),
        version::short_now(),
        version::build_id()
    );

    let backend_name = matches.opt_str("backend");
    if backend_name == Some("?".into()) {
        list_backends();
        exit(0);
    }

    let backend = audio_backend::find(backend_name).expect("Invalid backend");

    let device = matches.opt_str("device");

    let mixer_name = matches.opt_str("mixer");
    let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer");

    let use_audio_cache = !matches.opt_present("disable-audio-cache");

    let cache = matches
        .opt_str("c")
        .map(|cache_location| Cache::new(PathBuf::from(cache_location), use_audio_cache));

    let initial_volume = matches
        .opt_str("initial-volume")
        .map(|volume| {
            let volume = volume.parse::<u16>().unwrap();
            if volume > 100 {
                panic!("Initial volume must be in the range 0-100");
            }
            (volume as i32 * 0xFFFF / 100) as u16
        })
        .or_else(|| cache.as_ref().and_then(Cache::volume))
        .unwrap_or(0x8000);

    let zeroconf_port = matches
        .opt_str("zeroconf-port")
        .map(|port| port.parse::<u16>().unwrap())
        .unwrap_or(0);

    let name = matches.opt_str("name").unwrap();

    let credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

        let password = |username: &String| -> String {
            write!(stderr(), "Password for {}: ", username).unwrap();
            stderr().flush().unwrap();
            rpassword::read_password().unwrap()
        };

        get_credentials(
            matches.opt_str("username"),
            matches.opt_str("password"),
            cached_credentials,
            password,
        )
    };

    let session_config = {
        let device_id = device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
            proxy: matches.opt_str("proxy").or(std::env::var("http_proxy").ok()).map(
                |s| {
                    match Url::parse(&s) {
                        Ok(url) => {
                            if url.host().is_none() || url.port().is_none() {
                                panic!("Invalid proxy url, only urls on the format \"http://host:port\" are allowed");
                            }

                            if url.scheme() != "http" {
                                panic!("Only unsecure http:// proxies are supported");
                            }
                            url
                        },
                    Err(err) => panic!("Invalid proxy url: {}, only urls on the format \"http://host:port\" are allowed", err)
                    }
                },
            ),
            ap_port: matches
                .opt_str("ap-port")
                .map(|port| port.parse::<u16>().expect("Invalid port")),
        }
    };

    let player_config = {
        let bitrate = matches
            .opt_str("b")
            .as_ref()
            .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
            .unwrap_or(Bitrate::default());

        PlayerConfig {
            bitrate: bitrate,
            normalisation: matches.opt_present("enable-volume-normalisation"),
            normalisation_pregain: matches
                .opt_str("normalisation-pregain")
                .map(|pregain| pregain.parse::<f32>().expect("Invalid pregain float value"))
                .unwrap_or(PlayerConfig::default().normalisation_pregain),
        }
    };

    let connect_config = {
        let device_type = matches
            .opt_str("device-type")
            .as_ref()
            .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
            .unwrap_or(DeviceType::default());

        ConnectConfig {
            name: name,
            device_type: device_type,
            volume: initial_volume,
            linear_volume: matches.opt_present("linear-volume"),
        }
    };

    let enable_discovery = !matches.opt_present("disable-discovery");

    Setup {
        backend: backend,
        cache: cache,
        session_config: session_config,
        player_config: player_config,
        connect_config: connect_config,
        credentials: credentials,
        device: device,
        enable_discovery: enable_discovery,
        zeroconf_port: zeroconf_port,
        mixer: mixer,
        player_event_program: matches.opt_str("onevent"),
    }
}

struct Main {
    cache: Option<Cache>,
    player_config: PlayerConfig,
    session_config: SessionConfig,
    connect_config: ConnectConfig,
    backend: fn(Option<String>) -> Box<Sink>,
    device: Option<String>,
    mixer: fn() -> Box<Mixer>,
    handle: Handle,

    discovery: Option<DiscoveryStream>,
    signal: IoStream<()>,

    spirc: Option<Spirc>,
    spirc_task: Option<SpircTask>,
    connect: Box<Future<Item = Session, Error = io::Error>>,

    shutdown: bool,

    player_event_channel: Option<UnboundedReceiver<PlayerEvent>>,
    player_event_program: Option<String>,
}

impl Main {
    fn new(handle: Handle, setup: Setup) -> Main {
        let mut task = Main {
            handle: handle.clone(),
            cache: setup.cache,
            session_config: setup.session_config,
            player_config: setup.player_config,
            connect_config: setup.connect_config,
            backend: setup.backend,
            device: setup.device,
            mixer: setup.mixer,

            connect: Box::new(futures::future::empty()),
            discovery: None,
            spirc: None,
            spirc_task: None,
            shutdown: false,
            signal: Box::new(tokio_signal::ctrl_c(&handle).flatten_stream()),

            player_event_channel: None,
            player_event_program: setup.player_event_program,
        };

        if setup.enable_discovery {
            let config = task.connect_config.clone();
            let device_id = task.session_config.device_id.clone();

            task.discovery = Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap());
        }

        if let Some(credentials) = setup.credentials {
            task.credentials(credentials);
        }

        task
    }

    fn credentials(&mut self, credentials: Credentials) {
        let config = self.session_config.clone();
        let handle = self.handle.clone();

        let connection = Session::connect(config, credentials, self.cache.clone(), handle);

        self.connect = connection;
        self.spirc = None;
        let task = mem::replace(&mut self.spirc_task, None);
        if let Some(task) = task {
            self.handle.spawn(task);
        }
    }
}

impl Future for Main {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        loop {
            let mut progress = false;

            if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
                if let Some(ref spirc) = self.spirc {
                    spirc.shutdown();
                }
                self.credentials(creds);

                progress = true;
            }

            if let Async::Ready(session) = self.connect.poll().unwrap() {
                self.connect = Box::new(futures::future::empty());
                let device = self.device.clone();
                let mixer = (self.mixer)();
                let player_config = self.player_config.clone();
                let connect_config = self.connect_config.clone();

                let audio_filter = mixer.get_audio_filter();
                let backend = self.backend;
                let (player, event_channel) =
                    Player::new(player_config, session.clone(), audio_filter, move || {
                        (backend)(device)
                    });

                let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
                self.spirc = Some(spirc);
                self.spirc_task = Some(spirc_task);
                self.player_event_channel = Some(event_channel);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                trace!("Ctrl-C received");
                if !self.shutdown {
                    if let Some(ref spirc) = self.spirc {
                        spirc.shutdown();
                    } else {
                        return Ok(Async::Ready(()));
                    }
                    self.shutdown = true;
                } else {
                    return Ok(Async::Ready(()));
                }

                progress = true;
            }

            if let Some(ref mut spirc_task) = self.spirc_task {
                if let Async::Ready(()) = spirc_task.poll().unwrap() {
                    if self.shutdown {
                        return Ok(Async::Ready(()));
                    } else {
                        panic!("Spirc shut down unexpectedly");
                    }
                }
            }

            if let Some(ref mut player_event_channel) = self.player_event_channel {
                if let Async::Ready(Some(event)) = player_event_channel.poll().unwrap() {
                    if let Some(ref program) = self.player_event_program {
                        let child = run_program_on_events(event, program)
                            .expect("program failed to start")
                            .map(|status| if !status.success() {
                                error!("child exited with status {:?}", status.code());
                            })
                            .map_err(|e| error!("failed to wait on child process: {}", e));

                        self.handle.spawn(child);

                    }
                }
            }

            if !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    if env::var("RUST_BACKTRACE").is_err() {
        env::set_var("RUST_BACKTRACE", "full")
    }
    let mut core = Core::new().unwrap();
    let handle = core.handle();

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

    core.run(Main::new(handle, setup(&args))).unwrap()
}
<|endoftext|>