<commit_before>use std::path::PathBuf;
use structopt::StructOpt;

use anyhow::Result;

use strum_macros::{
    Display,
    EnumIter,
    EnumString,
};

pub mod genes;
pub mod search_xml;
pub mod sequences;
pub mod utils;

#[derive(Debug, Display, PartialEq, Eq, Hash, EnumString, EnumIter)]
#[strum(serialize_all = "kebab-case")]
pub enum Groupable {
    #[strum(ascii_case_insensitive)]
    Base,

    #[strum(ascii_case_insensitive)]
    Crs,

    #[strum(ascii_case_insensitive)]
    Feedback,

    #[strum(ascii_case_insensitive)]
    GoAnnotations,

    #[strum(ascii_case_insensitive)]
    InteractingProteins,

    #[strum(ascii_case_insensitive)]
    InteractingRnas,

    #[strum(ascii_case_insensitive)]
    RegionInfo,

    #[strum(ascii_case_insensitive)]
    Orfs,

    #[strum(ascii_case_insensitive)]
    Precompute,

    #[strum(ascii_case_insensitive)]
    QaStatus,

    #[strum(ascii_case_insensitive)]
    R2dtHits,

    #[strum(ascii_case_insensitive)]
    RfamHits,

    #[strum(ascii_case_insensitive)]
    SoInfo,
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum GenesCommand {
    /// This command does a join between the sequence information, locus information and
    /// gene information. Any sequence not part of a gene is ignored. It then splits
    /// the resulting merged entries by assembly so it produces a directory of files,
    /// one per assmebly. These can then be merge together to produce complete gene
    /// entries. This assumes the locus and sequence information is sorted by a common
    /// id.
    SelectAndSplit {
        /// Filename to read the locus level information from
        #[structopt(parse(from_os_str))]
        locus: PathBuf,

        /// Filename to read the selected members from
        #[structopt(parse(from_os_str))]
        sequences: PathBuf,

        /// Output directory to write to, does not need to exist
        #[structopt(parse(from_os_str))]
        output: PathBuf,
    },

    /// This will take all gene member information and merge them into genes. This needs
    /// enough memory to fit information on all members from an assembly into memory
    /// at once.
    MergeAssembly {
        /// A file of all gene members for a given assembly. The file does not need to be
        /// sorted.
        #[structopt(parse(from_os_str))]
        members: PathBuf,

        /// Filename to write complete gene to
        #[structopt(parse(from_os_str))]
        output: PathBuf,
    },

    AsXml {
        /// File of the complete genes to read
        #[structopt(parse(from_os_str))]
        genes: PathBuf,

        /// Filename to write the xml data to
        #[structopt(parse(from_os_str))]
        xml_output: PathBuf,

        /// Filename to write the counts to
        #[structopt(parse(from_os_str))]
        count_output: PathBuf,
    },
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum SequenceCommand {
    Merge {
        #[structopt(parse(from_os_str))]
        base: PathBuf,

        #[structopt(parse(from_os_str))]
        crs: PathBuf,

        #[structopt(parse(from_os_str))]
        feedback: PathBuf,

        #[structopt(parse(from_os_str))]
        go_annotations: PathBuf,

        #[structopt(parse(from_os_str))]
        interacting_proteins: PathBuf,

        #[structopt(parse(from_os_str))]
        interacting_rnas: PathBuf,

        #[structopt(parse(from_os_str))]
        precompute: PathBuf,

        #[structopt(parse(from_os_str))]
        qa_status: PathBuf,

        #[structopt(parse(from_os_str))]
        r2dt_hits: PathBuf,

        #[structopt(parse(from_os_str))]
        rfam_hits: PathBuf,

        #[structopt(parse(from_os_str))]
        orfs: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename of the SO term tree metadata.
        so_term_tree: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename to write the results to, '-' means stdout
        output: PathBuf,
    },

    Normalize {
        #[structopt(parse(from_os_str))]
        /// Filename of the accessions
        accessions: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename for the merged metadata
        metadata: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename to write the results to, '-' means stdout
        output: PathBuf,
    },
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum Subcommand {
    /// Merge each distinct type of metadata into single merged entries. This assumes that
    /// all files are sorted in the same way. Additionally, the xref file must have at
    /// least one entry for all possible urs_taxids.
    Group {
        /// Type of data to group
        #[structopt(case_insensitive = true)]
        data_type: Groupable,

        /// Filename to read the results from, '-' means stdin
        #[structopt(parse(from_os_str))]
        path: PathBuf,

        /// The maximum count of the entries.
        max_count: usize,

        /// Filename to write the results to, '-' means stdout
        #[structopt(parse(from_os_str))]
        output: PathBuf,
    },

    /// Set of commands dealing with building data for sequence data export
    Sequences {
        #[structopt(subcommand)]
        command: SequenceCommand,
    },

    /// Commands dealing with building data for gene export
    Genes {
        #[structopt(subcommand)]
        command: GenesCommand,
    },
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
struct Opt {
    /// Set the logging option, more is more verbose.
    #[structopt(short = "v", long = "verbose", parse(from_occurrences))]
    verbose: u32,

    #[structopt(subcommand)]
    command: Subcommand,
}

fn main() -> Result<()> {
    let opt = Opt::from_args();

    let level = match opt.verbose {
        0 => simplelog::LevelFilter::Warn,
        1 => simplelog::LevelFilter::Info,
        2 => simplelog::LevelFilter::Debug,
        _ => simplelog::LevelFilter::Trace,
    };
    simplelog::TermLogger::init(
        level,
        simplelog::Config::default(),
        simplelog::TerminalMode::Stderr,
    )
    .unwrap_or_else(|_| eprintln!("Failed to create logger, ignore"));

    match opt.command {
        Subcommand::Group {
            data_type,
            path,
            max_count,
            output,
        } => match data_type {
            Groupable::Base => sequences::basic::group(&path, max_count, &output)?,
            Groupable::Crs => sequences::crs::group(&path, max_count, &output)?,
            Groupable::Feedback => sequences::feedback::group(&path, max_count, &output)?,
            Groupable::GoAnnotations => sequences::go_annotation::group(&path, max_count, &output)?,
            Groupable::InteractingProteins => {
                sequences::interacting_protein::group(&path, max_count, &output)?
            },
            Groupable::InteractingRnas => {
                sequences::interacting_rna::group(&path, max_count, &output)?
            },
            Groupable::RegionInfo => genes::region::group(&path, max_count, &output)?,
            Groupable::Precompute => sequences::precompute::group(&path, max_count, &output)?,
            Groupable::QaStatus => sequences::qa_status::group(&path, max_count, &output)?,
            Groupable::R2dtHits => sequences::r2dt::group(&path, max_count, &output)?,
            Groupable::RfamHits => sequences::rfam_hit::group(&path, max_count, &output)?,
            Groupable::Orfs => sequences::orf::group(&path, max_count, &output)?,
            Groupable::SoInfo => Err(anyhow::anyhow!("May not group so info"))?,
        },
        Subcommand::Sequences {
            command,
        } => match command {
            SequenceCommand::Merge {
                base,
                crs,
                feedback,
                go_annotations,
                interacting_proteins,
                interacting_rnas,
                precompute,
                qa_status,
                r2dt_hits,
                rfam_hits,
                orfs,
                so_term_tree,
                output,
            } => sequences::writers::write_merge(
                vec![
                    base,
                    crs,
                    feedback,
                    go_annotations,
                    interacting_proteins,
                    interacting_rnas,
                    precompute,
                    qa_status,
                    r2dt_hits,
                    rfam_hits,
                    orfs,
                    so_term_tree,
                ],
                &output,
            )?,
            SequenceCommand::Normalize {
                accessions,
                metadata,
                output,
            } => sequences::writers::write(&accessions, &metadata, &output)?,
        },
        Subcommand::Genes {
            command,
        } => match command {
            GenesCommand::SelectAndSplit {
                locus,
                sequences,
                output,
            } => genes::writers::write_selected_members(&locus, &sequences, &output)?,
            GenesCommand::MergeAssembly {
                members,
                output,
            } => genes::writers::write_merged_members(&members, &output)?,
            GenesCommand::AsXml {
                genes,
                xml_output,
                count_output,
            } => genes::writers::write_search_files(&genes, &xml_output, &count_output)?,
        },
    }

    Ok(())
}
<commit_msg>Fix name of r2dt file type<commit_after>use std::path::PathBuf;
use structopt::StructOpt;

use anyhow::Result;

use strum_macros::{
    Display,
    EnumIter,
    EnumString,
};

pub mod genes;
pub mod search_xml;
pub mod sequences;
pub mod utils;

#[derive(Debug, Display, PartialEq, Eq, Hash, EnumString, EnumIter)]
#[strum(serialize_all = "kebab-case")]
pub enum Groupable {
    #[strum(ascii_case_insensitive)]
    Base,

    #[strum(ascii_case_insensitive)]
    Crs,

    #[strum(ascii_case_insensitive)]
    Feedback,

    #[strum(ascii_case_insensitive)]
    GoAnnotations,

    #[strum(ascii_case_insensitive)]
    InteractingProteins,

    #[strum(ascii_case_insensitive)]
    InteractingRnas,

    #[strum(ascii_case_insensitive)]
    RegionInfo,

    #[strum(ascii_case_insensitive)]
    Orfs,

    #[strum(ascii_case_insensitive)]
    Precompute,

    #[strum(ascii_case_insensitive)]
    QaStatus,

    #[strum(ascii_case_insensitive)]
    R2dt,

    #[strum(ascii_case_insensitive)]
    RfamHits,

    #[strum(ascii_case_insensitive)]
    SoInfo,
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum GenesCommand {
    /// This command does a join between the sequence information, locus information and
    /// gene information. Any sequence not part of a gene is ignored. It then splits
    /// the resulting merged entries by assembly so it produces a directory of files,
    /// one per assmebly. These can then be merge together to produce complete gene
    /// entries. This assumes the locus and sequence information is sorted by a common
    /// id.
    SelectAndSplit {
        /// Filename to read the locus level information from
        #[structopt(parse(from_os_str))]
        locus: PathBuf,

        /// Filename to read the selected members from
        #[structopt(parse(from_os_str))]
        sequences: PathBuf,

        /// Output directory to write to, does not need to exist
        #[structopt(parse(from_os_str))]
        output: PathBuf,
    },

    /// This will take all gene member information and merge them into genes. This needs
    /// enough memory to fit information on all members from an assembly into memory
    /// at once.
    MergeAssembly {
        /// A file of all gene members for a given assembly. The file does not need to be
        /// sorted.
        #[structopt(parse(from_os_str))]
        members: PathBuf,

        /// Filename to write complete gene to
        #[structopt(parse(from_os_str))]
        output: PathBuf,
    },

    AsXml {
        /// File of the complete genes to read
        #[structopt(parse(from_os_str))]
        genes: PathBuf,

        /// Filename to write the xml data to
        #[structopt(parse(from_os_str))]
        xml_output: PathBuf,

        /// Filename to write the counts to
        #[structopt(parse(from_os_str))]
        count_output: PathBuf,
    },
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum SequenceCommand {
    Merge {
        #[structopt(parse(from_os_str))]
        base: PathBuf,

        #[structopt(parse(from_os_str))]
        crs: PathBuf,

        #[structopt(parse(from_os_str))]
        feedback: PathBuf,

        #[structopt(parse(from_os_str))]
        go_annotations: PathBuf,

        #[structopt(parse(from_os_str))]
        interacting_proteins: PathBuf,

        #[structopt(parse(from_os_str))]
        interacting_rnas: PathBuf,

        #[structopt(parse(from_os_str))]
        precompute: PathBuf,

        #[structopt(parse(from_os_str))]
        qa_status: PathBuf,

        #[structopt(parse(from_os_str))]
        r2dt_hits: PathBuf,

        #[structopt(parse(from_os_str))]
        rfam_hits: PathBuf,

        #[structopt(parse(from_os_str))]
        orfs: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename of the SO term tree metadata.
        so_term_tree: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename to write the results to, '-' means stdout
        output: PathBuf,
    },

    Normalize {
        #[structopt(parse(from_os_str))]
        /// Filename of the accessions
        accessions: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename for the merged metadata
        metadata: PathBuf,

        #[structopt(parse(from_os_str))]
        /// Filename to write the results to, '-' means stdout
        output: PathBuf,
    },
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum Subcommand {
    /// Merge each distinct type of metadata into single merged entries. This assumes that
    /// all files are sorted in the same way. Additionally, the xref file must have at
    /// least one entry for all possible urs_taxids.
    Group {
        /// Type of data to group
        #[structopt(case_insensitive = true)]
        data_type: Groupable,

        /// Filename to read the results from, '-' means stdin
        #[structopt(parse(from_os_str))]
        path: PathBuf,

        /// The maximum count of the entries.
        max_count: usize,

        /// Filename to write the results to, '-' means stdout
        #[structopt(parse(from_os_str))]
        output: PathBuf,
    },

    /// Set of commands dealing with building data for sequence data export
    Sequences {
        #[structopt(subcommand)]
        command: SequenceCommand,
    },

    /// Commands dealing with building data for gene export
    Genes {
        #[structopt(subcommand)]
        command: GenesCommand,
    },
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
struct Opt {
    /// Set the logging option, more is more verbose.
    #[structopt(short = "v", long = "verbose", parse(from_occurrences))]
    verbose: u32,

    #[structopt(subcommand)]
    command: Subcommand,
}

fn main() -> Result<()> {
    let opt = Opt::from_args();

    let level = match opt.verbose {
        0 => simplelog::LevelFilter::Warn,
        1 => simplelog::LevelFilter::Info,
        2 => simplelog::LevelFilter::Debug,
        _ => simplelog::LevelFilter::Trace,
    };
    simplelog::TermLogger::init(
        level,
        simplelog::Config::default(),
        simplelog::TerminalMode::Stderr,
    )
    .unwrap_or_else(|_| eprintln!("Failed to create logger, ignore"));

    match opt.command {
        Subcommand::Group {
            data_type,
            path,
            max_count,
            output,
        } => match data_type {
            Groupable::Base => sequences::basic::group(&path, max_count, &output)?,
            Groupable::Crs => sequences::crs::group(&path, max_count, &output)?,
            Groupable::Feedback => sequences::feedback::group(&path, max_count, &output)?,
            Groupable::GoAnnotations => sequences::go_annotation::group(&path, max_count, &output)?,
            Groupable::InteractingProteins => {
                sequences::interacting_protein::group(&path, max_count, &output)?
            },
            Groupable::InteractingRnas => {
                sequences::interacting_rna::group(&path, max_count, &output)?
            },
            Groupable::RegionInfo => genes::region::group(&path, max_count, &output)?,
            Groupable::Precompute => sequences::precompute::group(&path, max_count, &output)?,
            Groupable::QaStatus => sequences::qa_status::group(&path, max_count, &output)?,
            Groupable::R2dt=> sequences::r2dt::group(&path, max_count, &output)?,
            Groupable::RfamHits => sequences::rfam_hit::group(&path, max_count, &output)?,
            Groupable::Orfs => sequences::orf::group(&path, max_count, &output)?,
            Groupable::SoInfo => Err(anyhow::anyhow!("May not group so info"))?,
        },
        Subcommand::Sequences {
            command,
        } => match command {
            SequenceCommand::Merge {
                base,
                crs,
                feedback,
                go_annotations,
                interacting_proteins,
                interacting_rnas,
                precompute,
                qa_status,
                r2dt_hits,
                rfam_hits,
                orfs,
                so_term_tree,
                output,
            } => sequences::writers::write_merge(
                vec![
                    base,
                    crs,
                    feedback,
                    go_annotations,
                    interacting_proteins,
                    interacting_rnas,
                    precompute,
                    qa_status,
                    r2dt_hits,
                    rfam_hits,
                    orfs,
                    so_term_tree,
                ],
                &output,
            )?,
            SequenceCommand::Normalize {
                accessions,
                metadata,
                output,
            } => sequences::writers::write(&accessions, &metadata, &output)?,
        },
        Subcommand::Genes {
            command,
        } => match command {
            GenesCommand::SelectAndSplit {
                locus,
                sequences,
                output,
            } => genes::writers::write_selected_members(&locus, &sequences, &output)?,
            GenesCommand::MergeAssembly {
                members,
                output,
            } => genes::writers::write_merged_members(&members, &output)?,
            GenesCommand::AsXml {
                genes,
                xml_output,
                count_output,
            } => genes::writers::write_search_files(&genes, &xml_output, &count_output)?,
        },
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate tempfile;
extern crate gluon;
extern crate tokio;

use std::fs;
use tempfile::NamedTempFile;
use gluon::vm::api::{Hole, OpaqueValue, ValueRef, IO};
use gluon::{new_vm, Compiler, Thread};

#[macro_use]
mod support;

use support::*;

#[test]
fn read_file() {
    let _ = ::env_logger::try_init();

    let thread = new_vm();
    let text = r#"
        let prelude = import! std.prelude
        let array = import! std.array
        let { assert }  = import! std.test
        let io @ { ? } = import! std.io
        let { wrap } = io.applicative
        let { ? } = import! std.byte
        let { unwrap } = import! std.option
        let { flat_map, (>>=) } = import! std.monad

        do file = io.open_file "Cargo.toml"
        do bytes = io.read_file file 9
        let bytes = unwrap bytes

        assert (array.len bytes == 9)
        assert (array.index bytes 0 == 91b) // [
        assert (array.index bytes 1 == 112b) // p

        wrap (array.index bytes 8)
        "#;
    let result = Compiler::new()
        .run_io(true)
        .run_expr::<IO<u8>>(&thread, "<top>", text);

    match result {
        Ok((IO::Value(value), _)) => assert_eq!(value, b']'),
        Ok((IO::Exception(err), _)) => assert!(false, "{}", err),
        Err(err) => assert!(false, "{}", err),
    }
}

#[test]
fn write_and_flush_file() {
    let _ = ::env_logger::try_init();

    let file =  NamedTempFile::new().unwrap();

    let thread = new_vm();
    let text = format!(
        r#"
        let {{ assert }} = import! std.test
        let {{ open_file, write_slice_file, flush_file, ? }} = import! std.io

        do file = open_file "{path}"
        do bytes_written = write_slice_file file [1b, 2b, 3b, 4b] 0 4
        assert (bytes_written == 4)
        flush_file file
        "#,
        path = file.path().to_str().unwrap()
    );

    Compiler::new()
        .run_io(true)
        .run_expr::<IO<()>>(&thread, "<top>", &text)
        .unwrap_or_else(|err| panic!("{}", err));

    let content = fs::read(file.path()).unwrap();
    assert_eq!(content, vec![1, 2, 3, 4]);
}

test_expr!{ no_io_eval,
r#"
let { error } = import! std.prim
let io = import! std.io
let x = io.flat_map (\x -> error "NOOOOOOOO") (io.println "1")
in { x }
"#
}

test_expr! { io_print,
r#"
let io = import! std.io
io.print "123"
"#
}

#[test]
fn run_expr_int() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io = import! std.io
        let { flat_map } = io.monad
        do result = io.run_expr "123"
        io.applicative.wrap result.value
    "#;
    let mut vm = make_vm();
    let (result, _) = Compiler::new()
        .run_io(true)
        .run_expr::<IO<String>>(&mut vm, "<top>", text)
        .unwrap();
    match result {
        IO::Value(result) => {
            let expected = "123";
            assert_eq!(result, expected);
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

test_expr! { io run_expr_io,
r#"
let io = import! std.io
io.flat_map (\x -> io.wrap 100)
            (io.run_expr "
                let io = import! \"std/io.glu\"
                io.print \"123\"
            ")
"#,
100i32
}

#[test]
fn dont_execute_io_in_run_expr_async() {
    let _ = ::env_logger::try_init();
    let vm = make_vm();
    let expr = r#"
let prelude  = import! std.prelude
let io = import! std.io
let { wrap } = io.applicative
wrap 123
"#;
    let value = Compiler::new()
        .run_expr::<OpaqueValue<&Thread, Hole>>(&vm, "example", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    assert!(
        value.0.get_ref() != ValueRef::Int(123),
        "Unexpected {:?}",
        value.0
    );
}

#[test]
fn spawn_on_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { applicative = { wrap }, monad = { flat_map } } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> wrap "abc")
        action
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_runexpr() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io@{ applicative = applicative@{ wrap }, monad = { flat_map }, ? } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> io.run_expr "123")
        do x = action
        do _ = io.println x.value
        wrap x.value
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_do_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative
        let { join } = import! std.monad

        let counter = ref 0

        do child = thread.new_thread ()
        let action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = join action
        do _ = join action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(2));
}

#[test]
fn spawn_on_force_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative

        let counter = ref 0

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = action
        do _ = action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(1));
}

#[test]
fn spawn_on_runexpr_in_catch() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let prelude = import! std.prelude
        let io @ { ? } = import! std.io
        let { Applicative, (*>), wrap } = import! std.applicative
        let { flat_map, (>>=) } = import! std.monad
        let thread = import! std.thread

        let action =
            do eval_thread = thread.new_thread ()
            let f _ = io.run_expr "123"
            do a = thread.spawn_on eval_thread f
            do result = a
            wrap result.value
        (io.catch action wrap >>= io.println) *> wrap "123"
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn io_error_in_catch1() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io = import! std.io
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch2() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let { (*>) } = import! std.applicative
        let io @ { ? } = import! std.io
        io.catch (io.println "asd" *> io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch3() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io @ { ? } = import! std.io
        let { flat_map } = import! std.monad
        do _ = io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error2")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error2".to_string());

    assert_eq!(result, expected);
}
<commit_msg>test: Fix write_and_flush_file<commit_after>extern crate env_logger;
extern crate tempfile;
extern crate gluon;
extern crate tokio;

use std::fs;
use tempfile::NamedTempFile;
use gluon::vm::api::{Hole, OpaqueValue, ValueRef, IO};
use gluon::{new_vm, Compiler, Thread};

#[macro_use]
mod support;

use support::*;

#[test]
fn read_file() {
    let _ = ::env_logger::try_init();

    let thread = new_vm();
    let text = r#"
        let prelude = import! std.prelude
        let array = import! std.array
        let { assert }  = import! std.test
        let io @ { ? } = import! std.io
        let { wrap } = io.applicative
        let { ? } = import! std.byte
        let { unwrap } = import! std.option
        let { flat_map, (>>=) } = import! std.monad

        do file = io.open_file "Cargo.toml"
        do bytes = io.read_file file 9
        let bytes = unwrap bytes

        assert (array.len bytes == 9)
        assert (array.index bytes 0 == 91b) // [
        assert (array.index bytes 1 == 112b) // p

        wrap (array.index bytes 8)
        "#;
    let result = Compiler::new()
        .run_io(true)
        .run_expr::<IO<u8>>(&thread, "<top>", text);

    match result {
        Ok((IO::Value(value), _)) => assert_eq!(value, b']'),
        Ok((IO::Exception(err), _)) => assert!(false, "{}", err),
        Err(err) => assert!(false, "{}", err),
    }
}

#[test]
fn write_and_flush_file() {
    let _ = ::env_logger::try_init();

    let file =  NamedTempFile::new().unwrap();

    let thread = new_vm();
    let text = format!(
        r#"
        let {{ assert }} = import! std.test
        let {{ OpenOptions, open_file_with, write_slice_file, flush_file, ? }} = import! std.io

        do file = open_file_with "{path}" [Write]
        do bytes_written = write_slice_file file [1b, 2b, 3b, 4b] 0 4
        assert (bytes_written == 4)
        flush_file file
        "#,
        path = file.path().to_str().unwrap()
    );

    Compiler::new()
        .run_io(true)
        .run_expr::<IO<()>>(&thread, "<top>", &text)
        .unwrap_or_else(|err| panic!("{}", err));

    let content = fs::read(file.path()).unwrap();
    assert_eq!(content, vec![1, 2, 3, 4]);
}

test_expr!{ no_io_eval,
r#"
let { error } = import! std.prim
let io = import! std.io
let x = io.flat_map (\x -> error "NOOOOOOOO") (io.println "1")
in { x }
"#
}

test_expr! { io_print,
r#"
let io = import! std.io
io.print "123"
"#
}

#[test]
fn run_expr_int() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io = import! std.io
        let { flat_map } = io.monad
        do result = io.run_expr "123"
        io.applicative.wrap result.value
    "#;
    let mut vm = make_vm();
    let (result, _) = Compiler::new()
        .run_io(true)
        .run_expr::<IO<String>>(&mut vm, "<top>", text)
        .unwrap();
    match result {
        IO::Value(result) => {
            let expected = "123";
            assert_eq!(result, expected);
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

test_expr! { io run_expr_io,
r#"
let io = import! std.io
io.flat_map (\x -> io.wrap 100)
            (io.run_expr "
                let io = import! \"std/io.glu\"
                io.print \"123\"
            ")
"#,
100i32
}

#[test]
fn dont_execute_io_in_run_expr_async() {
    let _ = ::env_logger::try_init();
    let vm = make_vm();
    let expr = r#"
let prelude  = import! std.prelude
let io = import! std.io
let { wrap } = io.applicative
wrap 123
"#;
    let value = Compiler::new()
        .run_expr::<OpaqueValue<&Thread, Hole>>(&vm, "example", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    assert!(
        value.0.get_ref() != ValueRef::Int(123),
        "Unexpected {:?}",
        value.0
    );
}

#[test]
fn spawn_on_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { applicative = { wrap }, monad = { flat_map } } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> wrap "abc")
        action
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "abc");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_runexpr() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let io@{ applicative = applicative@{ wrap }, monad = { flat_map }, ? } = import! std.io
        let thread = import! std.thread

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ -> io.run_expr "123")
        do x = action
        do _ = io.println x.value
        wrap x.value
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn spawn_on_do_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative
        let { join } = import! std.monad

        let counter = ref 0

        do child = thread.new_thread ()
        let action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = join action
        do _ = join action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(2));
}

#[test]
fn spawn_on_force_action_twice() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let { ? } = import! std.io
        let { ref, (<-), load } = import! std.reference
        let thread = import! std.thread
        let { wrap } = import! std.applicative

        let counter = ref 0

        do child = thread.new_thread ()
        do action = thread.spawn_on child (\_ ->
                counter <- (load counter + 1)
                wrap ())
        do _ = action
        do _ = action
        wrap (load counter)
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<i32>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    assert_eq!(result, IO::Value(1));
}

#[test]
fn spawn_on_runexpr_in_catch() {
    let _ = ::env_logger::try_init();

    let text = r#"
        let prelude = import! std.prelude
        let io @ { ? } = import! std.io
        let { Applicative, (*>), wrap } = import! std.applicative
        let { flat_map, (>>=) } = import! std.monad
        let thread = import! std.thread

        let action =
            do eval_thread = thread.new_thread ()
            let f _ = io.run_expr "123"
            do a = thread.spawn_on eval_thread f
            do result = a
            wrap result.value
        (io.catch action wrap >>= io.println) *> wrap "123"
    "#;

    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
    let vm = make_vm();
    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }

    let (result, _) = runtime
        .block_on(
            Compiler::new()
                .run_io(true)
                .run_expr_async::<IO<String>>(&vm, "<top>", text),
        )
        .unwrap_or_else(|err| panic!("{}", err));
    match result {
        IO::Value(result) => {
            assert_eq!(result, "123");
        }
        IO::Exception(err) => panic!("{}", err),
    }
}

#[test]
fn io_error_in_catch1() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io = import! std.io
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch2() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let { (*>) } = import! std.applicative
        let io @ { ? } = import! std.io
        io.catch (io.println "asd" *> io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error".to_string());

    assert_eq!(result, expected);
}

#[test]
fn io_error_in_catch3() {
    let _ = ::env_logger::try_init();

    let expr = r#"
        let io @ { ? } = import! std.io
        let { flat_map } = import! std.monad
        do _ = io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error")
        io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error2")
    "#;

    let vm = make_vm();

    let (result, _) = Compiler::new()
        .run_io(true)
        .implicit_prelude(false)
        .run_expr::<IO<String>>(&vm, "<top>", expr)
        .unwrap_or_else(|err| panic!("{}", err));
    let expected = IO::Value("error2".to_string());

    assert_eq!(result, expected);
}
<|endoftext|>


<commit_before>extern crate regex;

use std::io::prelude::*;
use std::io::BufReader;
use std::str;
use std::fs::File;
use std::collections::HashMap;
use regex::Regex;
use std::hash::{Hash, Hasher};
use std::fmt;

#[derive(Debug)]
struct Person {
    name: String,
    preferences: HashMap<String, isize>,
}

impl Person {
    fn update_preferences(&mut self, other_person_name: &str, func: &str, amount: &str) {
        let value: isize = amount.parse::<isize>().unwrap();
        match func {
            "gain" => {
                self.preferences.insert(String::from(other_person_name), value);
                return;
            }
            "lose" => {
                self.preferences.insert(String::from(other_person_name), -value);
                return;
            }
            s => {
                panic!("Unknown func '{}'", s);
            }
        }
    }
}

impl Hash for Person {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
    }
}

impl PartialEq for Person {
    fn eq(&self, other: &Person) -> bool {
        return self.name == other.name;
    }
}

impl Eq for Person {}

// impl fmt::Debug for Person {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "Person {{ x: {}, y: {} }}", self.x, self.y)
//     }
// }

struct Table {
    chairs: Vec<Person>,
}

fn part1() {
    let file = File::open("test-input.txt").unwrap();
    let reader = BufReader::new(&file);
    let mut people: HashMap<String, Person> = HashMap::new();
    let regex = Regex::new(r"(?P<person>[[:alpha:]]+).*(?P<func>gain|lose) (?P<amount>[[:digit:]]+) happiness units by sitting next to (?P<neighbor>[[:alpha:]]+)\.$").unwrap();
    for wrapped_line in reader.lines() {
        let line = wrapped_line.unwrap();
        println!("{} ", line);
        let maybe_match = regex.captures(line.as_str());
        match maybe_match {
            Some(capture) => {
                let person_name = capture.name("person").unwrap().as_str();
                let func = capture.name("func").unwrap().as_str();
                let amount = capture.name("amount").unwrap().as_str();
                let neighbor = capture.name("neighbor").unwrap().as_str();
                println!("{:?}", capture);
                let new_map: HashMap<String, isize> = HashMap::new();
                let person: &mut Person;
                if !people.contains_key(person_name) {
                    people.insert(String::from(person_name),
                                  Person {
                                      name: String::from(person_name),
                                      preferences: new_map,
                                  });
                }
                person = people.get_mut(person_name).unwrap();
                person.update_preferences(neighbor, func, amount);
            }
            None => panic!("No match for '{}'", line),
        }
    }
    println!("{:?}", people);
}

fn main() {
    part1();
}
<commit_msg>Broken fighting with rustc about lifetimes and borrowing checking...<commit_after>extern crate regex;

use std::io::prelude::*;
use std::io::BufReader;
use std::str;
use std::fs::File;
use std::collections::HashMap;
use regex::Regex;
use std::hash::{Hash, Hasher};
use std::fmt;

#[derive(Debug)]
struct Person {
    name: String,
    preferences: HashMap<String, isize>,
}

impl Person {
    fn update_preferences(&mut self, other_person_name: &str, func: &str, amount: &str) {
        let value: isize = amount.parse::<isize>().unwrap();
        match func {
            "gain" => {
                self.preferences.insert(String::from(other_person_name), value);
                return;
            }
            "lose" => {
                self.preferences.insert(String::from(other_person_name), -value);
                return;
            }
            s => {
                panic!("Unknown func '{}'", s);
            }
        }
    }

    /**
    * Calculate the total happiness of sitting by neighbors n1 and n2.
    */
    fn total_happiness(&self, n1: &Person, n2: &Person) -> isize {
        let pn1 = match self.preferences.get(n1.name.as_str()) {
            Some(v) => *v,
            None => 0,
        };
        let pn2 = match self.preferences.get(n2.name.as_str()) {
            Some(v) => *v,
            None => 0,
        };
        return pn1 + pn2;
    }
}

struct Table {
    chairs: Vec<Person>,
}

// impl fmt::Debug for Table {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "Table {{ chairs: }}")
//     }
// }

impl Table {
    fn contains_name(&self, name: &str) -> bool {
        for p in &(self.chairs) {
            if p.name == name {
                return true;
            }
        }
        return false;
    }

    fn get_name(&self, name: &str) -> Option<&mut Person> {
        for p in self.chairs {
            if p.name == name {
                return Some(p);
            }
        }
        return None;
    }

    fn neighbors(&self, index: usize) -> (&Person, &Person) {
        if index == 0 {
            return (&self.chairs[&self.chairs.len() - 1], &self.chairs[1]);
        } else if index == (self.chairs.len() - 1) {
            return (&self.chairs[index - 1], &self.chairs[0]);
        } else {
            return (&self.chairs[index - 1], &self.chairs[index + 1]);
        }
    }

    fn total_happiness(&self) -> isize {
        let mut happiness: isize = 0;
        for (i, p) in self.chairs.iter().enumerate() {
            let (n1, n2) = self.neighbors(i);
            happiness += p.total_happiness(n1, n2);
        }
        return happiness;
    }
}

fn part1() {
    let file = File::open("test-input.txt").unwrap();
    let reader = BufReader::new(&file);
    let mut table = Table { chairs: Vec::with_capacity(10) };
    let regex = Regex::new(r"(?P<person>[[:alpha:]]+).*(?P<func>gain|lose) (?P<amount>[[:digit:]]+) happiness units by sitting next to (?P<neighbor>[[:alpha:]]+)\.$").unwrap();
    for wrapped_line in reader.lines() {
        let line = wrapped_line.unwrap();
        println!("{} ", line);
        let maybe_match = regex.captures(line.as_str());
        match maybe_match {
            Some(capture) => {
                let person_name = capture.name("person").unwrap().as_str();
                let func = capture.name("func").unwrap().as_str();
                let amount = capture.name("amount").unwrap().as_str();
                let neighbor = capture.name("neighbor").unwrap().as_str();
                println!("{:?}", capture);
                let new_map: HashMap<String, isize> = HashMap::new();
                if !table.contains_name(person_name) {
                    let person = Person {
                        name: String::from(person_name),
                        preferences: new_map,
                    };
                    table.chairs.push(person);
                }
                let mut person = table.get_name(person_name).unwrap();
                person.update_preferences(neighbor, func, amount);
            }
            None => panic!("No match for '{}'", line),
        }
    }
}

fn main() {
    part1();
}
<|endoftext|>


<commit_before><commit_msg>Remove toggles.rs, unused<commit_after><|endoftext|>


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

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

use lib::DownloadError;

use CONFIG;

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

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

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

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

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

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

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

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

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

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

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

        Ok(true)
    }

    ///Trys to get the original name of a file, while checking for availability
    ///
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_filename_process());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

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

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_file_name: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

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

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

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

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

        try!(child.wait());

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

        Ok(id_list)
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        try!(child.wait());

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

        Ok(out)
    }

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

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

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

    ///Check if is AAC file
    fn is_aac(&self) -> bool {
        CONFIG.extensions.aac.contains(&self.ddb.quality)
    }
}<commit_msg>added from-to for playlist retrieval<commit_after>extern crate regex;
use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

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

use lib::DownloadError;

use CONFIG;

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

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

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

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

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

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

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

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

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

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

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

        Ok(true)
    }

    ///Trys to get the original name of a file, while checking for availability
    ///
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_filename_process());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

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

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_file_name: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

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

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

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

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

        try!(child.wait());

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

        Ok(id_list)
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        try!(child.wait());

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

        Ok(out)
    }

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

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

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

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


<commit_before>use clap::{App, Arg};

#[test]
fn flag_subcommand_normal() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").short_flag('S').long_flag("some").arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "some", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_normal_with_alias() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .long_flag("S")
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .alias("result"),
        )
        .get_matches_from(vec!["myprog", "result", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").short_flag('S').arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "-S", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short_with_args() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").short_flag('S').arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "-St"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short_with_alias() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_alias('M')
                .short_flag_alias('B'),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short_with_aliases_vis_and_hidden() {
    let app = App::new("test").subcommand(
        App::new("some")
            .short_flag('S')
            .arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            )
            .visible_short_flag_aliases(&['M', 'B'])
            .short_flag_alias('C'),
    );
    let app1 = app.clone();
    let matches1 = app1.get_matches_from(vec!["test", "-M"]);
    assert_eq!(matches1.subcommand_name().unwrap(), "some");

    let app2 = app.clone();
    let matches2 = app2.get_matches_from(vec!["test", "-C"]);
    assert_eq!(matches2.subcommand_name().unwrap(), "some");

    let app3 = app.clone();
    let matches3 = app3.get_matches_from(vec!["test", "-B"]);
    assert_eq!(matches3.subcommand_name().unwrap(), "some");
}

#[test]
fn flag_subcommand_short_with_aliases() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_aliases(&['M', 'B']),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
#[should_panic]
fn flag_subcommand_short_with_alias_hyphen() {
    let _ = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_alias('-'),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
}

#[test]
#[should_panic]
fn flag_subcommand_short_with_aliases_hyphen() {
    let _ = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_aliases(&['-', '-', '-']),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
}

#[test]
fn flag_subcommand_long() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").long_flag("some").arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "--some", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_long_with_alias() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .long_flag("some")
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .alias("result"),
        )
        .get_matches_from(vec!["myprog", "--result", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_multiple() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .long_flag("some")
                .arg(Arg::from("-f, --flag 'some flag'"))
                .arg(Arg::from("-p, --print 'print something'"))
                .subcommand(
                    App::new("result")
                        .short_flag('R')
                        .long_flag("result")
                        .arg(Arg::from("-f, --flag 'some flag'"))
                        .arg(Arg::from("-p, --print 'print something'")),
                ),
        )
        .get_matches_from(vec!["myprog", "-SfpRfp"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("flag"));
    assert!(sub_matches.is_present("print"));
    assert_eq!(sub_matches.subcommand_name().unwrap(), "result");
    let result_matches = sub_matches.subcommand_matches("result").unwrap();
    assert!(result_matches.is_present("flag"));
    assert!(result_matches.is_present("print"));
}

#[test]
#[should_panic = "Short option names must be unique for each argument, but \'-f\' is used by both an App named \'some\' and an Arg named \'test\'"]
fn flag_subcommand_short_conflict_with_arg() {
    let _ = App::new("test")
        .subcommand(App::new("some").short_flag('f').long_flag("some"))
        .arg(Arg::new("test").short('f'))
        .get_matches_from(vec!["myprog", "-f"]);
}

#[test]
#[should_panic = "Long option names must be unique for each argument, but \'--flag\' is used by both an App named \'some\' and an Arg named \'flag\'"]
fn flag_subcommand_long_conflict_with_arg() {
    let _ = App::new("test")
        .subcommand(App::new("some").short_flag('a').long_flag("flag"))
        .arg(Arg::new("flag").long("flag"))
        .get_matches_from(vec!["myprog", "--flag"]);
}

#[test]
fn flag_subcommand_conflict_with_help() {
    let _ = App::new("test")
        .subcommand(App::new("help").short_flag('h').long_flag("help"))
        .get_matches_from(vec!["myprog", "--help"]);
}

#[test]
fn flag_subcommand_conflict_with_version() {
    let _ = App::new("test")
        .subcommand(App::new("ver").short_flag('V').long_flag("version"))
        .get_matches_from(vec!["myprog", "--version"]);
}
<commit_msg>fix: Flag Subcommands tests behind debug_asserts<commit_after>use clap::{App, Arg};

#[test]
fn flag_subcommand_normal() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").short_flag('S').long_flag("some").arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "some", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_normal_with_alias() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .long_flag("S")
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .alias("result"),
        )
        .get_matches_from(vec!["myprog", "result", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").short_flag('S').arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "-S", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short_with_args() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").short_flag('S').arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "-St"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short_with_alias() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_alias('M')
                .short_flag_alias('B'),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_short_with_aliases_vis_and_hidden() {
    let app = App::new("test").subcommand(
        App::new("some")
            .short_flag('S')
            .arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            )
            .visible_short_flag_aliases(&['M', 'B'])
            .short_flag_alias('C'),
    );
    let app1 = app.clone();
    let matches1 = app1.get_matches_from(vec!["test", "-M"]);
    assert_eq!(matches1.subcommand_name().unwrap(), "some");

    let app2 = app.clone();
    let matches2 = app2.get_matches_from(vec!["test", "-C"]);
    assert_eq!(matches2.subcommand_name().unwrap(), "some");

    let app3 = app.clone();
    let matches3 = app3.get_matches_from(vec!["test", "-B"]);
    assert_eq!(matches3.subcommand_name().unwrap(), "some");
}

#[test]
fn flag_subcommand_short_with_aliases() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_aliases(&['M', 'B']),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
#[should_panic]
fn flag_subcommand_short_with_alias_hyphen() {
    let _ = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_alias('-'),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
}

#[test]
#[should_panic]
fn flag_subcommand_short_with_aliases_hyphen() {
    let _ = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .short_flag_aliases(&['-', '-', '-']),
        )
        .get_matches_from(vec!["myprog", "-Bt"]);
}

#[test]
fn flag_subcommand_long() {
    let matches = App::new("test")
        .subcommand(
            App::new("some").long_flag("some").arg(
                Arg::new("test")
                    .short('t')
                    .long("test")
                    .about("testing testing"),
            ),
        )
        .get_matches_from(vec!["myprog", "--some", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_long_with_alias() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .long_flag("some")
                .arg(
                    Arg::new("test")
                        .short('t')
                        .long("test")
                        .about("testing testing"),
                )
                .alias("result"),
        )
        .get_matches_from(vec!["myprog", "--result", "--test"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("test"));
}

#[test]
fn flag_subcommand_multiple() {
    let matches = App::new("test")
        .subcommand(
            App::new("some")
                .short_flag('S')
                .long_flag("some")
                .arg(Arg::from("-f, --flag 'some flag'"))
                .arg(Arg::from("-p, --print 'print something'"))
                .subcommand(
                    App::new("result")
                        .short_flag('R')
                        .long_flag("result")
                        .arg(Arg::from("-f, --flag 'some flag'"))
                        .arg(Arg::from("-p, --print 'print something'")),
                ),
        )
        .get_matches_from(vec!["myprog", "-SfpRfp"]);
    assert_eq!(matches.subcommand_name().unwrap(), "some");
    let sub_matches = matches.subcommand_matches("some").unwrap();
    assert!(sub_matches.is_present("flag"));
    assert!(sub_matches.is_present("print"));
    assert_eq!(sub_matches.subcommand_name().unwrap(), "result");
    let result_matches = sub_matches.subcommand_matches("result").unwrap();
    assert!(result_matches.is_present("flag"));
    assert!(result_matches.is_present("print"));
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Short option names must be unique for each argument, but \'-f\' is used by both an App named \'some\' and an Arg named \'test\'"]
fn flag_subcommand_short_conflict_with_arg() {
    let _ = App::new("test")
        .subcommand(App::new("some").short_flag('f').long_flag("some"))
        .arg(Arg::new("test").short('f'))
        .get_matches_from(vec!["myprog", "-f"]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Long option names must be unique for each argument, but \'--flag\' is used by both an App named \'some\' and an Arg named \'flag\'"]
fn flag_subcommand_long_conflict_with_arg() {
    let _ = App::new("test")
        .subcommand(App::new("some").short_flag('a').long_flag("flag"))
        .arg(Arg::new("flag").long("flag"))
        .get_matches_from(vec!["myprog", "--flag"]);
}

#[test]
fn flag_subcommand_conflict_with_help() {
    let _ = App::new("test")
        .subcommand(App::new("help").short_flag('h').long_flag("help"))
        .get_matches_from(vec!["myprog", "--help"]);
}

#[test]
fn flag_subcommand_conflict_with_version() {
    let _ = App::new("test")
        .subcommand(App::new("ver").short_flag('V').long_flag("version"))
        .get_matches_from(vec!["myprog", "--version"]);
}
<|endoftext|>


<commit_before>use html::HtmlRenderer;
use book::Book;
use parser::Parser;
use token::Token;
use super::test_eq;

fn ast_to_html(v: &[Token]) -> String {
    let mut book = Book::new(&[]);
    book.options.set("numbering", "0").unwrap();
    let mut html = HtmlRenderer::new(&book);
    html.render_vec(v).unwrap()
}

#[test]
fn html_combination() {
    let doc = "
Foo
===

```rust
fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}
```

Bar
---

Some paragraph

* a list
    * inside a list
* another item

3. three
4. four
5. five

[& some link](http://foo/bar?baz=42&coin=plop)
";
    let expected = r#"
Foo

fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}

Bar

Some paragraph

a list

inside a list

another item

three

four

five

& some link

"#;
    let actual = ast_to_html(&Parser::new().parse(doc).unwrap());
    println!("ecpected:\n {}", expected);
    println!("actual:\n {}", actual);
    test_eq(&actual, &expected);
}
<commit_msg>Use Renderer in tests/html.rs<commit_after>use html::HtmlRenderer;
use book::Book;
use parser::Parser;
use token::Token;
use super::test_eq;
use renderer::Renderer;

fn ast_to_html(v: &[Token]) -> String {
    let mut book = Book::new(&[]);
    book.options.set("numbering", "0").unwrap();
    let mut html = HtmlRenderer::new(&book);
    html.render_vec(v).unwrap()
}

#[test]
fn html_combination() {
    let doc = "
Foo
===

```rust
fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}
```

Bar
---

Some paragraph

* a list
    * inside a list
* another item

3. three
4. four
5. five

[& some link](http://foo/bar?baz=42&coin=plop)
";
    let expected = r#"
Foo

fn min(x: &u32, y: u32) -> &u32 {
    if x < y { x } else { y }
}

Bar

Some paragraph

a list

inside a list

another item

three

four

five

& some link

"#;
    let actual = ast_to_html(&Parser::new().parse(doc).unwrap());
    println!("ecpected:\n {}", expected);
    println!("actual:\n {}", actual);
    test_eq(&actual, &expected);
}
<|endoftext|>