<commit_before>extern crate clap;

use clap::{App, Arg, SubCommand, AppSettings, ErrorKind};

#[test]
fn sub_command_negate_required() {
    App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from(vec!["myprog", "sub1"]);
}

#[test]
fn global_version() {
    let mut app = App::new("global_version")
        .setting(AppSettings::GlobalVersion)
        .version("1.1")
        .subcommand(SubCommand::with_name("sub1"));
    app.p.propogate_settings();
    assert_eq!(app.p.subcommands[0].p.meta.version, Some("1.1"));
}

#[test]
fn sub_command_negate_required_2() {
    let result = App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn sub_command_required() {
    let result = App::new("sc_required")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingSubcommand);
}

#[test]
fn arg_required_else_help() {
    let result = App::new("arg_required")
        .setting(AppSettings::ArgRequiredElseHelp)
        .arg(Arg::with_name("test")
               .index(1))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingArgumentOrSubcommand);
}

#[test]
fn no_bin_name() {
    let result = App::new("arg_required")
        .setting(AppSettings::NoBinaryName)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .get_matches_from_safe(vec!["testing"]);
    assert!(result.is_ok());
    let matches = result.unwrap();
    assert_eq!(matches.value_of("test").unwrap(), "testing");
}

#[test]
fn unified_help() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::UnifiedHelpMessage)
        .args_from_usage("-f, --flag 'some flag'
                          [arg1] 'some pos arg'
                          --option [opt] 'some option'");
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [OPTIONS] [arg1]

OPTIONS:
    -f, --flag            some flag
    -h, --help            Prints help information
        --option <opt>    some option
    -V, --version         Prints version information

ARGS:
    <arg1>    some pos arg"));
}

#[test]
fn skip_possible_values() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::HidePossibleValuesInHelp)
        .args(&[Arg::from_usage("-o, --opt [opt] 'some option'").possible_values(&["one", "two"]),
                Arg::from_usage("[arg1] 'some pos arg'").possible_values(&["three", "four"])]);
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [OPTIONS] [arg1]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --opt <opt>    some option

ARGS:
    <arg1>    some pos arg"));
}

#[test]
fn global_setting() {
    let mut app = App::new("test")
        .global_setting(AppSettings::ColoredHelp)
        .subcommand(SubCommand::with_name("subcmd"));
    app.p.propogate_settings();
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::ColoredHelp));
}

#[test]
fn global_settings() {
    let mut app = App::new("test")
        .global_settings(&[AppSettings::ColoredHelp, AppSettings::TrailingVarArg])
        .subcommand(SubCommand::with_name("subcmd"));
    app.p.propogate_settings();
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::ColoredHelp));
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::TrailingVarArg));

}

#[test]
fn stop_delim_values_only_pos_follows() {
    let r = App::new("onlypos")
        .setting(AppSettings::DontDelimitTrailingValues)
        .args(&[Arg::from_usage("-f [flag] 'some opt'"),
                Arg::from_usage("[arg]... 'some arg'")])
        .get_matches_from_safe(vec!["", "--", "-f", "-g,x"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert!(!m.is_present("f"));
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), &["-f", "-g,x"]);
}

#[test]
fn dont_delim_values_trailingvararg() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .setting(AppSettings::DontDelimitTrailingValues)
        .arg(
            Arg::from_usage("[opt]... 'some pos'"),
        )
        .get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["test", "--foo", "-Wl,-bar"]);
}

#[test]
fn delim_values_only_pos_follows() {
    let r = App::new("onlypos")
        .args(&[Arg::from_usage("-f [flag] 'some opt'"),
                Arg::from_usage("[arg]... 'some arg'")])
        .get_matches_from_safe(vec!["", "--", "-f", "-g,x"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert!(!m.is_present("f"));
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), &["-f", "-g,x"]);
}

#[test]
fn delim_values_trailingvararg() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::from_usage("[opt]... 'some pos'"),
        )
        .get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["test", "--foo", "-Wl,-bar"]);
}

#[test]
fn delim_values_only_pos_follows_with_delim() {
    let r = App::new("onlypos")
        .args(&[Arg::from_usage("-f [flag] 'some opt'"),
                Arg::from_usage("[arg]... 'some arg'").use_delimiter(true)])
        .get_matches_from_safe(vec!["", "--", "-f", "-g,x"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert!(!m.is_present("f"));
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), &["-f", "-g", "x"]);
}

#[test]
fn delim_values_trailingvararg_with_delim() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::from_usage("[opt]... 'some pos'").use_delimiter(true),
        )
        .get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["test", "--foo", "-Wl", "-bar"]);
}

#[test]
fn leading_hyphen_short() {
    let res = App::new("leadhy")
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(Arg::with_name("some"))
        .arg(Arg::with_name("other")
            .short("o"))
        .get_matches_from_safe(vec!["", "-bar", "-o"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert!(m.is_present("some"));
    assert!(m.is_present("other"));
    assert_eq!(m.value_of("some").unwrap(), "-bar");
}

#[test]
fn leading_hyphen_long() {
    let res = App::new("leadhy")
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(Arg::with_name("some"))
        .arg(Arg::with_name("other")
            .short("o"))
        .get_matches_from_safe(vec!["", "--bar", "-o"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert!(m.is_present("some"));
    assert!(m.is_present("other"));
    assert_eq!(m.value_of("some").unwrap(), "--bar");
}

#[test]
fn leading_hyphen_opt() {
    let res = App::new("leadhy")
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(Arg::with_name("some")
            .takes_value(true)
            .long("opt"))
        .arg(Arg::with_name("other")
            .short("o"))
        .get_matches_from_safe(vec!["", "--opt", "--bar", "-o"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert!(m.is_present("some"));
    assert!(m.is_present("other"));
    assert_eq!(m.value_of("some").unwrap(), "--bar");
}

#[test]
fn allow_negative_numbers() {
    let res = App::new("negnum")
        .setting(AppSettings::AllowNegativeNumbers)
        .arg(Arg::with_name("panum"))
        .arg(Arg::with_name("onum")
            .short("o")
            .takes_value(true))
        .get_matches_from_safe(vec!["negnum", "-20", "-o", "-1.2"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert_eq!(m.value_of("panum").unwrap(), "-20");
    assert_eq!(m.value_of("onum").unwrap(), "-1.2");
}

#[test]
fn allow_negative_numbers_fail() {
    let res = App::new("negnum")
        .setting(AppSettings::AllowNegativeNumbers)
        .arg(Arg::with_name("panum"))
        .arg(Arg::with_name("onum")
            .short("o")
            .takes_value(true))
        .get_matches_from_safe(vec!["negnum", "--foo", "-o", "-1.2"]);
    assert!(res.is_err());
    assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument)
}

#[test]
fn leading_double_hyphen_trailingvararg() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(
            Arg::from_usage("[opt]... 'some pos'"),
        )
        .get_matches_from(vec!["", "--foo", "-Wl", "bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["--foo", "-Wl", "bar"]);
}

#[test]
fn test_unset_setting() {
    let m = App::new("unset_setting");
    assert!(m.p.is_set(AppSettings::AllowInvalidUtf8));

    let m = m.unset_setting(AppSettings::AllowInvalidUtf8);
    assert!(!m.p.is_set(AppSettings::AllowInvalidUtf8));
}

#[test]
fn test_unset_settings() {
    let m = App::new("unset_settings");
    assert!(&m.p.is_set(AppSettings::AllowInvalidUtf8));
    assert!(&m.p.is_set(AppSettings::ColorAuto));

    let m = m.unset_settings(&[AppSettings::AllowInvalidUtf8,
                               AppSettings::ColorAuto]);
    assert!(!m.p.is_set(AppSettings::AllowInvalidUtf8));
    assert!(!m.p.is_set(AppSettings::ColorAuto));
}
<commit_msg>tests(DisableHelpSubcommand): adds tests for AppSettings::DisableHelpSubcommand<commit_after>extern crate clap;

use clap::{App, Arg, SubCommand, AppSettings, ErrorKind};

#[test]
fn sub_command_negate_required() {
    App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from(vec!["myprog", "sub1"]);
}

#[test]
fn global_version() {
    let mut app = App::new("global_version")
        .setting(AppSettings::GlobalVersion)
        .version("1.1")
        .subcommand(SubCommand::with_name("sub1"));
    app.p.propogate_settings();
    assert_eq!(app.p.subcommands[0].p.meta.version, Some("1.1"));
}

#[test]
fn sub_command_negate_required_2() {
    let result = App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn sub_command_required() {
    let result = App::new("sc_required")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingSubcommand);
}

#[test]
fn arg_required_else_help() {
    let result = App::new("arg_required")
        .setting(AppSettings::ArgRequiredElseHelp)
        .arg(Arg::with_name("test")
               .index(1))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingArgumentOrSubcommand);
}

#[test]
fn no_bin_name() {
    let result = App::new("arg_required")
        .setting(AppSettings::NoBinaryName)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .get_matches_from_safe(vec!["testing"]);
    assert!(result.is_ok());
    let matches = result.unwrap();
    assert_eq!(matches.value_of("test").unwrap(), "testing");
}

#[test]
fn unified_help() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::UnifiedHelpMessage)
        .args_from_usage("-f, --flag 'some flag'
                          [arg1] 'some pos arg'
                          --option [opt] 'some option'");
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [OPTIONS] [arg1]

OPTIONS:
    -f, --flag            some flag
    -h, --help            Prints help information
        --option <opt>    some option
    -V, --version         Prints version information

ARGS:
    <arg1>    some pos arg"));
}

#[test]
fn skip_possible_values() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::HidePossibleValuesInHelp)
        .args(&[Arg::from_usage("-o, --opt [opt] 'some option'").possible_values(&["one", "two"]),
                Arg::from_usage("[arg1] 'some pos arg'").possible_values(&["three", "four"])]);
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [OPTIONS] [arg1]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --opt <opt>    some option

ARGS:
    <arg1>    some pos arg"));
}

#[test]
fn global_setting() {
    let mut app = App::new("test")
        .global_setting(AppSettings::ColoredHelp)
        .subcommand(SubCommand::with_name("subcmd"));
    app.p.propogate_settings();
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::ColoredHelp));
}

#[test]
fn global_settings() {
    let mut app = App::new("test")
        .global_settings(&[AppSettings::ColoredHelp, AppSettings::TrailingVarArg])
        .subcommand(SubCommand::with_name("subcmd"));
    app.p.propogate_settings();
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::ColoredHelp));
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::TrailingVarArg));

}

#[test]
fn stop_delim_values_only_pos_follows() {
    let r = App::new("onlypos")
        .setting(AppSettings::DontDelimitTrailingValues)
        .args(&[Arg::from_usage("-f [flag] 'some opt'"),
                Arg::from_usage("[arg]... 'some arg'")])
        .get_matches_from_safe(vec!["", "--", "-f", "-g,x"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert!(!m.is_present("f"));
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), &["-f", "-g,x"]);
}

#[test]
fn dont_delim_values_trailingvararg() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .setting(AppSettings::DontDelimitTrailingValues)
        .arg(
            Arg::from_usage("[opt]... 'some pos'"),
        )
        .get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["test", "--foo", "-Wl,-bar"]);
}

#[test]
fn delim_values_only_pos_follows() {
    let r = App::new("onlypos")
        .args(&[Arg::from_usage("-f [flag] 'some opt'"),
                Arg::from_usage("[arg]... 'some arg'")])
        .get_matches_from_safe(vec!["", "--", "-f", "-g,x"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert!(!m.is_present("f"));
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), &["-f", "-g,x"]);
}

#[test]
fn delim_values_trailingvararg() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::from_usage("[opt]... 'some pos'"),
        )
        .get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["test", "--foo", "-Wl,-bar"]);
}

#[test]
fn delim_values_only_pos_follows_with_delim() {
    let r = App::new("onlypos")
        .args(&[Arg::from_usage("-f [flag] 'some opt'"),
                Arg::from_usage("[arg]... 'some arg'").use_delimiter(true)])
        .get_matches_from_safe(vec!["", "--", "-f", "-g,x"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert!(!m.is_present("f"));
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), &["-f", "-g", "x"]);
}

#[test]
fn delim_values_trailingvararg_with_delim() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::from_usage("[opt]... 'some pos'").use_delimiter(true),
        )
        .get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["test", "--foo", "-Wl", "-bar"]);
}

#[test]
fn leading_hyphen_short() {
    let res = App::new("leadhy")
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(Arg::with_name("some"))
        .arg(Arg::with_name("other")
            .short("o"))
        .get_matches_from_safe(vec!["", "-bar", "-o"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert!(m.is_present("some"));
    assert!(m.is_present("other"));
    assert_eq!(m.value_of("some").unwrap(), "-bar");
}

#[test]
fn leading_hyphen_long() {
    let res = App::new("leadhy")
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(Arg::with_name("some"))
        .arg(Arg::with_name("other")
            .short("o"))
        .get_matches_from_safe(vec!["", "--bar", "-o"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert!(m.is_present("some"));
    assert!(m.is_present("other"));
    assert_eq!(m.value_of("some").unwrap(), "--bar");
}

#[test]
fn leading_hyphen_opt() {
    let res = App::new("leadhy")
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(Arg::with_name("some")
            .takes_value(true)
            .long("opt"))
        .arg(Arg::with_name("other")
            .short("o"))
        .get_matches_from_safe(vec!["", "--opt", "--bar", "-o"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert!(m.is_present("some"));
    assert!(m.is_present("other"));
    assert_eq!(m.value_of("some").unwrap(), "--bar");
}

#[test]
fn allow_negative_numbers() {
    let res = App::new("negnum")
        .setting(AppSettings::AllowNegativeNumbers)
        .arg(Arg::with_name("panum"))
        .arg(Arg::with_name("onum")
            .short("o")
            .takes_value(true))
        .get_matches_from_safe(vec!["negnum", "-20", "-o", "-1.2"]);
    assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind);
    let m = res.unwrap();
    assert_eq!(m.value_of("panum").unwrap(), "-20");
    assert_eq!(m.value_of("onum").unwrap(), "-1.2");
}

#[test]
fn allow_negative_numbers_fail() {
    let res = App::new("negnum")
        .setting(AppSettings::AllowNegativeNumbers)
        .arg(Arg::with_name("panum"))
        .arg(Arg::with_name("onum")
            .short("o")
            .takes_value(true))
        .get_matches_from_safe(vec!["negnum", "--foo", "-o", "-1.2"]);
    assert!(res.is_err());
    assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument)
}

#[test]
fn leading_double_hyphen_trailingvararg() {
    let m = App::new("positional")
        .setting(AppSettings::TrailingVarArg)
        .setting(AppSettings::AllowLeadingHyphen)
        .arg(
            Arg::from_usage("[opt]... 'some pos'"),
        )
        .get_matches_from(vec!["", "--foo", "-Wl", "bar"]);
    assert!(m.is_present("opt"));
    assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["--foo", "-Wl", "bar"]);
}

#[test]
fn test_unset_setting() {
    let m = App::new("unset_setting");
    assert!(m.p.is_set(AppSettings::AllowInvalidUtf8));

    let m = m.unset_setting(AppSettings::AllowInvalidUtf8);
    assert!(!m.p.is_set(AppSettings::AllowInvalidUtf8));
}

#[test]
fn test_unset_settings() {
    let m = App::new("unset_settings");
    assert!(&m.p.is_set(AppSettings::AllowInvalidUtf8));
    assert!(&m.p.is_set(AppSettings::ColorAuto));

    let m = m.unset_settings(&[AppSettings::AllowInvalidUtf8,
                               AppSettings::ColorAuto]);
    assert!(!m.p.is_set(AppSettings::AllowInvalidUtf8));
    assert!(!m.p.is_set(AppSettings::ColorAuto));
}

#[test]
fn disable_help_subcommand() {
    let result = App::new("disablehelp")
        .setting(AppSettings::DisableHelpSubcommand)
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec!["", "help"]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::UnknownArgument);
}<|endoftext|>


<commit_before>//! バージョン情報。
//!
//! vergen クレートによる。
//! [build.rs] を参照。

/*
https://docs.rs/vergen/latest/vergen/

Variable	Sample
See Build to configure the following
VERGEN_BUILD_DATE	2021-02-25
VERGEN_BUILD_TIME	23:28:39.493201
VERGEN_BUILD_TIMESTAMP	2021-02-25T23:28:39.493201+00:00
VERGEN_BUILD_SEMVER	5.0.0
See Git to configure the following
VERGEN_GIT_BRANCH	feature/fun
VERGEN_GIT_COMMIT_DATE	2021-02-24
VERGEN_GIT_COMMIT_TIME	20:55:21
VERGEN_GIT_COMMIT_TIMESTAMP	2021-02-24T20:55:21+00:00
VERGEN_GIT_SEMVER	5.0.0-2-gf49246c
VERGEN_GIT_SEMVER_LIGHTWEIGHT	feature-test
VERGEN_GIT_SHA	f49246ce334567bff9f950bfd0f3078184a2738a
VERGEN_GIT_SHA_SHORT	f49246c
See Rustc to configure the following
VERGEN_RUSTC_CHANNEL	nightly
VERGEN_RUSTC_COMMIT_DATE	2021-02-24
VERGEN_RUSTC_COMMIT_HASH	a8486b64b0c87dabd045453b6c81500015d122d6
VERGEN_RUSTC_HOST_TRIPLE	x86_64-apple-darwin
VERGEN_RUSTC_LLVM_VERSION	11.0
VERGEN_RUSTC_SEMVER	1.52.0-nightly
See Cargo to configure the following
VERGEN_CARGO_FEATURES	git,build
VERGEN_CARGO_PROFILE	debug
VERGEN_CARGO_TARGET_TRIPLE	x86_64-unknown-linux-gnu
See Sysinfo to configure the following
VERGEN_SYSINFO_NAME	Manjaro Linux
VERGEN_SYSINFO_OS_VERSION	Linux Manjaro Linux
VERGEN_SYSINFO_USER	Yoda
VERGEN_SYSINFO_TOTAL_MEMORY	33 GB
VERGEN_SYSINFO_CPU_VENDOR	Authentic AMD
VERGEN_SYSINFO_CPU_CORE_COUNT	8
VERGEN_SYSINFO_CPU_NAME	cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7
VERGEN_SYSINFO_CPU_BRAND	AMD Ryzen Threadripper 1900X 8-Core Processor
VERGEN_SYSINFO_CPU_FREQUENCY	3792
*/

use once_cell::sync::Lazy;

pub const GIT_BRANCH:       &str = env!("VERGEN_GIT_BRANCH");
pub const GIT_HASH:         &str = env!("VERGEN_GIT_SHA");
pub const GIT_SEMVER:       &str = env!("VERGEN_GIT_SEMVER");
pub const GIT_DATE:         &str = env!("VERGEN_GIT_COMMIT_DATE");
pub const GIT_TIMESTAMP:    &str = env!("VERGEN_GIT_COMMIT_TIMESTAMP");
pub const BUILD_PROFILE:    &str = env!("VERGEN_CARGO_PROFILE");
pub const BUILD_TARGET:     &str = env!("VERGEN_CARGO_TARGET_TRIPLE");

pub static VERSION_INFO: Lazy<String> = Lazy::new(|| {
    format!(
"Build: {} {}
Branch: {}
{}
{}",
        BUILD_TARGET,
        BUILD_PROFILE,
        GIT_BRANCH,
        GIT_HASH,
        GIT_DATE,
    )
});
<commit_msg>git semver に変更<commit_after>//! バージョン情報。
//!
//! vergen クレートによる。
//! [build.rs] を参照。

/*
https://docs.rs/vergen/latest/vergen/

Variable	Sample
See Build to configure the following
VERGEN_BUILD_DATE	2021-02-25
VERGEN_BUILD_TIME	23:28:39.493201
VERGEN_BUILD_TIMESTAMP	2021-02-25T23:28:39.493201+00:00
VERGEN_BUILD_SEMVER	5.0.0
See Git to configure the following
VERGEN_GIT_BRANCH	feature/fun
VERGEN_GIT_COMMIT_DATE	2021-02-24
VERGEN_GIT_COMMIT_TIME	20:55:21
VERGEN_GIT_COMMIT_TIMESTAMP	2021-02-24T20:55:21+00:00
VERGEN_GIT_SEMVER	5.0.0-2-gf49246c
VERGEN_GIT_SEMVER_LIGHTWEIGHT	feature-test
VERGEN_GIT_SHA	f49246ce334567bff9f950bfd0f3078184a2738a
VERGEN_GIT_SHA_SHORT	f49246c
See Rustc to configure the following
VERGEN_RUSTC_CHANNEL	nightly
VERGEN_RUSTC_COMMIT_DATE	2021-02-24
VERGEN_RUSTC_COMMIT_HASH	a8486b64b0c87dabd045453b6c81500015d122d6
VERGEN_RUSTC_HOST_TRIPLE	x86_64-apple-darwin
VERGEN_RUSTC_LLVM_VERSION	11.0
VERGEN_RUSTC_SEMVER	1.52.0-nightly
See Cargo to configure the following
VERGEN_CARGO_FEATURES	git,build
VERGEN_CARGO_PROFILE	debug
VERGEN_CARGO_TARGET_TRIPLE	x86_64-unknown-linux-gnu
See Sysinfo to configure the following
VERGEN_SYSINFO_NAME	Manjaro Linux
VERGEN_SYSINFO_OS_VERSION	Linux Manjaro Linux
VERGEN_SYSINFO_USER	Yoda
VERGEN_SYSINFO_TOTAL_MEMORY	33 GB
VERGEN_SYSINFO_CPU_VENDOR	Authentic AMD
VERGEN_SYSINFO_CPU_CORE_COUNT	8
VERGEN_SYSINFO_CPU_NAME	cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7
VERGEN_SYSINFO_CPU_BRAND	AMD Ryzen Threadripper 1900X 8-Core Processor
VERGEN_SYSINFO_CPU_FREQUENCY	3792
*/

use once_cell::sync::Lazy;

pub const GIT_BRANCH:       &str = env!("VERGEN_GIT_BRANCH");
pub const GIT_HASH:         &str = env!("VERGEN_GIT_SHA");
pub const GIT_SEMVER:       &str = env!("VERGEN_GIT_SEMVER");
pub const GIT_DATE:         &str = env!("VERGEN_GIT_COMMIT_DATE");
pub const GIT_TIMESTAMP:    &str = env!("VERGEN_GIT_COMMIT_TIMESTAMP");
pub const BUILD_PROFILE:    &str = env!("VERGEN_CARGO_PROFILE");
pub const BUILD_TARGET:     &str = env!("VERGEN_CARGO_TARGET_TRIPLE");

pub static VERSION_INFO: Lazy<String> = Lazy::new(|| {
    format!(
"Build: {} {}
Branch: {}
{}
{}",
        BUILD_TARGET,
        BUILD_PROFILE,
        GIT_BRANCH,
        GIT_SEMVER,
        GIT_DATE,
    )
});
<|endoftext|>


<commit_before>use super::{context, ActorChildren, ActorRef};
use std::any::Any;
use uuid::Uuid;

pub trait BaseActor: Send {
    fn handle_any(&mut self, message: Box<Any + Send>);

    fn on_start(&mut self);

    fn on_stop(&mut self);
}

pub trait Actor {
    type Message: 'static;

    fn handle_message(&mut self, message: Self::Message);

    fn on_start_impl(&mut self) {}

    fn on_stop_impl(&mut self) {}

    fn with_children<F, R>(&self, f: F) -> R
    where
        F: FnOnce(&ActorChildren) -> R,
    {
        context::with(|ctx| ctx.self_ref.with_children(f))
    }

    fn sender_ref(&self) -> ActorRef {
        context::with(|ctx| ctx.sender.clone())
    }

    fn sender<T: TypedActor>(&self) -> T::RefType {
        context::with(|ctx| T::from_ref(ctx.sender.clone()))
    }

    fn reply<T: Any + Send>(&self, message: T) {
        context::with(|ctx| ctx.reply(message))
    }
}

impl<T> BaseActor for T
where
    T: Actor + Send,
{
    fn handle_any(&mut self, any_message: Box<Any + Send>) {
        match any_message.downcast() {
            Ok(message) => self.handle_message(*message),
            _ => {
                println!("Unhandled message");
            }
        }
    }

    fn on_start(&mut self) {
        self.on_start_impl()
    }

    fn on_stop(&mut self) {
        self.on_stop_impl()
    }
}

pub trait TypedActor {
    type RefType;

    fn from_ref(actor_ref: ActorRef) -> Self::RefType;
}

pub fn spawn<T>(id: Uuid, actor: T) -> T::RefType
where
    T: Actor + TypedActor + Send + 'static,
{
    context::with_mut(|ctx| {
        let actor_ref = ctx.self_ref.spawn(id, actor);
        T::from_ref(actor_ref)
    })
}
<commit_msg>Add id helper in actors<commit_after>use super::{context, ActorChildren, ActorRef};
use std::any::Any;
use uuid::Uuid;

pub trait BaseActor: Send {
    fn handle_any(&mut self, message: Box<Any + Send>);

    fn on_start(&mut self);

    fn on_stop(&mut self);
}

pub trait Actor {
    type Message: 'static;

    fn handle_message(&mut self, message: Self::Message);

    fn on_start_impl(&mut self) {}

    fn on_stop_impl(&mut self) {}

    fn id(&self) -> Uuid {
        context::with(|ctx| ctx.self_ref.id())
    }

    fn with_children<F, R>(&self, f: F) -> R
    where
        F: FnOnce(&ActorChildren) -> R,
    {
        context::with(|ctx| ctx.self_ref.with_children(f))
    }

    fn sender_ref(&self) -> ActorRef {
        context::with(|ctx| ctx.sender.clone())
    }

    fn sender<T: TypedActor>(&self) -> T::RefType {
        context::with(|ctx| T::from_ref(ctx.sender.clone()))
    }

    fn reply<T: Any + Send>(&self, message: T) {
        context::with(|ctx| ctx.reply(message))
    }
}

impl<T> BaseActor for T
where
    T: Actor + Send,
{
    fn handle_any(&mut self, any_message: Box<Any + Send>) {
        match any_message.downcast() {
            Ok(message) => self.handle_message(*message),
            _ => {
                println!("Unhandled message");
            }
        }
    }

    fn on_start(&mut self) {
        self.on_start_impl()
    }

    fn on_stop(&mut self) {
        self.on_stop_impl()
    }
}

pub trait TypedActor {
    type RefType;

    fn from_ref(actor_ref: ActorRef) -> Self::RefType;
}

pub fn spawn<T>(id: Uuid, actor: T) -> T::RefType
where
    T: Actor + TypedActor + Send + 'static,
{
    context::with_mut(|ctx| {
        let actor_ref = ctx.self_ref.spawn(id, actor);
        T::from_ref(actor_ref)
    })
}
<|endoftext|>


<commit_before>use super::ValidatorHarness;
use beacon_chain::{BeaconChain, BlockProcessingOutcome};
pub use beacon_chain::{CheckPoint, Error as BeaconChainError};
use bls::create_proof_of_possession;
use db::{
    stores::{BeaconBlockStore, BeaconStateStore},
    MemoryDB,
};
use fork_choice::BitwiseLMDGhost;
use log::debug;
use rayon::prelude::*;
use slot_clock::TestingSlotClock;
use ssz::TreeHash;
use std::collections::HashSet;
use std::fs::File;
use std::io::prelude::*;
use std::iter::FromIterator;
use std::sync::Arc;
use types::*;

/// The beacon chain harness simulates a single beacon node with `validator_count` validators connected
/// to it. Each validator is provided a borrow to the beacon chain, where it may read
/// information and submit blocks/attestations for processing.
///
/// This test harness is useful for testing validator and internal state transition logic. It
/// is not useful for testing that multiple beacon nodes can reach consensus.
pub struct BeaconChainHarness {
    pub db: Arc<MemoryDB>,
    pub beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock, BitwiseLMDGhost<MemoryDB>>>,
    pub block_store: Arc<BeaconBlockStore<MemoryDB>>,
    pub state_store: Arc<BeaconStateStore<MemoryDB>>,
    pub validators: Vec<ValidatorHarness>,
    pub spec: Arc<ChainSpec>,
}

impl BeaconChainHarness {
    /// Create a new harness with:
    ///
    /// - A keypair, `BlockProducer` and `Attester` for each validator.
    /// - A new BeaconChain struct where the given validators are in the genesis.
    pub fn new(spec: ChainSpec, validator_count: usize) -> Self {
        let db = Arc::new(MemoryDB::open());
        let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
        let state_store = Arc::new(BeaconStateStore::new(db.clone()));
        let genesis_time = 1_549_935_547; // 12th Feb 2018 (arbitrary value in the past).
        let slot_clock = TestingSlotClock::new(spec.genesis_slot.as_u64());
        let fork_choice = BitwiseLMDGhost::new(block_store.clone(), state_store.clone());
        let latest_eth1_data = Eth1Data {
            deposit_root: Hash256::zero(),
            block_hash: Hash256::zero(),
        };

        debug!("Generating validator keypairs...");

        let keypairs: Vec<Keypair> = (0..validator_count)
            .collect::<Vec<usize>>()
            .par_iter()
            .map(|_| Keypair::random())
            .collect();

        debug!("Creating validator deposits...");

        let initial_validator_deposits = keypairs
            .par_iter()
            .map(|keypair| Deposit {
                branch: vec![], // branch verification is not specified.
                index: 0,       // index verification is not specified.
                deposit_data: DepositData {
                    amount: 32_000_000_000, // 32 ETH (in Gwei)
                    timestamp: genesis_time - 1,
                    deposit_input: DepositInput {
                        pubkey: keypair.pk.clone(),
                        withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
                        proof_of_possession: create_proof_of_possession(&keypair),
                    },
                },
            })
            .collect();

        debug!("Creating the BeaconChain...");

        // Create the Beacon Chain
        let beacon_chain = Arc::new(
            BeaconChain::genesis(
                state_store.clone(),
                block_store.clone(),
                slot_clock,
                genesis_time,
                latest_eth1_data,
                initial_validator_deposits,
                spec.clone(),
                fork_choice,
            )
            .unwrap(),
        );

        let spec = Arc::new(spec);

        debug!("Creating validator producer and attester instances...");

        // Spawn the test validator instances.
        let validators: Vec<ValidatorHarness> = keypairs
            .iter()
            .map(|keypair| {
                ValidatorHarness::new(keypair.clone(), beacon_chain.clone(), spec.clone())
            })
            .collect();

        debug!("Created {} ValidatorHarnesss", validators.len());

        Self {
            db,
            beacon_chain,
            block_store,
            state_store,
            validators,
            spec,
        }
    }

    /// Move the `slot_clock` for the `BeaconChain` forward one slot.
    ///
    /// This is the equivalent of advancing a system clock forward one `SLOT_DURATION`.
    ///
    /// Returns the new slot.
    pub fn increment_beacon_chain_slot(&mut self) -> Slot {
        let slot = self.beacon_chain.present_slot() + 1;

        let nth_slot = slot
            - slot
                .epoch(self.spec.epoch_length)
                .start_slot(self.spec.epoch_length);
        let nth_epoch = slot.epoch(self.spec.epoch_length) - self.spec.genesis_epoch;
        debug!(
            "Advancing BeaconChain to slot {}, epoch {} (epoch height: {}, slot {} in epoch.).",
            slot,
            slot.epoch(self.spec.epoch_length),
            nth_epoch,
            nth_slot
        );

        self.beacon_chain.slot_clock.set_slot(slot.as_u64());
        self.beacon_chain.advance_state(slot).unwrap();
        slot
    }

    /// Gather the `FreeAttestation`s from the valiators.
    ///
    /// Note: validators will only produce attestations _once per slot_. So, if you call this twice
    /// you'll only get attestations on the first run.
    pub fn gather_free_attesations(&mut self) -> Vec<FreeAttestation> {
        let present_slot = self.beacon_chain.present_slot();

        let attesting_validators = self
            .beacon_chain
            .state
            .read()
            .get_crosslink_committees_at_slot(present_slot, &self.spec)
            .unwrap()
            .iter()
            .fold(vec![], |mut acc, (committee, _slot)| {
                acc.append(&mut committee.clone());
                acc
            });
        let attesting_validators: HashSet<usize> =
            HashSet::from_iter(attesting_validators.iter().cloned());

        let free_attestations: Vec<FreeAttestation> = self
            .validators
            .par_iter_mut()
            .enumerate()
            .filter_map(|(i, validator)| {
                if attesting_validators.contains(&i) {
                    // Advance the validator slot.
                    validator.set_slot(present_slot);

                    // Prompt the validator to produce an attestation (if required).
                    validator.produce_free_attestation().ok()
                } else {
                    None
                }
            })
            .collect();

        debug!(
            "Gathered {} FreeAttestations for slot {}.",
            free_attestations.len(),
            present_slot
        );

        free_attestations
    }

    /// Get the block from the proposer for the slot.
    ///
    /// Note: the validator will only produce it _once per slot_. So, if you call this twice you'll
    /// only get a block once.
    pub fn produce_block(&mut self) -> BeaconBlock {
        let present_slot = self.beacon_chain.present_slot();

        let proposer = self.beacon_chain.block_proposer(present_slot).unwrap();

        debug!(
            "Producing block from validator #{} for slot {}.",
            proposer, present_slot
        );

        // Ensure the validators slot clock is accurate.
        self.validators[proposer].set_slot(present_slot);
        self.validators[proposer].produce_block().unwrap()
    }

    /// Advances the chain with a BeaconBlock and attestations from all validators.
    ///
    /// This is the ideal scenario for the Beacon Chain, 100% honest participation from
    /// validators.
    pub fn advance_chain_with_block(&mut self) {
        self.increment_beacon_chain_slot();

        // Produce a new block.
        debug!("Producing block...");
        let block = self.produce_block();
        debug!("Submitting block for processing...");
        match self.beacon_chain.process_block(block) {
            Ok(BlockProcessingOutcome::ValidBlock(_)) => {}
            other => panic!("block processing failed with {:?}", other),
        };
        debug!("...block processed by BeaconChain.");

        debug!("Producing free attestations...");

        // Produce new attestations.
        let free_attestations = self.gather_free_attesations();

        debug!("Processing free attestations...");

        free_attestations.par_iter().for_each(|free_attestation| {
            self.beacon_chain
                .process_free_attestation(free_attestation.clone())
                .unwrap();
        });

        debug!("Free attestations processed.");
    }

    pub fn add_deposit(&mut self, deposit: Deposit, keypair: Option<Keypair>) {
        self.beacon_chain.receive_deposit_for_inclusion(deposit);

        // If a keypair is present, add a new `ValidatorHarness` to the rig.
        if let Some(keypair) = keypair {
            let validator =
                ValidatorHarness::new(keypair, self.beacon_chain.clone(), self.spec.clone());
            self.validators.push(validator);
        }
    }

    pub fn add_proposer_slashing(&mut self, mut proposer_slashing: ProposerSlashing) {
        let validator = &self.validators[proposer_slashing.proposer_index as usize];

        // This following code is a little awkward, but managing the data_1 and data_1 was getting
        // rather confusing. I think this is better
        let proposals = vec![
            &proposer_slashing.proposal_data_1,
            &proposer_slashing.proposal_data_2,
        ];
        let signatures: Vec<Signature> = proposals
            .iter()
            .map(|proposal_data| {
                let message = proposal_data.hash_tree_root();
                let epoch = proposal_data.slot.epoch(self.spec.epoch_length);
                let domain = self
                    .beacon_chain
                    .state
                    .read()
                    .fork
                    .get_domain(epoch, self.spec.domain_proposal);
                Signature::new(&message[..], domain, &validator.keypair.sk)
            })
            .collect();
        proposer_slashing.proposal_signature_1 = signatures[0].clone();
        proposer_slashing.proposal_signature_2 = signatures[1].clone();

        self.beacon_chain
            .receive_proposer_slashing_for_inclusion(proposer_slashing);
    }

    pub fn run_fork_choice(&mut self) {
        self.beacon_chain.fork_choice().unwrap()
    }

    /// Dump all blocks and states from the canonical beacon chain.
    pub fn chain_dump(&self) -> Result<Vec<CheckPoint>, BeaconChainError> {
        self.beacon_chain.chain_dump()
    }

    /// Write the output of `chain_dump` to a JSON file.
    pub fn dump_to_file(&self, filename: String, chain_dump: &[CheckPoint]) {
        let json = serde_json::to_string(chain_dump).unwrap();
        let mut file = File::create(filename).unwrap();
        file.write_all(json.as_bytes())
            .expect("Failed writing dump to file.");
    }
}
<commit_msg>Add fn to BeaconChainHarness validator signing<commit_after>use super::ValidatorHarness;
use beacon_chain::{BeaconChain, BlockProcessingOutcome};
pub use beacon_chain::{CheckPoint, Error as BeaconChainError};
use bls::create_proof_of_possession;
use db::{
    stores::{BeaconBlockStore, BeaconStateStore},
    MemoryDB,
};
use fork_choice::BitwiseLMDGhost;
use log::debug;
use rayon::prelude::*;
use slot_clock::TestingSlotClock;
use ssz::TreeHash;
use std::collections::HashSet;
use std::fs::File;
use std::io::prelude::*;
use std::iter::FromIterator;
use std::sync::Arc;
use types::*;

/// The beacon chain harness simulates a single beacon node with `validator_count` validators connected
/// to it. Each validator is provided a borrow to the beacon chain, where it may read
/// information and submit blocks/attestations for processing.
///
/// This test harness is useful for testing validator and internal state transition logic. It
/// is not useful for testing that multiple beacon nodes can reach consensus.
pub struct BeaconChainHarness {
    pub db: Arc<MemoryDB>,
    pub beacon_chain: Arc<BeaconChain<MemoryDB, TestingSlotClock, BitwiseLMDGhost<MemoryDB>>>,
    pub block_store: Arc<BeaconBlockStore<MemoryDB>>,
    pub state_store: Arc<BeaconStateStore<MemoryDB>>,
    pub validators: Vec<ValidatorHarness>,
    pub spec: Arc<ChainSpec>,
}

impl BeaconChainHarness {
    /// Create a new harness with:
    ///
    /// - A keypair, `BlockProducer` and `Attester` for each validator.
    /// - A new BeaconChain struct where the given validators are in the genesis.
    pub fn new(spec: ChainSpec, validator_count: usize) -> Self {
        let db = Arc::new(MemoryDB::open());
        let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
        let state_store = Arc::new(BeaconStateStore::new(db.clone()));
        let genesis_time = 1_549_935_547; // 12th Feb 2018 (arbitrary value in the past).
        let slot_clock = TestingSlotClock::new(spec.genesis_slot.as_u64());
        let fork_choice = BitwiseLMDGhost::new(block_store.clone(), state_store.clone());
        let latest_eth1_data = Eth1Data {
            deposit_root: Hash256::zero(),
            block_hash: Hash256::zero(),
        };

        debug!("Generating validator keypairs...");

        let keypairs: Vec<Keypair> = (0..validator_count)
            .collect::<Vec<usize>>()
            .par_iter()
            .map(|_| Keypair::random())
            .collect();

        debug!("Creating validator deposits...");

        let initial_validator_deposits = keypairs
            .par_iter()
            .map(|keypair| Deposit {
                branch: vec![], // branch verification is not specified.
                index: 0,       // index verification is not specified.
                deposit_data: DepositData {
                    amount: 32_000_000_000, // 32 ETH (in Gwei)
                    timestamp: genesis_time - 1,
                    deposit_input: DepositInput {
                        pubkey: keypair.pk.clone(),
                        withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
                        proof_of_possession: create_proof_of_possession(&keypair),
                    },
                },
            })
            .collect();

        debug!("Creating the BeaconChain...");

        // Create the Beacon Chain
        let beacon_chain = Arc::new(
            BeaconChain::genesis(
                state_store.clone(),
                block_store.clone(),
                slot_clock,
                genesis_time,
                latest_eth1_data,
                initial_validator_deposits,
                spec.clone(),
                fork_choice,
            )
            .unwrap(),
        );

        let spec = Arc::new(spec);

        debug!("Creating validator producer and attester instances...");

        // Spawn the test validator instances.
        let validators: Vec<ValidatorHarness> = keypairs
            .iter()
            .map(|keypair| {
                ValidatorHarness::new(keypair.clone(), beacon_chain.clone(), spec.clone())
            })
            .collect();

        debug!("Created {} ValidatorHarnesss", validators.len());

        Self {
            db,
            beacon_chain,
            block_store,
            state_store,
            validators,
            spec,
        }
    }

    /// Move the `slot_clock` for the `BeaconChain` forward one slot.
    ///
    /// This is the equivalent of advancing a system clock forward one `SLOT_DURATION`.
    ///
    /// Returns the new slot.
    pub fn increment_beacon_chain_slot(&mut self) -> Slot {
        let slot = self.beacon_chain.present_slot() + 1;

        let nth_slot = slot
            - slot
                .epoch(self.spec.epoch_length)
                .start_slot(self.spec.epoch_length);
        let nth_epoch = slot.epoch(self.spec.epoch_length) - self.spec.genesis_epoch;
        debug!(
            "Advancing BeaconChain to slot {}, epoch {} (epoch height: {}, slot {} in epoch.).",
            slot,
            slot.epoch(self.spec.epoch_length),
            nth_epoch,
            nth_slot
        );

        self.beacon_chain.slot_clock.set_slot(slot.as_u64());
        self.beacon_chain.advance_state(slot).unwrap();
        slot
    }

    /// Gather the `FreeAttestation`s from the valiators.
    ///
    /// Note: validators will only produce attestations _once per slot_. So, if you call this twice
    /// you'll only get attestations on the first run.
    pub fn gather_free_attesations(&mut self) -> Vec<FreeAttestation> {
        let present_slot = self.beacon_chain.present_slot();

        let attesting_validators = self
            .beacon_chain
            .state
            .read()
            .get_crosslink_committees_at_slot(present_slot, &self.spec)
            .unwrap()
            .iter()
            .fold(vec![], |mut acc, (committee, _slot)| {
                acc.append(&mut committee.clone());
                acc
            });
        let attesting_validators: HashSet<usize> =
            HashSet::from_iter(attesting_validators.iter().cloned());

        let free_attestations: Vec<FreeAttestation> = self
            .validators
            .par_iter_mut()
            .enumerate()
            .filter_map(|(i, validator)| {
                if attesting_validators.contains(&i) {
                    // Advance the validator slot.
                    validator.set_slot(present_slot);

                    // Prompt the validator to produce an attestation (if required).
                    validator.produce_free_attestation().ok()
                } else {
                    None
                }
            })
            .collect();

        debug!(
            "Gathered {} FreeAttestations for slot {}.",
            free_attestations.len(),
            present_slot
        );

        free_attestations
    }

    /// Get the block from the proposer for the slot.
    ///
    /// Note: the validator will only produce it _once per slot_. So, if you call this twice you'll
    /// only get a block once.
    pub fn produce_block(&mut self) -> BeaconBlock {
        let present_slot = self.beacon_chain.present_slot();

        let proposer = self.beacon_chain.block_proposer(present_slot).unwrap();

        debug!(
            "Producing block from validator #{} for slot {}.",
            proposer, present_slot
        );

        // Ensure the validators slot clock is accurate.
        self.validators[proposer].set_slot(present_slot);
        self.validators[proposer].produce_block().unwrap()
    }

    /// Advances the chain with a BeaconBlock and attestations from all validators.
    ///
    /// This is the ideal scenario for the Beacon Chain, 100% honest participation from
    /// validators.
    pub fn advance_chain_with_block(&mut self) {
        self.increment_beacon_chain_slot();

        // Produce a new block.
        debug!("Producing block...");
        let block = self.produce_block();
        debug!("Submitting block for processing...");
        match self.beacon_chain.process_block(block) {
            Ok(BlockProcessingOutcome::ValidBlock(_)) => {}
            other => panic!("block processing failed with {:?}", other),
        };
        debug!("...block processed by BeaconChain.");

        debug!("Producing free attestations...");

        // Produce new attestations.
        let free_attestations = self.gather_free_attesations();

        debug!("Processing free attestations...");

        free_attestations.par_iter().for_each(|free_attestation| {
            self.beacon_chain
                .process_free_attestation(free_attestation.clone())
                .unwrap();
        });

        debug!("Free attestations processed.");
    }

    pub fn validator_sign(
        &self,
        validator_index: usize,
        message: &[u8],
        epoch: Epoch,
        domain_type: u64,
    ) -> Option<Signature> {
        let validator = self.validators.get(validator_index)?;

        let domain = self
            .beacon_chain
            .state
            .read()
            .fork
            .get_domain(epoch, domain_type);

        Some(Signature::new(message, domain, &validator.keypair.sk))
    }

    pub fn add_deposit(&mut self, deposit: Deposit, keypair: Option<Keypair>) {
        self.beacon_chain.receive_deposit_for_inclusion(deposit);

        // If a keypair is present, add a new `ValidatorHarness` to the rig.
        if let Some(keypair) = keypair {
            let validator =
                ValidatorHarness::new(keypair, self.beacon_chain.clone(), self.spec.clone());
            self.validators.push(validator);
        }
    }

    pub fn add_proposer_slashing(&mut self, mut proposer_slashing: ProposerSlashing) {
        let validator = &self.validators[proposer_slashing.proposer_index as usize];

        // This following code is a little awkward, but managing the data_1 and data_1 was getting
        // rather confusing. I think this is better
        let proposals = vec![
            &proposer_slashing.proposal_data_1,
            &proposer_slashing.proposal_data_2,
        ];
        let signatures: Vec<Signature> = proposals
            .iter()
            .map(|proposal_data| {
                let message = proposal_data.hash_tree_root();
                let epoch = proposal_data.slot.epoch(self.spec.epoch_length);
                let domain = self
                    .beacon_chain
                    .state
                    .read()
                    .fork
                    .get_domain(epoch, self.spec.domain_proposal);
                Signature::new(&message[..], domain, &validator.keypair.sk)
            })
            .collect();
        proposer_slashing.proposal_signature_1 = signatures[0].clone();
        proposer_slashing.proposal_signature_2 = signatures[1].clone();

        self.beacon_chain
            .receive_proposer_slashing_for_inclusion(proposer_slashing);
    }

    pub fn run_fork_choice(&mut self) {
        self.beacon_chain.fork_choice().unwrap()
    }

    /// Dump all blocks and states from the canonical beacon chain.
    pub fn chain_dump(&self) -> Result<Vec<CheckPoint>, BeaconChainError> {
        self.beacon_chain.chain_dump()
    }

    /// Write the output of `chain_dump` to a JSON file.
    pub fn dump_to_file(&self, filename: String, chain_dump: &[CheckPoint]) {
        let json = serde_json::to_string(chain_dump).unwrap();
        let mut file = File::create(filename).unwrap();
        file.write_all(json.as_bytes())
            .expect("Failed writing dump to file.");
    }
}
<|endoftext|>


<commit_before>use serde::{Deserialize, Serialize};

use crate::Version;

use super::{Author, Hub};

const VERSION: Version = Version::V1_1;

#[derive(Deserialize, Serialize)]
pub struct JSONFeed {
    /// The URL of the version of the format the feed uses. This should appear
    /// at the very top, though we recognize that not all JSON generators allow
    /// for ordering.
    pub version: Version,

    /// The name of the feed, which will often correspond to the name of the
    /// website (blog, for instance), though not necessarily
    pub title: String,

    /// (optional but strongly recommended) The URL of the resource that the
    /// feed describes. This resource may or may not actually be a “home” page,
    /// but it should be an HTML page. If a feed is published on the public web,
    /// this should be considered as required. But it may not make sense in the
    /// case of a file created on a desktop computer, when that file is not
    /// shared or is shared only privately.
    pub home_page_url: Option<String>,

    /// (optional but strongly recommended) The URL of the feed, and serves as
    /// the unique identifier for the feed. As with `home_page_url`, this should
    /// be considered required for feeds on the public web.
    pub feed_url: Option<String>,

    /// Provides more detail, beyond the `title`, on what the feed is about. A
    /// feed reader may display this text.
    pub description: Option<String>,

    /// Description of the purpose of the feed. This is for the use of people
    /// looking at the raw JSON, and should be ignored by feed readers.
    pub user_comment: Option<String>,

    /// The URL of a feed that provides the next n items, where n is determined
    /// by the publisher. This allows for pagination, but with the expectation
    /// that reader software is not required to use it and probably won’t use it
    /// very often. `next_url` must not be the same as `feed_url`, and it must
    /// not be the same as a previous `next_url` (to avoid infinite loops).
    pub next_url: Option<String>,

    /// The URL of an image for the feed suitable to be used in a timeline, much
    /// the way an avatar might be used. It should be square and relatively
    /// large ― such as 512 x 512 ― so that it can be scaled-down and so that it
    /// can look good on retina displays. It should use transparency where
    /// appropriate, since it may be rendered on a non-white background.
    pub icon: Option<String>,

    /// The URL of an image for the feed suitable to be used in a source list.
    /// It should be square and relatively small, but not smaller than 64 x 64
    /// (so that it can look good on retina displays). As with `icon`, this
    /// image should use transparency where appropriate, since it may be
    /// rendered on a non-white background.
    pub favicon: Option<String>,

    /// Specifies the feed author. The author object has several members. These
    /// are all optional ― but if you provide an author object, then at least
    /// one is required.
    pub author: Option<Author>,

    /// Says whether or not the feed is finished ― that is, whether or not it
    /// will ever update again. A feed for a temporary event, such as an
    /// instance of the Olympics, could expire. If the value is true, then it’s
    /// expired. Any other value, or the absence of expired, means the feed may
    /// continue to update.
    pub expired: Option<bool>,

    /// Describes endpoints that can be used to subscribe to real-time
    /// notifications from the publisher of this feed. Each object has a type
    /// and url, both of which are required. See the section [“Subscribing to
    /// Real-time Notifications”] for details.
    ///
    /// [“Subscribing to Real-time Notifications”]:
    /// https://jsonfeed.org/version/1#subscribing-to-real-time-notifications
    pub hubs: Option<Vec<Hub>>,

    /** The items in the feed. */
    pub items: Vec<FeedItem>,
}

impl JSONFeed {
    pub fn builder(title: &str, items: Vec<FeedItem>) -> Builder {
        Builder::new(title, items)
    }
}

#[derive(Clone, Deserialize, Serialize)]
pub struct FeedItem {
    /// A unique identifier for that item for that feed over time. If an item is
    /// ever updated, the `id` should be unchanged. New items should never use a
    /// previously-used `id`. Ideally, the `id` is the full URL of the resource
    /// described by the item, since URLs make great unique identifiers.
    pub id: String,

    /// The URL of the resource described by the item. It’s the permalink. This
    /// may be the same as the id ― but should be present regardless.
    pub url: Option<String>,

    /// The URL of a page elsewhere. This is especially useful for linkblogs. If
    /// `url` links to where you’re talking about a thing, then `external_url`
    /// links to the thing you’re talking about.
    pub external_url: Option<String>,

    /// Plain text. Microblog items in particular may omit titles.
    pub title: Option<String>,

    /// The plain text of the item.
    ///
    /// At least one of `content_html` and `content_text` must be present. A
    /// Twitter-like service might use `content_text`, while a blog might use
    /// `content_html`. Use whichever makes sense for your resource. (It doesn’t
    /// even have to be the same for each item in a feed.)
    pub content_text: Option<String>,

    /// The HTML of the item. Important: the only place HTML is allowed in this
    /// format is in `content_html`.
    ///
    /// At least one of `content_html` and `content_text` must be present. A
    /// Twitter-like service might use `content_text`, while a blog might use
    /// `content_html`. Use whichever makes sense for your resource. (It doesn’t
    /// even have to be the same for each item in a feed.)
    pub content_html: Option<String>,

    /// A plain text sentence or two describing the item. This might be
    /// presented in a timeline, for instance, where a detail view would display
    /// all of `content_html` or `content_text`.
    pub summary: Option<String>,

    /// The URL of the main image for the item. This image may also appear in
    /// the `content_html` ― if so, it’s a hint to the feed reader that this is
    /// the main, featured image. Feed readers may use the image as a preview
    /// (probably resized as a thumbnail and placed in a timeline).
    pub image: Option<String>,

    /// The URL of an image to use as a banner. Some blogging systems (such as
    /// [Medium]) display a different banner image chosen to go with each post,
    /// but that image wouldn’t otherwise appear in the `content_html`. A feed
    /// reader with a detail view may choose to show this banner image at the
    /// top of the detail view, possibly with the title overlaid.
    ///
    /// [Medium]: https://medium.com/
    pub banner_image: Option<String>,

    /// Specifies the date in [RFC 3339](https://tools.ietf.org/html/rfc3339)
    /// format. (Example: `2010-02-07T14:04:00-05:00`.)
    pub date_published: Option<String>,

    /// Specifies the modification date in [RFC 3339] format.
    ///
    /// [RFC 3339]: https://www.ietf.org/rfc/rfc3339.txt
    pub date_modified: Option<String>,

    /// The same structure as the top-level `author`. If not specified in an
    /// item, then the top-level `author`, if present, is the author of the
    /// item.
    pub author: Option<Author>,

    /// Any plain text values you want. Tags tend to be just one word, but they
    /// may be anything. Note: they are not the equivalent of Twitter hashtags.
    /// Some blogging systems and other feed formats call these categories.
    pub tags: Option<String>,

    /// An individual item may have one or more attachments. List related
    /// resources. Podcasts, for instance, would include an attachment that’s an
    /// audio or video file.
    pub attachments: Option<Vec<Attachment>>,
}

#[derive(Clone, Deserialize, Serialize)]
pub struct Attachment {
    // Yeah, this is an odd type -- but that's because it's representing a JS
    // number, which is... an odd type.
    /// Specifies how long it takes to listen to or watch, when played at normal
    /// speed
    pub duration_in_seconds: Option<f64>,

    /// Specifies the type of the attachment, such as “audio/mpeg.”
    pub mime_type: String,

    // As above. No, this doesn't make any sense in the usual case, but we're
    // generating JSON. :shrug:
    /// Specifies how large the file is.
    pub size_in_bytes: Option<f64>,

    /// A name for the attachment. Important: if there are multiple attachments,
    /// and two or more have the exact same `title` (when `title` is present),
    /// then they are considered as alternate representations of the same thing.
    /// In this way a podcaster, for instance, might provide an audio recording
    /// in different formats.
    pub title: Option<String>,

    /// Specifies the location of the attachment.
    pub url: String,
}

#[derive(Default)]
pub struct Builder {
    title: String,
    items: Vec<FeedItem>,
    home_page_url: Option<String>,
    feed_url: Option<String>,
    description: Option<String>,
    user_comment: Option<String>,
    next_url: Option<String>,
    icon: Option<String>,
    favicon: Option<String>,
    author: Option<Author>,
    expired: Option<bool>,
    hubs: Option<Vec<Hub>>,
}

pub struct AuthorOptions<'a, 'n, 'u> {
    pub avatar: Option<&'a str>,
    pub name: Option<&'n str>,
    pub url: Option<&'u str>,
}

impl Builder {
    pub fn new(title: &str, items: Vec<FeedItem>) -> Builder {
        Builder {
            title: title.into(),
            items: items,
            ..Default::default()
        }
    }

    pub fn with_author(&mut self, options: &AuthorOptions) -> Result<&mut Self, String> {
        let &AuthorOptions { avatar, name, url } = options;
        let author = match (avatar, name, url) {
            (None, None, None) => {
                return Err(String::from(
                    "Cannot build `author` without at least one of name, url, and avatar",
                ))
            }
            (None, None, Some(u)) => Author::UrlOnly { url: u.into() },
            (None, Some(n), None) => Author::NameOnly { name: n.into() },
            (None, Some(n), Some(u)) => Author::NameAndUrl {
                name: n.into(),
                url: u.into(),
            },
            (Some(a), None, None) => Author::AvatarOnly { avatar: a.into() },
            (Some(a), None, Some(u)) => Author::AvatarAndUrl {
                avatar: a.into(),
                url: u.into(),
            },
            (Some(a), Some(n), None) => Author::AvatarAndName {
                avatar: a.into(),
                name: n.into(),
            },
            (Some(a), Some(n), Some(u)) => Author::All {
                avatar: a.into(),
                name: n.into(),
                url: u.into(),
            },
        };

        self.author = Some(author);

        Ok(self)
    }

    pub fn with_home_page_url(&mut self, url: &str) -> &mut Self {
        self.home_page_url = Some(url.into());
        self
    }

    pub fn with_feed_url(&mut self, url: &str) -> &mut Self {
        self.feed_url = Some(url.into());
        self
    }

    pub fn build(&self) -> JSONFeed {
        let Self {
            home_page_url,
            feed_url,
            ..
        } = self;
        let mut warnings = vec![];

        if home_page_url.is_none() {
            warnings.push("missing home_page_url for feed");
        }

        if feed_url.is_none() {
            warnings.push("missing feed_url for feed");
        }

        // TODO: use real logging!
        if warnings.len() > 0 {
            dbg!(warnings);
        }

        JSONFeed {
            version: VERSION,
            title: self.title.clone(),
            author: self.author.clone(),
            home_page_url: home_page_url.clone(),
            feed_url: feed_url.clone(),
            description: self.description.clone(),
            user_comment: self.user_comment.clone(),
            next_url: self.next_url.clone(),
            icon: self.icon.clone(),
            favicon: self.favicon.clone(),
            expired: self.expired,
            hubs: self.hubs.clone(),
            items: self.items.clone(),
        }
    }
}
<commit_msg>Finish implementing builder for JSONFeed<commit_after>use serde::{Deserialize, Serialize};

use crate::Version;

use super::{Author, Hub};

const VERSION: Version = Version::V1_1;

#[derive(Deserialize, Serialize)]
pub struct JSONFeed {
    /// The URL of the version of the format the feed uses. This should appear
    /// at the very top, though we recognize that not all JSON generators allow
    /// for ordering.
    pub version: Version,

    /// The name of the feed, which will often correspond to the name of the
    /// website (blog, for instance), though not necessarily
    pub title: String,

    /// (optional but strongly recommended) The URL of the resource that the
    /// feed describes. This resource may or may not actually be a “home” page,
    /// but it should be an HTML page. If a feed is published on the public web,
    /// this should be considered as required. But it may not make sense in the
    /// case of a file created on a desktop computer, when that file is not
    /// shared or is shared only privately.
    pub home_page_url: Option<String>,

    /// (optional but strongly recommended) The URL of the feed, and serves as
    /// the unique identifier for the feed. As with `home_page_url`, this should
    /// be considered required for feeds on the public web.
    pub feed_url: Option<String>,

    /// Provides more detail, beyond the `title`, on what the feed is about. A
    /// feed reader may display this text.
    pub description: Option<String>,

    /// Description of the purpose of the feed. This is for the use of people
    /// looking at the raw JSON, and should be ignored by feed readers.
    pub user_comment: Option<String>,

    /// The URL of a feed that provides the next n items, where n is determined
    /// by the publisher. This allows for pagination, but with the expectation
    /// that reader software is not required to use it and probably won’t use it
    /// very often. `next_url` must not be the same as `feed_url`, and it must
    /// not be the same as a previous `next_url` (to avoid infinite loops).
    pub next_url: Option<String>,

    /// The URL of an image for the feed suitable to be used in a timeline, much
    /// the way an avatar might be used. It should be square and relatively
    /// large ― such as 512 x 512 ― so that it can be scaled-down and so that it
    /// can look good on retina displays. It should use transparency where
    /// appropriate, since it may be rendered on a non-white background.
    pub icon: Option<String>,

    /// The URL of an image for the feed suitable to be used in a source list.
    /// It should be square and relatively small, but not smaller than 64 x 64
    /// (so that it can look good on retina displays). As with `icon`, this
    /// image should use transparency where appropriate, since it may be
    /// rendered on a non-white background.
    pub favicon: Option<String>,

    /// Specifies the feed author. The author object has several members. These
    /// are all optional ― but if you provide an author object, then at least
    /// one is required.
    pub author: Option<Author>,

    /// Says whether or not the feed is finished ― that is, whether or not it
    /// will ever update again. A feed for a temporary event, such as an
    /// instance of the Olympics, could expire. If the value is true, then it’s
    /// expired. Any other value, or the absence of expired, means the feed may
    /// continue to update.
    pub expired: Option<bool>,

    /// Describes endpoints that can be used to subscribe to real-time
    /// notifications from the publisher of this feed. Each object has a type
    /// and url, both of which are required. See the section [“Subscribing to
    /// Real-time Notifications”] for details.
    ///
    /// [“Subscribing to Real-time Notifications”]:
    /// https://jsonfeed.org/version/1#subscribing-to-real-time-notifications
    pub hubs: Option<Vec<Hub>>,

    /** The items in the feed. */
    pub items: Vec<FeedItem>,
}

impl JSONFeed {
    pub fn builder(title: &str, items: Vec<FeedItem>) -> Builder {
        Builder::new(title, items)
    }
}

#[derive(Clone, Deserialize, Serialize)]
pub struct FeedItem {
    /// A unique identifier for that item for that feed over time. If an item is
    /// ever updated, the `id` should be unchanged. New items should never use a
    /// previously-used `id`. Ideally, the `id` is the full URL of the resource
    /// described by the item, since URLs make great unique identifiers.
    pub id: String,

    /// The URL of the resource described by the item. It’s the permalink. This
    /// may be the same as the id ― but should be present regardless.
    pub url: Option<String>,

    /// The URL of a page elsewhere. This is especially useful for linkblogs. If
    /// `url` links to where you’re talking about a thing, then `external_url`
    /// links to the thing you’re talking about.
    pub external_url: Option<String>,

    /// Plain text. Microblog items in particular may omit titles.
    pub title: Option<String>,

    /// The plain text of the item.
    ///
    /// At least one of `content_html` and `content_text` must be present. A
    /// Twitter-like service might use `content_text`, while a blog might use
    /// `content_html`. Use whichever makes sense for your resource. (It doesn’t
    /// even have to be the same for each item in a feed.)
    pub content_text: Option<String>,

    /// The HTML of the item. Important: the only place HTML is allowed in this
    /// format is in `content_html`.
    ///
    /// At least one of `content_html` and `content_text` must be present. A
    /// Twitter-like service might use `content_text`, while a blog might use
    /// `content_html`. Use whichever makes sense for your resource. (It doesn’t
    /// even have to be the same for each item in a feed.)
    pub content_html: Option<String>,

    /// A plain text sentence or two describing the item. This might be
    /// presented in a timeline, for instance, where a detail view would display
    /// all of `content_html` or `content_text`.
    pub summary: Option<String>,

    /// The URL of the main image for the item. This image may also appear in
    /// the `content_html` ― if so, it’s a hint to the feed reader that this is
    /// the main, featured image. Feed readers may use the image as a preview
    /// (probably resized as a thumbnail and placed in a timeline).
    pub image: Option<String>,

    /// The URL of an image to use as a banner. Some blogging systems (such as
    /// [Medium]) display a different banner image chosen to go with each post,
    /// but that image wouldn’t otherwise appear in the `content_html`. A feed
    /// reader with a detail view may choose to show this banner image at the
    /// top of the detail view, possibly with the title overlaid.
    ///
    /// [Medium]: https://medium.com/
    pub banner_image: Option<String>,

    /// Specifies the date in [RFC 3339](https://tools.ietf.org/html/rfc3339)
    /// format. (Example: `2010-02-07T14:04:00-05:00`.)
    pub date_published: Option<String>,

    /// Specifies the modification date in [RFC 3339] format.
    ///
    /// [RFC 3339]: https://www.ietf.org/rfc/rfc3339.txt
    pub date_modified: Option<String>,

    /// The same structure as the top-level `author`. If not specified in an
    /// item, then the top-level `author`, if present, is the author of the
    /// item.
    pub author: Option<Author>,

    /// Any plain text values you want. Tags tend to be just one word, but they
    /// may be anything. Note: they are not the equivalent of Twitter hashtags.
    /// Some blogging systems and other feed formats call these categories.
    pub tags: Option<String>,

    /// An individual item may have one or more attachments. List related
    /// resources. Podcasts, for instance, would include an attachment that’s an
    /// audio or video file.
    pub attachments: Option<Vec<Attachment>>,
}

#[derive(Clone, Deserialize, Serialize)]
pub struct Attachment {
    // Yeah, this is an odd type -- but that's because it's representing a JS
    // number, which is... an odd type.
    /// Specifies how long it takes to listen to or watch, when played at normal
    /// speed
    pub duration_in_seconds: Option<f64>,

    /// Specifies the type of the attachment, such as “audio/mpeg.”
    pub mime_type: String,

    // As above. No, this doesn't make any sense in the usual case, but we're
    // generating JSON. :shrug:
    /// Specifies how large the file is.
    pub size_in_bytes: Option<f64>,

    /// A name for the attachment. Important: if there are multiple attachments,
    /// and two or more have the exact same `title` (when `title` is present),
    /// then they are considered as alternate representations of the same thing.
    /// In this way a podcaster, for instance, might provide an audio recording
    /// in different formats.
    pub title: Option<String>,

    /// Specifies the location of the attachment.
    pub url: String,
}

#[derive(Default)]
pub struct Builder {
    title: String,
    items: Vec<FeedItem>,
    home_page_url: Option<String>,
    feed_url: Option<String>,
    description: Option<String>,
    user_comment: Option<String>,
    next_url: Option<String>,
    icon: Option<String>,
    favicon: Option<String>,
    author: Option<Author>,
    expired: Option<bool>,
    hubs: Option<Vec<Hub>>,
}

pub struct AuthorOptions<'a, 'n, 'u> {
    pub avatar: Option<&'a str>,
    pub name: Option<&'n str>,
    pub url: Option<&'u str>,
}

impl Builder {
    pub fn new(title: &str, items: Vec<FeedItem>) -> Builder {
        Builder {
            title: title.into(),
            items,
            ..Default::default()
        }
    }

    pub fn with_home_page_url(&mut self, url: &str) -> &mut Self {
        self.home_page_url = Some(url.into());
        self
    }

    pub fn with_feed_url(&mut self, url: &str) -> &mut Self {
        self.feed_url = Some(url.into());
        self
    }

    pub fn with_description(&mut self, description: &str) -> &mut Self {
        self.description = Some(description.into());
        self
    }

    pub fn with_user_comment(&mut self, comment: &str) -> &mut Self {
        self.user_comment = Some(comment.into());
        self
    }

    pub fn with_next_url(&mut self, next_url: &str) -> &mut Self {
        self.next_url = Some(next_url.into());
        self
    }

    pub fn with_icon(&mut self, icon: &str) -> &mut Self {
        self.icon = Some(icon.into());
        self
    }

    pub fn with_favicon(&mut self, favicon: &str) -> &mut Self {
        self.favicon = Some(favicon.into());
        self
    }

    pub fn with_author(&mut self, options: &AuthorOptions) -> Result<&mut Self, String> {
        let &AuthorOptions { avatar, name, url } = options;
        let author = match (avatar, name, url) {
            (None, None, None) => {
                return Err(String::from(
                    "Cannot build `author` without at least one of name, url, and avatar",
                ))
            }
            (None, None, Some(u)) => Author::UrlOnly { url: u.into() },
            (None, Some(n), None) => Author::NameOnly { name: n.into() },
            (None, Some(n), Some(u)) => Author::NameAndUrl {
                name: n.into(),
                url: u.into(),
            },
            (Some(a), None, None) => Author::AvatarOnly { avatar: a.into() },
            (Some(a), None, Some(u)) => Author::AvatarAndUrl {
                avatar: a.into(),
                url: u.into(),
            },
            (Some(a), Some(n), None) => Author::AvatarAndName {
                avatar: a.into(),
                name: n.into(),
            },
            (Some(a), Some(n), Some(u)) => Author::All {
                avatar: a.into(),
                name: n.into(),
                url: u.into(),
            },
        };

        self.author = Some(author);

        Ok(self)
    }

    pub fn with_expired(&mut self, expired: bool) -> &mut Self {
        self.expired = Some(expired);
        self
    }

    pub fn with_hubs(&mut self, hubs: &[Hub]) -> &mut Self {
        self.hubs = Some(hubs.into());
        self
    }

    pub fn build(self) -> JSONFeed {
        let mut warnings = vec![];

        if self.home_page_url.is_none() {
            warnings.push("missing home_page_url for feed");
        }

        if self.feed_url.is_none() {
            warnings.push("missing feed_url for feed");
        }

        // TODO: use real logging!
        if warnings.len() > 0 {
            dbg!(warnings);
        }

        JSONFeed {
            version: VERSION,
            title: self.title,
            author: self.author,
            home_page_url: self.home_page_url,
            feed_url: self.feed_url,
            description: self.description,
            user_comment: self.user_comment,
            next_url: self.next_url,
            icon: self.icon,
            favicon: self.favicon,
            expired: self.expired,
            hubs: self.hubs,
            items: self.items,
        }
    }
}
<|endoftext|>