<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#![feature(iter_arith, zero_one, custom_derive,
           custom_attribute, plugin, const_fn)]
#![plugin(serde_macros)]
#![plugin(clippy)]

extern crate devicemapper;
#[macro_use]
extern crate clap;
extern crate nix;
extern crate crc;
extern crate byteorder;
extern crate uuid;
extern crate time;
extern crate serde;
extern crate serde_json;
extern crate bytesize;
extern crate dbus;

#[macro_use] extern crate custom_derive;
#[macro_use] extern crate newtype_derive;

pub static mut debug: bool = false;

macro_rules! dbgp {
    ($($arg:tt)*) => (
        unsafe {
            if ::debug {
                println!($($arg)*)
            }
        })
}

mod types;
mod consts;
mod froyo;
mod blockdev;
mod raid;
mod thin;
mod util;
mod dbus_api;

use std::io::Write;
use std::error::Error;
use std::process::exit;
use std::path::{Path, PathBuf};

use clap::{App, Arg, SubCommand, ArgMatches};
use uuid::Uuid;
use bytesize::ByteSize;
use dbus::{Connection, BusType};

use types::{DataBlocks, FroyoResult};
use froyo::{Froyo, FroyoStatus};

// We are given BlockDevs to start.
// We allocate LinearDevs from each for the meta and data devices.
// We use all these to make RaidDevs.
// We create two RaidLinearDevs from these for meta and data devices.
// We use these to make a ThinPoolDev.
// From that, we allocate a ThinDev.

fn list(_args: &ArgMatches) -> FroyoResult<()> {
    let froyos = try!(Froyo::find_all());
    for f in &froyos {
        println!("{}", f.name);
    }

    Ok(())
}

fn status(args: &ArgMatches) -> FroyoResult<()> {
    let name = args.value_of("froyodevname").unwrap();
    match try!(Froyo::find(&name)) {
        Some(f) => {
            let status = match try!(f.status()) {
                FroyoStatus::Good(_) => "good",
                FroyoStatus::RaidFailed => "RAID failed",
                FroyoStatus::ThinPoolFailed => "Thin pool failed",
                FroyoStatus::ThinFailed => "Thin device failed",
            };

            let space = try!(f.avail_redundant_space());
            let total = f.total_redundant_space();

            let percent = (*(total-space) * 100) / *total;

            println!("Status: {}, {}% used ({} of {} {} blocks free)",
                     status, percent,
                     *space, *total,
                     ByteSize::b(f.data_block_size() as usize).to_string(true));
        },
        None => println!("Froyodev \"{}\" not found", name),
    }
    Ok(())
}

fn add(_args: &ArgMatches) -> FroyoResult<()> {
    println!("hello from add()");
    Ok(())
}

fn remove(_args: &ArgMatches) -> FroyoResult<()> {
    println!("hello from remove()");
    Ok(())
}

fn create(args: &ArgMatches) -> FroyoResult<()> {
    let name = args.value_of("froyodevname").unwrap();
    let dev_paths: Vec<_> = args.values_of("devices").unwrap().into_iter()
        .map(|dev| {
            if !Path::new(dev).is_absolute() {
                PathBuf::from(format!("/dev/{}", dev))
            } else {
                PathBuf::from(dev)
            }})
        .collect();
    let force = args.is_present("force");

    let froyo = try!(Froyo::create(name, &Uuid::new_v4().to_simple_string(), &dev_paths, force));

    try!(froyo.save_state());

    dbgp!("Froyodev {} created", froyo.name);

    Ok(())
}

fn dump_meta(args: &ArgMatches) -> FroyoResult<()> {
    let name = args.value_of("froyodevname").unwrap();
    match try!(Froyo::find(&name)) {
        Some(f) =>
            println!("{}", try!(f.to_metadata_pretty())),
        None => println!("Froyodev \"{}\" not found", name),
    }

    Ok(())
}

fn dbus_server(_args: &ArgMatches) -> FroyoResult<()> {
    let c = Connection::get_private(BusType::Session).unwrap();
    let froyos = try!(Froyo::find_all());
    let froyos = froyos.into_iter()
        .map(|f| Rc::new(RefCell::new(f)))
        .collect::<Vec<_>>();
    let froyos = Rc::new(RefCell::new(froyos));
    let tree = try!(dbus_api::get_tree(&c, froyos.clone()));

    // TODO: event loop needs to handle dbus and also dm events (or polling)
    // so we can extend/reshape/delay/whatever in a timely fashion
    for _ in tree.run(&c, c.iter(1000)) {

    }

    Ok(())
}

fn main() {

    let matches = App::new("froyo")
        .version(&crate_version!())
        .author("Andy Grover <andy@groveronline.com>")
        .about("Drobo + Free + YOLO")
        .arg(Arg::with_name("debug")
             .short("d")
             .long("debug")
             .help("Print additional output for debugging")
             )
        .subcommand(SubCommand::with_name("list")
                    .about("List all froyodevs")
                    .arg(Arg::with_name("long")
                         .short("l")
                         .long("long")
                         .help("Use a long listing format")
                         )
                    )
        .subcommand(SubCommand::with_name("status")
                    .about("Get the status of a single froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .required(true)
                         .help("Froyodev to get info on")
                         .index(1)
                         )
                    )
        .subcommand(SubCommand::with_name("add")
                    .about("Add one or more additional block devices to a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to add the device to")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("device(s) to add")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("remove")
                    .about("Remove a block device from a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to remove the device from")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Block device(s) to remove")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("create")
                    .about("Create a new froyodev")
                    .arg(Arg::with_name("force")
                         .short("f")
                         .long("force")
                         .help("Force")
                         )
                    .arg(Arg::with_name("froyodevname")
                         .help("Name of the new froyodev")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Initial block device(s) to use")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("dev")
                    .about("Developer/debug commands")
                    .subcommand(SubCommand::with_name("dump_meta")
                                .about("Output the JSON metadata for a froyodev")
                                .arg(Arg::with_name("froyodevname")
                                     .help("Name of the froyodev")
                                     .required(true)
                                     .index(1)
                                     )
                                )
                    .subcommand(SubCommand::with_name("dbus_server")
                                .about("Serve the Froyo DBus API")
                                )
                    )
        .get_matches();

    if matches.is_present("debug") {
        // must use unsafe to change a mut static, sigh
        unsafe { debug = true };
    }

    let r = match matches.subcommand() {
        ("list", Some(matches)) => list(matches),
        ("status", Some(matches)) => status(matches),
        ("add", Some(matches)) => add(matches),
        ("remove", Some(matches)) => remove(matches),
        ("create", Some(matches)) => create(matches),
        ("dev", Some(matches)) => match matches.subcommand() {
            ("dump_meta", Some(matches)) => dump_meta(matches),
            ("dbus_server", Some(matches)) => dbus_server(matches),
            ("", None) => {
                println!("No command given, try \"help\"");
                Ok(())
            }
            _ => unreachable!(),
        },
        ("", None) => {
            println!("No command given, try \"help\"");
            Ok(())
        }
        _ => unreachable!(),
    };

    if let Err(r) = r {
        if let Err(e) = writeln!(&mut ::std::io::stderr(), "{}", r.description()) {
            panic!("Unable to write to stderr: {}", e)
        }
        exit(1);
    }
}
<commit_msg>Add missing use stmts for Rc and RefCellto main.rs<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#![feature(iter_arith, zero_one, custom_derive,
           custom_attribute, plugin, const_fn)]
#![plugin(serde_macros)]
#![plugin(clippy)]

extern crate devicemapper;
#[macro_use]
extern crate clap;
extern crate nix;
extern crate crc;
extern crate byteorder;
extern crate uuid;
extern crate time;
extern crate serde;
extern crate serde_json;
extern crate bytesize;
extern crate dbus;

#[macro_use] extern crate custom_derive;
#[macro_use] extern crate newtype_derive;

pub static mut debug: bool = false;

macro_rules! dbgp {
    ($($arg:tt)*) => (
        unsafe {
            if ::debug {
                println!($($arg)*)
            }
        })
}

mod types;
mod consts;
mod froyo;
mod blockdev;
mod raid;
mod thin;
mod util;
mod dbus_api;

use std::io::Write;
use std::error::Error;
use std::process::exit;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::cell::RefCell;

use clap::{App, Arg, SubCommand, ArgMatches};
use uuid::Uuid;
use bytesize::ByteSize;
use dbus::{Connection, BusType};

use types::{DataBlocks, FroyoResult};
use froyo::{Froyo, FroyoStatus};

// We are given BlockDevs to start.
// We allocate LinearDevs from each for the meta and data devices.
// We use all these to make RaidDevs.
// We create two RaidLinearDevs from these for meta and data devices.
// We use these to make a ThinPoolDev.
// From that, we allocate a ThinDev.

fn list(_args: &ArgMatches) -> FroyoResult<()> {
    let froyos = try!(Froyo::find_all());
    for f in &froyos {
        println!("{}", f.name);
    }

    Ok(())
}

fn status(args: &ArgMatches) -> FroyoResult<()> {
    let name = args.value_of("froyodevname").unwrap();
    match try!(Froyo::find(&name)) {
        Some(f) => {
            let status = match try!(f.status()) {
                FroyoStatus::Good(_) => "good",
                FroyoStatus::RaidFailed => "RAID failed",
                FroyoStatus::ThinPoolFailed => "Thin pool failed",
                FroyoStatus::ThinFailed => "Thin device failed",
            };

            let space = try!(f.avail_redundant_space());
            let total = f.total_redundant_space();

            let percent = (*(total-space) * 100) / *total;

            println!("Status: {}, {}% used ({} of {} {} blocks free)",
                     status, percent,
                     *space, *total,
                     ByteSize::b(f.data_block_size() as usize).to_string(true));
        },
        None => println!("Froyodev \"{}\" not found", name),
    }
    Ok(())
}

fn add(_args: &ArgMatches) -> FroyoResult<()> {
    println!("hello from add()");
    Ok(())
}

fn remove(_args: &ArgMatches) -> FroyoResult<()> {
    println!("hello from remove()");
    Ok(())
}

fn create(args: &ArgMatches) -> FroyoResult<()> {
    let name = args.value_of("froyodevname").unwrap();
    let dev_paths: Vec<_> = args.values_of("devices").unwrap().into_iter()
        .map(|dev| {
            if !Path::new(dev).is_absolute() {
                PathBuf::from(format!("/dev/{}", dev))
            } else {
                PathBuf::from(dev)
            }})
        .collect();
    let force = args.is_present("force");

    let froyo = try!(Froyo::create(name, &Uuid::new_v4().to_simple_string(), &dev_paths, force));

    try!(froyo.save_state());

    dbgp!("Froyodev {} created", froyo.name);

    Ok(())
}

fn dump_meta(args: &ArgMatches) -> FroyoResult<()> {
    let name = args.value_of("froyodevname").unwrap();
    match try!(Froyo::find(&name)) {
        Some(f) =>
            println!("{}", try!(f.to_metadata_pretty())),
        None => println!("Froyodev \"{}\" not found", name),
    }

    Ok(())
}

fn dbus_server(_args: &ArgMatches) -> FroyoResult<()> {
    let c = Connection::get_private(BusType::Session).unwrap();
    let froyos = try!(Froyo::find_all());
    let froyos = froyos.into_iter()
        .map(|f| Rc::new(RefCell::new(f)))
        .collect::<Vec<_>>();
    let froyos = Rc::new(RefCell::new(froyos));
    let tree = try!(dbus_api::get_tree(&c, froyos.clone()));

    // TODO: event loop needs to handle dbus and also dm events (or polling)
    // so we can extend/reshape/delay/whatever in a timely fashion
    for _ in tree.run(&c, c.iter(1000)) {

    }

    Ok(())
}

fn main() {

    let matches = App::new("froyo")
        .version(&crate_version!())
        .author("Andy Grover <andy@groveronline.com>")
        .about("Drobo + Free + YOLO")
        .arg(Arg::with_name("debug")
             .short("d")
             .long("debug")
             .help("Print additional output for debugging")
             )
        .subcommand(SubCommand::with_name("list")
                    .about("List all froyodevs")
                    .arg(Arg::with_name("long")
                         .short("l")
                         .long("long")
                         .help("Use a long listing format")
                         )
                    )
        .subcommand(SubCommand::with_name("status")
                    .about("Get the status of a single froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .required(true)
                         .help("Froyodev to get info on")
                         .index(1)
                         )
                    )
        .subcommand(SubCommand::with_name("add")
                    .about("Add one or more additional block devices to a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to add the device to")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("device(s) to add")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("remove")
                    .about("Remove a block device from a froyodev")
                    .arg(Arg::with_name("froyodevname")
                         .help("Froyodev to remove the device from")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Block device(s) to remove")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("create")
                    .about("Create a new froyodev")
                    .arg(Arg::with_name("force")
                         .short("f")
                         .long("force")
                         .help("Force")
                         )
                    .arg(Arg::with_name("froyodevname")
                         .help("Name of the new froyodev")
                         .required(true)
                         .index(1)
                         )
                    .arg(Arg::with_name("devices")
                         .help("Initial block device(s) to use")
                         .multiple(true)
                         .required(true)
                         .index(2)
                         )
                    )
        .subcommand(SubCommand::with_name("dev")
                    .about("Developer/debug commands")
                    .subcommand(SubCommand::with_name("dump_meta")
                                .about("Output the JSON metadata for a froyodev")
                                .arg(Arg::with_name("froyodevname")
                                     .help("Name of the froyodev")
                                     .required(true)
                                     .index(1)
                                     )
                                )
                    .subcommand(SubCommand::with_name("dbus_server")
                                .about("Serve the Froyo DBus API")
                                )
                    )
        .get_matches();

    if matches.is_present("debug") {
        // must use unsafe to change a mut static, sigh
        unsafe { debug = true };
    }

    let r = match matches.subcommand() {
        ("list", Some(matches)) => list(matches),
        ("status", Some(matches)) => status(matches),
        ("add", Some(matches)) => add(matches),
        ("remove", Some(matches)) => remove(matches),
        ("create", Some(matches)) => create(matches),
        ("dev", Some(matches)) => match matches.subcommand() {
            ("dump_meta", Some(matches)) => dump_meta(matches),
            ("dbus_server", Some(matches)) => dbus_server(matches),
            ("", None) => {
                println!("No command given, try \"help\"");
                Ok(())
            }
            _ => unreachable!(),
        },
        ("", None) => {
            println!("No command given, try \"help\"");
            Ok(())
        }
        _ => unreachable!(),
    };

    if let Err(r) = r {
        if let Err(e) = writeln!(&mut ::std::io::stderr(), "{}", r.description()) {
            panic!("Unable to write to stderr: {}", e)
        }
        exit(1);
    }
}
<|endoftext|>


<commit_before>#![feature(slice_patterns)]

extern crate getopts;

use std::fs;
use std::io;
use std::io::{Read, Write};

use getopts::Options;

mod optimizer;
mod parser;
mod runner;
mod structs;

use parser::parse;
use runner::State;
use structs::OpStream;

fn main() {
    let mut opts = Options::new();
    opts.optflag("n", "dry-run", "don't actually run");
    opts.optflag("0", "no-optimize", "don't optimize");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(std::env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut io::stderr(), "{}", f).unwrap();
            std::process::exit(2);
        }
    };
    if matches.opt_present("h") {
        write!(&mut io::stderr(), "{}", opts.usage("Usage: brain_rust [options] FILE... ")).unwrap();
        return;
    }

    let dry_run = matches.opt_present("n");
    let no_optimize = matches.opt_present("0");

    for filename in &matches.free[..] {
        let buffer = match read_file(filename) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while reading {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let ops = match parse(&buffer[..]) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while parsing {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let mut opstream = OpStream { ops: ops };
        if !no_optimize {
            opstream.optimize();
        }
        if !dry_run {
            State::new(&mut io::stdin(), &mut io::stdout()).run(opstream.get());
        }
    }
}

fn read_file(filename: &str) -> Result<Vec<u8>, io::Error> {
    let mut buffer = Vec::new();
    fs::File::open(filename)
        .map(|mut file| file.read_to_end(&mut buffer))
        .map(|_| buffer)
}
<commit_msg>style: back to using try/`?`<commit_after>#![feature(slice_patterns)]

extern crate getopts;

use std::fs;
use std::io;
use std::io::{Read, Write};

use getopts::Options;

mod optimizer;
mod parser;
mod runner;
mod structs;

use parser::parse;
use runner::State;
use structs::OpStream;

fn main() {
    let mut opts = Options::new();
    opts.optflag("n", "dry-run", "don't actually run");
    opts.optflag("0", "no-optimize", "don't optimize");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(std::env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut io::stderr(), "{}", f).unwrap();
            std::process::exit(2);
        }
    };
    if matches.opt_present("h") {
        write!(&mut io::stderr(), "{}", opts.usage("Usage: brain_rust [options] FILE... ")).unwrap();
        return;
    }

    let dry_run = matches.opt_present("n");
    let no_optimize = matches.opt_present("0");

    for filename in &matches.free[..] {
        let buffer = match read_file(filename) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while reading {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let ops = match parse(&buffer[..]) {
            Ok(v) => v,
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while parsing {}: {}", filename, e).unwrap();
                continue;
            }
        };
        let mut opstream = OpStream { ops: ops };
        if !no_optimize {
            opstream.optimize();
        }
        if !dry_run {
            State::new(&mut io::stdin(), &mut io::stdout()).run(opstream.get());
        }
    }
}

fn read_file(filename: &str) -> Result<Vec<u8>, io::Error> {
    let mut buffer = Vec::new();
    fs::File::open(filename)?.read_to_end(&mut buffer)?;
    Ok(buffer)
}
<|endoftext|>


<commit_before>mod game;

extern crate gfx_device_gl;
extern crate piston_window;

use piston_window::*;

use game::{Game, GameWindow};

fn main() {
    let mut window = GameWindow::new(800, 600, "TetRS");
    let mut game = Game::new(window.piston_window.factory.clone());

    while let Some(e) = window.next() {

        if !game.process(&e) {
            break;
        }

        game.render(&mut window, &e);
    }
}
<commit_msg>Changed window width<commit_after>mod game;

extern crate gfx_device_gl;
extern crate piston_window;

use piston_window::*;

use game::{Game, GameWindow};

fn main() {
    let mut window = GameWindow::new(450, 600, "TetRS");
    let mut game = Game::new(window.piston_window.factory.clone());

    while let Some(e) = window.next() {

        if !game.process(&e) {
            break;
        }

        game.render(&mut window, &e);
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // TODO: make sure "topological" here means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|t| t.get_path(submodule_path))
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&subtree_id);

                // TODO: rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    let head = repo.head().expect("Couldn't obtain current repo's HEAD");
    let current_ref = head.name().expect("Couldn't obtain the name HEAD points to");
    let mut reference = repo.find_reference(current_ref).expect("Couldn't find the reference HEAD points to");
    let current_id = reference.target().expect("Couldn't resolve current reference to ID");
    let updated_id = old_id_to_new[&current_id];
    reference.set_target(updated_id, &format!("git-submerge reset {} to rewritten history", current_ref)).expect("Couldn't update current reference to point to revritten history");
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<commit_msg>Only commits that updated the sub are merges now<commit_after>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // TODO: make sure "topological" here means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|t| t.get_path(submodule_path))
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&submodule_commit_id);

                // TODO: rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    let head = repo.head().expect("Couldn't obtain current repo's HEAD");
    let current_ref = head.name().expect("Couldn't obtain the name HEAD points to");
    let mut reference = repo.find_reference(current_ref).expect("Couldn't find the reference HEAD points to");
    let current_id = reference.target().expect("Couldn't resolve current reference to ID");
    let updated_id = old_id_to_new[&current_id];
    reference.set_target(updated_id, &format!("git-submerge reset {} to rewritten history", current_ref)).expect("Couldn't update current reference to point to revritten history");

    // TODO: reset working directory to match new HEAD
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<|endoftext|>


<commit_before>fn main(expr: &str) -> i32 {
  let mut result = 0;
  let mut stack:Vec<i32> = Vec::new();
  for token in expr.split_whitespace() {
    let wrapped_operand = token.parse::<i32>();
    let is_operator = wrapped_operand.is_err();
    if is_operator {
      if stack.len() < 2 {
        panic!("Unsufficient operands before operator");
      }
      // xxxFlorent: Any way to destructure like this? Not sure of what I am doing below
      // let [ operand1, operand2 ] = stack.drain((token.len() - 3)..).collect();
      let operand1 = stack.pop().expect("expected i32 values only in stack");
      let operand2 = stack.pop().expect("expected i32 values only in stack");
      result += match token {
        "+" => operand1 + operand2,
        _ => panic!("Unsupported operator")
      }
    } else {
      stack.push(wrapped_operand.unwrap());
    }
  }

  if stack.len() > 0 {
    panic!("Remaining untreated operands. Probably missing operator.");
  }
  return result;
}

#[test]
fn it_adds() {
  let result = main("1 2 +");
  assert_eq!(result, 3);
}

#[test]
#[should_panic]
fn it_panics_for_unsupported_operators() {
  main("1 2 =");
  // xxxFlorent: How to test the panic message is right?
}

#[test]
#[should_panic]
fn it_panics_for_unsufficient_operands() {
  main("1 +");
  // xxxFlorent: How to test the panic message is right?
}

#[test]
#[should_panic]
fn it_panics_for_unsufficient_operators() {
  main("1 2 3 +");
  // xxxFlorent: How to test the panic message is right?
}
<commit_msg>Support substractions and multiplications<commit_after>fn main(expr: &str) -> i32 {
  let mut result = 0;
  let mut stack:Vec<i32> = Vec::new();
  for token in expr.split_whitespace() {
    let wrapped_operand = token.parse::<i32>();
    let is_operator = wrapped_operand.is_err();
    if is_operator {
      if stack.len() < 2 {
        panic!("Unsufficient operands before operator");
      }
      // xxxFlorent: Any way to destructure like this? Not sure of what I am doing below
      // let [ operand1, operand2 ] = stack.drain((token.len() - 3)..).collect();
      let operand2 = stack.pop().expect("expected i32 values only in stack");
      let operand1 = stack.pop().expect("expected i32 values only in stack");
      result += match token {
        "+" => operand1 + operand2,
        "-" => operand1 - operand2,
        "*" => operand1 * operand2,
        _ => panic!("Unsupported operator")
      }
    } else {
      stack.push(wrapped_operand.unwrap());
    }
  }

  if stack.len() > 0 {
    panic!("Remaining untreated operands. Probably missing operator.");
  }
  return result;
}

#[test]
fn it_adds() {
  let result = main("1 2 +");
  assert_eq!(result, 3);
}

#[test]
fn it_substracts() {
  let result = main("1 2 -");
  assert_eq!(result, -1);
}

#[test]
fn it_multiplies() {
  let result = main("6 7 *");
  assert_eq!(result, 42);
}

#[test]
#[should_panic]
fn it_panics_for_unsupported_operators() {
  main("1 2 =");
  // xxxFlorent: How to test the panic message is right?
}

#[test]
#[should_panic]
fn it_panics_for_unsufficient_operands() {
  main("1 +");
  // xxxFlorent: How to test the panic message is right?
}

#[test]
#[should_panic]
fn it_panics_for_unsufficient_operators() {
  main("1 2 3 +");
  // xxxFlorent: How to test the panic message is right?
}
<|endoftext|>


<commit_before>

#![feature(path_ext)]
extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;

mod lib;

use mysql::conn::MyOpts;
use std::default::Default;
use mysql::conn::pool;
use mysql::conn::pool::MyPooledConn;
use mysql::value::from_value;

use std::error::Error;

use std::ascii::AsciiExt;

use lib::config;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::downloader::DownloadError;
use lib::converter::Converter;

use std::fs::rename;

static VERSION : &'static str = "0.1"; // String not valid
static SLEEP_MS: u32 = 5000;

lazy_static! {
    static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

fn main() {
    let opts = mysql_options();
    let pool = match pool::MyPool::new(opts) {
        Ok(conn) => { println!("Connected successfully."); conn},
        Err(err) => panic!("Unable to establish a connection!\n{}",err),
    };
    let converter = Converter::new(&CONFIG.general.ffmpeg_bin, &CONFIG.codecs.audio, pool.clone());
    loop {
        if let Some(result) = request_entry(& pool) {
            if result.playlist {
                println!("Playlist not supported atm!");
                //TODO: set playlist entry to errg
            }
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            set_query_state(&pool.clone(),&qid, "started", false);
            let succes = match handle_download(result, None, &converter) {
                Ok(v) => v,
                Err(e) => {println!("Error: {:?}", e); false }
            };
            let code = if succes {
                2//QueryCodes::Finished as i32
            } else {
                3//QueryCodes::Failed as i32
            };
            set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");
            let state = if code == 2 {
                "finished"
            } else {
                "failed"
            };
            set_query_state(&pool.clone(),&qid, state, true);
        } else {
            println!("Pausing..");
            std::thread::sleep_ms(SLEEP_MS);
        }
    }

    println!("EOL!");
}

///Download handler
///Used by the playlist/file handler to download one file
///Based on the quality it needs to download audio & video splitted & convert them together
///In case of a playlist download it depends on the target download folder & if should bezipped
///In case of a DMCA we need to download the file via the socket connector,
///which will output a mp3, or if requested, a hard quality depending on the max-available of
///the video url. Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///If it's a non-zipped single file, the file is moved after a success download,convert etc to the
///main folder from which it should be downloadable.
///The original non-ascii & url_encode name of the file is stored in the DB
fn handle_download(downl_db: DownloadDB, folder: Option<String>, converter: &Converter) -> Result<bool,DownloadError>{
    //update progress
    let is_zipped = match folder {
        Some(_) => true,
        None => false,
    };

    let dbcopy = downl_db.clone(); //copy, all implement copy & no &'s in use
    update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 1, 0);
    let download = Downloader::new(downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video() {
                Err(err) => { println!("Offliberty-call error {:?}", err); return Err(err); },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / video private etc.. abort
            println!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    let name_http_valid = url_encode(&name);

    println!("Filename: {}", name);

    if dmca {
        //TODO: insert title name for file,
        //copy file to download folder
        return Ok(true);
    }

    let file_path = format_file_path(&dbcopy.qid, folder.clone());
    let save_path = &format_save_path(folder.clone(),&name, &download);

    let is_splitted_format = is_split_container(&dbcopy.quality);
    let total_steps = if is_splitted_format {
        4
    } else {
        2
    };
    update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 2, total_steps);

    //download video, which is video/audio(m4a)
    try!(download.download_file(&file_path, false));

    if is_splitted_format { // download audio file & convert together
        update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 3, total_steps);

        let audio_path = format_audio_path(&dbcopy.qid, folder.clone());

        println!("Downloading audio.. {}", audio_path);
        try!(download.download_file(&audio_path, true));

        update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 4, total_steps);

        match converter.merge_files(&dbcopy.qid,&file_path, &audio_path,&save_path) {
            Err(e) => {println!("merge error: {:?}",e); return Err(e);},
            Ok(()) => {},
        }

    }else{ // we're already done, only need to copy / convert to mp3 if requested
        if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
            //TODO: convert -> saves already ?
        }else{
            try!(move_file(&file_path, &save_path));
        }

    }

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        add_file_entry(&dbcopy.pool.clone(), &dbcopy.qid,&name_http_valid, &name);
    }

    //TODO: download file, convert if audio ?!
    Ok(true)
}

fn update_steps(pool: & pool::MyPool, qid: &i64, step: i32, max_steps: i32){
    let finished = if max_steps == step {
        true
    }else{
        false
    };
    set_query_state(&pool,qid, &format!("{}|{}", step, max_steps), finished);
}

fn get_file_ext<'a>(download: &Downloader) -> &'a str {
    match download.is_audio() {
        true => "mp3",
        false => "mp4",
    }
}

fn move_file(original: &str, destination: &str) -> Result<(),DownloadError> {
    match rename(original, destination) { // no try possible..
        Err(v) => Err(v.into()),
        Ok(_) => Ok(()),
    }
}

fn format_file_path(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}", &CONFIG.general.save_dir, qid),
    }
}

///Formats the audio path, based on the qid & optional folders
fn format_audio_path(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}a", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}a", &CONFIG.general.save_dir, qid),
    }
}

///Format save path, which stays inside the optional folder, if set for zipping later
fn format_save_path<'a>(folder: Option<String>, name: &str, download: &'a Downloader) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}.{}", &CONFIG.general.save_dir, v, url_encode(name),get_file_ext(download)),
        None => format!("{}/{}.{}", &CONFIG.general.download_dir, url_encode(name),get_file_ext(download)),
    }
}

///Set the state of the current query, also in dependence of the code, see QueryCodes
fn set_query_state(pool: & pool::MyPool,qid: &i64 , state: &str, finished: bool){ // same here
    let mut conn = pool.get_conn().unwrap();
    let progress: i32 = if finished {
        100
    }else{
        0
    };
    let mut stmt = conn.prepare("UPDATE querydetails SET status = ? , progress = ? WHERE qid = ?").unwrap();
    let result = stmt.execute(&[&state,&progress,qid]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error setting query state: {}",why),
    }
}

fn add_file_entry(pool: & pool::MyPool, fid: &i64, name: &str, real_name: &str){
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("INSERT INTO files (fid,name,rname,valid) VALUES (?,?,?,?)").unwrap();
    let result = stmt.execute(&[fid,&real_name,&name,&1]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error adding file: {}",why),
    }
}

///Return whether the quality is a split container: video only
///as specified in the docs
fn is_split_container(quality: &i16) -> bool {
    match *quality {
        141 | 83 | 82 | 84 | 85 => false,
        _ => true,
    }
}

///Return an url-conform String
fn url_encode(input: &str) -> String {
    // iterator over input, apply function to each element(function
    input.chars().map(|char| {
        match char {
            ' ' | '?' | '!' | '\\' | '/' | '.' | '(' | ')' | '[' | ']' => '_',
            '&' => '-',
            c if c.is_ascii() => c,
            _ => '_'
        }
    }).collect()
    // match for each char, then do collect: loop through the iterator, collect all elements
    // into container FromIterator
}

///Request an entry from the DB that should be handled
fn request_entry(pool: & pool::MyPool) -> Option<DownloadDB> {
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("SELECT queries.qid,url,type,quality FROM querydetails \
                    INNER JOIN queries \
                    ON querydetails.qid = queries.qid \
                    WHERE querydetails.code = 0 \
                    ORDER BY queries.created \
                    LIMIT 1").unwrap();
    let mut result = stmt.execute(&[]).unwrap();
    let result = match result.next() {
        Some(val) => val.unwrap(),
        None => {return None; },
    };
    println!("Result: {:?}", result[0]);
    println!("result str: {}", result[1].into_str());
    let download_db = DownloadDB { url: from_value::<String>(&result[1]),
                                                quality: from_value::<i16>(&result[3]),
                                                qid: from_value::<i64>(&result[0]),
                                                audioquality: CONFIG.codecs.audio,
                                                folder: CONFIG.general.save_dir.clone(),
                                                pool: pool.clone(),
                                                playlist: false, //TEMP
                                                compress: false };
    Some(download_db)
}

fn set_query_code(conn: & mut MyPooledConn, code: &i8, qid: &i64) -> Result<(), DownloadError> { // same here
    let mut stmt = conn.prepare("UPDATE querydetails SET code = ? WHERE qid = ?").unwrap();
    let result = stmt.execute(&[code,qid]); // why is this var needed ?!
    match result {
        Ok(_) => Ok(()),
        Err(why) => Err(DownloadError::DBError(why.description().into())),
    }
}

///Set options for the connection
fn mysql_options() -> MyOpts {
    //let dbconfig = CONFIG.get("db").unwrap().clone();
    //let dbconfig = dbconfig.as_table().unwrap(); // shadow binding to workaround borrow / lifetime problems

    MyOpts {
        tcp_addr: Some(CONFIG.db.ip.clone()),
        tcp_port: CONFIG.db.port,
        user: Some(CONFIG.db.user.clone()),
        pass: Some(CONFIG.db.password.clone()),
        db_name: Some(CONFIG.db.db.clone()),
        ..Default::default() // set other to default
    }
}<commit_msg>+cache cleanup *not so aggressive logging<commit_after>

#![feature(path_ext)]
extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;

mod lib;

use mysql::conn::MyOpts;
use std::default::Default;
use mysql::conn::pool;
use mysql::conn::pool::MyPooledConn;
use mysql::value::from_value;

use std::error::Error;

use std::ascii::AsciiExt;

use lib::config;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::downloader::DownloadError;
use lib::converter::Converter;

use std::fs::{rename,remove_file};

static VERSION : &'static str = "0.1"; // String not valid
static SLEEP_MS: u32 = 5000;

lazy_static! {
    static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

fn main() {
    let opts = mysql_options();
    let pool = match pool::MyPool::new(opts) {
        Ok(conn) => { println!("Connected successfully."); conn},
        Err(err) => panic!("Unable to establish a connection!\n{}",err),
    };
    let converter = Converter::new(&CONFIG.general.ffmpeg_bin, &CONFIG.codecs.audio, pool.clone());
    let mut print_pause = true;
    loop {
        if let Some(result) = request_entry(& pool) {
            print_pause = true;
            if result.playlist {
                println!("Playlist not supported atm!");
                //TODO: set playlist entry to errg
            }
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            set_query_state(&pool.clone(),&qid, "started", false);
            let succes = match handle_download(result, None, &converter) {
                Ok(v) => v,
                Err(e) => {println!("Error: {:?}", e); false }
            };
            let code = if succes {
                2//QueryCodes::Finished as i32
            } else {
                3//QueryCodes::Failed as i32
            };
            set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");
            let state = if code == 2 {
                "finished"
            } else {
                "failed"
            };
            set_query_state(&pool.clone(),&qid, state, true);
        } else {
            if print_pause { println!("Pausing.."); print_pause = false; }
            std::thread::sleep_ms(SLEEP_MS);
        }
    }

    println!("EOL!");
}

///Download handler
///Used by the playlist/file handler to download one file
///Based on the quality it needs to download audio & video splitted & convert them together
///In case of a playlist download it depends on the target download folder & if should bezipped
///In case of a DMCA we need to download the file via the socket connector,
///which will output a mp3, or if requested, a hard quality depending on the max-available of
///the video url. Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///If it's a non-zipped single file, the file is moved after a success download,convert etc to the
///main folder from which it should be downloadable.
///The original non-ascii & url_encode name of the file is stored in the DB
fn handle_download(downl_db: DownloadDB, folder: Option<String>, converter: &Converter) -> Result<bool,DownloadError>{
    //update progress
    let is_zipped = match folder {
        Some(_) => true,
        None => false,
    };

    let dbcopy = downl_db.clone(); //copy, all implement copy & no &'s in use
    update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 1, 0);
    let download = Downloader::new(downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video() {
                Err(err) => { println!("Offliberty-call error {:?}", err); return Err(err); },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / video private etc.. abort
            println!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    let name_http_valid = url_encode(&name);

    println!("Filename: {}", name);

    if !dmca {
        //TODO: insert title name for file,
        //copy file to download folder

        let file_path = format_file_path(&dbcopy.qid, folder.clone());
        let save_path = &format_save_path(folder.clone(),&name, &download);

        let is_splitted_format = is_split_container(&dbcopy.quality);
        let total_steps = if is_splitted_format {
            4
        } else {
            2
        };
        update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 2, total_steps);

        //download video, which is video/audio(m4a)
        try!(download.download_file(&file_path, false));

        if is_splitted_format { // download audio file & convert together
            update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 3, total_steps);

            let audio_path = format_audio_path(&dbcopy.qid, folder.clone());

            println!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));

            update_steps(&dbcopy.pool.clone(),&dbcopy.qid, 4, total_steps);

            match converter.merge_files(&dbcopy.qid,&file_path, &audio_path,&save_path) {
                Err(e) => {println!("merge error: {:?}",e); return Err(e);},
                Ok(()) => {},
            }

            try!(remove_file(&audio_path));

        }else{ // we're already done, only need to copy / convert to mp3 if requested
            if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
                //TODO: convert -> saves already ?
            }else{
                try!(move_file(&file_path, &save_path));
            }
        }
        try!(remove_file(&file_path));
    }

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        add_file_entry(&dbcopy.pool.clone(), &dbcopy.qid,&name_http_valid, &name);
    }

    //TODO: download file, convert if audio ?!
    Ok(true)
}

fn update_steps(pool: & pool::MyPool, qid: &i64, step: i32, max_steps: i32){
    let finished = if max_steps == step {
        true
    }else{
        false
    };
    set_query_state(&pool,qid, &format!("{}|{}", step, max_steps), finished);
}

fn get_file_ext<'a>(download: &Downloader) -> &'a str {
    match download.is_audio() {
        true => "mp3",
        false => "mp4",
    }
}

fn move_file(original: &str, destination: &str) -> Result<(),DownloadError> {
    match rename(original, destination) { // no try possible..
        Err(v) => Err(v.into()),
        Ok(_) => Ok(()),
    }
}

fn format_file_path(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}", &CONFIG.general.save_dir, qid),
    }
}

///Formats the audio path, based on the qid & optional folders
fn format_audio_path(qid: &i64, folder: Option<String>) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}a", &CONFIG.general.save_dir, v, qid),
        None => format!("{}/{}a", &CONFIG.general.save_dir, qid),
    }
}

///Format save path, which stays inside the optional folder, if set for zipping later
fn format_save_path<'a>(folder: Option<String>, name: &str, download: &'a Downloader) -> String {
    match folder {
        Some(v) => format!("{}/{}/{}.{}", &CONFIG.general.save_dir, v, url_encode(name),get_file_ext(download)),
        None => format!("{}/{}.{}", &CONFIG.general.download_dir, url_encode(name),get_file_ext(download)),
    }
}

///Set the state of the current query, also in dependence of the code, see QueryCodes
fn set_query_state(pool: & pool::MyPool,qid: &i64 , state: &str, finished: bool){ // same here
    let mut conn = pool.get_conn().unwrap();
    let progress: i32 = if finished {
        100
    }else{
        0
    };
    let mut stmt = conn.prepare("UPDATE querydetails SET status = ? , progress = ? WHERE qid = ?").unwrap();
    let result = stmt.execute(&[&state,&progress,qid]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error setting query state: {}",why),
    }
}

fn add_file_entry(pool: & pool::MyPool, fid: &i64, name: &str, real_name: &str){
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("INSERT INTO files (fid,name,rname,valid) VALUES (?,?,?,?)").unwrap();
    let result = stmt.execute(&[fid,&real_name,&name,&1]); // why is this var needed ?!
    match result {
        Ok(_) => (),
        Err(why) => println!("Error adding file: {}",why),
    }
}

///Return whether the quality is a split container: video only
///as specified in the docs
fn is_split_container(quality: &i16) -> bool {
    match *quality {
        141 | 83 | 82 | 84 | 85 => false,
        _ => true,
    }
}

///Return an url-conform String
fn url_encode(input: &str) -> String {
    // iterator over input, apply function to each element(function
    input.chars().map(|char| {
        match char {
            ' ' | '?' | '!' | '\\' | '/' | '.' | '(' | ')' | '[' | ']' => '_',
            '&' => '-',
            c if c.is_ascii() => c,
            _ => '_'
        }
    }).collect()
    // match for each char, then do collect: loop through the iterator, collect all elements
    // into container FromIterator
}

///Request an entry from the DB that should be handled
fn request_entry(pool: & pool::MyPool) -> Option<DownloadDB> {
    let mut conn = pool.get_conn().unwrap();
    let mut stmt = conn.prepare("SELECT queries.qid,url,type,quality FROM querydetails \
                    INNER JOIN queries \
                    ON querydetails.qid = queries.qid \
                    WHERE querydetails.code = 0 \
                    ORDER BY queries.created \
                    LIMIT 1").unwrap();
    let mut result = stmt.execute(&[]).unwrap();
    let result = match result.next() {
        Some(val) => val.unwrap(),
        None => {return None; },
    };
    println!("Result: {:?}", result[0]);
    println!("result str: {}", result[1].into_str());
    let download_db = DownloadDB { url: from_value::<String>(&result[1]),
                                                quality: from_value::<i16>(&result[3]),
                                                qid: from_value::<i64>(&result[0]),
                                                audioquality: CONFIG.codecs.audio,
                                                folder: CONFIG.general.save_dir.clone(),
                                                pool: pool.clone(),
                                                playlist: false, //TEMP
                                                compress: false };
    Some(download_db)
}

fn set_query_code(conn: & mut MyPooledConn, code: &i8, qid: &i64) -> Result<(), DownloadError> { // same here
    let mut stmt = conn.prepare("UPDATE querydetails SET code = ? WHERE qid = ?").unwrap();
    let result = stmt.execute(&[code,qid]); // why is this var needed ?!
    match result {
        Ok(_) => Ok(()),
        Err(why) => Err(DownloadError::DBError(why.description().into())),
    }
}

///Set options for the connection
fn mysql_options() -> MyOpts {
    //let dbconfig = CONFIG.get("db").unwrap().clone();
    //let dbconfig = dbconfig.as_table().unwrap(); // shadow binding to workaround borrow / lifetime problems

    MyOpts {
        tcp_addr: Some(CONFIG.db.ip.clone()),
        tcp_port: CONFIG.db.port,
        user: Some(CONFIG.db.user.clone()),
        pass: Some(CONFIG.db.password.clone()),
        db_name: Some(CONFIG.db.db.clone()),
        ..Default::default() // set other to default
    }
}<|endoftext|>


<commit_before>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();
  let initial_search = args.initial_search.clone();
  if args.use_first {
    let matches = matching::compute_matches(&choices, initial_search.as_slice());
    println!("{}", matches[0]);
    return;
  } else {
    event_loop(choices, initial_search.as_slice());
  }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
  let mut search = String::from_str(initial_search);
  let mut screen = Screen::open_screen();
  let mut index = 0;

  let mut matches_stale = true;
  let mut matches = matching::compute_matches(&choices, search.as_slice());
  loop {
    if matches_stale {
      matches = matching::compute_matches(&choices, search.as_slice());
      matches_stale = false;
    }

    draw_screen(&mut screen, &matches, search.as_slice(), choices.len(), index);

    let chars = screen.get_buffered_keys();
    for char in chars.iter() {
      match *char {
        Char(x) => {
          search.push(x);
          index = 0;
          matches_stale = true;
        }
        Backspace => { search.pop(); matches_stale = true; }
        Control('h') => { search.pop(); matches_stale = true; }
        Control('u') => { search.clear(); matches_stale = true; }
        Control('c') => { return; }
        Control('n') => { index = min(index + 1, min(screen.visible_choices as usize - 1, matches.len() - 1)); }
        Control('p') => { index = if index == 0 { 0 } else { index - 1 }; }
        Enter => {
          screen.move_cursor_to_bottom();
          if matches_stale {
            matches = matching::compute_matches(&choices, search.as_slice());
          }
          println!("{}", matches[index]);
          return;
        }
        _ => panic!("Unexpected input"),
      }
    }
  }
}

fn draw_screen(screen: &mut Screen, matches: &Vec<&String>, search: &str, choices: usize, index: usize) {
  screen.hide_cursor();
  screen.blank_screen();
  screen.write(format!("> {} ({} choices)\n", search, choices).as_slice());

  print_matches(screen, matches, index);

  let start = screen.start_line;
  screen.move_cursor(start, 2 + search.len() as u16);
  screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
  let mut i = 1;
  for choice in matches.iter() {
    if i == index + 1 {
      screen.write_inverted(choice.as_slice());
    } else {
      screen.write(choice.as_slice());
    }
    if i >= screen.visible_choices as usize {
      return;
    } else {
      screen.write("\n");
    }
    i += 1;
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  while let Ok(mut s) = stdin.read_line() {
    trim(&mut s);
    lines.push(s);
  }

  lines
}

fn trim(s: &mut String) {
  while let Some(x) = s.pop() {
    if x != '\n' {
      s.push(x);
      return;
    }
  }
}
<commit_msg>Extract Search struct<commit_after>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();
  let initial_search = args.initial_search.clone();
  if args.use_first {
    let matches = matching::compute_matches(&choices, initial_search.as_slice());
    println!("{}", matches[0]);
    return;
  } else {
    event_loop(choices, initial_search.as_slice());
  }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
  let mut screen = Screen::open_screen();
  let mut index = 0;

  let mut search = Search {
    query: initial_search.to_string(),
    matches: Vec::new(),
    stale: true,
  };
  loop {
    if search.stale {
      search.matches = matching::compute_matches(&choices, search.query.as_slice());
      search.stale = false;
    }

    draw_screen(&mut screen, &search.matches, search.query.as_slice(), choices.len(), index);

    let chars = screen.get_buffered_keys();
    for char in chars.iter() {
      match *char {
        Char(x) => {
          search.query.push(x);
          index = 0;
          search.stale = true;
        }
        Backspace => { search.query.pop(); search.stale = true; }
        Control('h') => { search.query.pop(); search.stale = true; }
        Control('u') => { search.query.clear(); search.stale = true; }
        Control('c') => { return; }
        Control('n') => { index = min(index + 1, min(screen.visible_choices as usize - 1, search.matches.len() - 1)); }
        Control('p') => { index = if index == 0 { 0 } else { index - 1 }; }
        Enter => {
          screen.move_cursor_to_bottom();
          if search.stale {
            search.matches = matching::compute_matches(&choices, search.query.as_slice());
          }
          println!("{}", search.matches[index]);
          return;
        }
        _ => panic!("Unexpected input"),
      }
    }
  }
}

struct Search<'a> {
  query: String,
  matches: Vec<&'a String>,
  stale: bool,
}

fn draw_screen(screen: &mut Screen, matches: &Vec<&String>, search: &str, choices: usize, index: usize) {
  screen.hide_cursor();
  screen.blank_screen();
  screen.write(format!("> {} ({} choices)\n", search, choices).as_slice());

  print_matches(screen, matches, index);

  let start = screen.start_line;
  screen.move_cursor(start, 2 + search.len() as u16);
  screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
  let mut i = 1;
  for choice in matches.iter() {
    if i == index + 1 {
      screen.write_inverted(choice.as_slice());
    } else {
      screen.write(choice.as_slice());
    }
    if i >= screen.visible_choices as usize {
      return;
    } else {
      screen.write("\n");
    }
    i += 1;
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  while let Ok(mut s) = stdin.read_line() {
    trim(&mut s);
    lines.push(s);
  }

  lines
}

fn trim(s: &mut String) {
  while let Some(x) = s.pop() {
    if x != '\n' {
      s.push(x);
      return;
    }
  }
}
<|endoftext|>


<commit_before>/// Struct representing the interpreter state
///
/// # todo list
/// * implement it
pub struct Interpreter {
    /// Instruction pointer
    ip: usize,
    /// Data pointer
    dp: usize,
    /// Brainfuck machine memory
    mem: Box<[u8]>,
    /// Source code
    prog: Vec<u8>,
}

/// Minimal memory size according to [Wikipedia](https://en.wikipedia.org/wiki/Brainfuck)
const MEMORY_SIZE: usize = 30000;

impl Interpreter {
    /// Create new interpreter with empty program and default memory size.
    fn new() -> Interpreter {
        Interpreter {
            ip: 0,
            dp: 0,
            mem: Box::new([0u8; MEMORY_SIZE]),
            prog: Vec::new(),
        }
    }
}

fn main() {
    println!("Hello, world!");
    let mut a = Interpreter::new();
    a.mem[0] = 1;
    assert!(a.mem[0] != 0);
}

#[cfg(test)]
mod test {
    #[test]
    #[should_panic]
    fn test_sth() {
        panic!("Test");
    }
}
<commit_msg>function declarations, unit tests<commit_after>/// Struct representing the interpreter state
///
/// # todo list
/// * implement it
pub struct Interpreter {
    /// Instruction pointer
    ip: usize,
    /// Data pointer
    dp: usize,
    /// Brainfuck machine memory
    mem: Box<[u8]>,
    /// Source code
    prog: Vec<u8>,
}

/// Minimal memory size according to [Wikipedia](https://en.wikipedia.org/wiki/Brainfuck)
const MEMORY_SIZE: usize = 30000;

impl Interpreter {
    /// Create new interpreter with empty program and default memory size.
    pub fn new() -> Interpreter {
        Interpreter {
            ip: 0,
            dp: 0,
            mem: Box::new([0u8; MEMORY_SIZE]),
            prog: Vec::new(),
        }
    }

    /// Read tape with program (where 'tape' is represented by string)
    /// Return number of valid brainfuck characters
    pub fn read_tape(&self, input: String) -> usize {
        for c in input.chars() {
            //println!("{}", c);
            match c {
                '+' | '-' | '>' | '<' | '[' | ']' | '.' | ',' => {
                    //println!("bf code");
                },
                _ => {
                    //println!("non bf");
                }
            }
        }
        0
    }

    /// Read next bf instruction
    fn read_next_instruction(&self) {
    }

    /// Run brainfuck program
    pub fn run(&self) {
    }
}

fn main() {
    println!("Hello, world!");
    let mut a = Interpreter::new();
    a.mem[0] = 1;
    assert!(a.mem[0] != 0);
    a.read_tape("Ahoj +++>++>+".to_string());
}

#[cfg(test)]
#[test]
#[should_panic]
fn test_sth() {
    panic!("Test");
}

#[cfg(test)]
#[test]
fn test_tape_reading() {
    let bfi = Interpreter::new();
    bfi.read_tape("abcd*()@+-[]<>.,".to_string());
    assert!(bfi.prog == b"+-[]<>.,");
}

#[cfg(test)]
#[test]
fn test_read_instruction() {
    let bfi = Interpreter::new();
    bfi.read_tape("+".to_string());
    bfi.read_next_instruction();
    assert!(bfi.mem[0] == 1);

    let bfi = Interpreter::new();
    bfi.read_tape("++-".to_string());
    bfi.read_next_instruction();
    assert!(bfi.mem[0] == 1);

    let bfi = Interpreter::new();
    bfi.read_tape(">+".to_string());
    bfi.read_next_instruction();
    assert!(bfi.mem[1] == 1);

    let bfi = Interpreter::new();
    bfi.read_tape(">><+".to_string());
    bfi.read_next_instruction();
    assert!(bfi.mem[1] == 1);

    let bfi = Interpreter::new();
    bfi.read_tape("[+++>>>]+++++".to_string());
    bfi.read_next_instruction();
    assert!(bfi.ip == 8);

    let mut bfi = Interpreter::new();
    bfi.read_tape("++[+-><]+".to_string());
    bfi.ip = 7;
    bfi.read_next_instruction();
    assert!(bfi.ip == 3);
}
<|endoftext|>