<commit_before>use std::{env, ffi::OsString, fmt, net::SocketAddr, path::PathBuf, str::FromStr};

use anyhow::bail;
use clap::{Arg, ArgGroup, Command as ClapCommand};
use sccache::{config, dist::ServerId};
use syslog::Facility;

use crate::cmdline::{AuthSubcommand, Command};

#[derive(Debug)]
struct TokenLength(usize);

impl TokenLength {
    fn as_bytes(&self) -> usize {
        self.0 / 8
    }

    fn from_bits(bits: &str) -> anyhow::Result<Self> {
        let bits: usize = bits.parse()?;
        if bits % 8 != 0 || bits < 64 || bits > 4_096 {
            bail!("Number of bits must be divisible by 8, greater than 64 and less than 4096")
        }

        Ok(Self(bits))
    }
}

impl fmt::Display for TokenLength {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(Clone, Copy)]
enum LogLevel {
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

impl LogLevel {
    fn as_str(&self) -> &'static str {
        match self {
            Self::Error => "error",
            Self::Warn => "warn",
            Self::Info => "info",
            Self::Debug => "debug",
            Self::Trace => "trace",
        }
    }

    fn values() -> &'static [Self] {
        &[
            Self::Error,
            Self::Warn,
            Self::Info,
            Self::Debug,
            Self::Trace,
        ]
    }
}

impl FromStr for LogLevel {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let variant = match s {
            "error" => Self::Error,
            "warn" => Self::Warn,
            "info" => Self::Info,
            "debug" => Self::Debug,
            "trace" => Self::Trace,
            _ => bail!("Unknown log level: {:?}", s),
        };

        Ok(variant)
    }
}

impl From<LogLevel> for log::LevelFilter {
    fn from(log_level: LogLevel) -> Self {
        match log_level {
            LogLevel::Error => Self::Error,
            LogLevel::Warn => Self::Warn,
            LogLevel::Info => Self::Info,
            LogLevel::Debug => Self::Debug,
            LogLevel::Trace => Self::Trace,
        }
    }
}

fn flag_infer_long(name: &'static str) -> Arg<'static> {
    Arg::new(name).long(name)
}

fn get_clap_command() -> ClapCommand<'static> {
    let syslog = flag_infer_long("syslog")
        .help("Log to the syslog with LEVEL")
        .value_name("LEVEL")
        .possible_values(LogLevel::values().iter().map(LogLevel::as_str));
    let config_with_help_message =
        |help: &'static str| flag_infer_long("config").help(help).value_name("PATH");

    ClapCommand::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .subcommand_required(true)
        .subcommand(
            ClapCommand::new("auth")
                .subcommand_required(true)
                .subcommand(ClapCommand::new("generate-jwt-hs256-key"))
                .subcommand(
                    ClapCommand::new("generate-jwt-hs256-server-token")
                        .args(&[
                            flag_infer_long("server")
                                .help("Generate a key for the specified server")
                                .value_name("SERVER_ADDR")
                                .required(true),
                            flag_infer_long("secret-key")
                                .help("Use specified key to create the token")
                                .value_name("KEY"),
                            config_with_help_message(
                                "Use the key from the scheduler config file at PATH",
                            ),
                        ])
                        .group(
                            ArgGroup::new("key_source_mutual_exclusion")
                                .args(&["config", "secret-key"])
                                .required(true),
                        ),
                )
                .subcommand(
                    ClapCommand::new("generate-shared-token").arg(
                        flag_infer_long("bits")
                            .help("Use the specified number of bits of randomness")
                            .value_name("BITS")
                            .default_value("256")
                            .validator(TokenLength::from_bits),
                    ),
                ),
        )
        .subcommand(ClapCommand::new("scheduler").args(&[
            config_with_help_message("Use the scheduler config file at PATH").required(true),
            syslog.clone(),
        ]))
        .subcommand(ClapCommand::new("server").args(&[
            config_with_help_message("Use the server config file at PATH").required(true),
            syslog,
        ]))
}

fn check_init_syslog(name: &str, log_level: LogLevel) {
    let level = log::LevelFilter::from(log_level);
    drop(syslog::init(Facility::LOG_DAEMON, level, Some(name)));
}

/// Parse commandline `args` into a `Result<Command>` to execute.
pub fn try_parse_from(
    args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
) -> anyhow::Result<Command> {
    let matches = get_clap_command().try_get_matches_from(args)?;

    Ok(match matches.subcommand() {
        Some(("auth", matches)) => Command::Auth(match matches.subcommand() {
            // Size based on https://briansmith.org/rustdoc/ring/hmac/fn.recommended_key_len.html
            Some(("generate-jwt-hs256-key", _)) => AuthSubcommand::Base64 { num_bytes: 256 / 8 },
            Some(("generate-jwt-hs256-server-token", matches)) => {
                let server_addr: SocketAddr = matches.value_of_t("server")?;
                let server_id = ServerId::new(server_addr);

                let secret_key = if matches.is_present("config") {
                    let config_path: PathBuf = matches.value_of_t("config")?;
                    if let Some(config) = config::scheduler::from_path(&config_path)? {
                        match config.server_auth {
                            config::scheduler::ServerAuth::JwtHS256 { secret_key } => secret_key,
                            config::scheduler::ServerAuth::Insecure
                            | config::scheduler::ServerAuth::Token { .. } => {
                                bail!("Scheduler not configured with JWT HS256")
                            }
                        }
                    } else {
                        bail!("Could not load config")
                    }
                } else {
                    matches.value_of_t("secret-key")?
                };

                AuthSubcommand::JwtHS256ServerToken {
                    secret_key,
                    server_id,
                }
            }
            Some(("generate-shared-token", matches)) => {
                let token_bits = TokenLength::from_bits(
                    matches.value_of("bits").expect("clap provides default"),
                )
                .expect("clap uses `from_bits` as a validator");

                AuthSubcommand::Base64 {
                    num_bytes: token_bits.as_bytes(),
                }
            }
            _ => unreachable!("Subcommand is enforced by clap"),
        }),
        Some(("scheduler", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-scheduler", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::scheduler::from_path(&config_path)? {
                Command::Scheduler(config)
            } else {
                bail!("Could not load config")
            }
        }
        Some(("server", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-buildserver", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::server::from_path(&config_path)? {
                Command::Server(config)
            } else {
                bail!("Could not load config")
            }
        }
        _ => unreachable!("Subcommand is enforced by clap"),
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    const EXE: &str = "sccache-dist";

    fn auth_generate_shared_tokens_bits(bit_val: &'static str) -> Vec<&'static str> {
        vec![EXE, "auth", "generate-shared-token", "--bits", bit_val]
    }

    fn auth_generate_jwt_hs256_server_token(subcommand_args: &[&'static str]) -> Vec<&'static str> {
        let mut args = vec![EXE, "auth", "generate-jwt-hs256-server-token"];
        args.extend_from_slice(subcommand_args);
        args
    }

    #[test]
    fn debug_assert() {
        get_clap_command().debug_assert()
    }

    #[test]
    fn missing_required_subcommands_fails() {
        let args_sets = &[vec![EXE], vec![EXE, "auth"]];

        for args in args_sets {
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn invalid_token_bits_fails() {
        let invalid_vals = vec!["not_a_num", "58", "8000", "70"];

        for invalid_val in invalid_vals {
            let args = auth_generate_shared_tokens_bits(invalid_val);
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn auth_generate_server_token_needs_key_source() {
        let server_args = &["--server", "127.0.0.1:4321"];

        let no_key = auth_generate_jwt_hs256_server_token(server_args);
        assert!(try_parse_from(no_key).is_err());

        let mut too_many_keys = auth_generate_jwt_hs256_server_token(server_args);
        too_many_keys.extend_from_slice(&["--secret-key", "secret", "--config", "some/path.toml"]);
        assert!(try_parse_from(too_many_keys).is_err());
    }

    // This is all just to work around `PartialEq` not being on some of the values used in variants
    // for `Command` yet
    fn assert_args_parse_to_auth(args: Vec<&'static str>, ideal_auth: AuthSubcommand) {
        match try_parse_from(&args) {
            Ok(Command::Auth(auth)) => assert_eq!(auth, ideal_auth),
            _ => panic!("Bad parsing for: {:#?}", args),
        }
    }

    #[test]
    fn auth_generate_jwt_hs256_key_good() {
        let args = vec![EXE, "auth", "generate-jwt-hs256-key"];

        assert_args_parse_to_auth(args, AuthSubcommand::Base64 { num_bytes: 256 / 8 });
    }

    #[test]
    fn auth_generate_jwt_hs256_server_token_good() {
        let base = auth_generate_jwt_hs256_server_token(&["--server", "127.0.0.1:4321"]);
        let server_socket: SocketAddr = "127.0.0.1:4321".parse().unwrap();
        let server_id = ServerId::new(server_socket);

        let mut secret_key = base.clone();
        secret_key.extend_from_slice(&["--secret-key", "very secret"]);
        assert_args_parse_to_auth(
            secret_key,
            AuthSubcommand::JwtHS256ServerToken {
                server_id,
                secret_key: "very secret".to_owned(),
            },
        );
    }

    #[test]
    fn auth_generate_shared_token_good() {
        let raw_to_expected_bit_vals = &[
            ("64", 64 / 8),
            ("128", 128 / 8),
            ("136", 136 / 8),
            ("4000", 4_000 / 8),
        ];

        for (raw, expected) in raw_to_expected_bit_vals {
            let args = auth_generate_shared_tokens_bits(raw);
            assert_args_parse_to_auth(
                args,
                AuthSubcommand::Base64 {
                    num_bytes: *expected,
                },
            );
        }
    }
}
<commit_msg>Make `TokenLength::from_bits()` error message more granular<commit_after>use std::{env, ffi::OsString, fmt, net::SocketAddr, path::PathBuf, str::FromStr};

use anyhow::{anyhow, bail};
use clap::{Arg, ArgGroup, Command as ClapCommand};
use sccache::{config, dist::ServerId};
use syslog::Facility;

use crate::cmdline::{AuthSubcommand, Command};

#[derive(Debug)]
struct TokenLength(usize);

impl TokenLength {
    fn as_bytes(&self) -> usize {
        self.0 / 8
    }

    fn from_bits(bits: &str) -> anyhow::Result<Self> {
        let bits: usize = bits.parse()?;

        if bits & 0x7 != 0 {
            Err(anyhow!("Number of bits must be divisible by 8"))
        } else if bits < 64 {
            Err(anyhow!(
                "Number of bits must be greater than or equal to 64"
            ))
        } else if bits > 4_096 {
            Err(anyhow!("Number of bits must be less than or equal to 4096"))
        } else {
            Ok(Self(bits))
        }
    }
}

impl fmt::Display for TokenLength {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(Clone, Copy)]
enum LogLevel {
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

impl LogLevel {
    fn as_str(&self) -> &'static str {
        match self {
            Self::Error => "error",
            Self::Warn => "warn",
            Self::Info => "info",
            Self::Debug => "debug",
            Self::Trace => "trace",
        }
    }

    fn values() -> &'static [Self] {
        &[
            Self::Error,
            Self::Warn,
            Self::Info,
            Self::Debug,
            Self::Trace,
        ]
    }
}

impl FromStr for LogLevel {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let variant = match s {
            "error" => Self::Error,
            "warn" => Self::Warn,
            "info" => Self::Info,
            "debug" => Self::Debug,
            "trace" => Self::Trace,
            _ => bail!("Unknown log level: {:?}", s),
        };

        Ok(variant)
    }
}

impl From<LogLevel> for log::LevelFilter {
    fn from(log_level: LogLevel) -> Self {
        match log_level {
            LogLevel::Error => Self::Error,
            LogLevel::Warn => Self::Warn,
            LogLevel::Info => Self::Info,
            LogLevel::Debug => Self::Debug,
            LogLevel::Trace => Self::Trace,
        }
    }
}

fn flag_infer_long(name: &'static str) -> Arg<'static> {
    Arg::new(name).long(name)
}

fn get_clap_command() -> ClapCommand<'static> {
    let syslog = flag_infer_long("syslog")
        .help("Log to the syslog with LEVEL")
        .value_name("LEVEL")
        .possible_values(LogLevel::values().iter().map(LogLevel::as_str));
    let config_with_help_message =
        |help: &'static str| flag_infer_long("config").help(help).value_name("PATH");

    ClapCommand::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .subcommand_required(true)
        .subcommand(
            ClapCommand::new("auth")
                .subcommand_required(true)
                .subcommand(ClapCommand::new("generate-jwt-hs256-key"))
                .subcommand(
                    ClapCommand::new("generate-jwt-hs256-server-token")
                        .args(&[
                            flag_infer_long("server")
                                .help("Generate a key for the specified server")
                                .value_name("SERVER_ADDR")
                                .required(true),
                            flag_infer_long("secret-key")
                                .help("Use specified key to create the token")
                                .value_name("KEY"),
                            config_with_help_message(
                                "Use the key from the scheduler config file at PATH",
                            ),
                        ])
                        .group(
                            ArgGroup::new("key_source_mutual_exclusion")
                                .args(&["config", "secret-key"])
                                .required(true),
                        ),
                )
                .subcommand(
                    ClapCommand::new("generate-shared-token").arg(
                        flag_infer_long("bits")
                            .help("Use the specified number of bits of randomness")
                            .value_name("BITS")
                            .default_value("256")
                            .validator(TokenLength::from_bits),
                    ),
                ),
        )
        .subcommand(ClapCommand::new("scheduler").args(&[
            config_with_help_message("Use the scheduler config file at PATH").required(true),
            syslog.clone(),
        ]))
        .subcommand(ClapCommand::new("server").args(&[
            config_with_help_message("Use the server config file at PATH").required(true),
            syslog,
        ]))
}

fn check_init_syslog(name: &str, log_level: LogLevel) {
    let level = log::LevelFilter::from(log_level);
    drop(syslog::init(Facility::LOG_DAEMON, level, Some(name)));
}

/// Parse commandline `args` into a `Result<Command>` to execute.
pub fn try_parse_from(
    args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
) -> anyhow::Result<Command> {
    let matches = get_clap_command().try_get_matches_from(args)?;

    Ok(match matches.subcommand() {
        Some(("auth", matches)) => Command::Auth(match matches.subcommand() {
            // Size based on https://briansmith.org/rustdoc/ring/hmac/fn.recommended_key_len.html
            Some(("generate-jwt-hs256-key", _)) => AuthSubcommand::Base64 { num_bytes: 256 / 8 },
            Some(("generate-jwt-hs256-server-token", matches)) => {
                let server_addr: SocketAddr = matches.value_of_t("server")?;
                let server_id = ServerId::new(server_addr);

                let secret_key = if matches.is_present("config") {
                    let config_path: PathBuf = matches.value_of_t("config")?;
                    if let Some(config) = config::scheduler::from_path(&config_path)? {
                        match config.server_auth {
                            config::scheduler::ServerAuth::JwtHS256 { secret_key } => secret_key,
                            config::scheduler::ServerAuth::Insecure
                            | config::scheduler::ServerAuth::Token { .. } => {
                                bail!("Scheduler not configured with JWT HS256")
                            }
                        }
                    } else {
                        bail!("Could not load config")
                    }
                } else {
                    matches.value_of_t("secret-key")?
                };

                AuthSubcommand::JwtHS256ServerToken {
                    secret_key,
                    server_id,
                }
            }
            Some(("generate-shared-token", matches)) => {
                let token_bits = TokenLength::from_bits(
                    matches.value_of("bits").expect("clap provides default"),
                )
                .expect("clap uses `from_bits` as a validator");

                AuthSubcommand::Base64 {
                    num_bytes: token_bits.as_bytes(),
                }
            }
            _ => unreachable!("Subcommand is enforced by clap"),
        }),
        Some(("scheduler", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-scheduler", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::scheduler::from_path(&config_path)? {
                Command::Scheduler(config)
            } else {
                bail!("Could not load config")
            }
        }
        Some(("server", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-buildserver", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::server::from_path(&config_path)? {
                Command::Server(config)
            } else {
                bail!("Could not load config")
            }
        }
        _ => unreachable!("Subcommand is enforced by clap"),
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    const EXE: &str = "sccache-dist";

    fn auth_generate_shared_tokens_bits(bit_val: &'static str) -> Vec<&'static str> {
        vec![EXE, "auth", "generate-shared-token", "--bits", bit_val]
    }

    fn auth_generate_jwt_hs256_server_token(subcommand_args: &[&'static str]) -> Vec<&'static str> {
        let mut args = vec![EXE, "auth", "generate-jwt-hs256-server-token"];
        args.extend_from_slice(subcommand_args);
        args
    }

    #[test]
    fn debug_assert() {
        get_clap_command().debug_assert()
    }

    #[test]
    fn missing_required_subcommands_fails() {
        let args_sets = &[vec![EXE], vec![EXE, "auth"]];

        for args in args_sets {
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn invalid_token_bits_fails() {
        let invalid_vals = vec!["not_a_num", "58", "8000", "70"];

        for invalid_val in invalid_vals {
            let args = auth_generate_shared_tokens_bits(invalid_val);
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn auth_generate_server_token_needs_key_source() {
        let server_args = &["--server", "127.0.0.1:4321"];

        let no_key = auth_generate_jwt_hs256_server_token(server_args);
        assert!(try_parse_from(no_key).is_err());

        let mut too_many_keys = auth_generate_jwt_hs256_server_token(server_args);
        too_many_keys.extend_from_slice(&["--secret-key", "secret", "--config", "some/path.toml"]);
        assert!(try_parse_from(too_many_keys).is_err());
    }

    // This is all just to work around `PartialEq` not being on some of the values used in variants
    // for `Command` yet
    fn assert_args_parse_to_auth(args: Vec<&'static str>, ideal_auth: AuthSubcommand) {
        match try_parse_from(&args) {
            Ok(Command::Auth(auth)) => assert_eq!(auth, ideal_auth),
            _ => panic!("Bad parsing for: {:#?}", args),
        }
    }

    #[test]
    fn auth_generate_jwt_hs256_key_good() {
        let args = vec![EXE, "auth", "generate-jwt-hs256-key"];

        assert_args_parse_to_auth(args, AuthSubcommand::Base64 { num_bytes: 256 / 8 });
    }

    #[test]
    fn auth_generate_jwt_hs256_server_token_good() {
        let base = auth_generate_jwt_hs256_server_token(&["--server", "127.0.0.1:4321"]);
        let server_socket: SocketAddr = "127.0.0.1:4321".parse().unwrap();
        let server_id = ServerId::new(server_socket);

        let mut secret_key = base.clone();
        secret_key.extend_from_slice(&["--secret-key", "very secret"]);
        assert_args_parse_to_auth(
            secret_key,
            AuthSubcommand::JwtHS256ServerToken {
                server_id,
                secret_key: "very secret".to_owned(),
            },
        );
    }

    #[test]
    fn auth_generate_shared_token_good() {
        let raw_to_expected_bit_vals = &[
            ("64", 64 / 8),
            ("128", 128 / 8),
            ("136", 136 / 8),
            ("4000", 4_000 / 8),
        ];

        for (raw, expected) in raw_to_expected_bit_vals {
            let args = auth_generate_shared_tokens_bits(raw);
            assert_args_parse_to_auth(
                args,
                AuthSubcommand::Base64 {
                    num_bytes: *expected,
                },
            );
        }
    }
}
<|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 super::*;
use common::{SourceLocationKey, Span};
use graphql_syntax::{parse_executable_with_features, ParserFeatures};

fn test_resolution(source: &str, sub_str: &str, cb: impl Fn(&ResolutionPath<'_>)) {
    let document = parse_executable_with_features(
        source,
        SourceLocationKey::standalone("/test/file"),
        ParserFeatures {
            enable_variable_definitions: true,
        },
    )
    .unwrap();

    let pos = source.find(sub_str).unwrap() as u32;

    // Select the `uri` field
    let position_span = Span {
        start: pos,
        end: pos,
    };

    let resolved = document.resolve((), position_span);

    cb(&resolved);
}

#[test]
fn operation_definition_type_condition() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "Foo", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::OperationDefinitionName(_),
            })
        );
    })
}

#[test]
fn operation_definition_operation() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "query", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Operation(OperationPath {
                inner: (_, OperationKind::Query),
                parent: _,
            })
        );
    })
}

#[test]
fn linked_field_name() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "me", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::LinkedFieldName(_),
            })
        );
    })
}

#[test]
fn linked_field_alias() {
    let source = r#"
            query Foo {
                mario: me {
                    id
                }
            }
        "#;
    test_resolution(source, "mario", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::LinkedFieldAlias(_),
            })
        );
    })
}

#[test]
fn scalar_field_name() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "id", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::ScalarFieldName(_),
            })
        );
    })
}

#[test]
fn scalar_field_alias() {
    let source = r#"
            query Foo {
                me {
                    identity: id
                }
            }
        "#;
    test_resolution(source, "identity", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::ScalarFieldAlias(_),
            })
        );
    })
}

#[test]
fn inline_fragment() {
    let source = r#"
            query Foo {
                me {
                    ... on User {
                        id
                    }
                }
            }
        "#;
    test_resolution(source, "...", |resolved| {
        assert_matches!(resolved, ResolutionPath::InlineFragment(_));
    })
}

#[test]
fn inline_fragment_type_condition() {
    let source = r#"
            query Foo {
                me {
                    ... on User {
                        id
                    }
                }
            }
        "#;
    test_resolution(source, "User", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::TypeConditionType(_),
            })
        );
    })
}
#[test]
fn fragment_definition_name() {
    let source = r#"
            fragment Foo on User {
                id
            }
        "#;
    test_resolution(source, "Foo", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::FragmentDefinitionName(_),
            })
        );
    })
}

#[test]
fn fragment_definition_type() {
    let source = r#"
            fragment Foo on User {
                id
            }
        "#;
    test_resolution(source, "User", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::TypeConditionType(_),
            })
        );
    })
}

#[test]
fn fragment_spread_name() {
    let source = r#"
            fragment Foo on Node {
                ...someFragment
            }
        "#;
    test_resolution(source, "someFragment", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::FragmentSpreadName(_),
            })
        );
    })
}

#[test]
fn directive_name() {
    let source = r#"
            fragment Foo on Node {
                id @required(action: LOG)
            }
        "#;
    test_resolution(source, "required", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::DirectiveName(_),
            })
        );
    })
}

#[test]
fn argument_name() {
    let source = r#"
            fragment Foo on Node {
                id @required(action: LOG)
            }
        "#;
    test_resolution(source, "action", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::ArgumentName(_),
            })
        );
    })
}

#[test]
fn argument_value() {
    let source = r#"
            fragment Foo on Node {
                id @required(action: LOG)
            }
        "#;
    test_resolution(source, "LOG", |resolved| {
        assert_matches!(resolved, ResolutionPath::ConstantEnum(_));
    })
}

#[test]
fn list_literal() {
    let source = r#"
            query Foo {
                checkinSearchQuery(query: {
                    query: "Hello",
                    inputs: [{query: "Goodbye", inputs: []}]
                })
            }
        "#;
    test_resolution(source, "Goodbye", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::ConstantString(ConstantStringPath {
                inner: _,
                parent:
                    ConstantValuePath {
                        inner: _,
                        parent: ConstantValueParent::ConstantArgValue(_),
                    },
            })
        );
    })
}

#[test]
fn fragment_argument_name() {
    let source = r#"
        fragment Foo($localId: ID!) on User {
            id
          }
        "#;
    test_resolution(source, "localId", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::VariableIdentifier(VariableIdentifierPath {
                inner: _,
                parent:
                    VariableIdentifierParent::VariableDefinition(VariableDefinitionPath {
                        inner: _,
                        parent:
                            VariableDefinitionListPath {
                                inner: _,
                                parent: VariableDefinitionListParent::FragmentDefinition(_),
                            },
                    }),
            })
        );
    })
}

#[test]
fn fragment_argument_type() {
    let source = r#"
        fragment Foo($localId: ID!) on User {
            id
          }
        "#;
    test_resolution(source, "ID!", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent:
                    IdentParent::TypeAnnotation(TypeAnnotationPath {
                        inner: _,
                        parent: TypeAnnotationParent::NonNullTypeAnnotation(_),
                    }),
            })
        )
    })
}

#[test]
fn fragment_argument_default_value() {
    let source = r#"
        fragment Foo($localId: ID! = "1") on User {
            id
          }
        "#;
    test_resolution(source, r#""1""#, |resolved| {
        assert_matches!(resolved, ResolutionPath::ConstantString(_));
    })
}
#[test]
fn fragment_argument_directive() {
    let source = r#"
        fragment Foo($localId: ID! = "1") on User {
            id
          }
        "#;
    test_resolution(source, r#""1""#, |resolved| {
        assert_matches!(resolved, ResolutionPath::ConstantString(_));
    })
}
<commit_msg>Format fbsource with rustfmt-2.0.0-rc.2<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 super::*;
use common::{SourceLocationKey, Span};
use graphql_syntax::{parse_executable_with_features, ParserFeatures};

fn test_resolution(source: &str, sub_str: &str, cb: impl Fn(&ResolutionPath<'_>)) {
    let document = parse_executable_with_features(
        source,
        SourceLocationKey::standalone("/test/file"),
        ParserFeatures {
            enable_variable_definitions: true,
        },
    )
    .unwrap();

    let pos = source.find(sub_str).unwrap() as u32;

    // Select the `uri` field
    let position_span = Span {
        start: pos,
        end: pos,
    };

    let resolved = document.resolve((), position_span);

    cb(&resolved);
}

#[test]
fn operation_definition_type_condition() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "Foo", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::OperationDefinitionName(_),
            })
        );
    })
}

#[test]
fn operation_definition_operation() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "query", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Operation(OperationPath {
                inner: (_, OperationKind::Query),
                parent: _,
            })
        );
    })
}

#[test]
fn linked_field_name() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "me", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::LinkedFieldName(_),
            })
        );
    })
}

#[test]
fn linked_field_alias() {
    let source = r#"
            query Foo {
                mario: me {
                    id
                }
            }
        "#;
    test_resolution(source, "mario", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::LinkedFieldAlias(_),
            })
        );
    })
}

#[test]
fn scalar_field_name() {
    let source = r#"
            query Foo {
                me {
                    id
                }
            }
        "#;
    test_resolution(source, "id", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::ScalarFieldName(_),
            })
        );
    })
}

#[test]
fn scalar_field_alias() {
    let source = r#"
            query Foo {
                me {
                    identity: id
                }
            }
        "#;
    test_resolution(source, "identity", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::ScalarFieldAlias(_),
            })
        );
    })
}

#[test]
fn inline_fragment() {
    let source = r#"
            query Foo {
                me {
                    ... on User {
                        id
                    }
                }
            }
        "#;
    test_resolution(source, "...", |resolved| {
        assert_matches!(resolved, ResolutionPath::InlineFragment(_));
    })
}

#[test]
fn inline_fragment_type_condition() {
    let source = r#"
            query Foo {
                me {
                    ... on User {
                        id
                    }
                }
            }
        "#;
    test_resolution(source, "User", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::TypeConditionType(_),
            })
        );
    })
}
#[test]
fn fragment_definition_name() {
    let source = r#"
            fragment Foo on User {
                id
            }
        "#;
    test_resolution(source, "Foo", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::FragmentDefinitionName(_),
            })
        );
    })
}

#[test]
fn fragment_definition_type() {
    let source = r#"
            fragment Foo on User {
                id
            }
        "#;
    test_resolution(source, "User", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::TypeConditionType(_),
            })
        );
    })
}

#[test]
fn fragment_spread_name() {
    let source = r#"
            fragment Foo on Node {
                ...someFragment
            }
        "#;
    test_resolution(source, "someFragment", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::FragmentSpreadName(_),
            })
        );
    })
}

#[test]
fn directive_name() {
    let source = r#"
            fragment Foo on Node {
                id @required(action: LOG)
            }
        "#;
    test_resolution(source, "required", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::DirectiveName(_),
            })
        );
    })
}

#[test]
fn argument_name() {
    let source = r#"
            fragment Foo on Node {
                id @required(action: LOG)
            }
        "#;
    test_resolution(source, "action", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::ArgumentName(_),
            })
        );
    })
}

#[test]
fn argument_value() {
    let source = r#"
            fragment Foo on Node {
                id @required(action: LOG)
            }
        "#;
    test_resolution(source, "LOG", |resolved| {
        assert_matches!(resolved, ResolutionPath::ConstantEnum(_));
    })
}

#[test]
fn list_literal() {
    let source = r#"
            query Foo {
                checkinSearchQuery(query: {
                    query: "Hello",
                    inputs: [{query: "Goodbye", inputs: []}]
                })
            }
        "#;
    test_resolution(source, "Goodbye", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::ConstantString(ConstantStringPath {
                inner: _,
                parent: ConstantValuePath {
                    inner: _,
                    parent: ConstantValueParent::ConstantArgValue(_),
                },
            })
        );
    })
}

#[test]
fn fragment_argument_name() {
    let source = r#"
        fragment Foo($localId: ID!) on User {
            id
          }
        "#;
    test_resolution(source, "localId", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::VariableIdentifier(VariableIdentifierPath {
                inner: _,
                parent: VariableIdentifierParent::VariableDefinition(VariableDefinitionPath {
                    inner: _,
                    parent: VariableDefinitionListPath {
                        inner: _,
                        parent: VariableDefinitionListParent::FragmentDefinition(_),
                    },
                }),
            })
        );
    })
}

#[test]
fn fragment_argument_type() {
    let source = r#"
        fragment Foo($localId: ID!) on User {
            id
          }
        "#;
    test_resolution(source, "ID!", |resolved| {
        assert_matches!(
            resolved,
            ResolutionPath::Ident(IdentPath {
                inner: _,
                parent: IdentParent::TypeAnnotation(TypeAnnotationPath {
                    inner: _,
                    parent: TypeAnnotationParent::NonNullTypeAnnotation(_),
                }),
            })
        )
    })
}

#[test]
fn fragment_argument_default_value() {
    let source = r#"
        fragment Foo($localId: ID! = "1") on User {
            id
          }
        "#;
    test_resolution(source, r#""1""#, |resolved| {
        assert_matches!(resolved, ResolutionPath::ConstantString(_));
    })
}
#[test]
fn fragment_argument_directive() {
    let source = r#"
        fragment Foo($localId: ID! = "1") on User {
            id
          }
        "#;
    test_resolution(source, r#""1""#, |resolved| {
        assert_matches!(resolved, ResolutionPath::ConstantString(_));
    })
}
<|endoftext|>


<commit_before>use crate::utils::{
    is_type_diagnostic_item, match_qpath, paths, return_ty, snippet,
    span_lint_and_then,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{FnKind, Visitor};
use rustc_hir::*;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for private functions that only return `Ok` or `Some`.
    ///
    /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
    ///
    /// **Known problems:** Since this lint changes function type signature, you may need to
    /// adjust some code at callee side.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
    ///     if a && b {
    ///         return Some(50);
    ///     }
    ///     if a {
    ///         Some(0)
    ///     } else {
    ///         Some(10)
    ///     }
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// fn get_cool_number(a: bool, b: bool) -> i32 {
    ///     if a && b {
    ///         return 50;
    ///     }
    ///     if a {
    ///         0
    ///     } else {
    ///         10
    ///     }
    /// }
    /// ```
    pub UNNECESSARY_WRAP,
    complexity,
    "functions that only return `Ok` or `Some`"
}

declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]);

impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap {
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        fn_kind: FnKind<'tcx>,
        fn_decl: &FnDecl<'tcx>,
        body: &Body<'tcx>,
        span: Span,
        hir_id: HirId,
    ) {
        if_chain! {
            if let FnKind::ItemFn(.., visibility, _) = fn_kind;
            if visibility.node.is_pub();
            then {
                return;
            }
        }

        let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) {
            &paths::OPTION_SOME
        } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
            &paths::RESULT_OK
        } else {
            return;
        };

        let mut suggs = Vec::new();
        let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
            if_chain! {
                if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
                if let ExprKind::Path(ref qpath) = func.kind;
                if match_qpath(qpath, path);
                if args.len() == 1;
                then {
                    suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
                    true
                } else {
                    false
                }
            }
        });

        if can_sugg {
            span_lint_and_then(
                cx,
                UNNECESSARY_WRAP,
                span,
                "this function returns unnecessarily wrapping data",
                move |diag| {
                    diag.multipart_suggestion(
                        "factor this out to",
                        suggs.into_iter().chain({
                            let inner_ty = return_ty(cx, hir_id)
                                .walk()
                                .skip(1) // skip `std::option::Option` or `std::result::Result`
                                .take(1) // take the first outermost inner type
                                .filter_map(|inner| match inner.unpack() {
                                    GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()),
                                    _ => None,
                                });
                            inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty))
                        }).collect(),
                        Applicability::MachineApplicable,
                    );
                },
            );
        }
    }
}

// code below is copied from `bind_instead_of_map`

fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool
where
    F: FnMut(&'hir Expr<'hir>) -> bool,
{
    struct RetFinder<F> {
        in_stmt: bool,
        failed: bool,
        cb: F,
    }

    struct WithStmtGuarg<'a, F> {
        val: &'a mut RetFinder<F>,
        prev_in_stmt: bool,
    }

    impl<F> RetFinder<F> {
        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
            WithStmtGuarg {
                val: self,
                prev_in_stmt,
            }
        }
    }

    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
        type Target = RetFinder<F>;

        fn deref(&self) -> &Self::Target {
            self.val
        }
    }

    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            self.val
        }
    }

    impl<F> Drop for WithStmtGuarg<'_, F> {
        fn drop(&mut self) {
            self.val.in_stmt = self.prev_in_stmt;
        }
    }

    impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
        type Map = Map<'hir>;

        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
            intravisit::NestedVisitorMap::None
        }

        fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) {
            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
        }

        fn visit_expr(&mut self, expr: &'hir Expr<'_>) {
            if self.failed {
                return;
            }
            if self.in_stmt {
                match expr.kind {
                    ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
                    _ => intravisit::walk_expr(self, expr),
                }
            } else {
                match expr.kind {
                    ExprKind::Match(cond, arms, _) => {
                        self.inside_stmt(true).visit_expr(cond);
                        for arm in arms {
                            self.visit_expr(arm.body);
                        }
                    },
                    ExprKind::Block(..) => intravisit::walk_expr(self, expr),
                    ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
                    _ => self.failed |= !(self.cb)(expr),
                }
            }
        }
    }

    !contains_try(expr) && {
        let mut ret_finder = RetFinder {
            in_stmt: false,
            failed: false,
            cb: callback,
        };
        ret_finder.visit_expr(expr);
        !ret_finder.failed
    }
}

/// returns `true` if expr contains match expr desugared from try
fn contains_try(expr: &Expr<'_>) -> bool {
    struct TryFinder {
        found: bool,
    }

    impl<'hir> intravisit::Visitor<'hir> for TryFinder {
        type Map = Map<'hir>;

        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
            intravisit::NestedVisitorMap::None
        }

        fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
            if self.found {
                return;
            }
            match expr.kind {
                ExprKind::Match(_, _, MatchSource::TryDesugar) => self.found = true,
                _ => intravisit::walk_expr(self, expr),
            }
        }
    }

    let mut visitor = TryFinder { found: false };
    visitor.visit_expr(expr);
    visitor.found
}
<commit_msg>Run rustfmt<commit_after>use crate::utils::{is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{FnKind, Visitor};
use rustc_hir::*;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;

declare_clippy_lint! {
    /// **What it does:** Checks for private functions that only return `Ok` or `Some`.
    ///
    /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
    ///
    /// **Known problems:** Since this lint changes function type signature, you may need to
    /// adjust some code at callee side.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
    ///     if a && b {
    ///         return Some(50);
    ///     }
    ///     if a {
    ///         Some(0)
    ///     } else {
    ///         Some(10)
    ///     }
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// fn get_cool_number(a: bool, b: bool) -> i32 {
    ///     if a && b {
    ///         return 50;
    ///     }
    ///     if a {
    ///         0
    ///     } else {
    ///         10
    ///     }
    /// }
    /// ```
    pub UNNECESSARY_WRAP,
    complexity,
    "functions that only return `Ok` or `Some`"
}

declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]);

impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap {
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        fn_kind: FnKind<'tcx>,
        fn_decl: &FnDecl<'tcx>,
        body: &Body<'tcx>,
        span: Span,
        hir_id: HirId,
    ) {
        if_chain! {
            if let FnKind::ItemFn(.., visibility, _) = fn_kind;
            if visibility.node.is_pub();
            then {
                return;
            }
        }

        let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) {
            &paths::OPTION_SOME
        } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
            &paths::RESULT_OK
        } else {
            return;
        };

        let mut suggs = Vec::new();
        let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
            if_chain! {
                if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
                if let ExprKind::Path(ref qpath) = func.kind;
                if match_qpath(qpath, path);
                if args.len() == 1;
                then {
                    suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
                    true
                } else {
                    false
                }
            }
        });

        if can_sugg {
            span_lint_and_then(
                cx,
                UNNECESSARY_WRAP,
                span,
                "this function returns unnecessarily wrapping data",
                move |diag| {
                    diag.multipart_suggestion(
                        "factor this out to",
                        suggs
                            .into_iter()
                            .chain({
                                let inner_ty = return_ty(cx, hir_id)
                                    .walk()
                                    .skip(1) // skip `std::option::Option` or `std::result::Result`
                                    .take(1) // take the first outermost inner type
                                    .filter_map(|inner| match inner.unpack() {
                                        GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()),
                                        _ => None,
                                    });
                                inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty))
                            })
                            .collect(),
                        Applicability::MachineApplicable,
                    );
                },
            );
        }
    }
}

// code below is copied from `bind_instead_of_map`

fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool
where
    F: FnMut(&'hir Expr<'hir>) -> bool,
{
    struct RetFinder<F> {
        in_stmt: bool,
        failed: bool,
        cb: F,
    }

    struct WithStmtGuarg<'a, F> {
        val: &'a mut RetFinder<F>,
        prev_in_stmt: bool,
    }

    impl<F> RetFinder<F> {
        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
            WithStmtGuarg {
                val: self,
                prev_in_stmt,
            }
        }
    }

    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
        type Target = RetFinder<F>;

        fn deref(&self) -> &Self::Target {
            self.val
        }
    }

    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            self.val
        }
    }

    impl<F> Drop for WithStmtGuarg<'_, F> {
        fn drop(&mut self) {
            self.val.in_stmt = self.prev_in_stmt;
        }
    }

    impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
        type Map = Map<'hir>;

        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
            intravisit::NestedVisitorMap::None
        }

        fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) {
            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
        }

        fn visit_expr(&mut self, expr: &'hir Expr<'_>) {
            if self.failed {
                return;
            }
            if self.in_stmt {
                match expr.kind {
                    ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
                    _ => intravisit::walk_expr(self, expr),
                }
            } else {
                match expr.kind {
                    ExprKind::Match(cond, arms, _) => {
                        self.inside_stmt(true).visit_expr(cond);
                        for arm in arms {
                            self.visit_expr(arm.body);
                        }
                    },
                    ExprKind::Block(..) => intravisit::walk_expr(self, expr),
                    ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
                    _ => self.failed |= !(self.cb)(expr),
                }
            }
        }
    }

    !contains_try(expr) && {
        let mut ret_finder = RetFinder {
            in_stmt: false,
            failed: false,
            cb: callback,
        };
        ret_finder.visit_expr(expr);
        !ret_finder.failed
    }
}

/// returns `true` if expr contains match expr desugared from try
fn contains_try(expr: &Expr<'_>) -> bool {
    struct TryFinder {
        found: bool,
    }

    impl<'hir> intravisit::Visitor<'hir> for TryFinder {
        type Map = Map<'hir>;

        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
            intravisit::NestedVisitorMap::None
        }

        fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
            if self.found {
                return;
            }
            match expr.kind {
                ExprKind::Match(_, _, MatchSource::TryDesugar) => self.found = true,
                _ => intravisit::walk_expr(self, expr),
            }
        }
    }

    let mut visitor = TryFinder { found: false };
    visitor.visit_expr(expr);
    visitor.found
}
<|endoftext|>


<commit_before>use ra_syntax::{
    algo::visit::{visitor, Visitor},
    AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*, SyntaxNodeRef,
};

use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind};

fn keyword(kw: &str, snippet: &str) -> CompletionItem {
    CompletionItem::new(CompletionKind::Keyword, kw)
        .kind(CompletionItemKind::Keyword)
        .snippet(snippet)
        .build()
}

pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_trivial_path {
        return;
    }

    // complete keyword "crate" in use stmt
    if let (Some(use_item), None) = (&ctx.use_item_syntax, &ctx.path_prefix) {
        if use_item.use_tree().is_none() {
            CompletionItem::new(CompletionKind::Keyword, "crate")
                .kind(CompletionItemKind::Keyword)
                .add_to(acc);
        }
    }

    let fn_def = match ctx.function_syntax {
        Some(it) => it,
        None => return,
    };
    acc.add(keyword("if", "if $0 {}"));
    acc.add(keyword("match", "match $0 {}"));
    acc.add(keyword("while", "while $0 {}"));
    acc.add(keyword("loop", "loop {$0}"));

    if ctx.after_if {
        acc.add(keyword("else", "else {$0}"));
        acc.add(keyword("else if", "else if $0 {}"));
    }
    if is_in_loop_body(ctx.leaf) {
        if ctx.can_be_stmt {
            acc.add(keyword("continue", "continue;"));
            acc.add(keyword("break", "break;"));
        } else {
            acc.add(keyword("continue", "continue"));
            acc.add(keyword("break", "break"));
        }
    }
    acc.add_all(complete_return(fn_def, ctx.can_be_stmt));
}

fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
    for node in leaf.ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if leaf.range().is_subrange(&body.syntax().range()) {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> {
    let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

#[cfg(test)]
mod tests {
    use crate::completion::{CompletionKind, check_completion};
    fn check_keyword_completion(code: &str, expected_completions: &str) {
        check_completion(code, expected_completions, CompletionKind::Keyword);
    }

    #[test]
    fn completes_various_keywords_in_function() {
        check_keyword_completion(
            r"
            fn quux() {
                <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn completes_else_after_if() {
        check_keyword_completion(
            r"
            fn quux() {
                if true {
                    ()
                } <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            else "else {$0}"
            else if "else if $0 {}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn test_completion_return_value() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn dont_add_semi_after_return_if_not_a_statement() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                match () {
                    () => <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0"
            "#,
        );
    }

    #[test]
    fn last_return_in_block_has_semi() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
                let x = 92;
                x
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn completes_break_and_continue_in_loops() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { <|> }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue;"
            break "break;"
            return "return $0;"
            "#,
        );
        // No completion: lambda isolates control flow
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { || { <|> } }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn no_semi_after_break_continue_in_expr() {
        check_keyword_completion(
            r"
            fn f() {
                loop {
                    match () {
                        () => br<|>
                    }
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue"
            break "break"
            return "return"
            "#,
        )
    }

    fn completes_crate_in_use_stmt() {
        check_keyword_completion(
            r"
            use <|>
            ",
            r#"
            crate
            "#,
        );
        // No completion: lambda isolates control flow
        check_keyword_completion(
            r"
            use a<|>
            ",
            r#"
            "#,
        );
    }
}
<commit_msg>complete "use crate" for "use cr" too<commit_after>use ra_syntax::{
    algo::visit::{visitor, Visitor},
    AstNode,
    ast::{self, LoopBodyOwner},
    SyntaxKind::*, SyntaxNodeRef,
};

use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind};

fn keyword(kw: &str, snippet: &str) -> CompletionItem {
    CompletionItem::new(CompletionKind::Keyword, kw)
        .kind(CompletionItemKind::Keyword)
        .snippet(snippet)
        .build()
}

pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_trivial_path {
        return;
    }

    // complete keyword "crate" in use stmt
    if let (Some(use_item), None) = (&ctx.use_item_syntax, &ctx.path_prefix) {
        let mut complete_crate = false;
        let use_tree = use_item.use_tree();
        if let Some(use_tree) = use_tree {
            let tree_str = use_tree.syntax().text().to_string();
            if tree_str != "crate" && "crate".starts_with(&tree_str) {
                complete_crate = true;
            }
        } else {
            complete_crate = true;
        }
        if complete_crate {
            CompletionItem::new(CompletionKind::Keyword, "crate")
                .kind(CompletionItemKind::Keyword)
                .lookup_by("crate")
                .add_to(acc);
        }
    }

    let fn_def = match ctx.function_syntax {
        Some(it) => it,
        None => return,
    };
    acc.add(keyword("if", "if $0 {}"));
    acc.add(keyword("match", "match $0 {}"));
    acc.add(keyword("while", "while $0 {}"));
    acc.add(keyword("loop", "loop {$0}"));

    if ctx.after_if {
        acc.add(keyword("else", "else {$0}"));
        acc.add(keyword("else if", "else if $0 {}"));
    }
    if is_in_loop_body(ctx.leaf) {
        if ctx.can_be_stmt {
            acc.add(keyword("continue", "continue;"));
            acc.add(keyword("break", "break;"));
        } else {
            acc.add(keyword("continue", "continue"));
            acc.add(keyword("break", "break"));
        }
    }
    acc.add_all(complete_return(fn_def, ctx.can_be_stmt));
}

fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
    for node in leaf.ancestors() {
        if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
            break;
        }
        let loop_body = visitor()
            .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
            .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
            .accept(node);
        if let Some(Some(body)) = loop_body {
            if leaf.range().is_subrange(&body.syntax().range()) {
                return true;
            }
        }
    }
    false
}

fn complete_return(fn_def: ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> {
    let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword("return", snip))
}

#[cfg(test)]
mod tests {
    use crate::completion::{CompletionKind, check_completion};
    fn check_keyword_completion(code: &str, expected_completions: &str) {
        check_completion(code, expected_completions, CompletionKind::Keyword);
    }

    #[test]
    fn completes_various_keywords_in_function() {
        check_keyword_completion(
            r"
            fn quux() {
                <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn completes_else_after_if() {
        check_keyword_completion(
            r"
            fn quux() {
                if true {
                    ()
                } <|>
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            else "else {$0}"
            else if "else if $0 {}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn test_completion_return_value() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() {
                <|>
                92
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return;"
            "#,
        );
    }

    #[test]
    fn dont_add_semi_after_return_if_not_a_statement() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                match () {
                    () => <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0"
            "#,
        );
    }

    #[test]
    fn last_return_in_block_has_semi() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                if condition {
                    <|>
                }
                let x = 92;
                x
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn completes_break_and_continue_in_loops() {
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { <|> }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue;"
            break "break;"
            return "return $0;"
            "#,
        );
        // No completion: lambda isolates control flow
        check_keyword_completion(
            r"
            fn quux() -> i32 {
                loop { || { <|> } }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            return "return $0;"
            "#,
        );
    }

    #[test]
    fn no_semi_after_break_continue_in_expr() {
        check_keyword_completion(
            r"
            fn f() {
                loop {
                    match () {
                        () => br<|>
                    }
                }
            }
            ",
            r#"
            if "if $0 {}"
            match "match $0 {}"
            while "while $0 {}"
            loop "loop {$0}"
            continue "continue"
            break "break"
            return "return"
            "#,
        )
    }

    #[test]
    fn completes_crate_in_use_stmt() {
        check_keyword_completion(
            r"
            use <|>
            ",
            r#"
            crate "crate"
            "#,
        );
        // No completion: lambda isolates control flow
        check_keyword_completion(
            r"
            use a<|>
            ",
            r#"
            "#,
        );
    }
}
<|endoftext|>


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

use std::path::{Path, PathBuf};
use std::process::Command;

use devicemapper::DM;
use devicemapper::{ThinDev, ThinDevId, ThinStatus, ThinPoolDev};

use super::super::engine::{Filesystem, HasName, HasUuid};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::FilesystemUuid;

use super::serde_structs::{FilesystemSave, Recordable};

#[derive(Debug)]
pub struct StratFilesystem {
    fs_id: FilesystemUuid,
    name: String,
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    Failed,
}

impl StratFilesystem {
    /// Create a StratFilesystem on top of the given ThinDev.
    pub fn initialize(fs_id: FilesystemUuid,
                      name: &str,
                      thin_dev: ThinDev)
                      -> EngineResult<StratFilesystem> {
        let fs = StratFilesystem::setup(fs_id, name, thin_dev);

        try!(create_fs(try!(fs.devnode()).as_path()));
        Ok(fs)
    }

    /// Build a StratFilesystem that includes the ThinDev and related info.
    pub fn setup(fs_id: FilesystemUuid, name: &str, thin_dev: ThinDev) -> StratFilesystem {
        StratFilesystem {
            fs_id: fs_id,
            name: name.to_owned(),
            thin_dev: thin_dev,
        }
    }

    pub fn check(&self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match try!(self.thin_dev.status(dm)) {
            ThinStatus::Good((_mapped, _highest)) => {
                // TODO: check if filesystem is getting full and might need to
                // be extended (hint: use statfs(2))
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }

    /// The thin id for the thin device that backs this filesystem.
    pub fn thin_id(&self) -> ThinDevId {
        self.thin_dev.id()
    }

    /// Tear down the filesystem.
    pub fn teardown(self, dm: &DM) -> EngineResult<()> {
        Ok(try!(self.thin_dev.teardown(dm)))
    }

    /// Set the name of this filesystem to name.
    pub fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    /// Destroy the filesystem.
    pub fn destroy(self, dm: &DM, thin_pool: &ThinPoolDev) -> EngineResult<()> {
        Ok(try!(self.thin_dev.destroy(dm, thin_pool)))
    }
}

impl HasName for StratFilesystem {
    fn name(&self) -> &str {
        &self.name
    }
}

impl HasUuid for StratFilesystem {
    fn uuid(&self) -> &FilesystemUuid {
        &self.fs_id
    }
}

impl Filesystem for StratFilesystem {
    fn devnode(&self) -> EngineResult<PathBuf> {
        Ok(try!(self.thin_dev.devnode()))
    }
}

impl Recordable<FilesystemSave> for StratFilesystem {
    fn record(&self) -> EngineResult<FilesystemSave> {
        Ok(FilesystemSave {
               name: self.name.clone(),
               uuid: self.fs_id,
               thin_id: self.thin_dev.id(),
               size: self.thin_dev.size(),
           })
    }
}

/// Create a filesystem on devnode.
pub fn create_fs(devnode: &Path) -> EngineResult<()> {
    if try!(Command::new("mkfs.xfs")
                .arg("-f")
                .arg(&devnode)
                .status())
               .success() {
        Ok(())
    } else {
        let err_msg = format!("Failed to create new filesystem at {:?}", devnode);
        Err(EngineError::Engine(ErrorEnum::Error, err_msg))
    }
}
<commit_msg>Add function that uses df to  determine the mountpoint of a device<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::path::{Path, PathBuf};
use std::process::Command;

use devicemapper::{Bytes, DM, DataBlocks, Sectors, ThinDev, ThinDevId, ThinStatus, ThinPoolDev};

use nix::sys::statvfs::*;

use super::super::engine::{Filesystem, HasName, HasUuid};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::FilesystemUuid;

use super::serde_structs::{FilesystemSave, Recordable};

#[derive(Debug)]
pub struct StratFilesystem {
    fs_id: FilesystemUuid,
    name: String,
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    Failed,
}

impl StratFilesystem {
    /// Create a StratFilesystem on top of the given ThinDev.
    pub fn initialize(fs_id: FilesystemUuid,
                      name: &str,
                      thin_dev: ThinDev)
                      -> EngineResult<StratFilesystem> {
        let fs = StratFilesystem::setup(fs_id, name, thin_dev);

        try!(create_fs(try!(fs.devnode()).as_path()));
        Ok(fs)
    }

    /// Build a StratFilesystem that includes the ThinDev and related info.
    pub fn setup(fs_id: FilesystemUuid, name: &str, thin_dev: ThinDev) -> StratFilesystem {
        StratFilesystem {
            fs_id: fs_id,
            name: name.to_owned(),
            thin_dev: thin_dev,
        }
    }

    pub fn check(&self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match try!(self.thin_dev.status(dm)) {
            ThinStatus::Good((_mapped, _highest)) => {
                // TODO: check if filesystem is getting full and might need to
                // be extended (hint: use statfs(2))
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }

    /// The thin id for the thin device that backs this filesystem.
    pub fn thin_id(&self) -> ThinDevId {
        self.thin_dev.id()
    }

    /// Get the mount_point for this filesystem
    /// TODO Replace this code with something less brittle
    pub fn get_mount_point(&self) -> EngineResult<PathBuf> {
        let output = try!(Command::new("df")
                              .arg("--output=target")
                              .arg(&try!(self.devnode()))
                              .output());
        if output.status.success() {
            let output_str = String::from_utf8_lossy(&output.stdout);
            match output_str.lines().last() {
                Some(mount_point) => return Ok(PathBuf::from(mount_point)),
                _ => {}
            }
        }
        let err_msg = format!("Failed to get fielsystem mountpoint at {:?}",
                              self.devnode());
        Err(EngineError::Engine(ErrorEnum::Error, err_msg))
    }

    /// Tear down the filesystem.
    pub fn teardown(self, dm: &DM) -> EngineResult<()> {
        Ok(try!(self.thin_dev.teardown(dm)))
    }

    /// Set the name of this filesystem to name.
    pub fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    /// Destroy the filesystem.
    pub fn destroy(self, dm: &DM, thin_pool: &ThinPoolDev) -> EngineResult<()> {
        Ok(try!(self.thin_dev.destroy(dm, thin_pool)))
    }
}

impl HasName for StratFilesystem {
    fn name(&self) -> &str {
        &self.name
    }
}

impl HasUuid for StratFilesystem {
    fn uuid(&self) -> &FilesystemUuid {
        &self.fs_id
    }
}

impl Filesystem for StratFilesystem {
    fn devnode(&self) -> EngineResult<PathBuf> {
        Ok(try!(self.thin_dev.devnode()))
    }
}

impl Recordable<FilesystemSave> for StratFilesystem {
    fn record(&self) -> EngineResult<FilesystemSave> {
        Ok(FilesystemSave {
               name: self.name.clone(),
               uuid: self.fs_id,
               thin_id: self.thin_dev.id(),
               size: self.thin_dev.size(),
           })
    }
}

/// Create a filesystem on devnode.
pub fn create_fs(devnode: &Path) -> EngineResult<()> {
    if try!(Command::new("mkfs.xfs")
                .arg("-f")
                .arg(&devnode)
                .status())
               .success() {
        Ok(())
    } else {
        let err_msg = format!("Failed to create new filesystem at {:?}", devnode);
        Err(EngineError::Engine(ErrorEnum::Error, err_msg))
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Issue #7526: lowercase static constants in patterns look like bindings

// This is similar to compile-fail/match-static-const-lc, except it
// shows the expected usual workaround (choosing a different name for
// the static definition) and also demonstrates that one can work
// around this problem locally by reanming the constant in the `use`
// form to an uppercase identifier that placates the lint.

#[deny(non_uppercase_pattern_statics)];

pub static A : int = 97;

fn f() {
    let r = match (0,0) {
        (0, A) => 0,
        (x, y) => 1 + x + y,
    };
    assert!(r == 1);
}

mod m {
    pub static aha : int = 7;
}

fn g() {
    use AHA = m::aha;
    let r = match (0,0) {
        (0, AHA) => 0,
        (x, y)   => 1 + x + y,
    };
    assert!(r == 1);
}

fn h() {
    let r = match (0,0) {
        (0, m::aha) => 0,
        (x, y)      => 1 + x + y,
    };
    assert!(r == 1);
}

fn main () {
    f();
    g();
    h();
}
<commit_msg>Fix typo in comment.<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Issue #7526: lowercase static constants in patterns look like bindings

// This is similar to compile-fail/match-static-const-lc, except it
// shows the expected usual workaround (choosing a different name for
// the static definition) and also demonstrates that one can work
// around this problem locally by renaming the constant in the `use`
// form to an uppercase identifier that placates the lint.

#[deny(non_uppercase_pattern_statics)];

pub static A : int = 97;

fn f() {
    let r = match (0,0) {
        (0, A) => 0,
        (x, y) => 1 + x + y,
    };
    assert!(r == 1);
}

mod m {
    pub static aha : int = 7;
}

fn g() {
    use AHA = m::aha;
    let r = match (0,0) {
        (0, AHA) => 0,
        (x, y)   => 1 + x + y,
    };
    assert!(r == 1);
}

fn h() {
    let r = match (0,0) {
        (0, m::aha) => 0,
        (x, y)      => 1 + x + y,
    };
    assert!(r == 1);
}

fn main () {
    f();
    g();
    h();
}
<|endoftext|>


<commit_before>use parser::Node;
use std::str;

struct Generator {
    buf: String,
}

impl Generator {
    fn new() -> Generator {
        Generator { buf: String::new() }
    }
    fn write(&mut self, s: &str) {
        self.buf.push_str(s);
    }
    fn result(self) -> String {
        self.buf
    }
}

pub fn generate(ctx_name: &str, tokens: &Vec<Node>) -> String {
    let mut gen = Generator::new();
    gen.write("impl askama::Template for ");
    gen.write(ctx_name);
    gen.write(" {\n");
    gen.write("    fn render(&self) -> String {\n");
    gen.write("        let mut buf = String::new();\n");
    for n in tokens {
        gen.write("        buf.push_str(");
        gen.write(&match n {
            &Node::Lit(val) => format!("{:#?}", str::from_utf8(val).unwrap()),
            &Node::Expr(val) => format!("&self.{}", str::from_utf8(val).unwrap()),
        });
        gen.write(");\n");
    }
    gen.write("        buf");
    gen.write("    }\n");
    gen.write("}\n\n");
    gen.result()
}
<commit_msg>Make code generator impl more extensible<commit_after>use parser::Node;
use std::str;

struct Generator {
    buf: String,
}

impl Generator {

    fn new() -> Generator {
        Generator { buf: String::new() }
    }

    fn init(&mut self, name: &str) {
        self.write("impl askama::Template for ");
        self.write(name);
        self.write(" {\n");
        self.write("    fn render(&self) -> String {\n");
        self.write("        let mut buf = String::new();\n");
    }

    fn write(&mut self, s: &str) {
        self.buf.push_str(s);
    }

    fn visit_lit(&mut self, s: &[u8]) {
        self.write("        buf.push_str(");
        self.write(&format!("{:#?}", str::from_utf8(s).unwrap()));
        self.write(");\n");
    }

    fn visit_expr(&mut self, s: &[u8]) {
        self.write("        buf.push_str(");
        self.write(&format!("&self.{}", str::from_utf8(s).unwrap()));
        self.write(");\n");
    }

    fn handle(&mut self, tokens: &Vec<Node>) {
        for n in tokens {
            match n {
                &Node::Lit(val) => { self.visit_lit(val); },
                &Node::Expr(val) => { self.visit_expr(val); },
            }
        }
    }

    fn finalize(&mut self) {
        self.write("        buf");
        self.write("    }\n");
        self.write("}\n\n");
    }

    fn result(self) -> String {
        self.buf
    }

}

pub fn generate(ctx_name: &str, tokens: &Vec<Node>) -> String {
    let mut gen = Generator::new();
    gen.init(ctx_name);
    gen.handle(tokens);
    gen.finalize();
    gen.result()
}
<|endoftext|>


<commit_before>use EnumMap;
use Internal;

use core::iter::Extend;
use core::hash::{Hash, Hasher};
use core::ops::{Index, IndexMut};

impl<K: Internal<V>, V> Extend<(K, V)> for EnumMap<K, V> {
    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
        for (key, value) in iter {
            self[key] = value;
        }
    }
}

impl<'a, K, V> Extend<(&'a K, &'a V)> for EnumMap<K, V>
where
    K: Internal<V> + Copy,
    V: Copy,
{
    fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
        self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
    }
}

impl<K: Internal<V>, V> Index<K> for EnumMap<K, V> {
    type Output = V;

    fn index(&self, key: K) -> &V {
        &self.as_slice()[key.to_usize()]
    }
}

impl<K: Internal<V>, V> IndexMut<K> for EnumMap<K, V> {
    fn index_mut(&mut self, key: K) -> &mut V {
        &mut self.as_mut_slice()[key.to_usize()]
    }
}

// Implementations provided by derive attribute are too specific, and put requirements on K.
// This is caused by rust-lang/rust#26925.
impl<K: Internal<V>, V> Clone for EnumMap<K, V>
where
    K::Array: Clone,
{
    fn clone(&self) -> Self {
        EnumMap {
            array: self.array.clone(),
        }
    }
}

impl<K: Internal<V>, V> Copy for EnumMap<K, V>
where
    K::Array: Copy,
{
}

impl<K: Internal<V>, V: PartialEq> PartialEq for EnumMap<K, V> {
    fn eq(&self, other: &Self) -> bool {
        self.as_slice() == other.as_slice()
    }
}

impl<K: Internal<V>, V: Eq> Eq for EnumMap<K, V> {}

impl<K: Internal<V>, V: Hash> Hash for EnumMap<K, V> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_slice().hash(state);
    }
}

impl<K: Internal<V>, V: Default> Default for EnumMap<K, V> {
    fn default() -> Self {
        enum_map! { _ => V::default() }
    }
}
<commit_msg>Reformat code<commit_after>use EnumMap;
use Internal;

use core::iter::Extend;
use core::hash::{Hash, Hasher};
use core::ops::{Index, IndexMut};

impl<K: Internal<V>, V> Extend<(K, V)> for EnumMap<K, V> {
    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
        for (key, value) in iter {
            self[key] = value;
        }
    }
}

impl<'a, K, V> Extend<(&'a K, &'a V)> for EnumMap<K, V>
where
    K: Internal<V> + Copy,
    V: Copy,
{
    fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
        self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
    }
}

impl<K: Internal<V>, V> Index<K> for EnumMap<K, V> {
    type Output = V;

    fn index(&self, key: K) -> &V {
        &self.as_slice()[key.to_usize()]
    }
}

impl<K: Internal<V>, V> IndexMut<K> for EnumMap<K, V> {
    fn index_mut(&mut self, key: K) -> &mut V {
        &mut self.as_mut_slice()[key.to_usize()]
    }
}

// Implementations provided by derive attribute are too specific, and put requirements on K.
// This is caused by rust-lang/rust#26925.
impl<K: Internal<V>, V> Clone for EnumMap<K, V>
where
    K::Array: Clone,
{
    fn clone(&self) -> Self {
        EnumMap {
            array: self.array.clone(),
        }
    }
}

impl<K: Internal<V>, V> Copy for EnumMap<K, V>
where
    K::Array: Copy,
{
}

impl<K: Internal<V>, V: PartialEq> PartialEq for EnumMap<K, V> {
    fn eq(&self, other: &Self) -> bool {
        self.as_slice() == other.as_slice()
    }
}

impl<K: Internal<V>, V: Eq> Eq for EnumMap<K, V> {}

impl<K: Internal<V>, V: Hash> Hash for EnumMap<K, V> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_slice().hash(state);
    }
}

impl<K: Internal<V>, V: Default> Default for EnumMap<K, V> {
    fn default() -> Self {
        enum_map! { _ => V::default() }
    }
}
<|endoftext|>