<commit_before>// TODO: many items from tokio-core::io have been deprecated in favour of tokio-io
#![allow(deprecated)]

#[macro_use] extern crate log;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_signal;

use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_core::io::IoStream;
use std::mem;

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

use librespot::audio_backend::{self, Sink, BACKENDS};
use librespot::discovery::{discovery, DiscoveryStream};
use librespot::mixer::{self, Mixer};
use librespot::player::Player;
use librespot::spirc::{Spirc, SpircTask};

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,
}

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("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
        .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .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", "DEVICE")
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME");

    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 initial_volume;
    if matches.opt_present("initial-volume"){
        if matches.opt_str("initial-volume").unwrap().parse::<i32>().is_ok(){
            initial_volume = matches.opt_str("initial-volume").unwrap().parse::<i32>().unwrap()* 0xFFFF as i32 / 100 ;
            }
        else {
            initial_volume = 0x8000 as i32;
            }
        }
    else{
        initial_volume = 0x8000 as i32;
        }
    info!("Volume \"{}\" !", initial_volume);

    let name = matches.opt_str("name").unwrap();
    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 credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

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

    let session_config = {
        let device_id = librespot::core::session::device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
        }
    };

    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,
            onstart: matches.opt_str("onstart"),
            onstop: matches.opt_str("onstop"),
        }
    };

    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,
        }
    };

    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,
        mixer: mixer,
    }
}

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,
}

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: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
        };

        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).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 = 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);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                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 !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    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>check if argument of initial-value is in the [0,100 range<commit_after>// TODO: many items from tokio-core::io have been deprecated in favour of tokio-io
#![allow(deprecated)]

#[macro_use] extern crate log;
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_signal;

use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_core::io::IoStream;
use std::mem;

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

use librespot::audio_backend::{self, Sink, BACKENDS};
use librespot::discovery::{discovery, DiscoveryStream};
use librespot::mixer::{self, Mixer};
use librespot::player::Player;
use librespot::spirc::{Spirc, SpircTask};

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,
}

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("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
        .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
        .optflag("v", "verbose", "Enable verbose output")
        .optopt("u", "username", "Username to sign in with", "USERNAME")
        .optopt("p", "password", "Password", "PASSWORD")
        .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", "DEVICE")
        .optopt("", "mixer", "Mixer to use", "MIXER")
        .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME");

    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 initial_volume;
    if matches.opt_present("initial-volume"){
        if matches.opt_str("initial-volume").unwrap().parse::<i32>().is_ok(){
            if matches.opt_str("initial-volume").unwrap().parse::<i32>().unwrap() < 0 {
                initial_volume = 0 as i32;
            }
            else if matches.opt_str("initial-volume").unwrap().parse::<i32>().unwrap() > 100{
                initial_volume = 0xFFFF as i32;
            }
            else{
                initial_volume = matches.opt_str("initial-volume").unwrap().parse::<i32>().unwrap()* 0xFFFF as i32 / 100 ;
            }

        }
        else {
            initial_volume = 0x8000 as i32;
        }
    }
    else{
        initial_volume = 0x8000 as i32;
        }
    info!("Volume \"{}\" !", initial_volume);

    let name = matches.opt_str("name").unwrap();
    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 credentials = {
        let cached_credentials = cache.as_ref().and_then(Cache::credentials);

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

    let session_config = {
        let device_id = librespot::core::session::device_id(&name);

        SessionConfig {
            user_agent: version::version_string(),
            device_id: device_id,
        }
    };

    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,
            onstart: matches.opt_str("onstart"),
            onstop: matches.opt_str("onstop"),
        }
    };

    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,
        }
    };

    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,
        mixer: mixer,
    }
}

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,
}

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: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
        };

        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).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 = 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);

                progress = true;
            }

            if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
                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 !progress {
                return Ok(Async::NotReady);
            }
        }
    }
}

fn main() {
    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|>


<commit_before>use std::fs::File;
use std::io::{self, Read, Write};
use std::path::Path;

mod constants;
mod memory;
mod cpu;
mod rom_loader;

use rom_loader::NesRom;
use cpu::Cpu6502;

fn main() {
  let mut f = File::open("/Users/brian/Desktop/roms/6502_functional_test.bin")
      .unwrap();

  let mut vec = Vec::<u8>::new();
  let bytes_read = f.read_to_end(&mut vec).unwrap();
  if bytes_read < 65536 {
    panic!("expected 16kb of data");
  }

  let mut cpu = Cpu6502::new();
  println!("Press enter to step");
  cpu.load(0, &vec, 0x400);
  let mut last_pc: u16 = 0x400;;

  loop {
    match cpu.step() {
      Ok(instr) => {
          if last_pc == cpu.registers.pc {
            println!("Detected Trap");
            memdump(cpu.memory.get_bytes());
            return;
          }
          last_pc = cpu.registers.pc;
          println!("{} {} cyc: {}", instr.to_string(), cpu.registers.to_string(),
        cpu.cycles);
      },
      Err(msg) => {
        println!("{}", msg);
        memdump(&cpu.memory.get_bytes());
        return;
      }
    }
  }
}

fn memdump(bytes: &[u8]) {
  println!("writing memory dump");
  let mut f = File::create("/Users/brian/Desktop/6502dump.bin").unwrap();
  f.write_all(bytes).unwrap();
}
<commit_msg>Ghetto detect success for functional test<commit_after>use std::fs::File;
use std::io::{self, Read, Write};
use std::path::Path;

mod constants;
mod memory;
mod cpu;
mod rom_loader;

use rom_loader::NesRom;
use cpu::Cpu6502;
const PC_START: u16 = 0x400;

fn main() {
  let mut f = File::open("/Users/brian/Desktop/roms/6502_functional_test.bin")
      .unwrap();

  let mut vec = Vec::<u8>::new();
  let bytes_read = f.read_to_end(&mut vec).unwrap();
  if bytes_read < 65536 {
    panic!("expected 16kb of data");
  }

  let mut cpu = Cpu6502::new();
  cpu.load(0, &vec, PC_START);
  let mut last_pc: u16 = PC_START;

  loop {
    match cpu.step() {
      Ok(instr) => {
          if cpu.registers.pc == 0x3399 {
            println!("******* SUCCESS ********");
            return;
          }

          if last_pc == cpu.registers.pc {
            println!("{} {} cyc: {}", instr.to_string(), cpu.registers.to_string(),
                cpu.cycles);
            println!("Detected Trap");
            memdump(cpu.memory.get_bytes());
            return;
          }

          last_pc = cpu.registers.pc;

          if cpu.cycles % 1000000 == 0 {
            println!("{} cycles", cpu.cycles);
          }
      },
      Err(msg) => {
        println!("{}", msg);
        memdump(&cpu.memory.get_bytes());
        return;
      }
    }
  }
}

fn memdump(bytes: &[u8]) {
  println!("writing memory dump");
  let mut f = File::create("/Users/brian/Desktop/6502dump.bin").unwrap();
  f.write_all(bytes).unwrap();
}
<|endoftext|>


<commit_before>//  rtracker: bittorrent tracker
//  Copyright (C) 2019  Justin Noah <justinnoah@gmail.com>
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License.
//
//  This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Affero General Public License for more details.
//
//   You should have received a copy of the GNU Affero General Public License
//   along with this program.  If not, see <https://www.gnu.org/licenses/>.

extern crate bincode;
extern crate chrono;
extern crate docopt;
extern crate env_logger;
extern crate ini;
#[macro_use]
extern crate log;
extern crate r2d2;
extern crate r2d2_sqlite;
extern crate rand;
extern crate rusqlite;
extern crate serde;
#[macro_use]
extern crate serde_derive;

use std::net::UdpSocket;
use std::thread;
use std::time::Duration;

use docopt::Docopt;

use config::ServerConfig;
use database::{db_connection_pool, db_init, db_prune};
use handler::handle_received_packet;

mod config;
mod database;
mod handler;
mod packet_data_types;
mod parse_packets;

static USAGE: &str = "
Usage: rtracker [-c <conf>]
       rtracker (--help)

Options:
    -h, --help          Show this message
    -c, --conf=<conf>   Configuration File [default: ]
";

#[derive(Deserialize)]
struct Args {
    flag_conf: String,
}

fn main() {
    env_logger::init();
    trace!("Logging initialized!");

    // parse commandline args
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    let scfg = ServerConfig::new(&args.flag_conf);
    debug!("addr: {:?}", scfg.address);

    // Let's first initialize the database.
    let sock = match UdpSocket::bind(&scfg.address) {
        Ok(s) => s,
        Err(e) => panic!("{}", e),
    };
    info!("Listening on: {}", &scfg.address);
    let pool = db_connection_pool(scfg.pool_size);
    db_init(pool.get().unwrap());
    debug!("DB initialized");

    // Spawn the database pruning thread
    let prune_pool = pool.clone();
    thread::spawn(move || {
        loop {
            // Every minute run the prune function.
            // As of this comment, db_prune selects all torrents / connections with a (now -
            // last_active) > 30 minutes. Thus, our 30 minute prune has a polling resolution of one
            // minute.
            let prune_delay = Duration::new(60u64, 0);
            thread::sleep(prune_delay);
            debug!("Prune the database!");
            // Prune the database
            let prune_conn = prune_pool.get().unwrap();
            db_prune(prune_conn);
        }
    });

    loop {
        // This will become flexible. Simply a starting point
        debug!("Init udp packet buffer");
        // UDP packet max
        let mut buf = [0u8; 1500];
        debug!("IOWait");
        let (amt, src) = sock.recv_from(&mut buf).unwrap();
        let tsock = sock.try_clone().unwrap();
        let tpool = pool.clone();
        if amt >= 16 {
            debug!("Spawn a new thread to handle the packet");
            thread::spawn(move || {
                let mut packet: Vec<u8> = buf.to_vec();
                packet.resize(amt, 0);
                handle_received_packet(packet, src, tsock, tpool.get().unwrap());
            });
        } else {
            debug!("Received a tiny packet (size: {}), ignoring", amt)
        }
    }
}
<commit_msg>A new thread is not needed for every new packet...<commit_after>//  rtracker: bittorrent tracker
//  Copyright (C) 2019  Justin Noah <justinnoah@gmail.com>
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License.
//
//  This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Affero General Public License for more details.
//
//   You should have received a copy of the GNU Affero General Public License
//   along with this program.  If not, see <https://www.gnu.org/licenses/>.

extern crate bincode;
extern crate chrono;
extern crate docopt;
extern crate env_logger;
extern crate ini;
#[macro_use]
extern crate log;
extern crate r2d2;
extern crate r2d2_sqlite;
extern crate rand;
extern crate rusqlite;
extern crate serde;
#[macro_use]
extern crate serde_derive;

use std::net::UdpSocket;
use std::thread;
use std::time::Duration;

use docopt::Docopt;

use config::ServerConfig;
use database::{db_connection_pool, db_init, db_prune};
use handler::handle_received_packet;

mod config;
mod database;
mod handler;
mod packet_data_types;
mod parse_packets;

static USAGE: &str = "
Usage: rtracker [-c <conf>]
       rtracker (--help)

Options:
    -h, --help          Show this message
    -c, --conf=<conf>   Configuration File [default: ]
";

#[derive(Deserialize)]
struct Args {
    flag_conf: String,
}

fn main() {
    env_logger::init();
    trace!("Logging initialized!");

    // parse commandline args
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    let scfg = ServerConfig::new(&args.flag_conf);
    debug!("addr: {:?}", scfg.address);

    // Initialize the database.
    let sock = match UdpSocket::bind(&scfg.address) {
        Ok(s) => s,
        Err(e) => panic!("{}", e),
    };

    info!("Listening on: {}", &scfg.address);
    let pool = db_connection_pool(scfg.pool_size);
    db_init(pool.get().unwrap());
    debug!("DB initialized");

    // Spawn the database pruning thread
    let prune_pool = pool.clone();
    thread::spawn(move || {
        loop {
            // Every minute run the prune function.
            // As of this comment, db_prune selects all torrents / connections with a (now -
            // last_active) > 30 minutes. Thus, the 30 minute prune has a polling resolution of one
            // minute.
            let prune_delay = Duration::new(60u64, 0);
            thread::sleep(prune_delay);
            debug!("Prune the database!");
            // Prune the database
            let prune_conn = prune_pool.get().unwrap();
            db_prune(prune_conn);
        }
    });

    loop {
        // This will become flexible. Simply a starting point
        debug!("Init udp packet buffer");
        // UDP packet max
        let mut buf = [0u8; 1500];
        debug!("IOWait");
        let (amt, src) = sock.recv_from(&mut buf).unwrap();
        let tsock = sock.try_clone().unwrap();
        let tpool = pool.clone();
        if amt >= 16 {
            let mut packet: Vec<u8> = buf.to_vec();
            packet.resize(amt, 0);
            handle_received_packet(packet, src, tsock, tpool.get().unwrap());
        } else {
            debug!("Received a tiny packet (size: {}), ignoring", amt)
        }
    }
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use piston::input::{ Button };
use piston::input::mouse::MouseButton;

pub struct Square {
    rotation: f64,   // Rotation for the square.
    position: [f64; 2],
    target: [f64; 2],
    selected: bool,
}

impl Square {
    fn new(position: [f64;2]) -> Square {
        Square {
            rotation: 0.0,
            position: position,
            target: position,
            selected: false
        }
    }
}

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    square: Square,
}

impl App {
    fn select(&mut self, position: &[f64;2]) {
        self.square.selected = position[0]< self.square.position[0]+25.0 &&
            position[0]> self.square.position[0]-25.0 &&
            position[1]< self.square.position[1]+25.0 &&
            position[1]> self.square.position[1]-25.0;
    }

    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
        const RED:   [f32; 4] = [1.0, 0.0, 0.0, 1.0];
        const BLUE:  [f32; 4] = [0.0, 0.0, 1.0, 1.0];

        let square = rectangle::square(0.0, 0.0, 50.0);
        let rotation = self.square.rotation;
        let pos = self.square.position;
        let selected = self.square.selected;

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(GREEN, gl);

            let transform = c.transform.trans(pos[0], pos[1])
                                       .rot_rad(rotation)
                                       .trans(-25.0, -25.0);

            // Draw a box rotating around the middle of the screen.
            if selected {
                rectangle(RED, square, transform, gl);
            } else {
                rectangle(BLUE, square, transform, gl);
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        // Rotate 2 radians per second.
        self.square.rotation += 2.0 * args.dt;

        let diff = [self.square.target[0]-self.square.position[0], self.square.target[1]-self.square.position[1]];
        self.square.position[0] += diff[0]/2.0*args.dt;
        self.square.position[1] += diff[1]/2.0*args.dt;
    }
}

fn main() {
    let opengl = OpenGL::_3_2;

    // Create an Glutin window.
    let window = Window::new(
        WindowSettings::new(
            "spinning-square",
            [200, 200]
        )
        .exit_on_esc(true)
    );

    // Create a new game and run it.
    let mut app = App {
        gl: GlGraphics::new(opengl),
        square: Square::new([50.0, 50.0]),
    };

    let mut cursor = [0.0,0.0];

    for e in window.events() {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
        if let Some(Button::Mouse(button)) = e.press_args() {
            match button {
                MouseButton::Left  => app.select(&cursor),
                MouseButton::Right => if app.square.selected { app.square.target = cursor },
                _ => println!("Pressed mouse button '{:?}'", button),
            }
        }
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });

        if let Some(u) = e.update_args() {
            app.update(&u);
        }
    }
}

<commit_msg>make App have a vector of Square's<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use piston::input::{ Button };
use piston::input::mouse::MouseButton;

pub struct Square {
    rotation: f64,   // Rotation for the square.
    position: [f64; 2],
    target: [f64; 2],
    selected: bool,
}

impl Square {
    fn new(position: [f64;2]) -> Square {
        println!("Create square at {:?}", position);
        Square {
            rotation: 0.0,
            position: position,
            target: position,
            selected: false
        }
    }
}

pub struct App {
    gl: GlGraphics, // OpenGL drawing backend.
    squares: Vec<Square>,
}

impl App {
    fn select(&mut self, position: &[f64;2]) {
        for s in &mut self.squares {
            s.selected = position[0]< s.position[0]+25.0 &&
                position[0]> s.position[0]-25.0 &&
                position[1]< s.position[1]+25.0 &&
                position[1]> s.position[1]-25.0;
        };
    }

    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
        const RED:   [f32; 4] = [1.0, 0.0, 0.0, 1.0];
        const BLUE:  [f32; 4] = [0.0, 0.0, 1.0, 1.0];
        let squares = &self.squares;

        self.gl.draw(args.viewport(), |c, gl| {
            // Clear the screen.
            clear(GREEN, gl);
            for s in squares.iter() {
                let square = rectangle::square(0.0, 0.0, 50.0);
                let transform = c.transform.trans(s.position[0], s.position[1])
                    .rot_rad(s.rotation)
                    .trans(-25.0, -25.0);

                // Draw the box RED if selected
                if s.selected {
                    rectangle(RED, square, transform, gl);
                } else {
                    rectangle(BLUE, square, transform, gl);
                }
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        for s in &mut self.squares {
            // Rotate 2 radians per second.
            s.rotation += 2.0 * args.dt;

            let diff = [s.target[0]-s.position[0], s.target[1]-s.position[1]];
            s.position[0] += diff[0]/2.0*args.dt;
            s.position[1] += diff[1]/2.0*args.dt;
        }
    }

    fn move_selected(&mut self, position: [f64;2]) {
        for s in &mut self.squares {
            if s.selected {
                s.target = position;
            }
        }
    }
}

fn main() {
    let opengl = OpenGL::_3_2;

    // Create an Glutin window.
    let window = Window::new(
        WindowSettings::new(
            "spinning-square",
            [200, 200]
        )
        .exit_on_esc(true)
    );

    // Create a new game and run it.
    let mut app = App {
        gl: GlGraphics::new(opengl),
        squares: vec![Square::new([50.0, 50.0]), Square::new([100.0, 100.0])],
    };

    let mut cursor = [0.0,0.0];

    for e in window.events() {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
        if let Some(Button::Mouse(button)) = e.press_args() {
            match button {
                MouseButton::Left  => app.select(&cursor),
                MouseButton::Right => app.move_selected(cursor),
                _ => println!("Pressed mouse button '{:?}'", button),
            }
        }
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });

        if let Some(u) = e.update_args() {
            app.update(&u);
        }
    }
}

<|endoftext|>


<commit_before>extern crate mmap;

use std::ptr;
use std::fs::OpenOptions;
use mmap::{MemoryMap, MapOption};

#[path="pi.rs"]
mod Pi;

fn main() {
    let mut gpio = Pi::Bcm2835Peripheral { addr_p: &Pi::GPIO_BASE, mem_fd: OpenOptions::new().create(true).open("temp.txt").unwrap(), map: MemoryMap::new(1024, &[]).unwrap(), addr: ptr::null_mut()};
    gpio.map_peripheral();
}
<commit_msg>reformat<commit_after>extern crate mmap;

use std::ptr;
use std::fs::OpenOptions;
use mmap::{MemoryMap, MapOption};

#[path="pi.rs"]
mod Pi;

fn main() {
    let mut gpio = Pi::Bcm2835Peripheral {
                    addr_p: &Pi::GPIO_BASE,
                    mem_fd: OpenOptions::new().create(true).open("temp.txt").unwrap(),
                    map: MemoryMap::new(1024, &[]).unwrap(),
                    addr: ptr::null_mut()
    };
    gpio.map_peripheral();
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate stats;

extern crate clap;
use clap::{Arg, App};

mod http_status;
mod log_parser;
use log_parser::parse_line;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::*;
mod filter;
mod analyzer;
mod render;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

#[derive(Debug)]
#[derive(PartialEq)]
struct RequestLogAnalyzerArgs {
    filename: String,
    conditions: filter::FilterConditions,
    graphite_server: Option<String>,
    graphite_port: Option<u16>,
    graphite_prefix: Option<String>,
}

fn parse_args<'a, T>(args: T) -> Result<RequestLogAnalyzerArgs, &'a str>
    where T: IntoIterator<Item = String>
{
    let app = App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(false)
            .help("Log file to analyze, defaults to stdin")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches_from(args);

    let filename = app.value_of("filename").unwrap_or("-").to_string();

    let conditions = filter::FilterConditions {
        include_terms: match app.value_of("include_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None,
        },
        exclude_terms: match app.value_of("exclude_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None,
        },
        latest_time: match app.value_of("time_filter_minutes") {
            Some(minutes) => {
                Some(Duration::minutes(minutes.parse().expect("Minutes value must be numeric")))
            }
            None => None,
        },
    };

    let graphite_server = match app.value_of("graphite-server") {
        Some(value) => Some(String::from(value)),
        None => None,
    };

    let graphite_port: Option<u16> = match app.value_of("graphite-port") {
        Some(value) => Some(value.parse().expect("Port number must be numeric.")),
        None => None,
    };

    let graphite_prefix = match app.value_of("graphite-prefix") {
        Some(value) => Some(String::from(value)),
        None => None,
    };

    Ok(RequestLogAnalyzerArgs {
        filename: filename,
        conditions: conditions,
        graphite_server: graphite_server,
        graphite_port: graphite_port,
        graphite_prefix: graphite_prefix,
    })
}

fn main() {
    let args = parse_args(env::args()).unwrap();

    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).unwrap()),
    };

    let (times, first_request) = extract_times(input, &args.conditions);

    let result = analyzer::analyze(&times);

    match result {
        Some(result) => {
            if args.graphite_server.is_some() {
                let stream = TcpStream::connect((args.graphite_server.unwrap().as_ref(),
                                                 args.graphite_port.unwrap()))
                    .expect("Could not connect to the Graphite server");
                let timezone = first_request.unwrap().time.timezone();

                render::render_graphite(result,
                                        UTC::now().with_timezone(&timezone),
                                        args.graphite_prefix,
                                        stream);
            } else {
                render::render_terminal(result);
            }
        }
        None => println_stderr!("No matching log lines in file."),
    }
}

fn extract_times(input: Box<io::Read>, conditions: &filter::FilterConditions) -> (Vec<i64>, Option<Request>) {
    let reader = io::BufReader::new(input);
    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut times: Vec<i64> = Vec::new();

    // We need to store 1 Request in order to determine the timezone later
    let mut first_request: Option<Request> = None;

    for line in lines {
        let line_value = &line.unwrap();

        let event = parse_line(line_value).unwrap();

        match event {
            LogEvent::Request(request) => {
                if first_request.is_none() {
                    first_request = Some(request.clone());
                }
                requests.push(request)
            }
            LogEvent::Response(response) => responses.push(response),
        }

        let pairs = extract_matching_request_response_pairs(&mut requests, &mut responses);

        let mut new_times: Vec<i64> = pairs.iter()
            .filter(|pair| filter::matches_filter(&pair, conditions))
            .map(|pair| pair.response.response_time.num_milliseconds())
            .collect();

        times.append(&mut new_times);
    }

    (times, first_request)
}

#[test]
fn test_parse_args_default() {
    let raw_args = vec!["request_log_analyzer".to_string()];

    let expected = RequestLogAnalyzerArgs {
        filename: String::from("-"),
        conditions: filter::FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: None,
        },
        graphite_server: None,
        graphite_port: Some(2003),
        graphite_prefix: None,
    };

    let result = parse_args(raw_args).unwrap();

    assert_eq!(result, expected);
}

#[test]
fn test_parse_args_all() {
    let raw_args = vec![String::from("request_log_analyzer"),
                        String::from("--include"), String::from("one"),
                        String::from("--exclude"), String::from("this other"),
                        String::from("-t"), String::from("10"),
                        String::from("my-logfile.log"),
                        String::from("--graphite-server"), String::from("localhost"),
                        String::from("--graphite-port"), String::from("4000"),
                        String::from("--graphite-prefix"), String::from("prod"),
                        ];

    let expected = RequestLogAnalyzerArgs {
        filename: String::from("my-logfile.log"),
        conditions: filter::FilterConditions {
            include_terms: Some(vec![String::from("one")]),
            exclude_terms: Some(vec![String::from("this other")]),
            latest_time: Some(Duration::minutes(10)),
        },
        graphite_server: Some(String::from("localhost")),
        graphite_port: Some(4000),
        graphite_prefix: Some(String::from("prod")),
    };

    let result = parse_args(raw_args).unwrap();

    assert_eq!(result, expected);
}

#[test]
fn test_extract_times() {
    let conditions = filter::FilterConditions {
        include_terms: None,
        exclude_terms: None,
        latest_time: None,
    };

    let (times, first_request) = extract_times(Box::new(File::open("src/test/simple-1.log").unwrap()), &conditions);

    assert_eq!(times, vec![7, 10]);
    assert_eq!(first_request.unwrap().id, 1);
}
<commit_msg>Gracefully handle broken lines<commit_after>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate stats;

extern crate clap;
use clap::{Arg, App};

mod http_status;
mod log_parser;
use log_parser::parse_line;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::*;
mod filter;
mod analyzer;
mod render;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

#[derive(Debug)]
#[derive(PartialEq)]
struct RequestLogAnalyzerArgs {
    filename: String,
    conditions: filter::FilterConditions,
    graphite_server: Option<String>,
    graphite_port: Option<u16>,
    graphite_prefix: Option<String>,
}

fn parse_args<'a, T>(args: T) -> Result<RequestLogAnalyzerArgs, &'a str>
    where T: IntoIterator<Item = String>
{
    let app = App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(false)
            .help("Log file to analyze, defaults to stdin")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches_from(args);

    let filename = app.value_of("filename").unwrap_or("-").to_string();

    let conditions = filter::FilterConditions {
        include_terms: match app.value_of("include_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None,
        },
        exclude_terms: match app.value_of("exclude_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None,
        },
        latest_time: match app.value_of("time_filter_minutes") {
            Some(minutes) => {
                Some(Duration::minutes(minutes.parse().expect("Minutes value must be numeric")))
            }
            None => None,
        },
    };

    let graphite_server = match app.value_of("graphite-server") {
        Some(value) => Some(String::from(value)),
        None => None,
    };

    let graphite_port: Option<u16> = match app.value_of("graphite-port") {
        Some(value) => Some(value.parse().expect("Port number must be numeric.")),
        None => None,
    };

    let graphite_prefix = match app.value_of("graphite-prefix") {
        Some(value) => Some(String::from(value)),
        None => None,
    };

    Ok(RequestLogAnalyzerArgs {
        filename: filename,
        conditions: conditions,
        graphite_server: graphite_server,
        graphite_port: graphite_port,
        graphite_prefix: graphite_prefix,
    })
}

fn main() {
    let args = parse_args(env::args()).unwrap();

    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).unwrap()),
    };

    let (times, first_request) = extract_times(input, &args.conditions);

    let result = analyzer::analyze(&times);

    match result {
        Some(result) => {
            if args.graphite_server.is_some() {
                let stream = TcpStream::connect((args.graphite_server.unwrap().as_ref(),
                                                 args.graphite_port.unwrap()))
                    .expect("Could not connect to the Graphite server");
                let timezone = first_request.unwrap().time.timezone();

                render::render_graphite(result,
                                        UTC::now().with_timezone(&timezone),
                                        args.graphite_prefix,
                                        stream);
            } else {
                render::render_terminal(result);
            }
        }
        None => println_stderr!("No matching log lines in file."),
    }
}

fn extract_times(input: Box<io::Read>, conditions: &filter::FilterConditions) -> (Vec<i64>, Option<Request>) {
    let reader = io::BufReader::new(input);
    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut times: Vec<i64> = Vec::new();

    // We need to store 1 Request in order to determine the timezone later
    let mut first_request: Option<Request> = None;

    for line in lines {
        let line_value = &line.unwrap();

        let parsed_line = parse_line(line_value);

        match parsed_line {
            Ok(event) => {
                match event {
                    LogEvent::Request(request) => {
                        if first_request.is_none() {
                            first_request = Some(request.clone());
                        }
                        requests.push(request)
                    }
                    LogEvent::Response(response) => responses.push(response),
                }

                let pairs = extract_matching_request_response_pairs(&mut requests, &mut responses);

                let mut new_times: Vec<i64> = pairs.iter()
                    .filter(|pair| filter::matches_filter(&pair, conditions))
                    .map(|pair| pair.response.response_time.num_milliseconds())
                    .collect();

                times.append(&mut new_times);
            },
            Err(err) => println_stderr!("{}", err),
        }
    }
    (times, first_request)
}

#[test]
fn test_parse_args_default() {
    let raw_args = vec!["request_log_analyzer".to_string()];

    let expected = RequestLogAnalyzerArgs {
        filename: String::from("-"),
        conditions: filter::FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: None,
        },
        graphite_server: None,
        graphite_port: Some(2003),
        graphite_prefix: None,
    };

    let result = parse_args(raw_args).unwrap();

    assert_eq!(result, expected);
}

#[test]
fn test_parse_args_all() {
    let raw_args = vec![String::from("request_log_analyzer"),
                        String::from("--include"), String::from("one"),
                        String::from("--exclude"), String::from("this other"),
                        String::from("-t"), String::from("10"),
                        String::from("my-logfile.log"),
                        String::from("--graphite-server"), String::from("localhost"),
                        String::from("--graphite-port"), String::from("4000"),
                        String::from("--graphite-prefix"), String::from("prod"),
                        ];

    let expected = RequestLogAnalyzerArgs {
        filename: String::from("my-logfile.log"),
        conditions: filter::FilterConditions {
            include_terms: Some(vec![String::from("one")]),
            exclude_terms: Some(vec![String::from("this other")]),
            latest_time: Some(Duration::minutes(10)),
        },
        graphite_server: Some(String::from("localhost")),
        graphite_port: Some(4000),
        graphite_prefix: Some(String::from("prod")),
    };

    let result = parse_args(raw_args).unwrap();

    assert_eq!(result, expected);
}

#[test]
fn test_extract_times() {
    let conditions = filter::FilterConditions {
        include_terms: None,
        exclude_terms: None,
        latest_time: None,
    };

    let (times, first_request) = extract_times(Box::new(File::open("src/test/simple-1.log").unwrap()), &conditions);

    assert_eq!(times, vec![7, 10]);
    assert_eq!(first_request.unwrap().id, 1);
}

#[test]
fn test_extract_times_ignore_broken_lines() {
    let conditions = filter::FilterConditions {
        include_terms: None,
        exclude_terms: None,
        latest_time: None,
    };

    let (times, first_request) = extract_times(Box::new(File::open("src/test/broken.log").unwrap()), &conditions);

    assert_eq!(times, vec![7]);
    assert_eq!(first_request.unwrap().id, 1);
}
<|endoftext|>


<commit_before>// TODO: many items from tokio-core::io have been deprecated in favour of tokio-io
#![allow(deprecated)]

#[cfg(debug_assertions)]
#[macro_use] extern crate log;
#[cfg(debug_assertions)]
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_signal;

#[cfg(debug_assertions)]
use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
#[cfg(debug_assertions)]
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_core::io::IoStream;
use std::mem;

use librespot::spirc::{Spirc, SpircTask};
use librespot::authentication::{get_credentials, Credentials};
#[cfg(not(target_os="windows"))]
use librespot::authentication::discovery::{discovery, DiscoveryStream};
use librespot::audio_backend;
use librespot::cache::Cache;
use librespot::player::Player;
use librespot::session::{Bitrate, Config, Session};
use librespot::mixer;
use librespot::util::SpotifyId;

const VERSION: &'static str = concat!(env!("CARGO_PKG_NAME"), " v", env!("CARGO_PKG_VERSION"));

#[cfg(target_os="windows")]
const NULLDEVICE: &'static str = "NUL";
#[cfg(not(target_os="windows"))]
const NULLDEVICE: &'static str = "/dev/null";

fn usage(program: &str, opts: &getopts::Options) -> String {
	println!("{}", VERSION.to_string());

	let brief = format!("Usage: {} [options]", program);
	opts.usage(&brief)
}

#[cfg(debug_assertions)]
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=error,librespot=warn");
			}
			builder.init().unwrap();
		}
	}
}

#[derive(Clone)]
struct Setup {
	name: String,
	cache: Option<Cache>,
	config: Config,
	credentials: Option<Credentials>,
	authenticate: bool,
	enable_discovery: bool,

	get_token: bool,
	client_id: Option<String>,
	scope: Option<String>,

	single_track: Option<String>,
	start_position: u32,
}

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("", "enable-audio-cache", "Enable caching of the audio data.")
		.optflag("", "disable-audio-cache", "(Only here fore compatibility with librespot - audio cache is disabled by default).")
		.reqopt("n", "name", "Device name", "NAME")
		.optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 320.", "BITRATE")
		.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
		.optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
		.optopt("", "onchange", "Run PROGRAM when playback changes (new track, seeking etc.).", "PROGRAM")
		.optopt("", "player-mac", "MAC address of the Squeezebox to be controlled", "MAC")
		.optopt("", "lms", "hostname and port of Logitech Media Server instance (eg. localhost:9000)", "LMS")
		.optopt("", "single-track", "Play a single track ID and exit.", "ID")
		.optopt("", "start-position", "Position (in ms) where playback should be started. Only valid with the --single-track option.", "STARTPOSITION")
		.optopt("u", "username", "Username to sign in with", "USERNAME")
		.optopt("p", "password", "Password", "PASSWORD")
		.optflag("a", "authenticate", "Authenticate given username and password. Make sure you define a cache folder to store credentials.")
		.optflag("", "disable-discovery", "Disable discovery mode")
		.optflag("t", "get-token", "Get oauth token to be used with the web API etc.")
		.optopt("i", "client-id", "A Spotify client_id to be used to get the oauth token. Required with the --get-token request.", "CLIENT_ID")
		.optopt("", "scope", "The scopes you want to have access to with the oauth token.", "SCOPE")
		.optflag("x", "check", "Run quick internal check");

	#[cfg(debug_assertions)]
	opts.optflag("v", "verbose", "Enable verbose output");

	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);
		}
	};

	if matches.opt_present("check") {
		println!("ok {}", VERSION.to_string());
		exit(1);
	}

	#[cfg(debug_assertions)]
	{
		let verbose = matches.opt_present("verbose");
		setup_logging(verbose);
	}

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

	let name = matches.opt_str("name").unwrap();
	let device_id = librespot::session::device_id(&name);

	let use_audio_cache = matches.opt_present("enable-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 cached_credentials = cache.as_ref().and_then(Cache::credentials);

	let credentials = get_credentials(matches.opt_str("username"), matches.opt_str("password"), cached_credentials);

	let authenticate = matches.opt_present("authenticate");

#[cfg(not(target_os="windows"))]
	let enable_discovery = !matches.opt_present("disable-discovery");
#[cfg(target_os="windows")]
	let enable_discovery = false;

	let start_position = matches.opt_str("start-position")
		.unwrap_or("0".to_string())
		.parse().unwrap_or(0.0);

	let config = Config {
		user_agent: VERSION.to_string(),
		device_id: device_id,
		bitrate: bitrate,
		onstart: matches.opt_str("onstart"),
		onstop: matches.opt_str("onstop"),
		onchange: matches.opt_str("onchange"),
		mac: matches.opt_str("player-mac"),
		lms: matches.opt_str("lms"),
  };

	Setup {
		name: name,
		cache: cache,
		config: config,
		credentials: credentials,
		authenticate: authenticate,
		enable_discovery: enable_discovery,

		get_token: matches.opt_present("get-token"),
		client_id: matches.opt_str("client-id"),
		scope: matches.opt_str("scope"),

		single_track: matches.opt_str("single-track"),
		start_position: (start_position * 1000.0) as u32,
	}
}

struct Main {
	name: String,
	cache: Option<Cache>,
	config: Config,
	handle: Handle,

#[cfg(not(target_os="windows"))]
	discovery: Option<DiscoveryStream>,
#[cfg(target_os="windows")]
	discovery: Option<String>,
	signal: IoStream<()>,

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

	player: Option<Player>,

	shutdown: bool,
	authenticate: bool,
}

impl Main {
	fn new(
		handle: Handle,
		name: String,
		config: Config,
		cache: Option<Cache>,
		authenticate: bool,
	) -> Main
	{
		Main {
			handle: handle.clone(),
			name: name,
			cache: cache,
			config: config,

			connect: Box::new(futures::future::empty()),
			discovery: None,
			spirc: None,
			spirc_task: None,

			player: None,

			shutdown: false,
			authenticate: authenticate,
			signal: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
		}
	}

#[cfg(not(target_os="windows"))]
	fn discovery(&mut self) {
		let device_id = self.config.device_id.clone();
		let name = self.name.clone();

		self.discovery = Some(discovery(&self.handle, name, device_id).unwrap());
	}

	fn credentials(&mut self, credentials: Credentials) {
		let config = self.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;

#[cfg(not(target_os="windows"))] {
			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() {
				if self.authenticate {
					if !self.shutdown {
						if let Some(ref spirc) = self.spirc {
							spirc.shutdown();
						}
						self.shutdown = true;
					} else {
						return Ok(Async::Ready(()));
					}
				}
				else {
					self.connect = Box::new(futures::future::empty());

					let mixer = (mixer::find(Some("softvol")).unwrap())();

					let audio_filter = mixer.get_audio_filter();
					let backend = audio_backend::find(None).unwrap();
					let player = Player::new(session.clone(), audio_filter, move || {
						(backend)(Some(NULLDEVICE.to_string()))
					});

					self.player = Some(player.clone());

					let (spirc, spirc_task) = Spirc::new(self.name.clone(), session, player, mixer);
					self.spirc = Some(spirc);
					self.spirc_task = Some(spirc_task);
				}

				progress = true;
			}

			if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
				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 !progress {
				return Ok(Async::NotReady);
			}
		}
	}
}

fn main() {
	let mut core = Core::new().unwrap();
	let handle = core.handle();

	let args: Vec<String> = std::env::args().collect();
	let Setup { name, config, cache, enable_discovery, credentials, authenticate, get_token, client_id, scope, single_track, start_position } = setup(&args);

	if let Some(ref track_id) = single_track {
		match credentials {
			Some(credentials) => {
				let backend = audio_backend::find(None).unwrap();

				let track = SpotifyId::from_base62(
									track_id.replace("spotty://", "")
									.replace("spotify://", "")
									.replace("spotify:", "")
									.replace("track:", "")
									.as_str());

				let session = core.run(Session::connect(config, credentials, cache.clone(), handle)).unwrap();

				let player = Player::new(session.clone(), None, move || (backend)(None));

				core.run(player.load(track, true, start_position)).unwrap();
			}
			None => {
				println!("Missing credentials");
			}
		}
	}
	else if authenticate && !enable_discovery {
		core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
		println!("authorized");
	}
	else if get_token {
		if let Some(client_id) = client_id {
			let session = core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
			let scope = scope.unwrap_or("user-read-private,playlist-read-private,playlist-read-collaborative,playlist-modify-public,playlist-modify-private,user-follow-modify,user-follow-read,user-library-read,user-library-modify,user-top-read,user-read-recently-played".to_string());
			let url = format!("hm://keymaster/token/authenticated?client_id={}&scope={}", client_id, scope);
			core.run(session.mercury().get(url).map(move |response| {
				let data = response.payload.first().expect("Empty payload");
				let token = String::from_utf8(data.clone()).unwrap();
				println!("{}", token);
			}).boxed()).unwrap();
		}
		else {
			println!("Use --client-id to provide a CLIENT_ID");
		}
	}
	else {
		let mut task = Main::new(handle, name, config, cache, authenticate);
		if enable_discovery {
#[cfg(not(target_os="windows"))]
			task.discovery();
		}
		if let Some(credentials) = credentials {
			task.credentials(credentials);
		}

		core.run(task).unwrap()
	}
}

<commit_msg>Get rid of unused player property<commit_after>// TODO: many items from tokio-core::io have been deprecated in favour of tokio-io
#![allow(deprecated)]

#[cfg(debug_assertions)]
#[macro_use] extern crate log;
#[cfg(debug_assertions)]
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_signal;

#[cfg(debug_assertions)]
use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
#[cfg(debug_assertions)]
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_core::io::IoStream;
use std::mem;

use librespot::spirc::{Spirc, SpircTask};
use librespot::authentication::{get_credentials, Credentials};
#[cfg(not(target_os="windows"))]
use librespot::authentication::discovery::{discovery, DiscoveryStream};
use librespot::audio_backend;
use librespot::cache::Cache;
use librespot::player::Player;
use librespot::session::{Bitrate, Config, Session};
use librespot::mixer;
use librespot::util::SpotifyId;

const VERSION: &'static str = concat!(env!("CARGO_PKG_NAME"), " v", env!("CARGO_PKG_VERSION"));

#[cfg(target_os="windows")]
const NULLDEVICE: &'static str = "NUL";
#[cfg(not(target_os="windows"))]
const NULLDEVICE: &'static str = "/dev/null";

fn usage(program: &str, opts: &getopts::Options) -> String {
	println!("{}", VERSION.to_string());

	let brief = format!("Usage: {} [options]", program);
	opts.usage(&brief)
}

#[cfg(debug_assertions)]
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=error,librespot=warn");
			}
			builder.init().unwrap();
		}
	}
}

#[derive(Clone)]
struct Setup {
	name: String,
	cache: Option<Cache>,
	config: Config,
	credentials: Option<Credentials>,
	authenticate: bool,
	enable_discovery: bool,

	get_token: bool,
	client_id: Option<String>,
	scope: Option<String>,

	single_track: Option<String>,
	start_position: u32,
}

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("", "enable-audio-cache", "Enable caching of the audio data.")
		.optflag("", "disable-audio-cache", "(Only here fore compatibility with librespot - audio cache is disabled by default).")
		.reqopt("n", "name", "Device name", "NAME")
		.optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 320.", "BITRATE")
		.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
		.optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
		.optopt("", "onchange", "Run PROGRAM when playback changes (new track, seeking etc.).", "PROGRAM")
		.optopt("", "player-mac", "MAC address of the Squeezebox to be controlled", "MAC")
		.optopt("", "lms", "hostname and port of Logitech Media Server instance (eg. localhost:9000)", "LMS")
		.optopt("", "single-track", "Play a single track ID and exit.", "ID")
		.optopt("", "start-position", "Position (in ms) where playback should be started. Only valid with the --single-track option.", "STARTPOSITION")
		.optopt("u", "username", "Username to sign in with", "USERNAME")
		.optopt("p", "password", "Password", "PASSWORD")
		.optflag("a", "authenticate", "Authenticate given username and password. Make sure you define a cache folder to store credentials.")
		.optflag("", "disable-discovery", "Disable discovery mode")
		.optflag("t", "get-token", "Get oauth token to be used with the web API etc.")
		.optopt("i", "client-id", "A Spotify client_id to be used to get the oauth token. Required with the --get-token request.", "CLIENT_ID")
		.optopt("", "scope", "The scopes you want to have access to with the oauth token.", "SCOPE")
		.optflag("x", "check", "Run quick internal check");

	#[cfg(debug_assertions)]
	opts.optflag("v", "verbose", "Enable verbose output");

	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);
		}
	};

	if matches.opt_present("check") {
		println!("ok {}", VERSION.to_string());
		exit(1);
	}

	#[cfg(debug_assertions)]
	{
		let verbose = matches.opt_present("verbose");
		setup_logging(verbose);
	}

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

	let name = matches.opt_str("name").unwrap();
	let device_id = librespot::session::device_id(&name);

	let use_audio_cache = matches.opt_present("enable-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 cached_credentials = cache.as_ref().and_then(Cache::credentials);

	let credentials = get_credentials(matches.opt_str("username"), matches.opt_str("password"), cached_credentials);

	let authenticate = matches.opt_present("authenticate");

#[cfg(not(target_os="windows"))]
	let enable_discovery = !matches.opt_present("disable-discovery");
#[cfg(target_os="windows")]
	let enable_discovery = false;

	let start_position = matches.opt_str("start-position")
		.unwrap_or("0".to_string())
		.parse().unwrap_or(0.0);

	let config = Config {
		user_agent: VERSION.to_string(),
		device_id: device_id,
		bitrate: bitrate,
		onstart: matches.opt_str("onstart"),
		onstop: matches.opt_str("onstop"),
		onchange: matches.opt_str("onchange"),
		mac: matches.opt_str("player-mac"),
		lms: matches.opt_str("lms"),
  };

	Setup {
		name: name,
		cache: cache,
		config: config,
		credentials: credentials,
		authenticate: authenticate,
		enable_discovery: enable_discovery,

		get_token: matches.opt_present("get-token"),
		client_id: matches.opt_str("client-id"),
		scope: matches.opt_str("scope"),

		single_track: matches.opt_str("single-track"),
		start_position: (start_position * 1000.0) as u32,
	}
}

struct Main {
	name: String,
	cache: Option<Cache>,
	config: Config,
	handle: Handle,

#[cfg(not(target_os="windows"))]
	discovery: Option<DiscoveryStream>,
#[cfg(target_os="windows")]
	discovery: Option<String>,
	signal: IoStream<()>,

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

	shutdown: bool,
	authenticate: bool,
}

impl Main {
	fn new(
		handle: Handle,
		name: String,
		config: Config,
		cache: Option<Cache>,
		authenticate: bool,
	) -> Main
	{
		Main {
			handle: handle.clone(),
			name: name,
			cache: cache,
			config: config,

			connect: Box::new(futures::future::empty()),
			discovery: None,
			spirc: None,
			spirc_task: None,

			shutdown: false,
			authenticate: authenticate,
			signal: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
		}
	}

#[cfg(not(target_os="windows"))]
	fn discovery(&mut self) {
		let device_id = self.config.device_id.clone();
		let name = self.name.clone();

		self.discovery = Some(discovery(&self.handle, name, device_id).unwrap());
	}

	fn credentials(&mut self, credentials: Credentials) {
		let config = self.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;

#[cfg(not(target_os="windows"))] {
			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() {
				if self.authenticate {
					if !self.shutdown {
						if let Some(ref spirc) = self.spirc {
							spirc.shutdown();
						}
						self.shutdown = true;
					} else {
						return Ok(Async::Ready(()));
					}
				}
				else {
					self.connect = Box::new(futures::future::empty());

					let mixer = (mixer::find(Some("softvol")).unwrap())();

					let audio_filter = mixer.get_audio_filter();
					let backend = audio_backend::find(None).unwrap();
					let player = Player::new(session.clone(), audio_filter, move || {
						(backend)(Some(NULLDEVICE.to_string()))
					});

					let (spirc, spirc_task) = Spirc::new(self.name.clone(), session, player, mixer);
					self.spirc = Some(spirc);
					self.spirc_task = Some(spirc_task);
				}

				progress = true;
			}

			if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
				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 !progress {
				return Ok(Async::NotReady);
			}
		}
	}
}

fn main() {
	let mut core = Core::new().unwrap();
	let handle = core.handle();

	let args: Vec<String> = std::env::args().collect();
	let Setup { name, config, cache, enable_discovery, credentials, authenticate, get_token, client_id, scope, single_track, start_position } = setup(&args);

	if let Some(ref track_id) = single_track {
		match credentials {
			Some(credentials) => {
				let backend = audio_backend::find(None).unwrap();

				let track = SpotifyId::from_base62(
									track_id.replace("spotty://", "")
									.replace("spotify://", "")
									.replace("spotify:", "")
									.replace("track:", "")
									.as_str());

				let session = core.run(Session::connect(config, credentials, cache.clone(), handle)).unwrap();

				let player = Player::new(session.clone(), None, move || (backend)(None));

				core.run(player.load(track, true, start_position)).unwrap();
			}
			None => {
				println!("Missing credentials");
			}
		}
	}
	else if authenticate && !enable_discovery {
		core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
		println!("authorized");
	}
	else if get_token {
		if let Some(client_id) = client_id {
			let session = core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
			let scope = scope.unwrap_or("user-read-private,playlist-read-private,playlist-read-collaborative,playlist-modify-public,playlist-modify-private,user-follow-modify,user-follow-read,user-library-read,user-library-modify,user-top-read,user-read-recently-played".to_string());
			let url = format!("hm://keymaster/token/authenticated?client_id={}&scope={}", client_id, scope);
			core.run(session.mercury().get(url).map(move |response| {
				let data = response.payload.first().expect("Empty payload");
				let token = String::from_utf8(data.clone()).unwrap();
				println!("{}", token);
			}).boxed()).unwrap();
		}
		else {
			println!("Use --client-id to provide a CLIENT_ID");
		}
	}
	else {
		let mut task = Main::new(handle, name, config, cache, authenticate);
		if enable_discovery {
#[cfg(not(target_os="windows"))]
			task.discovery();
		}
		if let Some(credentials) = credentials {
			task.credentials(credentials);
		}

		core.run(task).unwrap()
	}
}

<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

extern crate ncurses;
use ncurses::*;

//extern crate getopts;
//use getopts::Options;

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    const SPALTEN:usize = 16;
//    let mut command = vec![]; // If command is typed by user, store it
//    command.push(":asdf");
    let mut command = String::new();

    initscr(); //start ncursesw
    let screenheight : usize = getmaxy(stdscr) as usize;
    cbreak();  //ctrl+z and fg works with this
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    /*
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("f", "", "set file name", "NAME");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    }
    let path = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    };*/

    let path = if args.len() > 1 {
        printw(&format!("Opening file {} (TODO)", args[1]));
        args[1].clone()
    } else {
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

//TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
//        Err(why) => printw("oh... file not opened..."),
        Err(why) => panic!("couldn't open {}: {}", display,
            Error::description(&why)),
        Ok(file) => file,
    };

//    file.read_to_end(&mut buffer).ok().expect("File could not be read.");
    file.read_to_end(&mut buffer).ok().expect("File could not be read.");

    let mut mode = 0; // 0 Command mode, 1 replace next char, 2 type a command, TODO 3 Insert

    draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);

    let mut key;
//    key = getch();
//    printw(&format!("{:?}", key));

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => if cursorpos != 0 {cursorpos-=1}, //left is "-1"
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => if cursorpos != buffer.len()-1 {cursorpos+=1}, //right is "+1"
                 48 => cursorpos -= cursorpos%16, //start of line is "to start"
                 36 => if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)} //dollar is "to end"
                        else {cursorpos = buffer.len()-1},
                114 => mode = 1, //r replaces the next char
                 58 => mode = 2, // ":" TODO use screenheight;
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 { // r was pressed so replace next char
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8; mode = 0 },
                27 => mode = 0,
                _ => (),
            }
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); }, //TODO check this
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            mode = 0;
                        }
                        if (command == "wq".to_string()) || (command == "q".to_string()) {
                            ragequitnow = 1;
                        }

                    },
                _ => (),
            }
        }
 //       printw(&format!("{:?}", key));
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);
    }

    refresh();
    endwin();
}

fn draw(buffer:&Vec<u8>, cursorpos:usize, spalten:usize, maxzeilen:usize, mode:usize, command:&String) {
//    let zeilen = buffer.len() / spalten;
    erase();

    let mut zeilen = buffer.len() / spalten;
    if zeilen >= maxzeilen-1 { // Last line reserved for Status/Commands/etc (Like in vim)
        zeilen = maxzeilen-2;
    }

    for z in 0 .. zeilen+1 {
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
            if z*spalten+s < buffer.len() { printw(&format!("{:02X} ", buffer[z*spalten+s]) ); }
                else {printw("-- "); }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw(" "); // Space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
                if z*spalten+s < buffer.len() {
                    if let c @ 32...126 = buffer[z*spalten+s] {
                        if c as char == '%' {
                            printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                        } else {
                            printw(&format!("{}", c as char) );
                        }
                    }
                    else {printw(&format!(".") );}
                }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw("\n");
    }
//TODO: use maxzeilen to draw the command (if in mode == 2) on last line of terminal
//show that the mode is 2 by adding a ':'
    if mode == 2 {
        printw(":");
    }
    printw(&format!("{}", command));
}
<commit_msg>Add bad command handling<commit_after>use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

extern crate ncurses;
use ncurses::*;

//extern crate getopts;
//use getopts::Options;

fn main() {
    let mut buffer = vec![];
    let mut cursorpos:usize = 0;
    const SPALTEN:usize = 16;
//    let mut command = vec![]; // If command is typed by user, store it
//    command.push(":asdf");
    let mut command = String::new();

    initscr(); //start ncursesw
    let screenheight : usize = getmaxy(stdscr) as usize;
    cbreak();  //ctrl+z and fg works with this
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    /*
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("f", "", "set file name", "NAME");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    }
    let path = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let brief = format!("Usage: {} FILE [options]", program);
            print!("{}", opts.usage(&brief));
        return;
    };*/

    let path = if args.len() > 1 {
        printw(&format!("Opening file {} (TODO)", args[1]));
        args[1].clone()
    } else {
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

//TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
//        Err(why) => printw("oh... file not opened..."),
        Err(why) => panic!("couldn't open {}: {}", display,
            Error::description(&why)),
        Ok(file) => file,
    };

//    file.read_to_end(&mut buffer).ok().expect("File could not be read.");
    file.read_to_end(&mut buffer).ok().expect("File could not be read.");

    let mut mode = 0; // 0 Command mode, 1 replace next char, 2 type a command, TODO 3 Insert

    draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);

    let mut key;
//    key = getch();
//    printw(&format!("{:?}", key));

    let mut ragequitnow = 0;
    while ragequitnow == 0 {
        key = getch();
        if mode == 0 { // movement mode
            match key {
                104 => if cursorpos != 0 {cursorpos-=1}, //left is "-1"
                106 => if cursorpos+SPALTEN < buffer.len() {cursorpos+=SPALTEN} //down is "+16"
                        else {cursorpos=buffer.len()-1}, //down if on last line is "to end"
                107 => if cursorpos >= SPALTEN {cursorpos-=SPALTEN}, //up is "-16"
                108 => if cursorpos != buffer.len()-1 {cursorpos+=1}, //right is "+1"
                 48 => cursorpos -= cursorpos%16, //start of line is "to start"
                 36 => if cursorpos-(cursorpos%16)+(SPALTEN-1) < buffer.len() {cursorpos = cursorpos-(cursorpos%16)+(SPALTEN-1)} //dollar is "to end"
                        else {cursorpos = buffer.len()-1},
                114 => mode = 1, //r replaces the next char
                 58 => {mode = 2; // ":" TODO use screenheight;
                            command.clear();},
//                 63 => printw("{:?}", asdf), //TODO: print available key helpfile
                _ => (),
            }
        } else
        if mode == 1 { // r was pressed so replace next char
            match key {
                c @ 32...126 => { buffer[cursorpos] = c as u8; mode = 0 },
                27 => mode = 0,
                _ => (),
            }
        } else
        if mode == 2 {
            match key {
                c @ 32...126 => { command.push(c as u8 as char); }, //TODO check this
                27 => {command.clear();mode = 0;},
                10 => { // Enter pressed, compute command!
                        if (command == "w".to_string()) || (command == "wq".to_string()) {
                            file.seek(SeekFrom::Start(0)).ok().expect("Filepointer could not be set to 0");
                            file.write_all(&mut buffer).ok().expect("File could not be written.");
                            if command == "wq".to_string() {
                                ragequitnow = 1;
                            }
                            command.clear();mode = 0;
                        }
                        else if command == "q".to_string() {
                            ragequitnow = 1;
                        }
                        else {
                            command.clear();
                            command.push_str("Bad_command!");
                            mode = 0;
                        }
                    },
                _ => (),
            }
        }
 //       printw(&format!("{:?}", key));
        draw(&buffer, cursorpos, SPALTEN, screenheight, mode, &command);
    }

    refresh();
    endwin();
}

fn draw(buffer:&Vec<u8>, cursorpos:usize, spalten:usize, maxzeilen:usize, mode:usize, command:&String) {
//    let zeilen = buffer.len() / spalten;
    erase();

    let mut zeilen = buffer.len() / spalten;
    if zeilen >= maxzeilen-1 { // Last line reserved for Status/Commands/etc (Like in vim)
        zeilen = maxzeilen-2;
    }

    for z in 0 .. zeilen+1 {
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
            if z*spalten+s < buffer.len() { printw(&format!("{:02X} ", buffer[z*spalten+s]) ); }
                else {printw("-- "); }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw(" "); // Space between hex and ascii
        for s in 0 .. spalten {
            if z*spalten+s == cursorpos { attron(COLOR_PAIR(1)); }
                if z*spalten+s < buffer.len() {
                    if let c @ 32...126 = buffer[z*spalten+s] {
                        if c as char == '%' {
                            printw("%%"); // '%' needs to be escaped by a '%' in ncurses
                        } else {
                            printw(&format!("{}", c as char) );
                        }
                    }
                    else {printw(&format!(".") );}
                }
            if z*spalten+s == cursorpos { attroff(COLOR_PAIR(1)); }
        }
        printw("\n");
    }
//TODO: use maxzeilen to draw the command (if in mode == 2) on last line of terminal
//show that the mode is 2 by adding a ':'
    if mode == 2 {
        printw(":");
    }
    printw(&format!("{}", command));
}
<|endoftext|>