<commit_before>use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, Id};

use super::utils;

#[test]
fn required_group_missing_arg() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!( -c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]).required(true))
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group 'req' contains non-existent argument"]
fn non_existing_arg() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flg", "color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name must be unique\n\n\t'req' is already in use"]
fn unique_group_name() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag"]).required(true))
        .group(ArgGroup::new("req").args(["color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn groups_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn arg_group_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .group(ArgGroup::new("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[test]
fn group_single_value() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue"]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn group_empty() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec![""]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(!m.contains_id("grp"));
    assert!(m.get_one::<String>("grp").map(|v| v.as_str()).is_none());
}

#[test]
fn group_required_flags_empty() {
    let result = Command::new("group")
        .arg(arg!(-c --color "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(
            ArgGroup::new("grp")
                .required(true)
                .args(["hostname", "color"]),
        )
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
fn group_multi_value_single_arg() {
    let res = Command::new("group")
        .arg(arg!(-c --color <color> "some option").num_args(1..))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]);
    assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn empty_group() {
    let r = Command::new("empty_group")
        .arg(arg!(-f --flag "some flag"))
        .group(ArgGroup::new("vers").required(true))
        .try_get_matches_from(vec!["empty_prog"]);
    assert!(r.is_err());
    let err = r.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_usage_string() {
    static REQ_GROUP_USAGE: &str = "error: The following required arguments were not provided:
  <base|--delete>

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(cmd, "clap-test", REQ_GROUP_USAGE, true);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string() {
    static REQ_GROUP_CONFLICT_USAGE: &str = "\
error: The argument '--delete' cannot be used with '[base]'

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(
        cmd,
        "clap-test --delete base",
        REQ_GROUP_CONFLICT_USAGE,
        true,
    );
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string_only_options() {
    static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = "\
error: The argument '--delete' cannot be used with '--all'

Usage: clap-test <--all|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!(-a --all "All").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("all_or_delete")
                .args(["all", "delete"])
                .required(true),
        );
    utils::assert_output(
        cmd,
        "clap-test --delete --all",
        REQ_GROUP_CONFLICT_ONLY_OPTIONS,
        true,
    );
}

#[test]
fn required_group_multiple_args() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag").action(ArgAction::SetTrue))
        .arg(arg!(-c --color "some other flag").action(ArgAction::SetTrue))
        .group(
            ArgGroup::new("req")
                .args(["flag", "color"])
                .required(true)
                .multiple(true),
        )
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
    assert!(*m.get_one::<bool>("color").expect("defaulted by clap"));
    assert_eq!(
        &*m.get_many::<Id>("req")
            .unwrap()
            .map(|v| v.as_str())
            .collect::<Vec<_>>(),
        &["flag", "color"]
    );
}

#[test]
fn group_multiple_args_error() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]))
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
}

#[test]
fn group_overrides_required() {
    let command = Command::new("group")
        .arg(arg!(--foo <FOO>).required(true))
        .arg(arg!(--bar <BAR>).required(true))
        .group(ArgGroup::new("group").args(["foo", "bar"]).required(true));
    let result = command.try_get_matches_from(vec!["group", "--foo", "value"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("foo"));
    assert!(!m.contains_id("bar"));
}

#[test]
fn group_usage_use_val_name() {
    static GROUP_USAGE_USE_VAL_NAME: &str = "\
Usage: prog

Arguments:
  [A]

Options:
  -h, --help  Print help information
";
    let cmd = Command::new("prog")
        .arg(Arg::new("a").value_name("A"))
        .group(ArgGroup::new("group").arg("a").required(true));
    utils::assert_output(cmd, "prog --help", GROUP_USAGE_USE_VAL_NAME, false);
}

#[test]
fn group_acts_like_arg() {
    let result = Command::new("prog")
        .arg(
            Arg::new("debug")
                .long("debug")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .arg(
            Arg::new("verbose")
                .long("verbose")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .try_get_matches_from(vec!["prog", "--debug"]);

    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("mode"));
    assert_eq!(m.get_one::<clap::Id>("mode").unwrap(), "debug");
}

#[test]
fn conflict_with_overlapping_group_in_error() {
    static ERR: &str = "\
error: The argument '--major' cannot be used with '--minor'

Usage: prog --major

For more information try '--help'
";

    let cmd = Command::new("prog")
        .group(ArgGroup::new("all").multiple(true))
        .arg(arg!(--major).group("vers").group("all"))
        .arg(arg!(--minor).group("vers").group("all"))
        .arg(arg!(--other).group("all"));

    utils::assert_output(cmd, "prog --major --minor", ERR, true);
}

/* This is used to be fixed in a hack, we need to find a better way to fix it.
#[test]
fn issue_1794() {
    let cmd = clap::Command::new("hello")
        .bin_name("deno")
        .arg(Arg::new("option1").long("option1").action(ArgAction::SetTrue))
        .arg(Arg::new("pos1").action(ArgAction::Set))
        .arg(Arg::new("pos2").action(ArgAction::Set))
        .group(
            ArgGroup::new("arg1")
                .args(["pos1", "option1"])
                .required(true),
        );

    let m = cmd.clone().try_get_matches_from(&["cmd", "pos1", "pos2"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), Some("pos1"));
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("pos2"));
    assert!(!*m.get_one::<bool>("option1").expect("defaulted by clap"));

    let m = cmd
        .clone()
        .try_get_matches_from(&["cmd", "--option1", "positional"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), None);
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("positional"));
    assert!(*m.get_one::<bool>("option1").expect("defaulted by clap"));
}
*/
<commit_msg>test: Demonstrate bad requires error<commit_after>use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, Id};

use super::utils;

#[test]
fn required_group_missing_arg() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!( -c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]).required(true))
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group 'req' contains non-existent argument"]
fn non_existing_arg() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flg", "color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name must be unique\n\n\t'req' is already in use"]
fn unique_group_name() {
    let _ = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag"]).required(true))
        .group(ArgGroup::new("req").args(["color"]).required(true))
        .try_get_matches_from(vec![""]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn groups_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"]
fn arg_group_new_of_arg_name() {
    let _ = Command::new("group")
        .arg(Arg::new("a").long("a").group("a"))
        .group(ArgGroup::new("a"))
        .try_get_matches_from(vec!["", "--a"]);
}

#[test]
fn group_single_value() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue"]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn group_empty() {
    let res = Command::new("group")
        .arg(arg!(-c --color [color] "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec![""]);
    assert!(res.is_ok(), "{}", res.unwrap_err());

    let m = res.unwrap();
    assert!(!m.contains_id("grp"));
    assert!(m.get_one::<String>("grp").map(|v| v.as_str()).is_none());
}

#[test]
fn group_required_flags_empty() {
    let result = Command::new("group")
        .arg(arg!(-c --color "some option"))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(
            ArgGroup::new("grp")
                .required(true)
                .args(["hostname", "color"]),
        )
        .try_get_matches_from(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
fn group_multi_value_single_arg() {
    let res = Command::new("group")
        .arg(arg!(-c --color <color> "some option").num_args(1..))
        .arg(arg!(-n --hostname <name> "another option"))
        .group(ArgGroup::new("grp").args(["hostname", "color"]))
        .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]);
    assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());

    let m = res.unwrap();
    assert!(m.contains_id("grp"));
    assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color");
}

#[test]
fn empty_group() {
    let r = Command::new("empty_group")
        .arg(arg!(-f --flag "some flag"))
        .group(ArgGroup::new("vers").required(true))
        .try_get_matches_from(vec!["empty_prog"]);
    assert!(r.is_err());
    let err = r.err().unwrap();
    assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_usage_string() {
    static REQ_GROUP_USAGE: &str = "error: The following required arguments were not provided:
  <base|--delete>

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(cmd, "clap-test", REQ_GROUP_USAGE, true);
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string() {
    static REQ_GROUP_CONFLICT_USAGE: &str = "\
error: The argument '--delete' cannot be used with '[base]'

Usage: clap-test <base|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!([base] "Base commit").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("base_or_delete")
                .args(["base", "delete"])
                .required(true),
        );

    utils::assert_output(
        cmd,
        "clap-test --delete base",
        REQ_GROUP_CONFLICT_USAGE,
        true,
    );
}

#[test]
#[cfg(feature = "error-context")]
fn req_group_with_conflict_usage_string_only_options() {
    static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = "\
error: The argument '--delete' cannot be used with '--all'

Usage: clap-test <--all|--delete>

For more information try '--help'
";

    let cmd = Command::new("req_group")
        .arg(arg!(-a --all "All").conflicts_with("delete"))
        .arg(arg!(
            -d --delete "Remove the base commit information"
        ))
        .group(
            ArgGroup::new("all_or_delete")
                .args(["all", "delete"])
                .required(true),
        );
    utils::assert_output(
        cmd,
        "clap-test --delete --all",
        REQ_GROUP_CONFLICT_ONLY_OPTIONS,
        true,
    );
}

#[test]
fn required_group_multiple_args() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag").action(ArgAction::SetTrue))
        .arg(arg!(-c --color "some other flag").action(ArgAction::SetTrue))
        .group(
            ArgGroup::new("req")
                .args(["flag", "color"])
                .required(true)
                .multiple(true),
        )
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
    assert!(*m.get_one::<bool>("color").expect("defaulted by clap"));
    assert_eq!(
        &*m.get_many::<Id>("req")
            .unwrap()
            .map(|v| v.as_str())
            .collect::<Vec<_>>(),
        &["flag", "color"]
    );
}

#[test]
fn group_multiple_args_error() {
    let result = Command::new("group")
        .arg(arg!(-f --flag "some flag"))
        .arg(arg!(-c --color "some other flag"))
        .group(ArgGroup::new("req").args(["flag", "color"]))
        .try_get_matches_from(vec!["group", "-f", "-c"]);
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
}

#[test]
fn group_overrides_required() {
    let command = Command::new("group")
        .arg(arg!(--foo <FOO>).required(true))
        .arg(arg!(--bar <BAR>).required(true))
        .group(ArgGroup::new("group").args(["foo", "bar"]).required(true));
    let result = command.try_get_matches_from(vec!["group", "--foo", "value"]);
    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("foo"));
    assert!(!m.contains_id("bar"));
}

#[test]
fn group_usage_use_val_name() {
    static GROUP_USAGE_USE_VAL_NAME: &str = "\
Usage: prog

Arguments:
  [A]

Options:
  -h, --help  Print help information
";
    let cmd = Command::new("prog")
        .arg(Arg::new("a").value_name("A"))
        .group(ArgGroup::new("group").arg("a").required(true));
    utils::assert_output(cmd, "prog --help", GROUP_USAGE_USE_VAL_NAME, false);
}

#[test]
fn group_acts_like_arg() {
    let result = Command::new("prog")
        .arg(
            Arg::new("debug")
                .long("debug")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .arg(
            Arg::new("verbose")
                .long("verbose")
                .group("mode")
                .action(ArgAction::SetTrue),
        )
        .try_get_matches_from(vec!["prog", "--debug"]);

    assert!(result.is_ok(), "{}", result.unwrap_err());
    let m = result.unwrap();
    assert!(m.contains_id("mode"));
    assert_eq!(m.get_one::<clap::Id>("mode").unwrap(), "debug");
}

#[test]
fn conflict_with_overlapping_group_in_error() {
    static ERR: &str = "\
error: The argument '--major' cannot be used with '--minor'

Usage: prog --major

For more information try '--help'
";

    let cmd = Command::new("prog")
        .group(ArgGroup::new("all").multiple(true))
        .arg(arg!(--major).group("vers").group("all"))
        .arg(arg!(--minor).group("vers").group("all"))
        .arg(arg!(--other).group("all"));

    utils::assert_output(cmd, "prog --major --minor", ERR, true);
}

#[test]
fn requires_group_with_overlapping_group_in_error() {
    static ERR: &str = "\
error: The following required arguments were not provided:
  <--in|--spec>

Usage: prog <--in|--spec> <--in|--spec|--config>

For more information try '--help'
";

    let cmd = Command::new("prog")
        .group(ArgGroup::new("all").multiple(true))
        .group(ArgGroup::new("input").required(true))
        .arg(arg!(--in).group("input").group("all"))
        .arg(arg!(--spec).group("input").group("all"))
        .arg(arg!(--config).requires("input").group("all"));

    utils::assert_output(cmd, "prog --config", ERR, true);
}

/* This is used to be fixed in a hack, we need to find a better way to fix it.
#[test]
fn issue_1794() {
    let cmd = clap::Command::new("hello")
        .bin_name("deno")
        .arg(Arg::new("option1").long("option1").action(ArgAction::SetTrue))
        .arg(Arg::new("pos1").action(ArgAction::Set))
        .arg(Arg::new("pos2").action(ArgAction::Set))
        .group(
            ArgGroup::new("arg1")
                .args(["pos1", "option1"])
                .required(true),
        );

    let m = cmd.clone().try_get_matches_from(&["cmd", "pos1", "pos2"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), Some("pos1"));
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("pos2"));
    assert!(!*m.get_one::<bool>("option1").expect("defaulted by clap"));

    let m = cmd
        .clone()
        .try_get_matches_from(&["cmd", "--option1", "positional"]).unwrap();
    assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), None);
    assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("positional"));
    assert!(*m.get_one::<bool>("option1").expect("defaulted by clap"));
}
*/
<|endoftext|>


<commit_before>/**
 * 15.3 Do syntax
 * http://static.rust-lang.org/doc/master/tutorial.html#do-syntax
 *
 * @license MIT license <http://www.opensource.org/licenses/mit-license.php>
 */
// The do expression makes it easier to call functions that take procedures as arguments.

// A function that takes a procedure as argument:
fn call_it(op: proc(v: int)) {
	op(10)
}

fn each(v: &[int], op: |v: &int|) {
	let mut n = 0;
	while n < v.len() {
		op(&v[n]);
		n += 1;
	}
}

fn main() {
	// As a caller, if we use a closure to provide the final operator argument, we can write it in a way that has a pleasant, block-like structure.
	call_it(proc(n) {
		println(n.to_str());
	});
	each([1, 2, 3], |n: &int| {
		println(n.to_str());
	});

	// This is such a useful pattern that Rust has a special form of function call for these functions.
	do call_it() |n| {
		println(n.to_str());
	}
}
<commit_msg>klutzy's remarks<commit_after>/**
 * 15.3 Do syntax
 * http://static.rust-lang.org/doc/master/tutorial.html#do-syntax
 *
 * @license MIT license <http://www.opensource.org/licenses/mit-license.php>
 */
// The do expression makes it easier to call functions that take procedures as arguments.

// A function that takes a procedure as argument:
fn call_it(op: proc(v: int)) {
	op(10)
}

fn each(v: &[int], op: |v: &int|) {
	let mut n = 0;
	while n < v.len() {
		op(&v[n]);
		n += 1;
	}
}

fn main() {
	// As a caller, if we use a closure to provide the final operator argument, we can write it in a way that has a pleasant, block-like structure.
	call_it(proc(n) {
		println(n.to_str());
	});
	each([1, 2, 3], |n: &int| {
		println(n.to_str());
	});

	// This is such a useful pattern that Rust has a special form of function call for these functions.
	do call_it() |n| {
		println(n.to_str());
	}
	// `do` only works with `proc`s, so no use of `do` with each, which is a |closure|.
	// |v: &int| is stack closure, not procedure: procedure is allocated at heap, and you can call it only once.
}
<|endoftext|>


<commit_before>use cocoa::base::id;

/// `MTLStructType` is part of the reflection API that allows Metal framework
/// code to query details of a struct that is passed as an argument of a Metal
/// shading language function. You do not allocate or initialize `MTLStructType`
/// objects. To obtain a `MTLStructType` object, you can query the
/// bufferStructType property of a `MTLArgument` object, or call the structType
/// method for a `MTLStructMember` object. To examine the details of the struct,
/// you can recursively drill down the members property of the `MTLStructType`
/// object, which contains details about struct members, each of which is
/// represented by a `MTLStructMember` object.
pub trait MTLStructType {
    /// An array of objects that describe the fields in the struct. (read-only)
    ///
    /// #Description
    ///
    /// Each array element in `members` is a `MTLStructMember` object that
    /// corresponds to one of the fields in the struct.
    unsafe fn members(self) -> id;

    /// Returns an object that represents the member of the struct with a given name.
    ///
    /// #Description
    ///
    /// An object that represents the named struct member. If `name` does not match
    /// a member name, `nil` is returned.
    unsafe fn memberByName(self, name: id) -> id;
}

impl MTLStructType for id {
    unsafe fn members(self) -> id {
        msg_send![self, members] }
    }

    unsafe fn memberByName(self, name: id) -> id {
        msg_send![self, memberByName:name]
    }
}
<commit_msg>Fix compile error by removing errant brace<commit_after>use cocoa::base::id;

/// `MTLStructType` is part of the reflection API that allows Metal framework
/// code to query details of a struct that is passed as an argument of a Metal
/// shading language function. You do not allocate or initialize `MTLStructType`
/// objects. To obtain a `MTLStructType` object, you can query the
/// bufferStructType property of a `MTLArgument` object, or call the structType
/// method for a `MTLStructMember` object. To examine the details of the struct,
/// you can recursively drill down the members property of the `MTLStructType`
/// object, which contains details about struct members, each of which is
/// represented by a `MTLStructMember` object.
pub trait MTLStructType {
    /// An array of objects that describe the fields in the struct. (read-only)
    ///
    /// #Description
    ///
    /// Each array element in `members` is a `MTLStructMember` object that
    /// corresponds to one of the fields in the struct.
    unsafe fn members(self) -> id;

    /// Returns an object that represents the member of the struct with a given name.
    ///
    /// #Description
    ///
    /// An object that represents the named struct member. If `name` does not match
    /// a member name, `nil` is returned.
    unsafe fn memberByName(self, name: id) -> id;
}

impl MTLStructType for id {
    unsafe fn members(self) -> id {
        msg_send![self, members]
    }

    unsafe fn memberByName(self, name: id) -> id {
        msg_send![self, memberByName:name]
    }
}
<|endoftext|>


<commit_before>use jansson_sys::json_t;
use std::os::raw::{c_char, c_int, c_void, c_short};

#[repr(C)]
#[derive(Debug)]
pub struct janus_callbacks {
    pub push_event: extern "C" fn(
        handle: *mut janus_plugin_session,
        plugin: *mut janus_plugin,
        transaction: *const c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> c_int,
    pub relay_rtp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub relay_rtcp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub relay_data: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub send_pli: extern "C" fn(handle: *mut janus_plugin_session),
    pub send_remb: extern "C" fn(handle: *mut janus_plugin_session, bitrate: c_int),
    pub close_pc: extern "C" fn(handle: *mut janus_plugin_session),
    pub end_session: extern "C" fn(handle: *mut janus_plugin_session),
    pub events_is_enabled: extern "C" fn() -> c_int,
    pub notify_event: extern "C" fn(plugin: *mut janus_plugin, handle: *mut janus_plugin_session, event: *mut json_t),
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum janus_plugin_result_type {
    JANUS_PLUGIN_ERROR = -1,
    JANUS_PLUGIN_OK = 0,
    JANUS_PLUGIN_OK_WAIT = 1,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped: c_int,
    pub ref_: crate::janus_refcount,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_result {
    pub type_: janus_plugin_result_type,
    pub text: *const c_char,
    pub content: *mut json_t,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp_extensions {
    pub audio_level : c_char,
    pub audio_level_vad : c_char,
    pub video_rotation : c_short,
    pub video_back_camera : c_char,
    pub video_flipped : c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp {
    pub video :  c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
    pub extensions : janus_plugin_rtp_extensions,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtcp {
    pub video : c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_data {
    pub label : *mut c_char,
    pub binary : c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin {
    pub init: unsafe extern "C" fn(callback: *mut janus_callbacks, config_path: *const c_char) -> c_int,
    pub destroy: unsafe extern "C" fn(),
    pub get_api_compatibility: unsafe extern "C" fn() -> c_int,
    pub get_version: unsafe extern "C" fn() -> c_int,
    pub get_version_string: unsafe extern "C" fn() -> *const c_char,
    pub get_description: unsafe extern "C" fn() -> *const c_char,
    pub get_name: unsafe extern "C" fn() -> *const c_char,
    pub get_author: unsafe extern "C" fn() -> *const c_char,
    pub get_package: unsafe extern "C" fn() -> *const c_char,
    pub create_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub handle_message: unsafe extern "C" fn(
        handle: *mut janus_plugin_session,
        transaction: *mut c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> *mut janus_plugin_result,
    pub handle_admin_message: unsafe extern "C" fn(message: *mut json_t) -> *mut json_t,
    pub setup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub incoming_rtp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub incoming_rtcp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub incoming_data: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub slow_link: unsafe extern "C" fn(handle: *mut janus_plugin_session, uplink: c_int, video: c_int),
    pub hangup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub destroy_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub query_session: unsafe extern "C" fn(handle: *mut janus_plugin_session) -> *mut json_t,
}

extern "C" {
    pub fn janus_plugin_result_new(type_: janus_plugin_result_type, text: *const c_char, content: *mut json_t) -> *mut janus_plugin_result;
    pub fn janus_plugin_result_destroy(result: *mut janus_plugin_result);
}
<commit_msg>Add plugin API v15 support<commit_after>use jansson_sys::json_t;
use std::os::raw::{c_char, c_int, c_void, c_short};
use glib_sys::gboolean;

#[repr(C)]
#[derive(Debug)]
pub struct janus_callbacks {
    pub push_event: extern "C" fn(
        handle: *mut janus_plugin_session,
        plugin: *mut janus_plugin,
        transaction: *const c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> c_int,
    pub relay_rtp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub relay_rtcp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub relay_data: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub send_pli: extern "C" fn(handle: *mut janus_plugin_session),
    pub send_remb: extern "C" fn(handle: *mut janus_plugin_session, bitrate: c_int),
    pub close_pc: extern "C" fn(handle: *mut janus_plugin_session),
    pub end_session: extern "C" fn(handle: *mut janus_plugin_session),
    pub events_is_enabled: extern "C" fn() -> c_int,
    pub notify_event: extern "C" fn(plugin: *mut janus_plugin, handle: *mut janus_plugin_session, event: *mut json_t),
    pub auth_is_signature_valid: extern "C" fn(plugin: *mut janus_plugin, token: *const c_char) -> gboolean,
    pub auth_signature_contains: extern "C" fn(plugin: *mut janus_plugin, token: *const c_char, descriptor: *const c_char) -> gboolean,
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum janus_plugin_result_type {
    JANUS_PLUGIN_ERROR = -1,
    JANUS_PLUGIN_OK = 0,
    JANUS_PLUGIN_OK_WAIT = 1,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped: c_int,
    pub ref_: crate::janus_refcount,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_result {
    pub type_: janus_plugin_result_type,
    pub text: *const c_char,
    pub content: *mut json_t,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp_extensions {
    pub audio_level : c_char,
    pub audio_level_vad : c_char,
    pub video_rotation : c_short,
    pub video_back_camera : c_char,
    pub video_flipped : c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp {
    pub video :  c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
    pub extensions : janus_plugin_rtp_extensions,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtcp {
    pub video : c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_data {
    pub label : *mut c_char,
    pub protocol : *mut c_char,
    pub binary : c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin {
    pub init: unsafe extern "C" fn(callback: *mut janus_callbacks, config_path: *const c_char) -> c_int,
    pub destroy: unsafe extern "C" fn(),
    pub get_api_compatibility: unsafe extern "C" fn() -> c_int,
    pub get_version: unsafe extern "C" fn() -> c_int,
    pub get_version_string: unsafe extern "C" fn() -> *const c_char,
    pub get_description: unsafe extern "C" fn() -> *const c_char,
    pub get_name: unsafe extern "C" fn() -> *const c_char,
    pub get_author: unsafe extern "C" fn() -> *const c_char,
    pub get_package: unsafe extern "C" fn() -> *const c_char,
    pub create_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub handle_message: unsafe extern "C" fn(
        handle: *mut janus_plugin_session,
        transaction: *mut c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> *mut janus_plugin_result,
    pub handle_admin_message: unsafe extern "C" fn(message: *mut json_t) -> *mut json_t,
    pub setup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub incoming_rtp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub incoming_rtcp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub incoming_data: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub data_ready: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub slow_link: unsafe extern "C" fn(handle: *mut janus_plugin_session, uplink: c_int, video: c_int),
    pub hangup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub destroy_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub query_session: unsafe extern "C" fn(handle: *mut janus_plugin_session) -> *mut json_t,
}

extern "C" {
    pub fn janus_plugin_result_new(type_: janus_plugin_result_type, text: *const c_char, content: *mut json_t) -> *mut janus_plugin_result;
    pub fn janus_plugin_result_destroy(result: *mut janus_plugin_result);
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Modular_exponentiation
extern crate num;
use num::{BigUint, One};
use num::bigint::ToBigUint;
use num::integer::Integer;

fn mod_exp<T: Integer + Clone>(mut a: T, mut b: T, m: T) -> T {
    let one: T = One::one();
    let two = one.clone() + one.clone();

    let mut res = one;
    while !b.is_zero() {
        if b.is_odd() {
            res = (res * a.clone()) % m.clone();
        }
        a = (a.clone() * a.clone()) % m.clone();
        b = b.div_floor(&two);
    }

    res
}

#[cfg(not(test))]
fn main() {
    use num::pow;

    let a_str = "2988348162058574136915891421498819466320163312926952423791023078876139";
    let b_str = "2351399303373464486466122544523690094744975233415544072992656881240319";
    let a: BigUint = BigUint::parse_bytes(a_str.as_bytes(), 10).unwrap();
    let b: BigUint = BigUint::parse_bytes(b_str.as_bytes(), 10).unwrap();
    let m: BigUint = pow(10.to_biguint().unwrap(), 40);
    println!("{}", mod_exp(a, b, m));
}

#[test]
fn test_mod_exp() {
    let tests = [
        (0, 10, 10, 0),
        (1, 10, 10, 1),
        (2, 1000, 2, 0),
        (2, 10, 2147483647, 1024),
        (1337, 100, 2147483647, 1398068914),
        (18, 112994442, 1000000001, 59108659),
    ];

    for &(a, b, m, expected) in tests.iter() {
        let a = FromPrimitive::from_int(a).unwrap();
        let b = FromPrimitive::from_int(b).unwrap();
        let m = FromPrimitive::from_int(m).unwrap();
        let ans: BigUint = mod_exp(a, b, m);

        assert_eq!(ans, FromPrimitive::from_int(expected).unwrap());
    }
}

<commit_msg>error in modular_exponentiation<commit_after>// http://rosettacode.org/wiki/Modular_exponentiation
extern crate num;
use num::{BigUint, One};
use num::bigint::ToBigUint;
use num::integer::Integer;

fn mod_exp<T: Integer + Clone>(mut a: T, mut b: T, m: T) -> T {
    let one: T = One::one();
    let two = one.clone() + one.clone();

    let mut res = one;
    while !b.is_zero() {
        if b.is_odd() {
            res = (res * a.clone()) % m.clone();
        }
        a = (a.clone() * a.clone()) % m.clone();
        b = b.div_floor(&two);
    }

    res
}

#[cfg(not(test))]
fn main() {
    use num::pow;

    let a_str = "2988348162058574136915891421498819466320163312926952423791023078876139";
    let b_str = "2351399303373464486466122544523690094744975233415544072992656881240319";
    let a: BigUint = BigUint::parse_bytes(a_str.as_bytes(), 10).unwrap();
    let b: BigUint = BigUint::parse_bytes(b_str.as_bytes(), 10).unwrap();
    let m: BigUint = pow(10.to_biguint().unwrap(), 40);
    println!("{}", mod_exp(a, b, m));
}

#[test]
fn test_mod_exp() {
    let tests = [
        (0, 10, 10, 0),
        (1, 10, 10, 1),
        (2, 1000, 2, 0),
        (2, 10, 2147483647, 1024),
        (1337, 100, 2147483647, 1398068914),
        (18, 112994442, 1000000001, 59108659),
    ];

    for &(a, b, m, expected) in tests.iter() {
        let a = a.to_biguint().unwrap();
        let b = b.to_biguint().unwrap();
        let m = m.to_biguint().unwrap();
        let ans: BigUint = mod_exp(a, b, m);

        assert_eq!(ans, expected.to_biguint().unwrap());
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::compiler_state::{CompilerState, SourceSetName};
use crate::config::{Config, ConfigProject};
use crate::watchman::GraphQLFinder;
use common::{FileKey, Timer};
use dependency_analyzer::get_reachable_ast;
use fnv::FnvHashMap;
use graphql_syntax::ExecutableDefinition;
use std::collections::HashMap;

pub struct Compiler {
    config: Config,
}

type Sources<'a> = FnvHashMap<FileKey, &'a str>;
type AstSets = HashMap<SourceSetName, Vec<ExecutableDefinition>>;

impl Compiler {
    pub fn new(config: Config) -> Self {
        Self { config }
    }

    pub async fn compile(&self) {
        let finder = GraphQLFinder::connect(&self.config).await.unwrap();

        let compiler_state = finder.query().await.unwrap();

        for (source_set_name, source_set) in &compiler_state.source_sets {
            let definition_count: usize = source_set
                .0
                .values()
                .map(|file_definitions| file_definitions.len())
                .sum();
            let file_count = source_set.0.len();
            println!(
                "{} has {} definitions from {} files",
                source_set_name.0, definition_count, file_count
            );
        }

        let ast_sets_timer = Timer::start("ast_sets");
        let mut ast_sets: AstSets = HashMap::new();
        let mut sources: Sources<'_> = FnvHashMap::default();
        let mut errors = Vec::new();
        for (source_set_name, source_set) in compiler_state.source_sets.iter() {
            let asts = ast_sets
                .entry(*source_set_name)
                .or_insert_with(|| Vec::new());
            for (file_name, file_sources) in source_set.0.iter() {
                for (index, file_source) in file_sources.iter().enumerate() {
                    let source = format!("{}:{}", file_name.to_string_lossy(), index);
                    let file_key = FileKey::new(&source);
                    match graphql_syntax::parse(&file_source, file_key) {
                        Ok(document) => {
                            asts.extend(document.definitions);
                        }
                        Err(ast_errors) => errors.extend(
                            ast_errors
                                .into_iter()
                                .map(|error| error.print(&file_source)),
                        ),
                    }
                    sources.insert(file_key, &file_source);
                }
            }
        }
        if !errors.is_empty() {
            println!("Failed with parse errors:");
            println!("{}", errors.join("\n\n"));
            return;
        }
        ast_sets_timer.stop();

        for project_config in self.config.projects.values() {
            self.build_project(&compiler_state, project_config, &sources, &ast_sets);
        }
    }

    fn build_project(
        &self,
        compiler_state: &CompilerState,
        project_config: &ConfigProject,
        sources: &Sources<'_>,
        ast_sets: &AstSets,
    ) {
        let project_document_asts = ast_sets[&project_config.name.as_source_set_name()].to_vec();

        let mut extensions = Vec::new();
        if let Some(project_extensions) = compiler_state.extensions.get(&project_config.name) {
            extensions.extend(project_extensions);
        }

        let mut base_document_asts = Vec::new();

        // if we have base project, add their asts and extensions.
        // TODO: this should probably work recursively
        if let Some(base_project_name) = project_config.base {
            base_document_asts.extend(
                ast_sets[&base_project_name.as_source_set_name()]
                    .iter()
                    .cloned(),
            );

            if let Some(base_project_extensions) = compiler_state.extensions.get(&base_project_name)
            {
                extensions.extend(base_project_extensions);
            }
        }

        let build_schema_timer = Timer::start(format!("build_schema {}", project_config.name));
        let mut schema_sources = vec![schema::RELAY_EXTENSIONS];
        schema_sources.extend(
            compiler_state.schemas[&project_config.name]
                .iter()
                .map(String::as_str),
        );
        let schema = schema::build_schema_with_extensions(&schema_sources, &extensions).unwrap();
        build_schema_timer.stop();

        let build_ir_timer = Timer::start(format!("build_ir {}", project_config.name));
        let reachable_ast = get_reachable_ast(project_document_asts, vec![base_document_asts])
            .unwrap()
            .0;
        let ir: Vec<_> = match graphql_ir::build(&schema, &reachable_ast) {
            Ok(ir) => ir,
            Err(errors) => {
                println!(
                    "[{}] Failed with {} validation errors:",
                    project_config.name,
                    errors.len()
                );
                println!(
                    "{}",
                    errors
                        .iter()
                        .map(|error| error.print(sources))
                        .collect::<Vec<_>>()
                        .join("\n\n")
                );
                return;
            }
        };
        build_ir_timer.stop();
        println!("[{}] IR node count {}", project_config.name, ir.len());
    }
}
<commit_msg>remove stats logging code from compiler.rs<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::compiler_state::{CompilerState, SourceSetName};
use crate::config::{Config, ConfigProject};
use crate::watchman::GraphQLFinder;
use common::{FileKey, Timer};
use dependency_analyzer::get_reachable_ast;
use fnv::FnvHashMap;
use graphql_syntax::ExecutableDefinition;
use std::collections::HashMap;

pub struct Compiler {
    config: Config,
}

type Sources<'a> = FnvHashMap<FileKey, &'a str>;
type AstSets = HashMap<SourceSetName, Vec<ExecutableDefinition>>;

impl Compiler {
    pub fn new(config: Config) -> Self {
        Self { config }
    }

    pub async fn compile(&self) {
        let finder = GraphQLFinder::connect(&self.config).await.unwrap();

        let compiler_state = finder.query().await.unwrap();

        let ast_sets_timer = Timer::start("ast_sets");
        let mut ast_sets: AstSets = HashMap::new();
        let mut sources: Sources<'_> = FnvHashMap::default();
        let mut errors = Vec::new();
        for (source_set_name, source_set) in compiler_state.source_sets.iter() {
            let asts = ast_sets
                .entry(*source_set_name)
                .or_insert_with(|| Vec::new());
            for (file_name, file_sources) in source_set.0.iter() {
                for (index, file_source) in file_sources.iter().enumerate() {
                    let source = format!("{}:{}", file_name.to_string_lossy(), index);
                    let file_key = FileKey::new(&source);
                    match graphql_syntax::parse(&file_source, file_key) {
                        Ok(document) => {
                            asts.extend(document.definitions);
                        }
                        Err(ast_errors) => errors.extend(
                            ast_errors
                                .into_iter()
                                .map(|error| error.print(&file_source)),
                        ),
                    }
                    sources.insert(file_key, &file_source);
                }
            }
        }
        if !errors.is_empty() {
            println!("Failed with parse errors:");
            println!("{}", errors.join("\n\n"));
            return;
        }
        ast_sets_timer.stop();

        for project_config in self.config.projects.values() {
            self.build_project(&compiler_state, project_config, &sources, &ast_sets);
        }
    }

    fn build_project(
        &self,
        compiler_state: &CompilerState,
        project_config: &ConfigProject,
        sources: &Sources<'_>,
        ast_sets: &AstSets,
    ) {
        let project_document_asts = ast_sets[&project_config.name.as_source_set_name()].to_vec();

        let mut extensions = Vec::new();
        if let Some(project_extensions) = compiler_state.extensions.get(&project_config.name) {
            extensions.extend(project_extensions);
        }

        let mut base_document_asts = Vec::new();

        // if we have base project, add their asts and extensions.
        // TODO: this should probably work recursively
        if let Some(base_project_name) = project_config.base {
            base_document_asts.extend(
                ast_sets[&base_project_name.as_source_set_name()]
                    .iter()
                    .cloned(),
            );

            if let Some(base_project_extensions) = compiler_state.extensions.get(&base_project_name)
            {
                extensions.extend(base_project_extensions);
            }
        }

        let build_schema_timer = Timer::start(format!("build_schema {}", project_config.name));
        let mut schema_sources = vec![schema::RELAY_EXTENSIONS];
        schema_sources.extend(
            compiler_state.schemas[&project_config.name]
                .iter()
                .map(String::as_str),
        );
        let schema = schema::build_schema_with_extensions(&schema_sources, &extensions).unwrap();
        build_schema_timer.stop();

        let build_ir_timer = Timer::start(format!("build_ir {}", project_config.name));
        let reachable_ast = get_reachable_ast(project_document_asts, vec![base_document_asts])
            .unwrap()
            .0;
        let ir: Vec<_> = match graphql_ir::build(&schema, &reachable_ast) {
            Ok(ir) => ir,
            Err(errors) => {
                println!(
                    "[{}] Failed with {} validation errors:",
                    project_config.name,
                    errors.len()
                );
                println!(
                    "{}",
                    errors
                        .iter()
                        .map(|error| error.print(sources))
                        .collect::<Vec<_>>()
                        .join("\n\n")
                );
                return;
            }
        };
        build_ir_timer.stop();
        println!("[{}] IR node count {}", project_config.name, ir.len());
    }
}
<|endoftext|>


<commit_before>pub fn median_filter(data: &Vec<f64>, window: usize) -> Vec<f64> {
    groups(data, window)
        .iter()
        .map(median_of)
        .collect()
}

fn median_of(data: &Vec<f64>) -> f64 {
    let n = data.len();
    let mut copy = vec!(0f64; n);
    copy.copy_from_slice(&data);
    copy.sort_by(|a,b| a.partial_cmp(b).unwrap());
    let middle: usize = n / 2;

    let result = if n % 2 == 1 {
        copy[middle]
    } else {
        (copy[middle] + copy[middle - 1])/2.0
    };

    result
}

fn groups(data: &Vec<f64>, group_size: usize) -> Vec<Vec<f64>> {
    let mut groups: Vec<Vec<f64>> = vec!();

    for end_index in group_size .. data.len() + 1 {
        let mut group: Vec<f64> = vec!();
        for index in (end_index - group_size) .. end_index {
            group.push(data[index])
        }
        groups.push(group)
    }

    groups
}

#[cfg(test)]
pub mod tests {
    const EPSILON: f64 = 0.00001;
    use super::*;

    #[test]
    fn median_of_odd_length_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0);

        let median = median_of(&data);

        assert!((median - 2.0).abs() < EPSILON);
    }

    #[test]
    fn median_of_even_length_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0);

        let median = median_of(&data);

        assert_eq!(median, 2.5);
        assert!((median - 2.5).abs() < EPSILON);
    }

    #[test]
    fn median_of_unsorted_vector() {
        let data: Vec<f64> = vec!(2.0, 4.0, 3.0, 1.0);

        let median = median_of(&data);

        assert_eq!(median, 2.5);
        assert!((median - 2.5).abs() < EPSILON);
    }

    #[test]
    fn groups_of_2_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let gs = groups(&data, 2);

        assert_eq!(vec![
            vec!(1.0, 2.0),
            vec!(2.0, 3.0),
            vec!(3.0, 4.0),
            vec!(4.0, 5.0),
            vec!(5.0, 6.0)], gs);
    }

    #[test]
    fn groups_of_3_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let gs = groups(&data, 3);

        assert_eq!(vec![
            vec!(1.0, 2.0, 3.0),
            vec!(2.0, 3.0, 4.0),
            vec!(3.0, 4.0, 5.0),
            vec!(4.0, 5.0, 6.0)], gs);
    }

    #[test]
    fn mad_of_2_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let mad = median_filter(&data, 2);

        assert_eq!(vec![
            1.5,
            2.5,
            3.5,
            4.5,
            5.5], mad);
    }

    #[test]
    fn mad_of_3_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let mad = median_filter(&data, 3);

        assert_eq!(vec![
            2.0,
            3.0,
            4.0,
            5.0], mad);
    }
}
<commit_msg>Rename result to median<commit_after>pub fn median_filter(data: &Vec<f64>, window: usize) -> Vec<f64> {
    groups(data, window)
        .iter()
        .map(median_of)
        .collect()
}

fn median_of(data: &Vec<f64>) -> f64 {
    let n = data.len();
    let mut copy = vec!(0f64; n);
    copy.copy_from_slice(&data);
    copy.sort_by(|a,b| a.partial_cmp(b).unwrap());
    let middle: usize = n / 2;

    let median = if n % 2 == 1 {
        copy[middle]
    } else {
        (copy[middle] + copy[middle - 1])/2.0
    };

    median
}

fn groups(data: &Vec<f64>, group_size: usize) -> Vec<Vec<f64>> {
    let mut groups: Vec<Vec<f64>> = vec!();

    for end_index in group_size .. data.len() + 1 {
        let mut group: Vec<f64> = vec!();
        for index in (end_index - group_size) .. end_index {
            group.push(data[index])
        }
        groups.push(group)
    }

    groups
}

#[cfg(test)]
pub mod tests {
    const EPSILON: f64 = 0.00001;
    use super::*;

    #[test]
    fn median_of_odd_length_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0);

        let median = median_of(&data);

        assert!((median - 2.0).abs() < EPSILON);
    }

    #[test]
    fn median_of_even_length_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0);

        let median = median_of(&data);

        assert_eq!(median, 2.5);
        assert!((median - 2.5).abs() < EPSILON);
    }

    #[test]
    fn median_of_unsorted_vector() {
        let data: Vec<f64> = vec!(2.0, 4.0, 3.0, 1.0);

        let median = median_of(&data);

        assert_eq!(median, 2.5);
        assert!((median - 2.5).abs() < EPSILON);
    }

    #[test]
    fn groups_of_2_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let gs = groups(&data, 2);

        assert_eq!(vec![
            vec!(1.0, 2.0),
            vec!(2.0, 3.0),
            vec!(3.0, 4.0),
            vec!(4.0, 5.0),
            vec!(5.0, 6.0)], gs);
    }

    #[test]
    fn groups_of_3_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let gs = groups(&data, 3);

        assert_eq!(vec![
            vec!(1.0, 2.0, 3.0),
            vec!(2.0, 3.0, 4.0),
            vec!(3.0, 4.0, 5.0),
            vec!(4.0, 5.0, 6.0)], gs);
    }

    #[test]
    fn mad_of_2_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let mad = median_filter(&data, 2);

        assert_eq!(vec![
            1.5,
            2.5,
            3.5,
            4.5,
            5.5], mad);
    }

    #[test]
    fn mad_of_3_for_a_vector() {
        let data: Vec<f64> = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

        let mad = median_filter(&data, 3);

        assert_eq!(vec![
            2.0,
            3.0,
            4.0,
            5.0], mad);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::completion::{
    completion_items_for_request, get_completion_request, send_completion_response,
};
use crate::goto_definition::{get_goto_definition_response, send_goto_definition_response};
use crate::hover::{get_hover_response_contents, send_hover_response};
use crate::lsp::{
    set_initializing_status, Completion, CompletionOptions, Connection, DidChangeTextDocument,
    DidCloseTextDocument, DidOpenTextDocument, Exit, GotoDefinition, HoverRequest,
    InitializeParams, LSPBridgeMessage, Message, Notification, Request, ServerCapabilities,
    ServerNotification, ServerRequest, ServerRequestId, ServerResponse, Shutdown,
    TextDocumentSyncCapability, TextDocumentSyncKind, Url, WorkDoneProgressOptions,
};
use crate::lsp_process_error::LSPProcessResult;
use crate::status_reporting::LSPStatusReporter;
use crate::text_documents::extract_graphql_sources;
use crate::text_documents::{
    on_did_change_text_document, on_did_close_text_document, on_did_open_text_document,
};
use crate::utils::get_node_resolution_info;
use common::{PerfLogEvent, PerfLogger};
use crossbeam::Sender;
use graphql_ir::Program;
use graphql_syntax::GraphQLSource;
use interner::StringKey;
use log::info;
use relay_compiler::{compiler::Compiler, config::Config, FileCategorizer};
use schema::Schema;
use std::{
    collections::HashMap,
    path::PathBuf,
    sync::{Arc, RwLock},
};
use tokio::sync::{mpsc, mpsc::Receiver, Notify};

pub struct Server {
    sender: Sender<Message>,
    schemas: Arc<RwLock<HashMap<StringKey, Arc<Schema>>>>,
    synced_graphql_documents: HashMap<Url, Vec<GraphQLSource>>,
    lsp_rx: Receiver<LSPBridgeMessage>,
    file_categorizer: FileCategorizer,
    root_dir: PathBuf,
    source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
}

impl Server {
    fn on_lsp_bridge_message(&mut self, message: LSPBridgeMessage) {
        match message {
            LSPBridgeMessage::HoverRequest {
                request_id,
                text_document_position,
            } => {
                let get_hover_response_contents = || {
                    let node_resolution_info = get_node_resolution_info(
                        text_document_position,
                        &self.synced_graphql_documents,
                        &self.file_categorizer,
                        &self.root_dir,
                    )?;
                    if let Some(schema) = self
                        .schemas
                        .read()
                        .unwrap()
                        .get(&node_resolution_info.project_name)
                    {
                        get_hover_response_contents(
                            node_resolution_info,
                            schema,
                            &self.source_programs,
                        )
                    } else {
                        None
                    }
                };

                send_hover_response(get_hover_response_contents(), request_id, &self.sender);
            }
            LSPBridgeMessage::CompletionRequest { request_id, params } => {
                if let Some(completion_request) = get_completion_request(
                    params,
                    &self.synced_graphql_documents,
                    &self.file_categorizer,
                    &self.root_dir,
                ) {
                    info!("completion_request {:#?}", &completion_request);
                    if let Some(schema) = self
                        .schemas
                        .read()
                        .unwrap()
                        .get(&completion_request.project_name)
                    {
                        if let Some(items) = completion_items_for_request(
                            completion_request,
                            schema,
                            &self.source_programs,
                        ) {
                            send_completion_response(items, request_id, &self.sender);
                        }
                    }
                }
            }
            LSPBridgeMessage::GotoDefinitionRequest {
                request_id,
                text_document_position,
            } => {
                let get_goto_definition_response = || {
                    let node_resolution_info = get_node_resolution_info(
                        text_document_position,
                        &self.synced_graphql_documents,
                        &self.file_categorizer,
                        &self.root_dir,
                    )?;
                    get_goto_definition_response(
                        node_resolution_info,
                        &self.source_programs,
                        &self.root_dir,
                    )
                };

                send_goto_definition_response(
                    get_goto_definition_response(),
                    request_id,
                    &self.sender,
                );
            }
            LSPBridgeMessage::DidOpenTextDocument(params) => {
                on_did_open_text_document(params, &mut self.synced_graphql_documents);
            }
            LSPBridgeMessage::DidChangeTextDocument(params) => {
                on_did_change_text_document(params, &mut self.synced_graphql_documents);
            }
            LSPBridgeMessage::DidCloseTextDocument(params) => {
                on_did_close_text_document(params, &mut self.synced_graphql_documents);
            }
        }
    }

    fn get_schemas(&self) -> Arc<RwLock<HashMap<StringKey, Arc<Schema>>>> {
        self.schemas.clone()
    }

    fn get_source_programs(&self) -> Arc<RwLock<HashMap<StringKey, Program>>> {
        self.source_programs.clone()
    }

    async fn watch(&mut self) -> LSPProcessResult<()> {
        loop {
            if let Some(message) = self.lsp_rx.recv().await {
                self.on_lsp_bridge_message(message)
            }
        }
    }
}

/// Initializes an LSP connection, handling the `initialize` message and `initialized` notification
/// handshake.
pub fn initialize(connection: &Connection) -> LSPProcessResult<InitializeParams> {
    let mut server_capabilities = ServerCapabilities::default();
    // Enable text document syncing so we can know when files are opened/changed/saved/closed
    server_capabilities.text_document_sync =
        Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::Full));

    server_capabilities.completion_provider = Some(CompletionOptions {
        resolve_provider: Some(true),
        trigger_characters: None,
        work_done_progress_options: WorkDoneProgressOptions {
            work_done_progress: None,
        },
    });

    server_capabilities.hover_provider = Some(true);
    server_capabilities.definition_provider = Some(true);

    let server_capabilities = serde_json::to_value(&server_capabilities)?;
    let params = connection.initialize(server_capabilities)?;
    let params: InitializeParams = serde_json::from_value(params)?;
    Ok(params)
}

/// Run the main server loop
pub async fn run<TPerfLogger>(
    connection: Connection,
    mut config: Config,
    _params: InitializeParams,
    perf_logger: Arc<TPerfLogger>,
) -> LSPProcessResult<()>
where
    TPerfLogger: PerfLogger + 'static,
{
    info!("Running language server");
    let receiver = connection.receiver.clone();
    let sender = connection.sender.clone();

    // A `Notify` instance used to signal that the compiler should be initialized.
    let compiler_notify = Arc::new(Notify::new());
    let compiler_notify_clone = compiler_notify.clone();
    let mut has_notified = false;
    let mut on_opened_document = move |text: &String| {
        if !has_notified && extract_graphql_sources(text).is_some() {
            has_notified = true;
            compiler_notify_clone.notify();
        }
    };

    // A channel to communicate between the LSP message loop and the compiler loop
    let (mut lsp_tx, lsp_rx) = mpsc::channel::<LSPBridgeMessage>(100);
    let logger_for_process = Arc::clone(&perf_logger);

    tokio::spawn(async move {
        for msg in receiver {
            let perf_logger_msg_event = logger_for_process.create_event("lsp_message");
            info!("Received LSP message\n{:?}", msg);
            match msg {
                Message::Request(req) => {
                    // Auto-complete request
                    if req.method == Completion::METHOD {
                        let (request_id, params) = extract_request_params::<Completion>(req);
                        lsp_tx
                            .send(LSPBridgeMessage::CompletionRequest { request_id, params })
                            .await
                            .ok();
                    } else if req.method == HoverRequest::METHOD {
                        info!("hover request....");
                        let (request_id, text_document_position) =
                            extract_request_params::<HoverRequest>(req);
                        lsp_tx
                            .send(LSPBridgeMessage::HoverRequest {
                                request_id,
                                text_document_position,
                            })
                            .await
                            .ok();
                    } else if req.method == Shutdown::METHOD {
                        perf_logger_msg_event.string("method", req.method.clone());
                        logger_for_process.complete_event(perf_logger_msg_event);
                        logger_for_process.flush();
                        let (request_id, _) = extract_request_params::<Shutdown>(req);
                        let response = ServerResponse {
                            id: request_id,
                            error: None,
                            result: None,
                        };
                        sender.send(Message::Response(response)).ok();
                        // TODO: We should exit when receiving Exit notification according to the protocol,
                        // but the notification is never received.
                        std::process::exit(0);
                    } else if req.method == GotoDefinition::METHOD {
                        let (request_id, text_document_position) =
                            extract_request_params::<GotoDefinition>(req);
                        lsp_tx
                            .send(LSPBridgeMessage::GotoDefinitionRequest {
                                request_id,
                                text_document_position,
                            })
                            .await
                            .ok();
                    }
                }
                Message::Notification(notif) => {
                    let mut should_flush_perf_log_event = true;
                    perf_logger_msg_event.string("method", notif.method.clone());
                    let lsp_message_processing_time =
                        perf_logger_msg_event.start("lsp_message_processing_time");

                    match &notif.method {
                        method if method == DidOpenTextDocument::METHOD => {
                            let params = extract_notif_params::<DidOpenTextDocument>(notif);
                            on_opened_document(&params.text_document.text);
                            lsp_tx
                                .send(LSPBridgeMessage::DidOpenTextDocument(params))
                                .await
                                .ok();
                        }
                        method if method == DidChangeTextDocument::METHOD => {
                            let params = extract_notif_params::<DidChangeTextDocument>(notif);
                            lsp_tx
                                .send(LSPBridgeMessage::DidChangeTextDocument(params))
                                .await
                                .ok();
                        }
                        method if method == DidCloseTextDocument::METHOD => {
                            let params = extract_notif_params::<DidCloseTextDocument>(notif);
                            lsp_tx
                                .send(LSPBridgeMessage::DidCloseTextDocument(params))
                                .await
                                .ok();
                        }
                        method if method == Exit::METHOD => {
                            perf_logger_msg_event.stop(lsp_message_processing_time);
                            logger_for_process.complete_event(perf_logger_msg_event);
                            logger_for_process.flush();
                            std::process::exit(0);
                        }
                        _ => {
                            should_flush_perf_log_event = false;
                            // Notifications we don't care about
                        }
                    }
                    if should_flush_perf_log_event {
                        perf_logger_msg_event.stop(lsp_message_processing_time);
                        logger_for_process.complete_event(perf_logger_msg_event);
                        logger_for_process.flush();
                    }
                }
                Message::Response(_) => {
                    // Ignore responses for now
                }
            }
        }
    });

    info!("Waiting for compiler to initialize...");
    compiler_notify.notified().await;
    set_initializing_status(&connection.sender);

    let mut server = Server {
        synced_graphql_documents: Default::default(),
        schemas: Default::default(),
        sender: connection.sender.clone(),
        lsp_rx,
        file_categorizer: FileCategorizer::from_config(&config),
        root_dir: config.root_dir.clone(),
        source_programs: Default::default(),
    };

    config.status_reporter = Box::new(LSPStatusReporter::new(
        config.root_dir.clone(),
        connection.sender.clone(),
    ));

    let schemas_writer = server.get_schemas();
    let source_programs_writer = server.get_source_programs();
    config.on_build_project_success =
        Some(Box::new(move |project_name, schema, source_program| {
            schemas_writer
                .write()
                .unwrap()
                .insert(project_name, schema.clone());
            source_programs_writer
                .write()
                .unwrap()
                .insert(project_name, source_program.clone());
        }));

    tokio::spawn(async move {
        let compiler = Compiler::new(config, Arc::clone(&perf_logger));
        compiler.watch().await
    });

    server.watch().await?;
    Ok(())
}

fn extract_notif_params<N>(notif: ServerNotification) -> N::Params
where
    N: Notification,
{
    notif.extract(N::METHOD).unwrap()
}

fn extract_request_params<R>(req: ServerRequest) -> (ServerRequestId, R::Params)
where
    R: Request,
{
    req.extract(R::METHOD).unwrap()
}
<commit_msg>handle errors in one place<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::goto_definition::{get_goto_definition_response, send_goto_definition_response};
use crate::hover::{get_hover_response_contents, send_hover_response};
use crate::lsp::{
    set_initializing_status, Completion, CompletionOptions, Connection, DidChangeTextDocument,
    DidCloseTextDocument, DidOpenTextDocument, Exit, GotoDefinition, HoverRequest,
    InitializeParams, LSPBridgeMessage, Message, Notification, Request, ServerCapabilities,
    ServerNotification, ServerRequest, ServerRequestId, ServerResponse, Shutdown,
    TextDocumentSyncCapability, TextDocumentSyncKind, Url, WorkDoneProgressOptions,
};
use crate::lsp_process_error::LSPProcessResult;
use crate::status_reporting::LSPStatusReporter;
use crate::text_documents::extract_graphql_sources;
use crate::text_documents::{
    on_did_change_text_document, on_did_close_text_document, on_did_open_text_document,
};
use crate::utils::get_node_resolution_info;
use crate::{
    completion::{completion_items_for_request, get_completion_request, send_completion_response},
    lsp_runtime_error::LSPRuntimeResult,
};
use common::{PerfLogEvent, PerfLogger};
use crossbeam::Sender;
use graphql_ir::Program;
use graphql_syntax::GraphQLSource;
use interner::StringKey;
use log::info;
use lsp_server::{RequestId, ResponseError};
use relay_compiler::{compiler::Compiler, config::Config, FileCategorizer};
use schema::Schema;
use std::{
    collections::HashMap,
    path::PathBuf,
    sync::{Arc, RwLock},
};
use tokio::sync::{mpsc, mpsc::Receiver, Notify};

pub struct Server<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    sender: Sender<Message>,
    schemas: Arc<RwLock<HashMap<StringKey, Arc<Schema>>>>,
    synced_graphql_documents: HashMap<Url, Vec<GraphQLSource>>,
    lsp_rx: Receiver<LSPBridgeMessage>,
    file_categorizer: FileCategorizer,
    root_dir: PathBuf,
    source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
    perf_logger: Arc<TPerfLogger>,
}

impl<TPerfLogger> Server<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    async fn watch(&mut self) -> LSPProcessResult<()> {
        loop {
            if let Some(message) = self.lsp_rx.recv().await {
                self.handle_lsp_bridge_message(message);
            }
        }
    }

    fn handle_lsp_bridge_message(&mut self, message: LSPBridgeMessage) {
        if let Some((id, lsp_response)) = self.get_lsp_bridge_message_response(message) {
            // TODO handle send error
            let _ = self
                .sender
                .send(Message::Response(create_server_response_and_log(
                    id,
                    lsp_response,
                    &self.perf_logger,
                )));
        }
    }

    fn get_lsp_bridge_message_response(
        &mut self,
        message: LSPBridgeMessage,
    ) -> Option<(RequestId, LSPRuntimeResult<serde_json::Value>)> {
        match message {
            LSPBridgeMessage::HoverRequest {
                request_id,
                text_document_position,
            } => {
                let get_hover_response_contents = || {
                    let node_resolution_info = get_node_resolution_info(
                        text_document_position,
                        &self.synced_graphql_documents,
                        &self.file_categorizer,
                        &self.root_dir,
                    )?;
                    if let Some(schema) = self
                        .schemas
                        .read()
                        .unwrap()
                        .get(&node_resolution_info.project_name)
                    {
                        get_hover_response_contents(
                            node_resolution_info,
                            schema,
                            &self.source_programs,
                        )
                    } else {
                        None
                    }
                };

                send_hover_response(get_hover_response_contents(), request_id, &self.sender);
                None
            }
            LSPBridgeMessage::CompletionRequest { request_id, params } => {
                if let Some(completion_request) = get_completion_request(
                    params,
                    &self.synced_graphql_documents,
                    &self.file_categorizer,
                    &self.root_dir,
                ) {
                    info!("completion_request {:#?}", &completion_request);
                    if let Some(schema) = self
                        .schemas
                        .read()
                        .unwrap()
                        .get(&completion_request.project_name)
                    {
                        if let Some(items) = completion_items_for_request(
                            completion_request,
                            schema,
                            &self.source_programs,
                        ) {
                            send_completion_response(items, request_id, &self.sender);
                        }
                    }
                }
                None
            }
            LSPBridgeMessage::GotoDefinitionRequest {
                request_id,
                text_document_position,
            } => {
                let get_goto_definition_response = || {
                    let node_resolution_info = get_node_resolution_info(
                        text_document_position,
                        &self.synced_graphql_documents,
                        &self.file_categorizer,
                        &self.root_dir,
                    )?;
                    get_goto_definition_response(
                        node_resolution_info,
                        &self.source_programs,
                        &self.root_dir,
                    )
                };

                send_goto_definition_response(
                    get_goto_definition_response(),
                    request_id,
                    &self.sender,
                );
                None
            }
            LSPBridgeMessage::DidOpenTextDocument(params) => {
                on_did_open_text_document(params, &mut self.synced_graphql_documents);
                None
            }
            LSPBridgeMessage::DidChangeTextDocument(params) => {
                on_did_change_text_document(params, &mut self.synced_graphql_documents);
                None
            }
            LSPBridgeMessage::DidCloseTextDocument(params) => {
                on_did_close_text_document(params, &mut self.synced_graphql_documents);
                None
            }
        }
    }

    fn get_schemas(&self) -> Arc<RwLock<HashMap<StringKey, Arc<Schema>>>> {
        self.schemas.clone()
    }

    fn get_source_programs(&self) -> Arc<RwLock<HashMap<StringKey, Program>>> {
        self.source_programs.clone()
    }
}

/// Initializes an LSP connection, handling the `initialize` message and `initialized` notification
/// handshake.
pub fn initialize(connection: &Connection) -> LSPProcessResult<InitializeParams> {
    let mut server_capabilities = ServerCapabilities::default();
    // Enable text document syncing so we can know when files are opened/changed/saved/closed
    server_capabilities.text_document_sync =
        Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::Full));

    server_capabilities.completion_provider = Some(CompletionOptions {
        resolve_provider: Some(true),
        trigger_characters: None,
        work_done_progress_options: WorkDoneProgressOptions {
            work_done_progress: None,
        },
    });

    server_capabilities.hover_provider = Some(true);
    server_capabilities.definition_provider = Some(true);

    let server_capabilities = serde_json::to_value(&server_capabilities)?;
    let params = connection.initialize(server_capabilities)?;
    let params: InitializeParams = serde_json::from_value(params)?;
    Ok(params)
}

/// Run the main server loop
pub async fn run<TPerfLogger>(
    connection: Connection,
    mut config: Config,
    _params: InitializeParams,
    perf_logger: Arc<TPerfLogger>,
) -> LSPProcessResult<()>
where
    TPerfLogger: PerfLogger + 'static,
{
    info!("Running language server");
    let receiver = connection.receiver.clone();
    let sender = connection.sender.clone();

    // A `Notify` instance used to signal that the compiler should be initialized.
    let compiler_notify = Arc::new(Notify::new());
    let compiler_notify_clone = compiler_notify.clone();
    let mut has_notified = false;
    let mut on_opened_document = move |text: &String| {
        if !has_notified && extract_graphql_sources(text).is_some() {
            has_notified = true;
            compiler_notify_clone.notify();
        }
    };

    // A channel to communicate between the LSP message loop and the compiler loop
    let (mut lsp_tx, lsp_rx) = mpsc::channel::<LSPBridgeMessage>(100);
    let logger_for_process = Arc::clone(&perf_logger);

    tokio::spawn(async move {
        for msg in receiver {
            let perf_logger_msg_event = logger_for_process.create_event("lsp_message");
            info!("Received LSP message\n{:?}", msg);
            match msg {
                Message::Request(req) => {
                    // Auto-complete request
                    if req.method == Completion::METHOD {
                        let (request_id, params) = extract_request_params::<Completion>(req);
                        lsp_tx
                            .send(LSPBridgeMessage::CompletionRequest { request_id, params })
                            .await
                            .ok();
                    } else if req.method == HoverRequest::METHOD {
                        info!("hover request....");
                        let (request_id, text_document_position) =
                            extract_request_params::<HoverRequest>(req);
                        lsp_tx
                            .send(LSPBridgeMessage::HoverRequest {
                                request_id,
                                text_document_position,
                            })
                            .await
                            .ok();
                    } else if req.method == Shutdown::METHOD {
                        perf_logger_msg_event.string("method", req.method.clone());
                        logger_for_process.complete_event(perf_logger_msg_event);
                        logger_for_process.flush();
                        let (request_id, _) = extract_request_params::<Shutdown>(req);
                        let response = ServerResponse {
                            id: request_id,
                            error: None,
                            result: None,
                        };
                        sender.send(Message::Response(response)).ok();
                        // TODO: We should exit when receiving Exit notification according to the protocol,
                        // but the notification is never received.
                        std::process::exit(0);
                    } else if req.method == GotoDefinition::METHOD {
                        let (request_id, text_document_position) =
                            extract_request_params::<GotoDefinition>(req);
                        lsp_tx
                            .send(LSPBridgeMessage::GotoDefinitionRequest {
                                request_id,
                                text_document_position,
                            })
                            .await
                            .ok();
                    }
                }
                Message::Notification(notif) => {
                    let mut should_flush_perf_log_event = true;
                    perf_logger_msg_event.string("method", notif.method.clone());
                    let lsp_message_processing_time =
                        perf_logger_msg_event.start("lsp_message_processing_time");

                    match &notif.method {
                        method if method == DidOpenTextDocument::METHOD => {
                            let params = extract_notif_params::<DidOpenTextDocument>(notif);
                            on_opened_document(&params.text_document.text);
                            lsp_tx
                                .send(LSPBridgeMessage::DidOpenTextDocument(params))
                                .await
                                .ok();
                        }
                        method if method == DidChangeTextDocument::METHOD => {
                            let params = extract_notif_params::<DidChangeTextDocument>(notif);
                            lsp_tx
                                .send(LSPBridgeMessage::DidChangeTextDocument(params))
                                .await
                                .ok();
                        }
                        method if method == DidCloseTextDocument::METHOD => {
                            let params = extract_notif_params::<DidCloseTextDocument>(notif);
                            lsp_tx
                                .send(LSPBridgeMessage::DidCloseTextDocument(params))
                                .await
                                .ok();
                        }
                        method if method == Exit::METHOD => {
                            perf_logger_msg_event.stop(lsp_message_processing_time);
                            logger_for_process.complete_event(perf_logger_msg_event);
                            logger_for_process.flush();
                            std::process::exit(0);
                        }
                        _ => {
                            should_flush_perf_log_event = false;
                            // Notifications we don't care about
                        }
                    }
                    if should_flush_perf_log_event {
                        perf_logger_msg_event.stop(lsp_message_processing_time);
                        logger_for_process.complete_event(perf_logger_msg_event);
                        logger_for_process.flush();
                    }
                }
                Message::Response(_) => {
                    // Ignore responses for now
                }
            }
        }
    });

    info!("Waiting for compiler to initialize...");
    compiler_notify.notified().await;
    set_initializing_status(&connection.sender);

    let mut server = Server {
        synced_graphql_documents: Default::default(),
        schemas: Default::default(),
        sender: connection.sender.clone(),
        lsp_rx,
        file_categorizer: FileCategorizer::from_config(&config),
        root_dir: config.root_dir.clone(),
        source_programs: Default::default(),
        perf_logger: Arc::clone(&perf_logger),
    };

    config.status_reporter = Box::new(LSPStatusReporter::new(
        config.root_dir.clone(),
        connection.sender.clone(),
    ));

    let schemas_writer = server.get_schemas();
    let source_programs_writer = server.get_source_programs();
    config.on_build_project_success =
        Some(Box::new(move |project_name, schema, source_program| {
            schemas_writer
                .write()
                .unwrap()
                .insert(project_name, schema.clone());
            source_programs_writer
                .write()
                .unwrap()
                .insert(project_name, source_program.clone());
        }));

    tokio::spawn(async move {
        let compiler = Compiler::new(config, Arc::clone(&perf_logger));
        compiler.watch().await
    });

    server.watch().await?;
    Ok(())
}

fn extract_notif_params<N>(notif: ServerNotification) -> N::Params
where
    N: Notification,
{
    notif.extract(N::METHOD).unwrap()
}

fn extract_request_params<R>(req: ServerRequest) -> (ServerRequestId, R::Params)
where
    R: Request,
{
    req.extract(R::METHOD).unwrap()
}

fn create_server_response_and_log<TPerfLogger>(
    id: RequestId,
    lsp_runtime_result: LSPRuntimeResult<serde_json::Value>,
    perf_logger: &Arc<TPerfLogger>,
) -> ServerResponse
where
    TPerfLogger: PerfLogger + 'static,
{
    match lsp_runtime_result {
        Ok(result) => ServerResponse {
            id,
            result: Some(result),
            error: None,
        },
        Err(error) => {
            let response_error: Option<ResponseError> = error.into();
            match response_error {
                Some(error) => {
                    log_response_error(perf_logger, error.clone());
                    ServerResponse {
                        id,
                        result: None,
                        error: Some(error),
                    }
                }
                None => {
                    // This is not correct behavior here. We should cancel the request.
                    // This will print an error like
                    // "Error: The received response has neither a result nor an error property."
                    ServerResponse {
                        id,
                        result: None,
                        error: None,
                    }
                }
            }
        }
    }
}

fn log_response_error<TPerfLogger>(perf_logger: &Arc<TPerfLogger>, error: ResponseError)
where
    TPerfLogger: PerfLogger + 'static,
{
    let event = perf_logger.create_event("lsp_error");
    event.string("lsp_error_code", error.code.to_string());
    event.string("lsp_error_message", error.message);
    if let Some(data) = error.data {
        event.string("lsp_error_data", data.to_string());
    }
    perf_logger.complete_event(event);
}
<|endoftext|>